Censure acidome

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript">
        // timing start
        _pageLoadingStartAt = +new Date;

        // offline detection
        IS_OFFLINE = (document.location.protocol == 'file:');

        // iframe --
        IS_IFRAME = (window.top !== window.self);
    </script>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Geodesic dome calculator - Acidome.ru</title>

    <!-- FB meta -->
    <!--		<meta property="og:image" content="http://acidome.ru/lab/calc/i/GeodesicTessellation.png" />-->
    <meta property="og:image"
          content="http://dreamscope-prod-v1.s3.amazonaws.com/images/d57c53fa-37f9-4a6b-b5d7-d17a6d355415.jpeg"/>

    <style type="text/css" rel="stylesheet">/* page */
    body, html {
        min-height: 101vh;
        margin: 0;
        padding: 0;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }

    /* offline */
    .offline .welcome .download {
        display: none;
    }

    /* iframe */
    .iframe .head,
    .iframe .welcome,
    .iframe .geodesic form,
    .iframe .budget-list {
        display: none
    }

    .iframe .copyleft {
        position: absolute;
        bottom: 8px;
        right: 8px;
    }

    /* shortcuts */
    .absolute {
        position: absolute;
    }

    .transparent {
        background-color: white;
        background-color: rgba(255, 255, 255, 0.9);
    }

    .right {
        right: 0;
    }

    .left {
        left: 0;
    }

    .bottom {
        bottom: 0;
    }

    .margin {
        margin: 1em;
    }

    .big-font {
        font-size: 141%;
    }

    .small-font {
        font-size: 70%;
    }

    .red {
        color: red
    }

    .gray {
        color: gray
    }

    .bold {
        font-weight: bold
    }

    .pseudo {
        border-bottom: 1px black dashed;
        text-decoration: none;
        color: green;
    }

    /* buttons */
    .button {
        padding: 6px 10px 6px 10px;
        border: 1px solid gray;
        border-radius: 10px;
        background-color: #ddd;
        cursor: pointer;
    }

    .button.round {
        height: 26px;
        border-radius: 50%;
        line-height: 26px;
        text-align: center;
    }

    .button.active {
        background-color: white;
    }

    .button:hover {
        background-color: #eee;
    }

    /*.button.active { color: white; }*/

    /* common calc place */
    .geodesic {
        position: relative;
        font-size: 12px;
    }

    /* preview place */
    .geodesic canvas.preview {
        margin: 0;
        border: 0;
    }

    /* head */
    .geodesic .head {
        position: absolute;
        top: 0;
        width: 100%;
        height: 50px;
        padding: 0;
        margin: 0;
        font-size: 13px;
        text-align: right;
    }

    .geodesic .head .title {
        float: left;
        font-size: 1.3em;
        padding: 5px;
    }

    /*.geodesic .head .revision { font-size: 38%; }*/
    /*.geodesic .head .version { position: absolute; top: 0; left: 38%; font-size: 12px; line-height: 20px; text-decoration: none; }*/

    .geodesic .welcome {
        float: right;
        padding: 5px;
        font-family: monospace
    }

    .geodesic .welcome .sample-to-use {
        color: red;
    }

    .geodesic .welcome .facebook-group {
        color: green;
    }

    .geodesic .welcome > * {
        margin-bottom: .6em;
    }

    .geodesic h1 {
        display: inline;
        font-size: 20px;
        margin: 0
    }

    .geodesic h2 {
        font-size: 18px;
        border-bottom: 1px solid gray;
        margin-top: 1em
    }

    .geodesic h3 {
        font-size: 16px;
    }

    .geodesic h4 {
        font-size: 14px;
    }

    .geodesic .lang-select {
        font-size: .8em
    }

    /* info */
    /*.geodesic .info { float: right }*/

    /* form */
    .geodesic .options {
        position: absolute;
        top: 50px;
        width: 300px;
        padding: 5px;
    }

    .geodesic .options h3 {
        display: inline-block;
        border-bottom: 1px solid gray;
        font-size: 15px;
        color: black;
    }

    .geodesic .options dl {
        min-height: 24px;
        line-height: 24px;
        overflow-x: auto;
        width: 100%
    }

    .geodesic .options dt {
        width: 133px;
        float: left;
        text-align: right;
        margin-right: 4px;
        overflow: visible;
    }

    .geodesic .options dd {
        margin-left: 133px;
        overflow: hidden
    }

    .geodesic .options .sub dt {
        width: 157px;
    }

    .geodesic .options .sub dd {
        margin-left: 157px;
    }

    .geodesic .options input {
        max-width: 50px;
    }

    .geodesic .progress {
        display: none;
        position: absolute;
        top: 0;
        left: 0;
        width: 280px;
        text-align: right;
        color: #bbb;
    }

    .geodesic .options input,
    .geodesic .options select {
        font-weight: bolder
    }

    .geodesic .mode-list {
        position: absolute;
        top: 0;
        left: 100%;
        width: 34em;
        margin-top: 10px;
        padding: 0;
    }

    .geodesic .mode:first-child {
        margin-left: -8px;
    }

    .geodesic .mode {
        list-style-type: none;
        display: inline-block;
        margin-right: -8px;
    }

    .geodesic .mode.active {
        border-color: black;
    }

    .geodesic .stat {
        position: absolute;
        top: 0;
        left: 100%;
        margin-top: 90px;
    }

    .geodesic .stat .toggle {
        display: inline-block;
        padding: 6px 10px 6px 10px;
        font-family: monospace;
        border: none;
        cursor: pointer;
    }

    .geodesic .stat .report {
        display: none;
    }

    .geodesic .stat.active .report {
        display: block;
        line-height: 20px;
        font-size: 1.2em;
    }

    .geodesic .stat.active {
        padding: 6px 10px 6px 10px;
    }

    .geodesic .stat.active .toggle {
        padding: 0;
        border-color: white;
    }

    .geodesic .stat.active .content {
        display: block;
    }

    /* plot */
    .geodesic .budget-list {
        margin: 1em
    }

    .geodesic .budget-list ul {
        list-style-type: none;
        padding: 0;
        margin-top: 20px;
        font-size: 0
    }

    .geodesic .budget-list li {
        margin-top: 20px;
        font-size: 12px
    }

    .geodesic .product {
        display: inline-block;
        padding: 0 0 16px 0;
        vertical-align: top;
    }

    .geodesic .product canvas {
        display: inline;
        padding-top: 10px;
    }

    .geodesic .product-index {
        display: inline-block;
        width: 2em;
        line-height: 2em;
        margin-right: 1em;
        border-radius: 50%;
        text-align: center;
        text-shadow: 0 0 .5em white, 0 0 .5em white;
        box-shadow: inset 0 0 2px white;
        font-size: 1.8em;
    }

    .geodesic .product-index + span {
        font-size: 1.6em;
    }

    /* ask */
    .donation {
        position: relative;
        min-height: 24px;
        padding-top: 5px;
    }

    .donation__popup {
        display: none;
        position: absolute;
        right: 0;
        margin-top: -24px;
        border: 1px gray solid;
        padding: 1em;
        min-width: 440px;
        background-color: white;
        z-index: 1;
    }

    .donation:hover + .donation__popup, .donation__popup:hover {
        display: block
    }

    .donation__popup > .yandex {
        margin-bottom: 1em;
        width: 100%;
    }

    .donation__popup > .paypal {
        float: right;
        display: inline-block;
    }

    .donation__popup > .visa {
        float: left;
        display: inline-block;
        padding-left: 40px;
        height: 32px;
        line-height: 32px;
        background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAKQWlDQ1BJQ0MgUHJvZmlsZQAAeAGdlndUU9kWh8+9N73QEiIgJfQaegkg0jtIFQRRiUmAUAKGhCZ2RAVGFBEpVmRUwAFHhyJjRRQLg4Ji1wnyEFDGwVFEReXdjGsJ7601896a/cdZ39nnt9fZZ+9917oAUPyCBMJ0WAGANKFYFO7rwVwSE8vE9wIYEAEOWAHA4WZmBEf4RALU/L09mZmoSMaz9u4ugGS72yy/UCZz1v9/kSI3QyQGAApF1TY8fiYX5QKUU7PFGTL/BMr0lSkyhjEyFqEJoqwi48SvbPan5iu7yZiXJuShGlnOGbw0noy7UN6aJeGjjAShXJgl4GejfAdlvVRJmgDl9yjT0/icTAAwFJlfzOcmoWyJMkUUGe6J8gIACJTEObxyDov5OWieAHimZ+SKBIlJYqYR15hp5ejIZvrxs1P5YjErlMNN4Yh4TM/0tAyOMBeAr2+WRQElWW2ZaJHtrRzt7VnW5mj5v9nfHn5T/T3IevtV8Sbsz55BjJ5Z32zsrC+9FgD2JFqbHbO+lVUAtG0GQOXhrE/vIADyBQC03pzzHoZsXpLE4gwnC4vs7GxzAZ9rLivoN/ufgm/Kv4Y595nL7vtWO6YXP4EjSRUzZUXlpqemS0TMzAwOl89k/fcQ/+PAOWnNycMsnJ/AF/GF6FVR6JQJhIlou4U8gViQLmQKhH/V4X8YNicHGX6daxRodV8AfYU5ULhJB8hvPQBDIwMkbj96An3rWxAxCsi+vGitka9zjzJ6/uf6Hwtcim7hTEEiU+b2DI9kciWiLBmj34RswQISkAd0oAo0gS4wAixgDRyAM3AD3iAAhIBIEAOWAy5IAmlABLJBPtgACkEx2AF2g2pwANSBetAEToI2cAZcBFfADXALDIBHQAqGwUswAd6BaQiC8BAVokGqkBakD5lC1hAbWgh5Q0FQOBQDxUOJkBCSQPnQJqgYKoOqoUNQPfQjdBq6CF2D+qAH0CA0Bv0BfYQRmALTYQ3YALaA2bA7HAhHwsvgRHgVnAcXwNvhSrgWPg63whfhG/AALIVfwpMIQMgIA9FGWAgb8URCkFgkAREha5EipAKpRZqQDqQbuY1IkXHkAwaHoWGYGBbGGeOHWYzhYlZh1mJKMNWYY5hWTBfmNmYQM4H5gqVi1bGmWCesP3YJNhGbjS3EVmCPYFuwl7ED2GHsOxwOx8AZ4hxwfrgYXDJuNa4Etw/XjLuA68MN4SbxeLwq3hTvgg/Bc/BifCG+Cn8cfx7fjx/GvyeQCVoEa4IPIZYgJGwkVBAaCOcI/YQRwjRRgahPdCKGEHnEXGIpsY7YQbxJHCZOkxRJhiQXUiQpmbSBVElqIl0mPSa9IZPJOmRHchhZQF5PriSfIF8lD5I/UJQoJhRPShxFQtlOOUq5QHlAeUOlUg2obtRYqpi6nVpPvUR9Sn0vR5Mzl/OX48mtk6uRa5Xrl3slT5TXl3eXXy6fJ18hf0r+pvy4AlHBQMFTgaOwVqFG4bTCPYVJRZqilWKIYppiiWKD4jXFUSW8koGStxJPqUDpsNIlpSEaQtOledK4tE20Otpl2jAdRzek+9OT6cX0H+i99AllJWVb5SjlHOUa5bPKUgbCMGD4M1IZpYyTjLuMj/M05rnP48/bNq9pXv+8KZX5Km4qfJUilWaVAZWPqkxVb9UU1Z2qbapP1DBqJmphatlq+9Uuq43Pp893ns+dXzT/5PyH6rC6iXq4+mr1w+o96pMamhq+GhkaVRqXNMY1GZpumsma5ZrnNMe0aFoLtQRa5VrntV4wlZnuzFRmJbOLOaGtru2nLdE+pN2rPa1jqLNYZ6NOs84TXZIuWzdBt1y3U3dCT0svWC9fr1HvoT5Rn62fpL9Hv1t/ysDQINpgi0GbwaihiqG/YZ5ho+FjI6qRq9Eqo1qjO8Y4Y7ZxivE+41smsImdSZJJjclNU9jU3lRgus+0zwxr5mgmNKs1u8eisNxZWaxG1qA5wzzIfKN5m/krCz2LWIudFt0WXyztLFMt6ywfWSlZBVhttOqw+sPaxJprXWN9x4Zq42Ozzqbd5rWtqS3fdr/tfTuaXbDdFrtOu8/2DvYi+yb7MQc9h3iHvQ732HR2KLuEfdUR6+jhuM7xjOMHJ3snsdNJp9+dWc4pzg3OowsMF/AX1C0YctFx4bgccpEuZC6MX3hwodRV25XjWuv6zE3Xjed2xG3E3dg92f24+ysPSw+RR4vHlKeT5xrPC16Il69XkVevt5L3Yu9q76c+Oj6JPo0+E752vqt9L/hh/QL9dvrd89fw5/rX+08EOASsCegKpARGBFYHPgsyCRIFdQTDwQHBu4IfL9JfJFzUFgJC/EN2hTwJNQxdFfpzGC4sNKwm7Hm4VXh+eHcELWJFREPEu0iPyNLIR4uNFksWd0bJR8VF1UdNRXtFl0VLl1gsWbPkRoxajCCmPRYfGxV7JHZyqffS3UuH4+ziCuPuLjNclrPs2nK15anLz66QX8FZcSoeGx8d3xD/iRPCqeVMrvRfuXflBNeTu4f7kufGK+eN8V34ZfyRBJeEsoTRRJfEXYljSa5JFUnjAk9BteB1sl/ygeSplJCUoykzqdGpzWmEtPi000IlYYqwK10zPSe9L8M0ozBDuspp1e5VE6JA0ZFMKHNZZruYjv5M9UiMJJslg1kLs2qy3mdHZZ/KUcwR5vTkmuRuyx3J88n7fjVmNXd1Z752/ob8wTXuaw6thdauXNu5Tnddwbrh9b7rj20gbUjZ8MtGy41lG99uit7UUaBRsL5gaLPv5sZCuUJR4b0tzlsObMVsFWzt3WazrWrblyJe0fViy+KK4k8l3JLr31l9V/ndzPaE7b2l9qX7d+B2CHfc3em681iZYlle2dCu4F2t5czyovK3u1fsvlZhW3FgD2mPZI+0MqiyvUqvakfVp+qk6oEaj5rmvep7t+2d2sfb17/fbX/TAY0DxQc+HhQcvH/I91BrrUFtxWHc4azDz+ui6rq/Z39ff0TtSPGRz0eFR6XHwo911TvU1zeoN5Q2wo2SxrHjccdv/eD1Q3sTq+lQM6O5+AQ4ITnx4sf4H++eDDzZeYp9qukn/Z/2ttBailqh1tzWibakNml7THvf6YDTnR3OHS0/m/989Iz2mZqzymdLz5HOFZybOZ93fvJCxoXxi4kXhzpXdD66tOTSna6wrt7LgZevXvG5cqnbvfv8VZerZ645XTt9nX297Yb9jdYeu56WX+x+aem172296XCz/ZbjrY6+BX3n+l37L972un3ljv+dGwOLBvruLr57/17cPel93v3RB6kPXj/Mejj9aP1j7OOiJwpPKp6qP6391fjXZqm99Oyg12DPs4hnj4a4Qy//lfmvT8MFz6nPK0a0RupHrUfPjPmM3Xqx9MXwy4yX0+OFvyn+tveV0auffnf7vWdiycTwa9HrmT9K3qi+OfrW9m3nZOjk03dp76anit6rvj/2gf2h+2P0x5Hp7E/4T5WfjT93fAn88ngmbWbm3/eE8/ul8iYiAAADiElEQVRYCe1WWUhUYRT+xmWcxhyrccZSNCtqIlMUNMqk1HwrS4OgghbspYcoqKDoJSl7iLaHog1bHyorjYLURMsoixaSlMSyLFu0hkYdl9FxdKZzfucOc620B6+9eODcfz/fd89y/6tyuVz4n+LzP8EZe4zAmAf8OBFUKlUENTNJQ3k8CvKDMOqpAr/AXYZpdru9k/qjIoxFBNIIDMIDNAhVq9WBKy89xq16s6IOyJppROGG5EDGZCCJgAAV4L6yKTE/ko/BL/g7Wn/fSOINa0tGwJGzfNgDI71B9h1wOp2Q9FhpFfy3X8CsAzc8c2cqapBxshhtth7477yIwyWv0N3rwOXKWhj3XsW0/dfR3Nrp2b+n8KmwUfCi3jPH9r1FRoCzUtKtS2KBAH+A7iqeM1u7sOXea3H20btvgNOFhKkG5Nx5jk0Fz9Da0YOvrV1IP1Ui9n62tOPQk7dif4vN7rHLtrxFFoLB7FKmTEBje7dgn1v8CrA7kDEnHPdqv9ItooIhSIOjzz8gMWwiHm5b6m0XW69XCpI8+aPdJmzINrgHf/UAM50bGgwnuaCq0YwzLxuwMFKPdfNNKHr/HQsIdIpOi4VhE/CiqRXltQPfFD5X19yCvKpPglimKQyWITwgIyDFX2pNBh0c5Oodt19SwfrgeNY8NJjb8KXNhtTpRmjVvri2fjESiETGtSc4cb8aHd12HCytRrwxGIXZqTCOD0Cz2wOSXW9PDBmC8GAtmgiMdV9aNGYQoXOVdeJ8QkSIcGs7Ad7cmILM8xXYVVoDFa3mc4hIInILRZsUMenfQiAxlNpQnUYYMBmCsHnRbGGkqK5JzM2erENWXjmij9zF8Qdv8I0qIy0qBL6UGyxxFL6Pu1cg2qjDd1uvOCvZFRvcjyFDMEmrFttOr0iAL/WsFMuyxp+IJeP6QA1WxUTCQCSPUH5sjovC+dVJXDIwhQQhf10ydBo/GMep0eBO5D8RUHHS0G24htorFovFm5xifb1ez5hrCfOqLAdctxMVA5UZzm7wDCUCA4GjaT91v2dRiU5fLwdTiMD0I1fQ5w4i21zGdPSZy9zryjSM4RYNY7MHGNxhtVpthmVntdKqki1jMSapht0wnjSeNIaUfxI84aC+EsKXAf+S1ZBWMRiHYBxpAKmsLGmslPCVaCftlsqQiSj95oNfhqrQ5RIEBq+M5ni0XP7Xdxoj8AvTyyj3bHcnXgAAAABJRU5ErkJggg==) no-repeat;
    }

    /* auto producer offer */
    .buyme {
        position: absolute;
        margin-top: 50px;
        display: none;
        opacity: 0;
        transition: opacity 600ms ease;
        -moz-transition: opacity 600ms ease;
        -webkit-transition: opacity 600ms ease;
        text-align: left;
        font-family: Tahoma, Verdana, Arial, sans-serif;
        max-width: 300px;
        right: 16px;
    }

    .buyme_visible {
        display: block;
        opacity: 1
    }

    .buyme-title:after {
        content: ' ▼'
    }

    .buyme_show .buyme-title:after {
    }

    .buyme .producer-list {
        display: none
    }

    .buyme_show .producer-list {
        display: block
    }

    .buyme .producer-list {
        list-style-type: none;
        padding: 0
    }

    .buyme .producer-item {
        margin-top: 2em
    }

    .buyme .producer-name {
        display: inline-block;
        margin: .5em 0;
        border-bottom: 1px solid black;
        font-size: 1.1em;
        font-weight: bolder
    }

    .buyme a.producer-name {
        border-bottom: none
    }

    .buyme a:after {
        content: ' →';
        position: absolute;
    }

    .buyme .offer-list {
        list-style-type: none;
        padding: 0
    }

    .buyme .offer-item {
        display: block;
        padding: .5em 0
    }

    .buyme .offer-name {
        display: table-cell;
        vertical-align: top
    }

    .buyme .offer-item .offer-currency {
        float: right;
        font-size: 0.8em;
        color: #888
    }

    .buyme .offer-item .offer-price {
        float: right;
        padding-left: 8px;
        font-size: 1.1em;
        font-weight: bold;
        color: green
    }

    .buyme .offer-item .conditional-price {
        float: right;
    }

    .buyme .offer-item input[type="checkbox"] {
        display: none;
        position: absolute;
        right: 100%
    }

    .buyme .offer-item input[type="checkbox"]:after {
        content: 's';
        position: absolute;
        left: 100%;
        opacity: 0
    }

    .buyme .offer-item:hover input[type="checkbox"],
    .buyme .offer-item input[type="checkbox"]:hover,
    .buyme .offer-item input[type="checkbox"]:checked {
        display: block
    }

    .buyme .offer-item_fuzzy {
        opacity: .618
    }

    .buyme .offer-item .offer-condition {
        display: table-cell;
        vertical-align: top;
        font-size: .8em
    }

    .buyme .offer-item .offer-condition a {
        float: right;
        text-decoration: none;
        color: black;
        border-bottom: 1px dashed black
    }

    .buyme .offer-item .offer-condition a:after {
        content: '';
        clear: both
    }

    /*http://jsfiddle.net/ostrio/MVDh6/6/*/
    .share {
        display: inline-block;
        margin-right: 8px;
        padding: 3px 8px;
        border: 1px dotted #eee;
        border-radius: 11px;
    }

    .share-btn {
        display: inline-block;
        width: 16px;
        height: 16px;
        padding-left: 3px;
        float: right;
    }</style>
</head>

<body class="offline" style="height: 6000px"><!-- scrollsaver -->
<!-- params & view -->
<div class="geodesic">
    <!-- viewport -->
    <canvas class="preview"></canvas>

    <!-- copyleft -->
    <div class="copyleft" style="display: none" data-bind="visible: IS_IFRAME">
        2011-<span data-bind="text: (new Date).getFullYear()"></span>
        <a href target="_blank" data-bind="attr: { href: 'http://acidome.ru/lab/calc' + location.hash }">acidome.ru/lab/calc</a>
        &rarr;
    </div>

    <!-- header -->
    <div class="head" style="display: none" data-bind="visible: detailList() && !IS_IFRAME">
        <div class="title transparent">
					<span style="font-size: .7em; color: gray">
						Acid<span style="margin: 0 -.1em;">☺</span>me
					</span>

            <h1 data-bind="text: 'title:Geodesic dome calculator'">Geodesic dome calculator</h1>

            <select class="lang-select" style="display: none" data-bind="
						options: i18n.langOptions(),
						optionsValue: 'id',
						optionsText: 'name',
						value: i18n.lang,
						event: { change: $.proxy(console, 'log') },
						visible: true
					"></select>
        </div>

        <div class="version"></div>

        <div class="welcome transparent">
            <!-- http://jsfiddle.net/ostrio/MVDh6/6/ -->
            <div class="share">
                <span data-bind="text: 'share:Share'">Share</span> &rarr;

                <a class="share-btn" target="_blank" title="Facebook" href data-bind=" attr: {
							href: 'http://www.facebook.com/sharer.php?u=' + encodeURIComponent(form.figureUrl()) + '&t=' + encodeURIComponent(form.figureName())
						}">
                    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QTZFREVBOTAxODJEMTFFMzkzM0E4RkVFOTc5OEYzOUQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QTZFREVBOTExODJEMTFFMzkzM0E4RkVFOTc5OEYzOUQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBNkVERUE4RTE4MkQxMUUzOTMzQThGRUU5Nzk4RjM5RCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBNkVERUE4RjE4MkQxMUUzOTMzQThGRUU5Nzk4RjM5RCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PpH2LXcAAAGmSURBVHjapFJNKERRFP7eu2/e84zI+ElJKYrJAitJSVKzkJ9IFrZ2VjZKbC0UWQsLO1nZSX7KYnaS1VB+EhtFz88Y7z3zfpx7Mat5MzSnzrvvnnvPd77z3SN1jW+sARjGtynIYb4vvuJfkiSPlkOeMIk/mGk78D0fiiLDo1UNMTBZGlP+mtzWXIORWBSV5Tos28Xq1gkubw3kBbA/HbQ0VmFlLobSEi0T3zm4QOLqKT+A43gY6G0SyRYxOTt/gGk5eDI+EKJ28oomyzKqIrrY78dvMLt8AI3611QFRRqDHJhMnjLTSKZspB1PxB6NFN5p77h+pkBWBh6dqApDf08jqc5QW1Mq4tGGSkwMtQoG8dN7vCWtAAB6Jo3ozU91Q9dCmXhne53wtONidGobLn/WbAA0JHCJZuLyEZEyHdUVYYSLVbxQxecXE9d3zzBeTSgsQEQaEKH49MIePaOLxZk+9HTUY/f4CkvrcRTrKvXvk8BSsIjcuHgcgLf03Zon5sJ1PcGSW04AXoExCT93RRIj2jyeuYMCrWAALuIm+eCvfv/MP/oSYAC/DZ1HI56fOwAAAABJRU5ErkJggg=="/>
                </a>

                <a class="share-btn" target="_blank" title="Twitter" href data-bind=" attr: {
							href: 'http://twitter.com/share?url=' + encodeURIComponent(form.figureUrl()) + '&text=' + encodeURIComponent(form.figureName() + ' | acidome.ru')
						}">
                    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzA5RkVFNjkxODJEMTFFMzkzM0E4RkVFOTc5OEYzOUQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzA5RkVFNkExODJEMTFFMzkzM0E4RkVFOTc5OEYzOUQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBNkVERUE5MjE4MkQxMUUzOTMzQThGRUU5Nzk4RjM5RCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDMDlGRUU2ODE4MkQxMUUzOTMzQThGRUU5Nzk4RjM5RCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PiemuFcAAAC6UExURQCs7QCs7QCs7QCs7QCs7QCs7QCs7QCs7QCs7QCh6wCm7ACn7ACo7ACp7ACp7QCq7ACq7QCr7QCs7QGs7QKq7AKq7QOr7Qis7Qmu7Qqu7RKx7hSv7hax7hey7ii48Cy58D698UXB8VDE8lHF8lHF81TG83PQ9XfR9X/U9n/V9oLV9orY94vY94zX94/Z95rd+Kzj+azk+bbm+rfn+sPr+83v+87v/NPx/Nfy/Nvz/OD1/ej4/fP7/v///zDzFH4AAAAJdFJOU5abr7Tp7e7v8TlIFh4AAACMSURBVBgZBcHbSsNAFADAOXsJjfoQQxUE///nCgFTizSbZJ0JRQag7zEDABIAHSTYO32r24Y80qejxfB1vYxa5JH9++24f3zW1/poUdD/ri/zgHW5KJCUCY4gIe4HOJ+FhLqsHW5LJiHefzf8LAMKzjKl7bneSiBmnEPuj14DCqTWVEABAUBEkQHQ/gFK1TB99kc61wAAAABJRU5ErkJggg=="/>
                </a>

                <a class="share-btn" target="_blank" title="Google Plus" href data-bind=" attr: {
							href: 'https://plusone.google.com/_/+1/confirm?hl=en&url=' + encodeURIComponent(form.figureUrl())
						}">
                    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzA5RkVFNzExODJEMTFFMzkzM0E4RkVFOTc5OEYzOUQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzA5RkVFNzIxODJEMTFFMzkzM0E4RkVFOTc5OEYzOUQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDMDlGRUU2RjE4MkQxMUUzOTMzQThGRUU5Nzk4RjM5RCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDMDlGRUU3MDE4MkQxMUUzOTMzQThGRUU5Nzk4RjM5RCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PlHKW2sAAAKTSURBVHjabFNNSFRRFP7ufe85M6Zm/mulNpVKRi0qyKCkZW1CREzDioqEIIiWrYIgqKBWrYuoRVCQmwgiI/oh0NQsTJjJoVLHH0ZpdH6c9969nfteM85kB87innvud893znfYRHvrDYD1wDUfucQaUyGWHWB/gwMseGSfhJQ5VzKVgrRtSCEAYQO6Ae7xgnGOnFwyXaoXbBVdrCTAav3w7NiNvJrNzp0VmUdq8APEzBSg6TkAXEpVgOsimYCx5wAqei9DBsbAwpOo6j4Lz4ZS2NO/IBlHdr5yF4BKVS6kQEnHSYjJn4gPD2DhyUNEXz1HQUsriBDl2LDpE0l5uQAZJ05eH4w6P6SuQ5gpJCYCYPRQmha1w4a2tcnpj7AtF0Co0hV35ZS48PgefE07UdjWDWxpgFa9EbN3b1L5DNzfiMrzl8AoLrnmAOZUwDw+RN+8ROhCNzSvFw0P+pAYG8Xiiz7oFdUo7+hB/rZGlHeeglZcCmFZBCBW+SiOFgVj4Sn8/jICGhrKuqiJ+1uR/BFC+M41LL3vR/jWVaRmp2kiGtjo4V0ZHdjEq+hYF/Jr6xEf+gi5vIyqcxdhbKpHsLcT1sw0tMIiZ1rp0evO+OIx51B4tB01x0/j+5k2mAsRp6rY50Fsv/8MBXtbEHn6iLhzEpSWEawuhXSaocxbVQ2jpAyW7oFtmmCGAYsoxkNBJAPjjiLBXS2kBc+GDzVLabkA2vpi1F25Dl5ahsW3/TAjc9CpWWbwG6LvXoNTY/81NnSwOSNuSb8qZa+jMRrllbCWl5AMBWDOz4GRPhhja1ZM52p86TiVTGJA4usIEmqRFF8jD4YvP+tJ7krq48nkbS5xImudhYPINDd/JYX/WPrLT38EGAA8kFoJbGb//AAAAABJRU5ErkJggg=="/>
                </a>

                <a class="share-btn" target="_blank" title="LinkedIn" href data-bind=" attr: {
							href: 'http://www.linkedin.com/shareArticle?mini=true&url=' + encodeURIComponent(form.figureUrl())
						}">
                    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzA5RkVFNkQxODJEMTFFMzkzM0E4RkVFOTc5OEYzOUQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzA5RkVFNkUxODJEMTFFMzkzM0E4RkVFOTc5OEYzOUQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDMDlGRUU2QjE4MkQxMUUzOTMzQThGRUU5Nzk4RjM5RCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDMDlGRUU2QzE4MkQxMUUzOTMzQThGRUU5Nzk4RjM5RCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Po54gukAAAIDSURBVHjajFNLSxxBEP5muufhjLuSLGo2qBgPCQjBQzyI3j2Id3MKMXjJJepRPOVijhpYvOQXiAQCYnKSQA4+IJeITxCyG0GNGpD1tePszFg1s7vuGsRtqG6quru6vu+rVjD+dQrASwQwQBMUBVWMoLAuSZqG4ZMfkKl02fMBoaLK0afyRUsTmOxvx9yrTrQ3xIC8X20CqHy4IxnHSM8T9D9rwOsXTcCVV3UCyeVu/c1iZnUPbQ8tfF47ADT1pgqGw7CK0GQlPIVIDBhG0jZg6wK/Ty6hEBeNth5Ss09+W8JCS9xEOptD+vicHhBlFVDmx7S5MNSFxlodb2Z/YfPwFD/e9iBHVXxZP8BgZzNilPzfhYuxb1v4tJIBdFHggKskexTT8aBGgykF+QrqbQ0tdSbedbfi6DRHtPhIWBre9z5FfZwU94LQ1KKorhdJ6xOccv/D9x08n1jA6NxG6CdjBloTdomXOwVXCg01v32Ii6s8lv+cwPWjpBaTGUS9c2/HMKHMvhRKqf+Cij64L8Gt9f9GQtTBcUOGAY1eElS+pkZXpBq9LGjlvVKsJCMdzro+Pi6mUWdKbJLOx46L1HImPLibdULJ9s4cpJYyIZRd6ofif4kaiYeTj8Cxvkwg+xzQqTJ+mQl0vJtYsUKyabIBGFKrAGfKys/LSctjJCLZz2sBBgATpK1IbdAo2wAAAABJRU5ErkJggg=="/>
                </a>

                <a class="share-btn" target="_blank" title="VK.com" href data-bind=" attr: {
							href: 'http://vk.com/share.php?url=' + encodeURIComponent(form.figureUrl()) + '&title=' + encodeURIComponent(form.figureName() + ' | acidome.ru') + '&description=&image='
						}">
                    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OEFBRDg3REE2NERBMTFFMzk5MTlCRUE2RDEyNDhFN0IiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OEFBRDg3REI2NERBMTFFMzk5MTlCRUE2RDEyNDhFN0IiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoyNjM2RTczNTY0OUIxMUUzOTkxOUJFQTZEMTI0OEU3QiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoyNjM2RTczNjY0OUIxMUUzOTkxOUJFQTZEMTI0OEU3QiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PiDRXYQAAADbUExURU50p050p050p050p050p0VpmkVqnEZzp0dspUhumkhum0pvnEx0oU1yn01zpk10pU10p050p1B2oFh8pll8pFp+pFyBoVyCrGeJsGuHqG2MrXCPr3CVsXKOq3SOr3aTs3uVs3yZtX2ZuoKeuoSctYuiuoumwZesw6G1zaK2zbXE0rrH18LO18vV3szU3tHc5dff5Njk69nk6tzk6eXt8Ofu8Ony9O7y9e7z8vP19/P29/T3+PX3+fr7+/r9+/v7+/v9/f37+f39/P79//7+/P7+/f///P///v///zEOGxsAAAAFdFJOUz9IyeTnJbi1yAAAAH9JREFUGNNjYGASRAJMDAwsgiiAhUEQDRAlIKOnxalmrs8vqqOrDBYQtvFQEnGz4NL0sJcEC/Cpexgp2JkoWtqqskHM4DX2sHZ2cvQwYGWHCAhIWHl4uJp6OGhwwGyRM3QxE9N2d1eBWyskK8XNIy0vTpTDmFH5zAyMKN5lYQQAgNYXvjpI3BoAAAAASUVORK5CYII="/>
                </a>
            </div>

            <div style="text-align: right">
                <!-- ko ifnot: i18n.lang() == 'ru' -->
                <a class="sample-to-use" href="http://northernhomestead.com/how-to-build-a-geodome-greenhouse/"
                   target="_blank">How to use it</a> &rarr;
                <!-- /ko -->

                <!-- ko if: i18n.lang() == 'ru' -->
                <a class="sample-to-use" href="http://dompodrobno.ru/instrukciya_kalkulyator_kupolnyi/" target="_blank">Как
                    этим пользоваться</a> &rarr;
                <!-- /ko -->
            </div>

            <div style="text-align: right">
                <a class="facebook-group" href="https://www.facebook.com/groups/136778896376859/" target="_blank">Facebook
                    group</a> &rarr;
            </div>

            <div style="text-align: right">
                <a class="help-to-translate"
                   href="https://www.facebook.com/groups/acidome.calc/permalink/753359898052086/" target="_blank">Translate
                    me</a> &rarr;
            </div>

            <div class="download" style="margin-right: 16px">
                <a id="js-download-link" class="pseudo" href="download.php" data-href-base="download.php"
                   title="Come in peace. Free license for non-commercial usage, free distribute this program, and free commercial usage for growing cannabis.">
                    Offline version
                </a>
            </div>

            <div class="download" style="margin-right: 16px">
                <a class="pseudo" href="javascript://" onclick="figure.clientDownload(); return false;">Download figure
                    as .obj</a>
            </div>

            <a href class="donation pseudo" onclick="event.preventDefault()" data-bind="text: 'Donate'">Donate</a>
            &larr;
            <div class="donation__popup">
                <iframe class="yandex" frameborder="0" allowtransparency="true" scrolling="no" style="z-index: 1"
                        src="http://money.yandex.ru/embed/donate.xml?account=41001351235394&quickpay=donate&default-sum=&targets=%D0%9D%D0%B0+%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D1%83+%D0%BA%D0%B0%D0%BB%D1%8C%D0%BA%D1%83%D0%BB%D1%8F%D1%82%D0%BE%D1%80%D0%B0&target-visibility=on&project-name=&project-site=&button-text=05"
                        width="439" height="104"></iframe>

                <div style="clear: both"></div>

                <div class="visa" title="Visa card number">
                    4276 3800 5748 2981
                </div>

                <div class="paypal" title="PayPal donation">
                    <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
                        <input type="hidden" name="cmd" value="_s-xclick">
                        <input type="hidden" name="hosted_button_id" value="XSULWE6C9RHZJ">
                        <input type="image"
                               src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAAVCAYAAADWxrdnAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABWFJREFUeNrsVwlsVFUUPX86e2famcJM6crUUhtLKbZFYpGCMICEBgImQsCQGAiIERRQohIMiaKJmhpFISoGEha1CbKYWBWNDZvYUkqhLQNdoO1Mp6XrLJ19ed7/Ck201YQ0MTH2JW+Sn//ufeedc+59fwTGGP6rQ4L/8BgHPw7+/wZeKv5cs9jYo698AfgDQDQ69Earwdr8bLz7wiKkJiUIY9nk1a/qWGmVFRCzRKm7KaQoMGrx3nwTFhQk/2PuHoeXJe+vRtgfRsvzhWjr9VK4BAPe0BDzF6/eATweQCbD3KlTMD0zHfD6cOSXS9ixt3w4kcPpYXd7nMzp8gz3V78/wLxe/5/6bTAYYoMeH59+f5CVNvUBkQgQp8LiKROJMglqWvuw8Ng1NNucPNZHeQZcPj79geBwvhvtLoTtDk6qXiuHUiaBQh4DOeXgzF+yECvhCGaZkvHzR+t4kPnlgzhfdxMtd/tR09DO5u8pg9PhHlJGqcCSzBT26dal2FR6GmfaunBy8zK2fEEeZ3HDnm9xuMaCF4vysHPjU4DbT6wLuPDMI5iZPQFVjf2YfeAKEIqgs9+LE9Vd7LXKdnqODhmZDlk2dzJbWTxZqG1zAnQXmQ0a6ONUQuEUKRMVlMtkAmf+3J1Ontw8LYMD7+h04nzPALEVxbRUI240dyNXp8GukiJsW/gYHTSE8t9rcej0ZUjES66zC2erW3jswROV7HBFFVdu7ZJ81FldJE+QACmRkx7P19RZ75FA7KmVMly968b23ETsM2diUaoO6HJi1fe34PEFWKWd1tIW89KGYuVymSAC556/0WRn7SJQssy+i/W4cMuGihZSwkknNhqw7dliZD9kxIzcFNy83QNbjxNyTSyCrkEkGeJQSOB/vFKPpu4BBEMhtuObCs7UujmFKMhNwxtlFk4MAhFsPFaP6+4AGq1kAxbFrsczkJcRj32JObD2+tDR70O+Q40zzbReIUMgGMHXXQReGoNZmbqRBVtrsVNiKtSYGPT3OVAxQExpY7GRJN+9wQyn24fpG/bDYiV1lHJApQLENXIFivMzUGPpIKklaOpzYefH5ejv7AYm6PHWpkV8gxM25xB4suXxxh4iKQZT0/TYU5yGkhlJ+PxMK7acu02FQjWhVfJ1ItPrSSW3N0x7+XiBZyVpRoKvtNjIayEkJRlx+cP1ZGcZ1Go5VEoFl2bFrqPMcqsZW1eY8f62EvxW04onXz9ANtAgy2SAQ/QzHaTR3oNSezcHWvr0HKRM0gudvYPsDnlaVOLIshwsnZEo4oJOq+K5z17vYlt+uMlB395RhFSDCiu/rMWpejvmZyagwUYkhcOIN+qQatSO6ErSXxuo00TCWJyVhpS/tESX28uqREtJZeh0DOLgycv44LtLxFIAc5JMUCjkQlpiPOOK+II8T0GWCZvXPMHjLaLfvUHO3OxsPeLvgb4/Wrqow0WJaYmA6pYBHLpoxSlRHVIyb3IcyipJ1XAUqydpR7+knFSUSE7G8rm5I17GadXCm2KBqmMpUQM++6kGpoQ4QJ+A5UU5fE2iQUugtNzDkErxznMLcb+g7A6yI6m46mEDTMnxI5hbMN1Ie+upTQexsrwRHW46qE6N3PQEmIxq9Poj/HnJ1ImjghfEHi1+FqvVyr+9LFqtvSxC3SF1ko5kZ+SyCLEu4yA9Hj8zrNsLX4cda+fNxOG31wznEft1KMzI5gKUSvmo+d0U3+sMQqeR8VY46A2wGOqBom3FOyJC2GJVitGxicDHMvcePcdQvJ2hZDdrbutmY833IHNM3zYDjkH20vGzpJ8En6w2IzPdIPyb3zbC+D+pcfAPPv4QYADNfs6Oc0aWEAAAAABJRU5ErkJggg=="
                               border="0" name="submit" alt="PayPal – The safer, easier way to pay online.">
                        <!--<img alt="" border="0" src="https://www.paypalobjects.com/ru_RU/i/scr/pixel.gif" width="1" height="1">-->
                    </form>
                </div>
            </div>

            <div class="buyme" data-bind="
						css: {
							buyme_visible: !_.isEmpty( OfferFactory.resultList() ),
							buyme_show: !localStorage.getItem('env.buymeHidden')
						}
					">
                <h4 class="buyme-title" style="cursor: pointer; color: green" data-bind="
							click: function($data, event) {
								localStorage.setItem('env.offerHidden', $('.buyme').toggleClass('buyme_show').is('.buyme_show') ? '' : 1);
							}
						">
                    <span data-bind="text: 'buyme:Buy me'">Buy me</span>
                </h4>

                <ul class="producer-list">

                    <!-- promo test -->
                    <li class="producer-item" style="display: none" data-bind="visible: i18n.lang() == 'en'">
                        <a class="producer-name"
                           href="http://www.vikingdome.com/geodesic-domes/?utm_source=acidome&utm_medium=link"
                           target="_blank" onclick="pageTracker._trackEvent('outbound', 'vikingdome.com')">Vikingdome.com</a>
                        <div>
                            Struts, hubs and covers for your Geodesic dome. Made in EU, worldwide delivery.
                            <a href="http://www.vikingdome.com/geodesic-domes/?utm_source=acidome&utm_medium=link"
                               target="_blank" onclick="pageTracker._trackEvent('outbound', 'vikingdome.com')">Learn
                                more…</a>
                        </div>
                    </li>
                    <!-- /promo test -->

                    <!-- ko foreach: OfferFactory.resultList -->
                    <li class="producer-item">
                        <!-- ko if: $data.producerLink -->
                        <a class="producer-name" target="_blank"
                           data-bind="text: producerName, attr: { href: producerLink }"></a>
                        <!-- /ko -->
                        <!-- ko if: !$data.producerLink -->
                        <div class="producer-name" data-bind="text: producerName"></div>
                        <!-- /ko -->

                        <form action="" target="_blank" data-bind="with: new OfferFactory.Order($data)">
                            <ul class="offer-list" data-bind="foreach: $parent.offer">
                                <!-- ko if: !$data.conditions -->
                                <li class="offer-item">
                                    <label>
                                        <input type="checkbox"
                                               data-bind="checked: $parent.selectedPositions, value: name"/>
                                        <sub class="offer-currency" data-bind="text: currency"></sub>
                                        <span class="offer-price"
                                              data-bind="text: OfferFactory.formatPrice(price)"></span>

                                        <span class="name" data-bind="html: name"></span>
                                    </label>
                                </li>
                                <!-- /ko -->

                                <!-- ko if: $data.conditions -->
                                <li class="offer-item offer-item_fuzzy">
                                    <div class="offer-name" data-bind="html: name"></div>

                                    <div class="offer-condition">
                                        <!-- ko if: $data.price -->
                                        <button class="conditional-price"
                                                data-bind="click: form.update.bind(null, conditions[0].condition)">
                                            <sub class="offer-currency" data-bind="text: currency"></sub>
                                            <span class="offer-price"
                                                  data-bind="text: OfferFactory.formatPrice(price)"></span>
                                        </button>
                                        <!-- /ko -->

                                        <!-- ko foreach: conditions -->
                                        <a href data-bind="click: form.update.bind(null, condition), html: name"></a>
                                        <!-- /ko -->
                                    </div>
                                </li>
                                <!-- /ko -->
                            </ul>

                            <!-- ko if: !_.isEmpty(selectedPositions()) -->
                            <input type="email" placeholder="E-mail"
                                   data-bind="value: email, attr: { disabled: pending }"/>
                            <button data-bind="text: 'buyme:Buy it', click: submit, attr: { disabled: pending }"></button>
                            <!-- /ko -->
                        </form>
                    </li>
                    <!-- /ko -->
                </ul>
            </div>
        </div>

        <p>
            <!--<i>"Носи пальто и мечтай о чем-нибудь великом",</i><br/>
					Кинофильм <a target="_blank" href="http://www.kinopoisk.ru/film/45028/>">«Курьер»</a>, СССР, 1986-->
        </p>
    </div>

    <!-- geodesic options form -->
    <form class="options transparent slow-toggle-visibility" style="display: none"
          data-bind="visible: detailList() && !IS_IFRAME">

        <!-- by right side of form -->
        <div class="stat transparent">
            <label class="toggle" style="white-space: nowrap;">
                <span data-bind="text: 'Resulting'"></span>
                ▼
            </label>
            <pre class="report" data-bind="
						html: function() {
							//debugger;
							return reportText()
						}()
					"></pre>
        </div>

        <ul class="mode-list">
            <li class="mode round button active" data-mode="carcass" data-bind="text: 'Carcass'"></li>
            <li class="mode round button" data-mode="schema" data-bind="text: 'Schema'"></li>
            <li class="mode round button" data-mode="cover" data-bind="text: 'Cover'"></li>
            <li class="mode round button" data-mode="base" data-bind="text: 'Base'"></li>
            <li class="mode round button" data-mode="clothier"
                data-bind="text: 'clothier:Clothier', visible: clothier.onRun"></li>
        </ul>

        <!-- form content start here -->

        <div class="progress"></div>

        <h3 data-bind="text: 'Figure options'"></h3>

        <dl>
            <dt data-bind="text: 'Level of detail, V'"></dt>
            <dd>
                <select data-bind="
							options: detailList,
							value: detail
						"></select>
            </dd>
        </dl>

        <dl data-bind="visible: detail() >= 2">
            <dt data-bind="text: 'Subdivision class'"></dt>
            <dd>
                <select data-bind="
							value: subdivClass,
							options: subdivClassList,
							optionsText: 'name',
							optionsValue: 'id',
							attr: {
								disabled: subdivClassList().length <= 1
							}
						"></select>
            </dd>
        </dl>

        <dl data-bind="visible: detail() >= 3 && 'I' == subdivClass()">
            <dt data-bind="text: 'Subdivision method'"></dt>
            <dd>
                <select data-bind="
							value: subdivMethod,
							options: subdivMethodList,
							optionsValue: 'id',
							optionsText: 'name'
						"></select>
            </dd>
        </dl>

        <dl data-bind="visible: 1 || (subdivClass() == 'II')">
            <dt data-bind="text: 'Rotational symmetry'"></dt>
            <dd>
                <select data-bind="options: ['Pentad', 'Cross', 'Triad'], value: symmetry"></select>
            </dd>
        </dl>

        <dl data-bind="visible: connType() != 'Joint'">
            <dt data-bind="text: 'Fullerene'"></dt>
            <dd>
                <select data-bind="
							value: fullerenType,
							options: FULLEREN_TYPE_LIST,
							optionsText: 'name',
							optionsValue: 'id',
							optionsCaption: __('fulleren:None')
						">
                </select>
            </dd>
        </dl>

        <dl>
            <dt data-bind="text: 'Part of full sphere'"></dt>
            <dd>
                <select data-bind="
							options: partialList,
							value: partial
						"></select>
            </dd>
        </dl>

        <dl class="sub" data-bind="visible: canAlignTheBase">
            <dt>
                <label for="alignTheBase_switcher" data-bind="text: 'Align the base'"></label>
            </dt>
            <dd>
                <input id="alignTheBase_switcher" type="checkbox" data-bind="
								checked: alignTheBase
							"></select>
            </dd>
        </dl>


        <h3 data-bind="text: 'Product options'"></h3>

        <dl>
            <dt data-bind="text: 'Sphere radius, m'"></dt>
            <dd><input data-bind="
							value: radius"/>
            </dd>
        </dl>

        <dl>
            <dt data-bind="text: 'Connection type'"></dt>
            <dd><select data-bind="options: connTypeList, value: connType"><select>
            </dd>
        </dl>

        <!-- properties what caused from connector type (and other) -->

        <dl class="sub" data-bind="
						visible: (connType() == 'Piped')
					">
            <dt data-bind="text: 'Pipe diameter, mm'"></dt>
            <dd><input data-bind="
							value: pipeD,
							valueUpdate: 'keyup'"/>
            </dd>
        </dl>

        <dl class="sub" data-bind="visible: _.contains(['Joint', 'Nose', 'GoodKarma'], connType())">
            <dt>
                <label for="clockwise_switcher" data-bind="text: 'Spinning clockwise'"></label>
            </dt>
            <dd>
                <input id="clockwise_switcher" type="checkbox" data-bind="checked: clockwise"/>
                <span class="gray">
								(<label for="clockwise_switcher" data-bind="text: 'else counter'"></label>)
							</span>
            </dd>
        </dl>


        <h3 data-bind="text: 'Timber size'"></h3>

        <dl>
            <dt data-bind="text: 'Width, mm'"></dt>
            <dd><input data-bind="
							value: beamsWidth"/>
            </dd>
        </dl>

        <dl>
            <dt data-bind="text: 'Thickness, mm'"></dt>
            <dd><input data-bind="
							value: beamsThickness"/>
            </dd>
        </dl>


        <!-- ko with: IS_OFFLINE && clothier -->
        <h3>
                    <span style="position: relative">
                        <span data-bind="text: 'clothier:Clothier'"></span>
                        <sup class="absolute red" style="top: 0; left: 100%; padding-left: .5em">will&nbsp;be</sup>
                    </span>
        </h3>

        <dl>
            <dt data-bind="text: 'clothier:Source width, mm'"></dt>
            <dd><input data-bind="value: width"/></dd>
        </dl>

        <dl>
            <dt data-bind="text: 'clothier:Source height, mm'"></dt>
            <dd><input data-bind="value: height"/></dd>
        </dl>

        <dl>
            <dt data-bind="text: 'clothier:Find the best pattern'"></dt>
            <dd>
                <!-- ko if: !onRun() || onPause() -->
                <button data-bind="click: run">▶</button>
                <!-- /ko -->

                <!-- ko if: onRun() && !onPause() -->
                <button data-bind="click: pause">▮▮</button>
                <!-- /ko -->

                <!-- ko if: onRun -->
                <button data-bind="click: stop">◾</button>
                <!-- /ko -->
            </dd>
        </dl>
        <!-- /ko -->
    </form>

    <!--<div class="info">&nbsp;</div>-->

    <div class="budget-list" data-bind="visible: true" style="display: none">
        <!-- ko foreach: budgetList -->
        <h2 data-bind="text: 'budget:title:' + type"></h2>

        <ul data-bind="attr: { 'class': type }">
            <!-- ko foreach: sizeList -->
            <li class="product" data-bind="visible: units().length > 0">
                <i class="product-index" data-bind="text: index, attr: { style: 'background-color: ' + color }"></i>
                <span data-bind="text: units().length + ' ' + __('pcs')"></span>

                <!-- ko if: $parent.type == 'line' -->
                <a href="http://popitch1.livejournal.com/7699.html" target="_blank"
                   style="border: 1px solid gray; border-radius: 50%; margin-left: 10px; text-decoration:none; width: .9em; display: inline-block; padding-left: 4px;">?</a>
                <!-- /ko -->
                <br/>

                <canvas data-bind="attr: $parent.canvasAttr, plot: product"></canvas>
            </li>
            <!-- /ko -->
        </ul>
        <!-- /ko -->
    </div>
</div>

<!-- timing start -->
<script type="text/javascript">
    var _jsLoadingStartAt = +new Date;
</script>

<!-- libs
		-->

<!-- jQuery -->
<script type="text/javascript">/*! jQuery v1.11.0 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
!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) {
    var c = [], d = c.slice, e = c.concat, f = c.push, g = c.indexOf, h = {}, i = h.toString, j = h.hasOwnProperty,
        k = "".trim, l = {}, m = "1.11.0", n = function (a, b) {
            return new n.fn.init(a, b)
        }, o = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, p = /^-ms-/, q = /-([\da-z])/gi, r = function (a, b) {
            return b.toUpperCase()
        };
    n.fn = n.prototype = {
        jquery: m, constructor: n, selector: "", length: 0, toArray: function () {
            return d.call(this)
        }, get: function (a) {
            return null != a ? 0 > a ? this[a + this.length] : this[a] : d.call(this)
        }, pushStack: function (a) {
            var b = n.merge(this.constructor(), a);
            return b.prevObject = this, b.context = this.context, b
        }, each: function (a, b) {
            return n.each(this, a, b)
        }, map: function (a) {
            return this.pushStack(n.map(this, function (b, c) {
                return a.call(b, c, b)
            }))
        }, slice: function () {
            return this.pushStack(d.apply(this, arguments))
        }, first: function () {
            return this.eq(0)
        }, last: function () {
            return this.eq(-1)
        }, eq: function (a) {
            var b = this.length, c = +a + (0 > a ? b : 0);
            return this.pushStack(c >= 0 && b > c ? [this[c]] : [])
        }, end: function () {
            return this.prevObject || this.constructor(null)
        }, push: f, sort: c.sort, splice: c.splice
    }, n.extend = n.fn.extend = function () {
        var a, b, c, d, e, f, g = arguments[0] || {}, h = 1, i = arguments.length, j = !1;
        for ("boolean" == typeof g && (j = g, g = arguments[h] || {}, h++), "object" == typeof g || n.isFunction(g) || (g = {}), h === i && (g = this, h--); i > h; h++) if (null != (e = arguments[h])) for (d in e) a = g[d], c = e[d], g !== c && (j && c && (n.isPlainObject(c) || (b = n.isArray(c))) ? (b ? (b = !1, f = a && n.isArray(a) ? a : []) : f = a && n.isPlainObject(a) ? a : {}, g[d] = n.extend(j, f, c)) : void 0 !== c && (g[d] = c));
        return g
    }, n.extend({
        expando: "jQuery" + (m + Math.random()).replace(/\D/g, ""), isReady: !0, error: function (a) {
            throw new Error(a)
        }, noop: function () {
        }, isFunction: function (a) {
            return "function" === n.type(a)
        }, isArray: Array.isArray || function (a) {
            return "array" === n.type(a)
        }, isWindow: function (a) {
            return null != a && a == a.window
        }, isNumeric: function (a) {
            return a - parseFloat(a) >= 0
        }, isEmptyObject: function (a) {
            var b;
            for (b in a) return !1;
            return !0
        }, isPlainObject: function (a) {
            var b;
            if (!a || "object" !== n.type(a) || a.nodeType || n.isWindow(a)) return !1;
            try {
                if (a.constructor && !j.call(a, "constructor") && !j.call(a.constructor.prototype, "isPrototypeOf")) return !1
            } catch (c) {
                return !1
            }
            if (l.ownLast) for (b in a) return j.call(a, b);
            for (b in a) ;
            return void 0 === b || j.call(a, b)
        }, type: function (a) {
            return null == a ? a + "" : "object" == typeof a || "function" == typeof a ? h[i.call(a)] || "object" : typeof a
        }, globalEval: function (b) {
            b && n.trim(b) && (a.execScript || function (b) {
                a.eval.call(a, b)
            })(b)
        }, camelCase: function (a) {
            return a.replace(p, "ms-").replace(q, r)
        }, nodeName: function (a, b) {
            return a.nodeName && a.nodeName.toLowerCase() === b.toLowerCase()
        }, each: function (a, b, c) {
            var d, e = 0, f = a.length, g = s(a);
            if (c) {
                if (g) {
                    for (; f > e; e++) if (d = b.apply(a[e], c), d === !1) break
                } else for (e in a) if (d = b.apply(a[e], c), d === !1) break
            } else if (g) {
                for (; f > e; e++) if (d = b.call(a[e], e, a[e]), d === !1) break
            } else for (e in a) if (d = b.call(a[e], e, a[e]), d === !1) break;
            return a
        }, trim: k && !k.call("\ufeff\xa0") ? function (a) {
            return null == a ? "" : k.call(a)
        } : function (a) {
            return null == a ? "" : (a + "").replace(o, "")
        }, makeArray: function (a, b) {
            var c = b || [];
            return null != a && (s(Object(a)) ? n.merge(c, "string" == typeof a ? [a] : a) : f.call(c, a)), c
        }, inArray: function (a, b, c) {
            var d;
            if (b) {
                if (g) return g.call(b, a, c);
                for (d = b.length, c = c ? 0 > c ? Math.max(0, d + c) : c : 0; d > c; c++) if (c in b && b[c] === a) return c
            }
            return -1
        }, merge: function (a, b) {
            var c = +b.length, d = 0, e = a.length;
            while (c > d) a[e++] = b[d++];
            if (c !== c) while (void 0 !== b[d]) a[e++] = b[d++];
            return a.length = e, a
        }, grep: function (a, b, c) {
            for (var d, e = [], f = 0, g = a.length, h = !c; g > f; f++) d = !b(a[f], f), d !== h && e.push(a[f]);
            return e
        }, map: function (a, b, c) {
            var d, f = 0, g = a.length, h = s(a), i = [];
            if (h) for (; g > f; f++) d = b(a[f], f, c), null != d && i.push(d); else for (f in a) d = b(a[f], f, c), null != d && i.push(d);
            return e.apply([], i)
        }, guid: 1, proxy: function (a, b) {
            var c, e, f;
            return "string" == typeof b && (f = a[b], b = a, a = f), n.isFunction(a) ? (c = d.call(arguments, 2), e = function () {
                return a.apply(b || this, c.concat(d.call(arguments)))
            }, e.guid = a.guid = a.guid || n.guid++, e) : void 0
        }, now: function () {
            return +new Date
        }, support: l
    }), n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function (a, b) {
        h["[object " + b + "]"] = b.toLowerCase()
    });

    function s(a) {
        var b = a.length, c = n.type(a);
        return "function" === c || n.isWindow(a) ? !1 : 1 === a.nodeType && b ? !0 : "array" === c || 0 === b || "number" == typeof b && b > 0 && b - 1 in a
    }

    var t = function (a) {
        var b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s = "sizzle" + -new Date, t = a.document, u = 0, v = 0,
            w = eb(), x = eb(), y = eb(), z = function (a, b) {
                return a === b && (j = !0), 0
            }, A = "undefined", B = 1 << 31, C = {}.hasOwnProperty, D = [], E = D.pop, F = D.push, G = D.push, H = D.slice,
            I = D.indexOf || function (a) {
                for (var b = 0, c = this.length; c > b; b++) if (this[b] === a) return b;
                return -1
            },
            J = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
            K = "[\\x20\\t\\r\\n\\f]", L = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", M = L.replace("w", "w#"),
            N = "\\[" + K + "*(" + L + ")" + K + "*(?:([*^$|!~]?=)" + K + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + M + ")|)|)" + K + "*\\]",
            O = ":(" + L + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + N.replace(3, 8) + ")*)|.*)\\)|)",
            P = new RegExp("^" + K + "+|((?:^|[^\\\\])(?:\\\\.)*)" + K + "+$", "g"),
            Q = new RegExp("^" + K + "*," + K + "*"), R = new RegExp("^" + K + "*([>+~]|" + K + ")" + K + "*"),
            S = new RegExp("=" + K + "*([^\\]'\"]*?)" + K + "*\\]", "g"), T = new RegExp(O),
            U = new RegExp("^" + M + "$"), V = {
                ID: new RegExp("^#(" + L + ")"),
                CLASS: new RegExp("^\\.(" + L + ")"),
                TAG: new RegExp("^(" + L.replace("w", "w*") + ")"),
                ATTR: new RegExp("^" + N),
                PSEUDO: new RegExp("^" + O),
                CHILD: new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + K + "*(even|odd|(([+-]|)(\\d*)n|)" + K + "*(?:([+-]|)" + K + "*(\\d+)|))" + K + "*\\)|)", "i"),
                bool: new RegExp("^(?:" + J + ")$", "i"),
                needsContext: new RegExp("^" + K + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + K + "*((?:-\\d)?\\d*)" + K + "*\\)|)(?=[^-]|$)", "i")
            }, W = /^(?:input|select|textarea|button)$/i, X = /^h\d$/i, Y = /^[^{]+\{\s*\[native \w/,
            Z = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, $ = /[+~]/, _ = /'|\\/g,
            ab = new RegExp("\\\\([\\da-f]{1,6}" + K + "?|(" + K + ")|.)", "ig"), bb = function (a, b, c) {
                var d = "0x" + b - 65536;
                return d !== d || c ? b : 0 > d ? String.fromCharCode(d + 65536) : String.fromCharCode(d >> 10 | 55296, 1023 & d | 56320)
            };
        try {
            G.apply(D = H.call(t.childNodes), t.childNodes), D[t.childNodes.length].nodeType
        } catch (cb) {
            G = {
                apply: D.length ? function (a, b) {
                    F.apply(a, H.call(b))
                } : function (a, b) {
                    var c = a.length, d = 0;
                    while (a[c++] = b[d++]) ;
                    a.length = c - 1
                }
            }
        }

        function db(a, b, d, e) {
            var f, g, h, i, j, m, p, q, u, v;
            if ((b ? b.ownerDocument || b : t) !== l && k(b), b = b || l, d = d || [], !a || "string" != typeof a) return d;
            if (1 !== (i = b.nodeType) && 9 !== i) return [];
            if (n && !e) {
                if (f = Z.exec(a)) if (h = f[1]) {
                    if (9 === i) {
                        if (g = b.getElementById(h), !g || !g.parentNode) return d;
                        if (g.id === h) return d.push(g), d
                    } else if (b.ownerDocument && (g = b.ownerDocument.getElementById(h)) && r(b, g) && g.id === h) return d.push(g), d
                } else {
                    if (f[2]) return G.apply(d, b.getElementsByTagName(a)), d;
                    if ((h = f[3]) && c.getElementsByClassName && b.getElementsByClassName) return G.apply(d, b.getElementsByClassName(h)), d
                }
                if (c.qsa && (!o || !o.test(a))) {
                    if (q = p = s, u = b, v = 9 === i && a, 1 === i && "object" !== b.nodeName.toLowerCase()) {
                        m = ob(a), (p = b.getAttribute("id")) ? q = p.replace(_, "\\$&") : b.setAttribute("id", q), q = "[id='" + q + "'] ", j = m.length;
                        while (j--) m[j] = q + pb(m[j]);
                        u = $.test(a) && mb(b.parentNode) || b, v = m.join(",")
                    }
                    if (v) try {
                        return G.apply(d, u.querySelectorAll(v)), d
                    } catch (w) {
                    } finally {
                        p || b.removeAttribute("id")
                    }
                }
            }
            return xb(a.replace(P, "$1"), b, d, e)
        }

        function eb() {
            var a = [];

            function b(c, e) {
                return a.push(c + " ") > d.cacheLength && delete b[a.shift()], b[c + " "] = e
            }

            return b
        }

        function fb(a) {
            return a[s] = !0, a
        }

        function gb(a) {
            var b = l.createElement("div");
            try {
                return !!a(b)
            } catch (c) {
                return !1
            } finally {
                b.parentNode && b.parentNode.removeChild(b), b = null
            }
        }

        function hb(a, b) {
            var c = a.split("|"), e = a.length;
            while (e--) d.attrHandle[c[e]] = b
        }

        function ib(a, b) {
            var c = b && a,
                d = c && 1 === a.nodeType && 1 === b.nodeType && (~b.sourceIndex || B) - (~a.sourceIndex || B);
            if (d) return d;
            if (c) while (c = c.nextSibling) if (c === b) return -1;
            return a ? 1 : -1
        }

        function jb(a) {
            return function (b) {
                var c = b.nodeName.toLowerCase();
                return "input" === c && b.type === a
            }
        }

        function kb(a) {
            return function (b) {
                var c = b.nodeName.toLowerCase();
                return ("input" === c || "button" === c) && b.type === a
            }
        }

        function lb(a) {
            return fb(function (b) {
                return b = +b, fb(function (c, d) {
                    var e, f = a([], c.length, b), g = f.length;
                    while (g--) c[e = f[g]] && (c[e] = !(d[e] = c[e]))
                })
            })
        }

        function mb(a) {
            return a && typeof a.getElementsByTagName !== A && a
        }

        c = db.support = {}, f = db.isXML = function (a) {
            var b = a && (a.ownerDocument || a).documentElement;
            return b ? "HTML" !== b.nodeName : !1
        }, k = db.setDocument = function (a) {
            var b, e = a ? a.ownerDocument || a : t, g = e.defaultView;
            return e !== l && 9 === e.nodeType && e.documentElement ? (l = e, m = e.documentElement, n = !f(e), g && g !== g.top && (g.addEventListener ? g.addEventListener("unload", function () {
                k()
            }, !1) : g.attachEvent && g.attachEvent("onunload", function () {
                k()
            })), c.attributes = gb(function (a) {
                return a.className = "i", !a.getAttribute("className")
            }), c.getElementsByTagName = gb(function (a) {
                return a.appendChild(e.createComment("")), !a.getElementsByTagName("*").length
            }), c.getElementsByClassName = Y.test(e.getElementsByClassName) && gb(function (a) {
                return a.innerHTML = "<div class='a'></div><div class='a i'></div>", a.firstChild.className = "i", 2 === a.getElementsByClassName("i").length
            }), c.getById = gb(function (a) {
                return m.appendChild(a).id = s, !e.getElementsByName || !e.getElementsByName(s).length
            }), c.getById ? (d.find.ID = function (a, b) {
                if (typeof b.getElementById !== A && n) {
                    var c = b.getElementById(a);
                    return c && c.parentNode ? [c] : []
                }
            }, d.filter.ID = function (a) {
                var b = a.replace(ab, bb);
                return function (a) {
                    return a.getAttribute("id") === b
                }
            }) : (delete d.find.ID, d.filter.ID = function (a) {
                var b = a.replace(ab, bb);
                return function (a) {
                    var c = typeof a.getAttributeNode !== A && a.getAttributeNode("id");
                    return c && c.value === b
                }
            }), d.find.TAG = c.getElementsByTagName ? function (a, b) {
                return typeof b.getElementsByTagName !== A ? b.getElementsByTagName(a) : void 0
            } : function (a, b) {
                var c, d = [], e = 0, f = b.getElementsByTagName(a);
                if ("*" === a) {
                    while (c = f[e++]) 1 === c.nodeType && d.push(c);
                    return d
                }
                return f
            }, d.find.CLASS = c.getElementsByClassName && function (a, b) {
                return typeof b.getElementsByClassName !== A && n ? b.getElementsByClassName(a) : void 0
            }, p = [], o = [], (c.qsa = Y.test(e.querySelectorAll)) && (gb(function (a) {
                a.innerHTML = "<select t=''><option selected=''></option></select>", a.querySelectorAll("[t^='']").length && o.push("[*^$]=" + K + "*(?:''|\"\")"), a.querySelectorAll("[selected]").length || o.push("\\[" + K + "*(?:value|" + J + ")"), a.querySelectorAll(":checked").length || o.push(":checked")
            }), gb(function (a) {
                var b = e.createElement("input");
                b.setAttribute("type", "hidden"), a.appendChild(b).setAttribute("name", "D"), a.querySelectorAll("[name=d]").length && o.push("name" + K + "*[*^$|!~]?="), a.querySelectorAll(":enabled").length || o.push(":enabled", ":disabled"), a.querySelectorAll("*,:x"), o.push(",.*:")
            })), (c.matchesSelector = Y.test(q = m.webkitMatchesSelector || m.mozMatchesSelector || m.oMatchesSelector || m.msMatchesSelector)) && gb(function (a) {
                c.disconnectedMatch = q.call(a, "div"), q.call(a, "[s!='']:x"), p.push("!=", O)
            }), o = o.length && new RegExp(o.join("|")), p = p.length && new RegExp(p.join("|")), b = Y.test(m.compareDocumentPosition), r = b || Y.test(m.contains) ? function (a, b) {
                var c = 9 === a.nodeType ? a.documentElement : a, d = b && b.parentNode;
                return a === d || !(!d || 1 !== d.nodeType || !(c.contains ? c.contains(d) : a.compareDocumentPosition && 16 & a.compareDocumentPosition(d)))
            } : function (a, b) {
                if (b) while (b = b.parentNode) if (b === a) return !0;
                return !1
            }, z = b ? function (a, b) {
                if (a === b) return j = !0, 0;
                var d = !a.compareDocumentPosition - !b.compareDocumentPosition;
                return d ? d : (d = (a.ownerDocument || a) === (b.ownerDocument || b) ? a.compareDocumentPosition(b) : 1, 1 & d || !c.sortDetached && b.compareDocumentPosition(a) === d ? a === e || a.ownerDocument === t && r(t, a) ? -1 : b === e || b.ownerDocument === t && r(t, b) ? 1 : i ? I.call(i, a) - I.call(i, b) : 0 : 4 & d ? -1 : 1)
            } : function (a, b) {
                if (a === b) return j = !0, 0;
                var c, d = 0, f = a.parentNode, g = b.parentNode, h = [a], k = [b];
                if (!f || !g) return a === e ? -1 : b === e ? 1 : f ? -1 : g ? 1 : i ? I.call(i, a) - I.call(i, b) : 0;
                if (f === g) return ib(a, b);
                c = a;
                while (c = c.parentNode) h.unshift(c);
                c = b;
                while (c = c.parentNode) k.unshift(c);
                while (h[d] === k[d]) d++;
                return d ? ib(h[d], k[d]) : h[d] === t ? -1 : k[d] === t ? 1 : 0
            }, e) : l
        }, db.matches = function (a, b) {
            return db(a, null, null, b)
        }, db.matchesSelector = function (a, b) {
            if ((a.ownerDocument || a) !== l && k(a), b = b.replace(S, "='$1']"), !(!c.matchesSelector || !n || p && p.test(b) || o && o.test(b))) try {
                var d = q.call(a, b);
                if (d || c.disconnectedMatch || a.document && 11 !== a.document.nodeType) return d
            } catch (e) {
            }
            return db(b, l, null, [a]).length > 0
        }, db.contains = function (a, b) {
            return (a.ownerDocument || a) !== l && k(a), r(a, b)
        }, db.attr = function (a, b) {
            (a.ownerDocument || a) !== l && k(a);
            var e = d.attrHandle[b.toLowerCase()],
                f = e && C.call(d.attrHandle, b.toLowerCase()) ? e(a, b, !n) : void 0;
            return void 0 !== f ? f : c.attributes || !n ? a.getAttribute(b) : (f = a.getAttributeNode(b)) && f.specified ? f.value : null
        }, db.error = function (a) {
            throw new Error("Syntax error, unrecognized expression: " + a)
        }, db.uniqueSort = function (a) {
            var b, d = [], e = 0, f = 0;
            if (j = !c.detectDuplicates, i = !c.sortStable && a.slice(0), a.sort(z), j) {
                while (b = a[f++]) b === a[f] && (e = d.push(f));
                while (e--) a.splice(d[e], 1)
            }
            return i = null, a
        }, e = db.getText = function (a) {
            var b, c = "", d = 0, f = a.nodeType;
            if (f) {
                if (1 === f || 9 === f || 11 === f) {
                    if ("string" == typeof a.textContent) return a.textContent;
                    for (a = a.firstChild; a; a = a.nextSibling) c += e(a)
                } else if (3 === f || 4 === f) return a.nodeValue
            } else while (b = a[d++]) c += e(b);
            return c
        }, d = db.selectors = {
            cacheLength: 50,
            createPseudo: fb,
            match: V,
            attrHandle: {},
            find: {},
            relative: {
                ">": {dir: "parentNode", first: !0},
                " ": {dir: "parentNode"},
                "+": {dir: "previousSibling", first: !0},
                "~": {dir: "previousSibling"}
            },
            preFilter: {
                ATTR: function (a) {
                    return a[1] = a[1].replace(ab, bb), a[3] = (a[4] || a[5] || "").replace(ab, bb), "~=" === a[2] && (a[3] = " " + a[3] + " "), a.slice(0, 4)
                }, CHILD: function (a) {
                    return a[1] = a[1].toLowerCase(), "nth" === a[1].slice(0, 3) ? (a[3] || db.error(a[0]), a[4] = +(a[4] ? a[5] + (a[6] || 1) : 2 * ("even" === a[3] || "odd" === a[3])), a[5] = +(a[7] + a[8] || "odd" === a[3])) : a[3] && db.error(a[0]), a
                }, PSEUDO: function (a) {
                    var b, c = !a[5] && a[2];
                    return V.CHILD.test(a[0]) ? null : (a[3] && void 0 !== a[4] ? a[2] = a[4] : c && T.test(c) && (b = ob(c, !0)) && (b = c.indexOf(")", c.length - b) - c.length) && (a[0] = a[0].slice(0, b), a[2] = c.slice(0, b)), a.slice(0, 3))
                }
            },
            filter: {
                TAG: function (a) {
                    var b = a.replace(ab, bb).toLowerCase();
                    return "*" === a ? function () {
                        return !0
                    } : function (a) {
                        return a.nodeName && a.nodeName.toLowerCase() === b
                    }
                }, CLASS: function (a) {
                    var b = w[a + " "];
                    return b || (b = new RegExp("(^|" + K + ")" + a + "(" + K + "|$)")) && w(a, function (a) {
                        return b.test("string" == typeof a.className && a.className || typeof a.getAttribute !== A && a.getAttribute("class") || "")
                    })
                }, ATTR: function (a, b, c) {
                    return function (d) {
                        var e = db.attr(d, a);
                        return null == e ? "!=" === b : b ? (e += "", "=" === b ? e === c : "!=" === b ? e !== c : "^=" === b ? c && 0 === e.indexOf(c) : "*=" === b ? c && e.indexOf(c) > -1 : "$=" === b ? c && e.slice(-c.length) === c : "~=" === b ? (" " + e + " ").indexOf(c) > -1 : "|=" === b ? e === c || e.slice(0, c.length + 1) === c + "-" : !1) : !0
                    }
                }, CHILD: function (a, b, c, d, e) {
                    var f = "nth" !== a.slice(0, 3), g = "last" !== a.slice(-4), h = "of-type" === b;
                    return 1 === d && 0 === e ? function (a) {
                        return !!a.parentNode
                    } : function (b, c, i) {
                        var j, k, l, m, n, o, p = f !== g ? "nextSibling" : "previousSibling", q = b.parentNode,
                            r = h && b.nodeName.toLowerCase(), t = !i && !h;
                        if (q) {
                            if (f) {
                                while (p) {
                                    l = b;
                                    while (l = l[p]) if (h ? l.nodeName.toLowerCase() === r : 1 === l.nodeType) return !1;
                                    o = p = "only" === a && !o && "nextSibling"
                                }
                                return !0
                            }
                            if (o = [g ? q.firstChild : q.lastChild], g && t) {
                                k = q[s] || (q[s] = {}), j = k[a] || [], n = j[0] === u && j[1], m = j[0] === u && j[2], l = n && q.childNodes[n];
                                while (l = ++n && l && l[p] || (m = n = 0) || o.pop()) if (1 === l.nodeType && ++m && l === b) {
                                    k[a] = [u, n, m];
                                    break
                                }
                            } else if (t && (j = (b[s] || (b[s] = {}))[a]) && j[0] === u) m = j[1]; else while (l = ++n && l && l[p] || (m = n = 0) || o.pop()) if ((h ? l.nodeName.toLowerCase() === r : 1 === l.nodeType) && ++m && (t && ((l[s] || (l[s] = {}))[a] = [u, m]), l === b)) break;
                            return m -= e, m === d || m % d === 0 && m / d >= 0
                        }
                    }
                }, PSEUDO: function (a, b) {
                    var c, e = d.pseudos[a] || d.setFilters[a.toLowerCase()] || db.error("unsupported pseudo: " + a);
                    return e[s] ? e(b) : e.length > 1 ? (c = [a, a, "", b], d.setFilters.hasOwnProperty(a.toLowerCase()) ? fb(function (a, c) {
                        var d, f = e(a, b), g = f.length;
                        while (g--) d = I.call(a, f[g]), a[d] = !(c[d] = f[g])
                    }) : function (a) {
                        return e(a, 0, c)
                    }) : e
                }
            },
            pseudos: {
                not: fb(function (a) {
                    var b = [], c = [], d = g(a.replace(P, "$1"));
                    return d[s] ? fb(function (a, b, c, e) {
                        var f, g = d(a, null, e, []), h = a.length;
                        while (h--) (f = g[h]) && (a[h] = !(b[h] = f))
                    }) : function (a, e, f) {
                        return b[0] = a, d(b, null, f, c), !c.pop()
                    }
                }), has: fb(function (a) {
                    return function (b) {
                        return db(a, b).length > 0
                    }
                }), contains: fb(function (a) {
                    return function (b) {
                        return (b.textContent || b.innerText || e(b)).indexOf(a) > -1
                    }
                }), lang: fb(function (a) {
                    return U.test(a || "") || db.error("unsupported lang: " + a), a = a.replace(ab, bb).toLowerCase(), function (b) {
                        var c;
                        do if (c = n ? b.lang : b.getAttribute("xml:lang") || b.getAttribute("lang")) return c = c.toLowerCase(), c === a || 0 === c.indexOf(a + "-"); while ((b = b.parentNode) && 1 === b.nodeType);
                        return !1
                    }
                }), target: function (b) {
                    var c = a.location && a.location.hash;
                    return c && c.slice(1) === b.id
                }, root: function (a) {
                    return a === m
                }, focus: function (a) {
                    return a === l.activeElement && (!l.hasFocus || l.hasFocus()) && !!(a.type || a.href || ~a.tabIndex)
                }, enabled: function (a) {
                    return a.disabled === !1
                }, disabled: function (a) {
                    return a.disabled === !0
                }, checked: function (a) {
                    var b = a.nodeName.toLowerCase();
                    return "input" === b && !!a.checked || "option" === b && !!a.selected
                }, selected: function (a) {
                    return a.parentNode && a.parentNode.selectedIndex, a.selected === !0
                }, empty: function (a) {
                    for (a = a.firstChild; a; a = a.nextSibling) if (a.nodeType < 6) return !1;
                    return !0
                }, parent: function (a) {
                    return !d.pseudos.empty(a)
                }, header: function (a) {
                    return X.test(a.nodeName)
                }, input: function (a) {
                    return W.test(a.nodeName)
                }, button: function (a) {
                    var b = a.nodeName.toLowerCase();
                    return "input" === b && "button" === a.type || "button" === b
                }, text: function (a) {
                    var b;
                    return "input" === a.nodeName.toLowerCase() && "text" === a.type && (null == (b = a.getAttribute("type")) || "text" === b.toLowerCase())
                }, first: lb(function () {
                    return [0]
                }), last: lb(function (a, b) {
                    return [b - 1]
                }), eq: lb(function (a, b, c) {
                    return [0 > c ? c + b : c]
                }), even: lb(function (a, b) {
                    for (var c = 0; b > c; c += 2) a.push(c);
                    return a
                }), odd: lb(function (a, b) {
                    for (var c = 1; b > c; c += 2) a.push(c);
                    return a
                }), lt: lb(function (a, b, c) {
                    for (var d = 0 > c ? c + b : c; --d >= 0;) a.push(d);
                    return a
                }), gt: lb(function (a, b, c) {
                    for (var d = 0 > c ? c + b : c; ++d < b;) a.push(d);
                    return a
                })
            }
        }, d.pseudos.nth = d.pseudos.eq;
        for (b in{radio: !0, checkbox: !0, file: !0, password: !0, image: !0}) d.pseudos[b] = jb(b);
        for (b in{submit: !0, reset: !0}) d.pseudos[b] = kb(b);

        function nb() {
        }

        nb.prototype = d.filters = d.pseudos, d.setFilters = new nb;

        function ob(a, b) {
            var c, e, f, g, h, i, j, k = x[a + " "];
            if (k) return b ? 0 : k.slice(0);
            h = a, i = [], j = d.preFilter;
            while (h) {
                (!c || (e = Q.exec(h))) && (e && (h = h.slice(e[0].length) || h), i.push(f = [])), c = !1, (e = R.exec(h)) && (c = e.shift(), f.push({
                    value: c,
                    type: e[0].replace(P, " ")
                }), h = h.slice(c.length));
                for (g in d.filter) !(e = V[g].exec(h)) || j[g] && !(e = j[g](e)) || (c = e.shift(), f.push({
                    value: c,
                    type: g,
                    matches: e
                }), h = h.slice(c.length));
                if (!c) break
            }
            return b ? h.length : h ? db.error(a) : x(a, i).slice(0)
        }

        function pb(a) {
            for (var b = 0, c = a.length, d = ""; c > b; b++) d += a[b].value;
            return d
        }

        function qb(a, b, c) {
            var d = b.dir, e = c && "parentNode" === d, f = v++;
            return b.first ? function (b, c, f) {
                while (b = b[d]) if (1 === b.nodeType || e) return a(b, c, f)
            } : function (b, c, g) {
                var h, i, j = [u, f];
                if (g) {
                    while (b = b[d]) if ((1 === b.nodeType || e) && a(b, c, g)) return !0
                } else while (b = b[d]) if (1 === b.nodeType || e) {
                    if (i = b[s] || (b[s] = {}), (h = i[d]) && h[0] === u && h[1] === f) return j[2] = h[2];
                    if (i[d] = j, j[2] = a(b, c, g)) return !0
                }
            }
        }

        function rb(a) {
            return a.length > 1 ? function (b, c, d) {
                var e = a.length;
                while (e--) if (!a[e](b, c, d)) return !1;
                return !0
            } : a[0]
        }

        function sb(a, b, c, d, e) {
            for (var f, g = [], h = 0, i = a.length, j = null != b; i > h; h++) (f = a[h]) && (!c || c(f, d, e)) && (g.push(f), j && b.push(h));
            return g
        }

        function tb(a, b, c, d, e, f) {
            return d && !d[s] && (d = tb(d)), e && !e[s] && (e = tb(e, f)), fb(function (f, g, h, i) {
                var j, k, l, m = [], n = [], o = g.length, p = f || wb(b || "*", h.nodeType ? [h] : h, []),
                    q = !a || !f && b ? p : sb(p, m, a, h, i), r = c ? e || (f ? a : o || d) ? [] : g : q;
                if (c && c(q, r, h, i), d) {
                    j = sb(r, n), d(j, [], h, i), k = j.length;
                    while (k--) (l = j[k]) && (r[n[k]] = !(q[n[k]] = l))
                }
                if (f) {
                    if (e || a) {
                        if (e) {
                            j = [], k = r.length;
                            while (k--) (l = r[k]) && j.push(q[k] = l);
                            e(null, r = [], j, i)
                        }
                        k = r.length;
                        while (k--) (l = r[k]) && (j = e ? I.call(f, l) : m[k]) > -1 && (f[j] = !(g[j] = l))
                    }
                } else r = sb(r === g ? r.splice(o, r.length) : r), e ? e(null, g, r, i) : G.apply(g, r)
            })
        }

        function ub(a) {
            for (var b, c, e, f = a.length, g = d.relative[a[0].type], i = g || d.relative[" "], j = g ? 1 : 0, k = qb(function (a) {
                return a === b
            }, i, !0), l = qb(function (a) {
                return I.call(b, a) > -1
            }, i, !0), m = [function (a, c, d) {
                return !g && (d || c !== h) || ((b = c).nodeType ? k(a, c, d) : l(a, c, d))
            }]; f > j; j++) if (c = d.relative[a[j].type]) m = [qb(rb(m), c)]; else {
                if (c = d.filter[a[j].type].apply(null, a[j].matches), c[s]) {
                    for (e = ++j; f > e; e++) if (d.relative[a[e].type]) break;
                    return tb(j > 1 && rb(m), j > 1 && pb(a.slice(0, j - 1).concat({value: " " === a[j - 2].type ? "*" : ""})).replace(P, "$1"), c, e > j && ub(a.slice(j, e)), f > e && ub(a = a.slice(e)), f > e && pb(a))
                }
                m.push(c)
            }
            return rb(m)
        }

        function vb(a, b) {
            var c = b.length > 0, e = a.length > 0, f = function (f, g, i, j, k) {
                var m, n, o, p = 0, q = "0", r = f && [], s = [], t = h, v = f || e && d.find.TAG("*", k),
                    w = u += null == t ? 1 : Math.random() || .1, x = v.length;
                for (k && (h = g !== l && g); q !== x && null != (m = v[q]); q++) {
                    if (e && m) {
                        n = 0;
                        while (o = a[n++]) if (o(m, g, i)) {
                            j.push(m);
                            break
                        }
                        k && (u = w)
                    }
                    c && ((m = !o && m) && p--, f && r.push(m))
                }
                if (p += q, c && q !== p) {
                    n = 0;
                    while (o = b[n++]) o(r, s, g, i);
                    if (f) {
                        if (p > 0) while (q--) r[q] || s[q] || (s[q] = E.call(j));
                        s = sb(s)
                    }
                    G.apply(j, s), k && !f && s.length > 0 && p + b.length > 1 && db.uniqueSort(j)
                }
                return k && (u = w, h = t), r
            };
            return c ? fb(f) : f
        }

        g = db.compile = function (a, b) {
            var c, d = [], e = [], f = y[a + " "];
            if (!f) {
                b || (b = ob(a)), c = b.length;
                while (c--) f = ub(b[c]), f[s] ? d.push(f) : e.push(f);
                f = y(a, vb(e, d))
            }
            return f
        };

        function wb(a, b, c) {
            for (var d = 0, e = b.length; e > d; d++) db(a, b[d], c);
            return c
        }

        function xb(a, b, e, f) {
            var h, i, j, k, l, m = ob(a);
            if (!f && 1 === m.length) {
                if (i = m[0] = m[0].slice(0), i.length > 2 && "ID" === (j = i[0]).type && c.getById && 9 === b.nodeType && n && d.relative[i[1].type]) {
                    if (b = (d.find.ID(j.matches[0].replace(ab, bb), b) || [])[0], !b) return e;
                    a = a.slice(i.shift().value.length)
                }
                h = V.needsContext.test(a) ? 0 : i.length;
                while (h--) {
                    if (j = i[h], d.relative[k = j.type]) break;
                    if ((l = d.find[k]) && (f = l(j.matches[0].replace(ab, bb), $.test(i[0].type) && mb(b.parentNode) || b))) {
                        if (i.splice(h, 1), a = f.length && pb(i), !a) return G.apply(e, f), e;
                        break
                    }
                }
            }
            return g(a, m)(f, b, !n, e, $.test(a) && mb(b.parentNode) || b), e
        }

        return c.sortStable = s.split("").sort(z).join("") === s, c.detectDuplicates = !!j, k(), c.sortDetached = gb(function (a) {
            return 1 & a.compareDocumentPosition(l.createElement("div"))
        }), gb(function (a) {
            return a.innerHTML = "<a href='#'></a>", "#" === a.firstChild.getAttribute("href")
        }) || hb("type|href|height|width", function (a, b, c) {
            return c ? void 0 : a.getAttribute(b, "type" === b.toLowerCase() ? 1 : 2)
        }), c.attributes && gb(function (a) {
            return a.innerHTML = "<input/>", a.firstChild.setAttribute("value", ""), "" === a.firstChild.getAttribute("value")
        }) || hb("value", function (a, b, c) {
            return c || "input" !== a.nodeName.toLowerCase() ? void 0 : a.defaultValue
        }), gb(function (a) {
            return null == a.getAttribute("disabled")
        }) || hb(J, function (a, b, c) {
            var d;
            return c ? void 0 : a[b] === !0 ? b.toLowerCase() : (d = a.getAttributeNode(b)) && d.specified ? d.value : null
        }), db
    }(a);
    n.find = t, n.expr = t.selectors, n.expr[":"] = n.expr.pseudos, n.unique = t.uniqueSort, n.text = t.getText, n.isXMLDoc = t.isXML, n.contains = t.contains;
    var u = n.expr.match.needsContext, v = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, w = /^.[^:#\[\.,]*$/;

    function x(a, b, c) {
        if (n.isFunction(b)) return n.grep(a, function (a, d) {
            return !!b.call(a, d, a) !== c
        });
        if (b.nodeType) return n.grep(a, function (a) {
            return a === b !== c
        });
        if ("string" == typeof b) {
            if (w.test(b)) return n.filter(b, a, c);
            b = n.filter(b, a)
        }
        return n.grep(a, function (a) {
            return n.inArray(a, b) >= 0 !== c
        })
    }

    n.filter = function (a, b, c) {
        var d = b[0];
        return c && (a = ":not(" + a + ")"), 1 === b.length && 1 === d.nodeType ? n.find.matchesSelector(d, a) ? [d] : [] : n.find.matches(a, n.grep(b, function (a) {
            return 1 === a.nodeType
        }))
    }, n.fn.extend({
        find: function (a) {
            var b, c = [], d = this, e = d.length;
            if ("string" != typeof a) return this.pushStack(n(a).filter(function () {
                for (b = 0; e > b; b++) if (n.contains(d[b], this)) return !0
            }));
            for (b = 0; e > b; b++) n.find(a, d[b], c);
            return c = this.pushStack(e > 1 ? n.unique(c) : c), c.selector = this.selector ? this.selector + " " + a : a, c
        }, filter: function (a) {
            return this.pushStack(x(this, a || [], !1))
        }, not: function (a) {
            return this.pushStack(x(this, a || [], !0))
        }, is: function (a) {
            return !!x(this, "string" == typeof a && u.test(a) ? n(a) : a || [], !1).length
        }
    });
    var y, z = a.document, A = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, B = n.fn.init = function (a, b) {
        var c, d;
        if (!a) return this;
        if ("string" == typeof a) {
            if (c = "<" === a.charAt(0) && ">" === a.charAt(a.length - 1) && a.length >= 3 ? [null, a, null] : A.exec(a), !c || !c[1] && b) return !b || b.jquery ? (b || y).find(a) : this.constructor(b).find(a);
            if (c[1]) {
                if (b = b instanceof n ? b[0] : b, n.merge(this, n.parseHTML(c[1], b && b.nodeType ? b.ownerDocument || b : z, !0)), v.test(c[1]) && n.isPlainObject(b)) for (c in b) n.isFunction(this[c]) ? this[c](b[c]) : this.attr(c, b[c]);
                return this
            }
            if (d = z.getElementById(c[2]), d && d.parentNode) {
                if (d.id !== c[2]) return y.find(a);
                this.length = 1, this[0] = d
            }
            return this.context = z, this.selector = a, this
        }
        return a.nodeType ? (this.context = this[0] = a, this.length = 1, this) : n.isFunction(a) ? "undefined" != typeof y.ready ? y.ready(a) : a(n) : (void 0 !== a.selector && (this.selector = a.selector, this.context = a.context), n.makeArray(a, this))
    };
    B.prototype = n.fn, y = n(z);
    var C = /^(?:parents|prev(?:Until|All))/, D = {children: !0, contents: !0, next: !0, prev: !0};
    n.extend({
        dir: function (a, b, c) {
            var d = [], e = a[b];
            while (e && 9 !== e.nodeType && (void 0 === c || 1 !== e.nodeType || !n(e).is(c))) 1 === e.nodeType && d.push(e), e = e[b];
            return d
        }, sibling: function (a, b) {
            for (var c = []; a; a = a.nextSibling) 1 === a.nodeType && a !== b && c.push(a);
            return c
        }
    }), n.fn.extend({
        has: function (a) {
            var b, c = n(a, this), d = c.length;
            return this.filter(function () {
                for (b = 0; d > b; b++) if (n.contains(this, c[b])) return !0
            })
        }, closest: function (a, b) {
            for (var c, d = 0, e = this.length, f = [], g = u.test(a) || "string" != typeof a ? n(a, b || this.context) : 0; e > d; d++) for (c = this[d]; c && c !== b; c = c.parentNode) if (c.nodeType < 11 && (g ? g.index(c) > -1 : 1 === c.nodeType && n.find.matchesSelector(c, a))) {
                f.push(c);
                break
            }
            return this.pushStack(f.length > 1 ? n.unique(f) : f)
        }, index: function (a) {
            return a ? "string" == typeof a ? n.inArray(this[0], n(a)) : n.inArray(a.jquery ? a[0] : a, this) : this[0] && this[0].parentNode ? this.first().prevAll().length : -1
        }, add: function (a, b) {
            return this.pushStack(n.unique(n.merge(this.get(), n(a, b))))
        }, addBack: function (a) {
            return this.add(null == a ? this.prevObject : this.prevObject.filter(a))
        }
    });

    function E(a, b) {
        do a = a[b]; while (a && 1 !== a.nodeType);
        return a
    }

    n.each({
        parent: function (a) {
            var b = a.parentNode;
            return b && 11 !== b.nodeType ? b : null
        }, parents: function (a) {
            return n.dir(a, "parentNode")
        }, parentsUntil: function (a, b, c) {
            return n.dir(a, "parentNode", c)
        }, next: function (a) {
            return E(a, "nextSibling")
        }, prev: function (a) {
            return E(a, "previousSibling")
        }, nextAll: function (a) {
            return n.dir(a, "nextSibling")
        }, prevAll: function (a) {
            return n.dir(a, "previousSibling")
        }, nextUntil: function (a, b, c) {
            return n.dir(a, "nextSibling", c)
        }, prevUntil: function (a, b, c) {
            return n.dir(a, "previousSibling", c)
        }, siblings: function (a) {
            return n.sibling((a.parentNode || {}).firstChild, a)
        }, children: function (a) {
            return n.sibling(a.firstChild)
        }, contents: function (a) {
            return n.nodeName(a, "iframe") ? a.contentDocument || a.contentWindow.document : n.merge([], a.childNodes)
        }
    }, function (a, b) {
        n.fn[a] = function (c, d) {
            var e = n.map(this, b, c);
            return "Until" !== a.slice(-5) && (d = c), d && "string" == typeof d && (e = n.filter(d, e)), this.length > 1 && (D[a] || (e = n.unique(e)), C.test(a) && (e = e.reverse())), this.pushStack(e)
        }
    });
    var F = /\S+/g, G = {};

    function H(a) {
        var b = G[a] = {};
        return n.each(a.match(F) || [], function (a, c) {
            b[c] = !0
        }), b
    }

    n.Callbacks = function (a) {
        a = "string" == typeof a ? G[a] || H(a) : n.extend({}, a);
        var b, c, d, e, f, g, h = [], i = !a.once && [], j = function (l) {
            for (c = a.memory && l, d = !0, f = g || 0, g = 0, e = h.length, b = !0; h && e > f; f++) if (h[f].apply(l[0], l[1]) === !1 && a.stopOnFalse) {
                c = !1;
                break
            }
            b = !1, h && (i ? i.length && j(i.shift()) : c ? h = [] : k.disable())
        }, k = {
            add: function () {
                if (h) {
                    var d = h.length;
                    !function f(b) {
                        n.each(b, function (b, c) {
                            var d = n.type(c);
                            "function" === d ? a.unique && k.has(c) || h.push(c) : c && c.length && "string" !== d && f(c)
                        })
                    }(arguments), b ? e = h.length : c && (g = d, j(c))
                }
                return this
            }, remove: function () {
                return h && n.each(arguments, function (a, c) {
                    var d;
                    while ((d = n.inArray(c, h, d)) > -1) h.splice(d, 1), b && (e >= d && e--, f >= d && f--)
                }), this
            }, has: function (a) {
                return a ? n.inArray(a, h) > -1 : !(!h || !h.length)
            }, empty: function () {
                return h = [], e = 0, this
            }, disable: function () {
                return h = i = c = void 0, this
            }, disabled: function () {
                return !h
            }, lock: function () {
                return i = void 0, c || k.disable(), this
            }, locked: function () {
                return !i
            }, fireWith: function (a, c) {
                return !h || d && !i || (c = c || [], c = [a, c.slice ? c.slice() : c], b ? i.push(c) : j(c)), this
            }, fire: function () {
                return k.fireWith(this, arguments), this
            }, fired: function () {
                return !!d
            }
        };
        return k
    }, n.extend({
        Deferred: function (a) {
            var b = [["resolve", "done", n.Callbacks("once memory"), "resolved"], ["reject", "fail", n.Callbacks("once memory"), "rejected"], ["notify", "progress", n.Callbacks("memory")]],
                c = "pending", d = {
                    state: function () {
                        return c
                    }, always: function () {
                        return e.done(arguments).fail(arguments), this
                    }, then: function () {
                        var a = arguments;
                        return n.Deferred(function (c) {
                            n.each(b, function (b, f) {
                                var g = n.isFunction(a[b]) && a[b];
                                e[f[1]](function () {
                                    var a = g && g.apply(this, arguments);
                                    a && n.isFunction(a.promise) ? a.promise().done(c.resolve).fail(c.reject).progress(c.notify) : c[f[0] + "With"](this === d ? c.promise() : this, g ? [a] : arguments)
                                })
                            }), a = null
                        }).promise()
                    }, promise: function (a) {
                        return null != a ? n.extend(a, d) : d
                    }
                }, e = {};
            return d.pipe = d.then, n.each(b, function (a, f) {
                var g = f[2], h = f[3];
                d[f[1]] = g.add, h && g.add(function () {
                    c = h
                }, b[1 ^ a][2].disable, b[2][2].lock), e[f[0]] = function () {
                    return e[f[0] + "With"](this === e ? d : this, arguments), this
                }, e[f[0] + "With"] = g.fireWith
            }), d.promise(e), a && a.call(e, e), e
        }, when: function (a) {
            var b = 0, c = d.call(arguments), e = c.length, f = 1 !== e || a && n.isFunction(a.promise) ? e : 0,
                g = 1 === f ? a : n.Deferred(), h = function (a, b, c) {
                    return function (e) {
                        b[a] = this, c[a] = arguments.length > 1 ? d.call(arguments) : e, c === i ? g.notifyWith(b, c) : --f || g.resolveWith(b, c)
                    }
                }, i, j, k;
            if (e > 1) for (i = new Array(e), j = new Array(e), k = new Array(e); e > b; b++) c[b] && n.isFunction(c[b].promise) ? c[b].promise().done(h(b, k, c)).fail(g.reject).progress(h(b, j, i)) : --f;
            return f || g.resolveWith(k, c), g.promise()
        }
    });
    var I;
    n.fn.ready = function (a) {
        return n.ready.promise().done(a), this
    }, n.extend({
        isReady: !1, readyWait: 1, holdReady: function (a) {
            a ? n.readyWait++ : n.ready(!0)
        }, ready: function (a) {
            if (a === !0 ? !--n.readyWait : !n.isReady) {
                if (!z.body) return setTimeout(n.ready);
                n.isReady = !0, a !== !0 && --n.readyWait > 0 || (I.resolveWith(z, [n]), n.fn.trigger && n(z).trigger("ready").off("ready"))
            }
        }
    });

    function J() {
        z.addEventListener ? (z.removeEventListener("DOMContentLoaded", K, !1), a.removeEventListener("load", K, !1)) : (z.detachEvent("onreadystatechange", K), a.detachEvent("onload", K))
    }

    function K() {
        (z.addEventListener || "load" === event.type || "complete" === z.readyState) && (J(), n.ready())
    }

    n.ready.promise = function (b) {
        if (!I) if (I = n.Deferred(), "complete" === z.readyState) setTimeout(n.ready); else if (z.addEventListener) z.addEventListener("DOMContentLoaded", K, !1), a.addEventListener("load", K, !1); else {
            z.attachEvent("onreadystatechange", K), a.attachEvent("onload", K);
            var c = !1;
            try {
                c = null == a.frameElement && z.documentElement
            } catch (d) {
            }
            c && c.doScroll && !function e() {
                if (!n.isReady) {
                    try {
                        c.doScroll("left")
                    } catch (a) {
                        return setTimeout(e, 50)
                    }
                    J(), n.ready()
                }
            }()
        }
        return I.promise(b)
    };
    var L = "undefined", M;
    for (M in n(l)) break;
    l.ownLast = "0" !== M, l.inlineBlockNeedsLayout = !1, n(function () {
        var a, b, c = z.getElementsByTagName("body")[0];
        c && (a = z.createElement("div"), a.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px", b = z.createElement("div"), c.appendChild(a).appendChild(b), typeof b.style.zoom !== L && (b.style.cssText = "border:0;margin:0;width:1px;padding:1px;display:inline;zoom:1", (l.inlineBlockNeedsLayout = 3 === b.offsetWidth) && (c.style.zoom = 1)), c.removeChild(a), a = b = null)
    }), function () {
        var a = z.createElement("div");
        if (null == l.deleteExpando) {
            l.deleteExpando = !0;
            try {
                delete a.test
            } catch (b) {
                l.deleteExpando = !1
            }
        }
        a = null
    }(), n.acceptData = function (a) {
        var b = n.noData[(a.nodeName + " ").toLowerCase()], c = +a.nodeType || 1;
        return 1 !== c && 9 !== c ? !1 : !b || b !== !0 && a.getAttribute("classid") === b
    };
    var N = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, O = /([A-Z])/g;

    function P(a, b, c) {
        if (void 0 === c && 1 === a.nodeType) {
            var d = "data-" + b.replace(O, "-$1").toLowerCase();
            if (c = a.getAttribute(d), "string" == typeof c) {
                try {
                    c = "true" === c ? !0 : "false" === c ? !1 : "null" === c ? null : +c + "" === c ? +c : N.test(c) ? n.parseJSON(c) : c
                } catch (e) {
                }
                n.data(a, b, c)
            } else c = void 0
        }
        return c
    }

    function Q(a) {
        var b;
        for (b in a) if (("data" !== b || !n.isEmptyObject(a[b])) && "toJSON" !== b) return !1;
        return !0
    }

    function R(a, b, d, e) {
        if (n.acceptData(a)) {
            var f, g, h = n.expando, i = a.nodeType, j = i ? n.cache : a, k = i ? a[h] : a[h] && h;
            if (k && j[k] && (e || j[k].data) || void 0 !== d || "string" != typeof b) return k || (k = i ? a[h] = c.pop() || n.guid++ : h), j[k] || (j[k] = i ? {} : {toJSON: n.noop}), ("object" == typeof b || "function" == typeof b) && (e ? j[k] = n.extend(j[k], b) : j[k].data = n.extend(j[k].data, b)), g = j[k], e || (g.data || (g.data = {}), g = g.data), void 0 !== d && (g[n.camelCase(b)] = d), "string" == typeof b ? (f = g[b], null == f && (f = g[n.camelCase(b)])) : f = g, f
        }
    }

    function S(a, b, c) {
        if (n.acceptData(a)) {
            var d, e, f = a.nodeType, g = f ? n.cache : a, h = f ? a[n.expando] : n.expando;
            if (g[h]) {
                if (b && (d = c ? g[h] : g[h].data)) {
                    n.isArray(b) ? b = b.concat(n.map(b, n.camelCase)) : b in d ? b = [b] : (b = n.camelCase(b), b = b in d ? [b] : b.split(" ")), e = b.length;
                    while (e--) delete d[b[e]];
                    if (c ? !Q(d) : !n.isEmptyObject(d)) return
                }
                (c || (delete g[h].data, Q(g[h]))) && (f ? n.cleanData([a], !0) : l.deleteExpando || g != g.window ? delete g[h] : g[h] = null)
            }
        }
    }

    n.extend({
        cache: {},
        noData: {"applet ": !0, "embed ": !0, "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},
        hasData: function (a) {
            return a = a.nodeType ? n.cache[a[n.expando]] : a[n.expando], !!a && !Q(a)
        },
        data: function (a, b, c) {
            return R(a, b, c)
        },
        removeData: function (a, b) {
            return S(a, b)
        },
        _data: function (a, b, c) {
            return R(a, b, c, !0)
        },
        _removeData: function (a, b) {
            return S(a, b, !0)
        }
    }), n.fn.extend({
        data: function (a, b) {
            var c, d, e, f = this[0], g = f && f.attributes;
            if (void 0 === a) {
                if (this.length && (e = n.data(f), 1 === f.nodeType && !n._data(f, "parsedAttrs"))) {
                    c = g.length;
                    while (c--) d = g[c].name, 0 === d.indexOf("data-") && (d = n.camelCase(d.slice(5)), P(f, d, e[d]));
                    n._data(f, "parsedAttrs", !0)
                }
                return e
            }
            return "object" == typeof a ? this.each(function () {
                n.data(this, a)
            }) : arguments.length > 1 ? this.each(function () {
                n.data(this, a, b)
            }) : f ? P(f, a, n.data(f, a)) : void 0
        }, removeData: function (a) {
            return this.each(function () {
                n.removeData(this, a)
            })
        }
    }), n.extend({
        queue: function (a, b, c) {
            var d;
            return a ? (b = (b || "fx") + "queue", d = n._data(a, b), c && (!d || n.isArray(c) ? d = n._data(a, b, n.makeArray(c)) : d.push(c)), d || []) : void 0
        }, dequeue: function (a, b) {
            b = b || "fx";
            var c = n.queue(a, b), d = c.length, e = c.shift(), f = n._queueHooks(a, b), g = function () {
                n.dequeue(a, b)
            };
            "inprogress" === e && (e = c.shift(), d--), e && ("fx" === b && c.unshift("inprogress"), delete f.stop, e.call(a, g, f)), !d && f && f.empty.fire()
        }, _queueHooks: function (a, b) {
            var c = b + "queueHooks";
            return n._data(a, c) || n._data(a, c, {
                empty: n.Callbacks("once memory").add(function () {
                    n._removeData(a, b + "queue"), n._removeData(a, c)
                })
            })
        }
    }), n.fn.extend({
        queue: function (a, b) {
            var c = 2;
            return "string" != typeof a && (b = a, a = "fx", c--), arguments.length < c ? n.queue(this[0], a) : void 0 === b ? this : this.each(function () {
                var c = n.queue(this, a, b);
                n._queueHooks(this, a), "fx" === a && "inprogress" !== c[0] && n.dequeue(this, a)
            })
        }, dequeue: function (a) {
            return this.each(function () {
                n.dequeue(this, a)
            })
        }, clearQueue: function (a) {
            return this.queue(a || "fx", [])
        }, promise: function (a, b) {
            var c, d = 1, e = n.Deferred(), f = this, g = this.length, h = function () {
                --d || e.resolveWith(f, [f])
            };
            "string" != typeof a && (b = a, a = void 0), a = a || "fx";
            while (g--) c = n._data(f[g], a + "queueHooks"), c && c.empty && (d++, c.empty.add(h));
            return h(), e.promise(b)
        }
    });
    var T = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, U = ["Top", "Right", "Bottom", "Left"], V = function (a, b) {
        return a = b || a, "none" === n.css(a, "display") || !n.contains(a.ownerDocument, a)
    }, W = n.access = function (a, b, c, d, e, f, g) {
        var h = 0, i = a.length, j = null == c;
        if ("object" === n.type(c)) {
            e = !0;
            for (h in c) n.access(a, b, h, c[h], !0, f, g)
        } else if (void 0 !== d && (e = !0, n.isFunction(d) || (g = !0), j && (g ? (b.call(a, d), b = null) : (j = b, b = function (a, b, c) {
                return j.call(n(a), c)
            })), b)) for (; i > h; h++) b(a[h], c, g ? d : d.call(a[h], h, b(a[h], c)));
        return e ? a : j ? b.call(a) : i ? b(a[0], c) : f
    }, X = /^(?:checkbox|radio)$/i;
    !function () {
        var a = z.createDocumentFragment(), b = z.createElement("div"), c = z.createElement("input");
        if (b.setAttribute("className", "t"), b.innerHTML = "  <link/><table></table><a href='/a'>a</a>", l.leadingWhitespace = 3 === b.firstChild.nodeType, l.tbody = !b.getElementsByTagName("tbody").length, l.htmlSerialize = !!b.getElementsByTagName("link").length, l.html5Clone = "<:nav></:nav>" !== z.createElement("nav").cloneNode(!0).outerHTML, c.type = "checkbox", c.checked = !0, a.appendChild(c), l.appendChecked = c.checked, b.innerHTML = "<textarea>x</textarea>", l.noCloneChecked = !!b.cloneNode(!0).lastChild.defaultValue, a.appendChild(b), b.innerHTML = "<input type='radio' checked='checked' name='t'/>", l.checkClone = b.cloneNode(!0).cloneNode(!0).lastChild.checked, l.noCloneEvent = !0, b.attachEvent && (b.attachEvent("onclick", function () {
                l.noCloneEvent = !1
            }), b.cloneNode(!0).click()), null == l.deleteExpando) {
            l.deleteExpando = !0;
            try {
                delete b.test
            } catch (d) {
                l.deleteExpando = !1
            }
        }
        a = b = c = null
    }(), function () {
        var b, c, d = z.createElement("div");
        for (b in{
            submit: !0,
            change: !0,
            focusin: !0
        }) c = "on" + b, (l[b + "Bubbles"] = c in a) || (d.setAttribute(c, "t"), l[b + "Bubbles"] = d.attributes[c].expando === !1);
        d = null
    }();
    var Y = /^(?:input|select|textarea)$/i, Z = /^key/, $ = /^(?:mouse|contextmenu)|click/,
        _ = /^(?:focusinfocus|focusoutblur)$/, ab = /^([^.]*)(?:\.(.+)|)$/;

    function bb() {
        return !0
    }

    function cb() {
        return !1
    }

    function db() {
        try {
            return z.activeElement
        } catch (a) {
        }
    }

    n.event = {
        global: {},
        add: function (a, b, c, d, e) {
            var f, g, h, i, j, k, l, m, o, p, q, r = n._data(a);
            if (r) {
                c.handler && (i = c, c = i.handler, e = i.selector), c.guid || (c.guid = n.guid++), (g = r.events) || (g = r.events = {}), (k = r.handle) || (k = r.handle = function (a) {
                    return typeof n === L || a && n.event.triggered === a.type ? void 0 : n.event.dispatch.apply(k.elem, arguments)
                }, k.elem = a), b = (b || "").match(F) || [""], h = b.length;
                while (h--) f = ab.exec(b[h]) || [], o = q = f[1], p = (f[2] || "").split(".").sort(), o && (j = n.event.special[o] || {}, o = (e ? j.delegateType : j.bindType) || o, j = n.event.special[o] || {}, l = n.extend({
                    type: o,
                    origType: q,
                    data: d,
                    handler: c,
                    guid: c.guid,
                    selector: e,
                    needsContext: e && n.expr.match.needsContext.test(e),
                    namespace: p.join(".")
                }, i), (m = g[o]) || (m = g[o] = [], m.delegateCount = 0, j.setup && j.setup.call(a, d, p, k) !== !1 || (a.addEventListener ? a.addEventListener(o, k, !1) : a.attachEvent && a.attachEvent("on" + o, k))), j.add && (j.add.call(a, l), l.handler.guid || (l.handler.guid = c.guid)), e ? m.splice(m.delegateCount++, 0, l) : m.push(l), n.event.global[o] = !0);
                a = null
            }
        },
        remove: function (a, b, c, d, e) {
            var f, g, h, i, j, k, l, m, o, p, q, r = n.hasData(a) && n._data(a);
            if (r && (k = r.events)) {
                b = (b || "").match(F) || [""], j = b.length;
                while (j--) if (h = ab.exec(b[j]) || [], o = q = h[1], p = (h[2] || "").split(".").sort(), o) {
                    l = n.event.special[o] || {}, o = (d ? l.delegateType : l.bindType) || o, m = k[o] || [], h = h[2] && new RegExp("(^|\\.)" + p.join("\\.(?:.*\\.|)") + "(\\.|$)"), i = f = m.length;
                    while (f--) g = m[f], !e && q !== g.origType || c && c.guid !== g.guid || h && !h.test(g.namespace) || d && d !== g.selector && ("**" !== d || !g.selector) || (m.splice(f, 1), g.selector && m.delegateCount--, l.remove && l.remove.call(a, g));
                    i && !m.length && (l.teardown && l.teardown.call(a, p, r.handle) !== !1 || n.removeEvent(a, o, r.handle), delete k[o])
                } else for (o in k) n.event.remove(a, o + b[j], c, d, !0);
                n.isEmptyObject(k) && (delete r.handle, n._removeData(a, "events"))
            }
        },
        trigger: function (b, c, d, e) {
            var f, g, h, i, k, l, m, o = [d || z], p = j.call(b, "type") ? b.type : b,
                q = j.call(b, "namespace") ? b.namespace.split(".") : [];
            if (h = l = d = d || z, 3 !== d.nodeType && 8 !== d.nodeType && !_.test(p + n.event.triggered) && (p.indexOf(".") >= 0 && (q = p.split("."), p = q.shift(), q.sort()), g = p.indexOf(":") < 0 && "on" + p, b = b[n.expando] ? b : new n.Event(p, "object" == typeof b && b), b.isTrigger = e ? 2 : 3, b.namespace = q.join("."), b.namespace_re = b.namespace ? new RegExp("(^|\\.)" + q.join("\\.(?:.*\\.|)") + "(\\.|$)") : null, b.result = void 0, b.target || (b.target = d), c = null == c ? [b] : n.makeArray(c, [b]), k = n.event.special[p] || {}, e || !k.trigger || k.trigger.apply(d, c) !== !1)) {
                if (!e && !k.noBubble && !n.isWindow(d)) {
                    for (i = k.delegateType || p, _.test(i + p) || (h = h.parentNode); h; h = h.parentNode) o.push(h), l = h;
                    l === (d.ownerDocument || z) && o.push(l.defaultView || l.parentWindow || a)
                }
                m = 0;
                while ((h = o[m++]) && !b.isPropagationStopped()) b.type = m > 1 ? i : k.bindType || p, f = (n._data(h, "events") || {})[b.type] && n._data(h, "handle"), f && f.apply(h, c), f = g && h[g], f && f.apply && n.acceptData(h) && (b.result = f.apply(h, c), b.result === !1 && b.preventDefault());
                if (b.type = p, !e && !b.isDefaultPrevented() && (!k._default || k._default.apply(o.pop(), c) === !1) && n.acceptData(d) && g && d[p] && !n.isWindow(d)) {
                    l = d[g], l && (d[g] = null), n.event.triggered = p;
                    try {
                        d[p]()
                    } catch (r) {
                    }
                    n.event.triggered = void 0, l && (d[g] = l)
                }
                return b.result
            }
        },
        dispatch: function (a) {
            a = n.event.fix(a);
            var b, c, e, f, g, h = [], i = d.call(arguments), j = (n._data(this, "events") || {})[a.type] || [],
                k = n.event.special[a.type] || {};
            if (i[0] = a, a.delegateTarget = this, !k.preDispatch || k.preDispatch.call(this, a) !== !1) {
                h = n.event.handlers.call(this, a, j), b = 0;
                while ((f = h[b++]) && !a.isPropagationStopped()) {
                    a.currentTarget = f.elem, g = 0;
                    while ((e = f.handlers[g++]) && !a.isImmediatePropagationStopped()) (!a.namespace_re || a.namespace_re.test(e.namespace)) && (a.handleObj = e, a.data = e.data, c = ((n.event.special[e.origType] || {}).handle || e.handler).apply(f.elem, i), void 0 !== c && (a.result = c) === !1 && (a.preventDefault(), a.stopPropagation()))
                }
                return k.postDispatch && k.postDispatch.call(this, a), a.result
            }
        },
        handlers: function (a, b) {
            var c, d, e, f, g = [], h = b.delegateCount, i = a.target;
            if (h && i.nodeType && (!a.button || "click" !== a.type)) for (; i != this; i = i.parentNode || this) if (1 === i.nodeType && (i.disabled !== !0 || "click" !== a.type)) {
                for (e = [], f = 0; h > f; f++) d = b[f], c = d.selector + " ", void 0 === e[c] && (e[c] = d.needsContext ? n(c, this).index(i) >= 0 : n.find(c, this, null, [i]).length), e[c] && e.push(d);
                e.length && g.push({elem: i, handlers: e})
            }
            return h < b.length && g.push({elem: this, handlers: b.slice(h)}), g
        },
        fix: function (a) {
            if (a[n.expando]) return a;
            var b, c, d, e = a.type, f = a, g = this.fixHooks[e];
            g || (this.fixHooks[e] = g = $.test(e) ? this.mouseHooks : Z.test(e) ? this.keyHooks : {}), d = g.props ? this.props.concat(g.props) : this.props, a = new n.Event(f), b = d.length;
            while (b--) c = d[b], a[c] = f[c];
            return a.target || (a.target = f.srcElement || z), 3 === a.target.nodeType && (a.target = a.target.parentNode), a.metaKey = !!a.metaKey, g.filter ? g.filter(a, f) : a
        },
        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 (a, b) {
                return null == a.which && (a.which = null != b.charCode ? b.charCode : b.keyCode), a
            }
        },
        mouseHooks: {
            props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
            filter: function (a, b) {
                var c, d, e, f = b.button, g = b.fromElement;
                return null == a.pageX && null != b.clientX && (d = a.target.ownerDocument || z, e = d.documentElement, c = d.body, a.pageX = b.clientX + (e && e.scrollLeft || c && c.scrollLeft || 0) - (e && e.clientLeft || c && c.clientLeft || 0), a.pageY = b.clientY + (e && e.scrollTop || c && c.scrollTop || 0) - (e && e.clientTop || c && c.clientTop || 0)), !a.relatedTarget && g && (a.relatedTarget = g === a.target ? b.toElement : g), a.which || void 0 === f || (a.which = 1 & f ? 1 : 2 & f ? 3 : 4 & f ? 2 : 0), a
            }
        },
        special: {
            load: {noBubble: !0}, focus: {
                trigger: function () {
                    if (this !== db() && this.focus) try {
                        return this.focus(), !1
                    } catch (a) {
                    }
                }, delegateType: "focusin"
            }, blur: {
                trigger: function () {
                    return this === db() && this.blur ? (this.blur(), !1) : void 0
                }, delegateType: "focusout"
            }, click: {
                trigger: function () {
                    return n.nodeName(this, "input") && "checkbox" === this.type && this.click ? (this.click(), !1) : void 0
                }, _default: function (a) {
                    return n.nodeName(a.target, "a")
                }
            }, beforeunload: {
                postDispatch: function (a) {
                    void 0 !== a.result && (a.originalEvent.returnValue = a.result)
                }
            }
        },
        simulate: function (a, b, c, d) {
            var e = n.extend(new n.Event, c, {type: a, isSimulated: !0, originalEvent: {}});
            d ? n.event.trigger(e, null, b) : n.event.dispatch.call(b, e), e.isDefaultPrevented() && c.preventDefault()
        }
    }, n.removeEvent = z.removeEventListener ? function (a, b, c) {
        a.removeEventListener && a.removeEventListener(b, c, !1)
    } : function (a, b, c) {
        var d = "on" + b;
        a.detachEvent && (typeof a[d] === L && (a[d] = null), a.detachEvent(d, c))
    }, n.Event = function (a, b) {
        return this instanceof n.Event ? (a && a.type ? (this.originalEvent = a, this.type = a.type, this.isDefaultPrevented = a.defaultPrevented || void 0 === a.defaultPrevented && (a.returnValue === !1 || a.getPreventDefault && a.getPreventDefault()) ? bb : cb) : this.type = a, b && n.extend(this, b), this.timeStamp = a && a.timeStamp || n.now(), void(this[n.expando] = !0)) : new n.Event(a, b)
    }, n.Event.prototype = {
        isDefaultPrevented: cb,
        isPropagationStopped: cb,
        isImmediatePropagationStopped: cb,
        preventDefault: function () {
            var a = this.originalEvent;
            this.isDefaultPrevented = bb, a && (a.preventDefault ? a.preventDefault() : a.returnValue = !1)
        },
        stopPropagation: function () {
            var a = this.originalEvent;
            this.isPropagationStopped = bb, a && (a.stopPropagation && a.stopPropagation(), a.cancelBubble = !0)
        },
        stopImmediatePropagation: function () {
            this.isImmediatePropagationStopped = bb, this.stopPropagation()
        }
    }, n.each({mouseenter: "mouseover", mouseleave: "mouseout"}, function (a, b) {
        n.event.special[a] = {
            delegateType: b, bindType: b, handle: function (a) {
                var c, d = this, e = a.relatedTarget, f = a.handleObj;
                return (!e || e !== d && !n.contains(d, e)) && (a.type = f.origType, c = f.handler.apply(this, arguments), a.type = b), c
            }
        }
    }), l.submitBubbles || (n.event.special.submit = {
        setup: function () {
            return n.nodeName(this, "form") ? !1 : void n.event.add(this, "click._submit keypress._submit", function (a) {
                var b = a.target, c = n.nodeName(b, "input") || n.nodeName(b, "button") ? b.form : void 0;
                c && !n._data(c, "submitBubbles") && (n.event.add(c, "submit._submit", function (a) {
                    a._submit_bubble = !0
                }), n._data(c, "submitBubbles", !0))
            })
        }, postDispatch: function (a) {
            a._submit_bubble && (delete a._submit_bubble, this.parentNode && !a.isTrigger && n.event.simulate("submit", this.parentNode, a, !0))
        }, teardown: function () {
            return n.nodeName(this, "form") ? !1 : void n.event.remove(this, "._submit")
        }
    }), l.changeBubbles || (n.event.special.change = {
        setup: function () {
            return Y.test(this.nodeName) ? (("checkbox" === this.type || "radio" === this.type) && (n.event.add(this, "propertychange._change", function (a) {
                "checked" === a.originalEvent.propertyName && (this._just_changed = !0)
            }), n.event.add(this, "click._change", function (a) {
                this._just_changed && !a.isTrigger && (this._just_changed = !1), n.event.simulate("change", this, a, !0)
            })), !1) : void n.event.add(this, "beforeactivate._change", function (a) {
                var b = a.target;
                Y.test(b.nodeName) && !n._data(b, "changeBubbles") && (n.event.add(b, "change._change", function (a) {
                    !this.parentNode || a.isSimulated || a.isTrigger || n.event.simulate("change", this.parentNode, a, !0)
                }), n._data(b, "changeBubbles", !0))
            })
        }, handle: function (a) {
            var b = a.target;
            return this !== b || a.isSimulated || a.isTrigger || "radio" !== b.type && "checkbox" !== b.type ? a.handleObj.handler.apply(this, arguments) : void 0
        }, teardown: function () {
            return n.event.remove(this, "._change"), !Y.test(this.nodeName)
        }
    }), l.focusinBubbles || n.each({focus: "focusin", blur: "focusout"}, function (a, b) {
        var c = function (a) {
            n.event.simulate(b, a.target, n.event.fix(a), !0)
        };
        n.event.special[b] = {
            setup: function () {
                var d = this.ownerDocument || this, e = n._data(d, b);
                e || d.addEventListener(a, c, !0), n._data(d, b, (e || 0) + 1)
            }, teardown: function () {
                var d = this.ownerDocument || this, e = n._data(d, b) - 1;
                e ? n._data(d, b, e) : (d.removeEventListener(a, c, !0), n._removeData(d, b))
            }
        }
    }), n.fn.extend({
        on: function (a, b, c, d, e) {
            var f, g;
            if ("object" == typeof a) {
                "string" != typeof b && (c = c || b, b = void 0);
                for (f in a) this.on(f, b, c, a[f], e);
                return this
            }
            if (null == c && null == d ? (d = b, c = b = void 0) : null == d && ("string" == typeof b ? (d = c, c = void 0) : (d = c, c = b, b = void 0)), d === !1) d = cb; else if (!d) return this;
            return 1 === e && (g = d, d = function (a) {
                return n().off(a), g.apply(this, arguments)
            }, d.guid = g.guid || (g.guid = n.guid++)), this.each(function () {
                n.event.add(this, a, d, c, b)
            })
        }, one: function (a, b, c, d) {
            return this.on(a, b, c, d, 1)
        }, off: function (a, b, c) {
            var d, e;
            if (a && a.preventDefault && a.handleObj) return d = a.handleObj, n(a.delegateTarget).off(d.namespace ? d.origType + "." + d.namespace : d.origType, d.selector, d.handler), this;
            if ("object" == typeof a) {
                for (e in a) this.off(e, b, a[e]);
                return this
            }
            return (b === !1 || "function" == typeof b) && (c = b, b = void 0), c === !1 && (c = cb), this.each(function () {
                n.event.remove(this, a, c, b)
            })
        }, trigger: function (a, b) {
            return this.each(function () {
                n.event.trigger(a, b, this)
            })
        }, triggerHandler: function (a, b) {
            var c = this[0];
            return c ? n.event.trigger(a, b, c, !0) : void 0
        }
    });

    function eb(a) {
        var b = fb.split("|"), c = a.createDocumentFragment();
        if (c.createElement) while (b.length) c.createElement(b.pop());
        return c
    }

    var fb = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
        gb = / jQuery\d+="(?:null|\d+)"/g, hb = new RegExp("<(?:" + fb + ")[\\s/>]", "i"), ib = /^\s+/,
        jb = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, kb = /<([\w:]+)/,
        lb = /<tbody/i, mb = /<|&#?\w+;/, nb = /<(?:script|style|link)/i, ob = /checked\s*(?:[^=]|=\s*.checked.)/i,
        pb = /^$|\/(?:java|ecma)script/i, qb = /^true\/(.*)/, rb = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g, sb = {
            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>"],
            _default: l.htmlSerialize ? [0, "", ""] : [1, "X<div>", "</div>"]
        }, tb = eb(z), ub = tb.appendChild(z.createElement("div"));
    sb.optgroup = sb.option, sb.tbody = sb.tfoot = sb.colgroup = sb.caption = sb.thead, sb.th = sb.td;

    function vb(a, b) {
        var c, d, e = 0,
            f = typeof a.getElementsByTagName !== L ? a.getElementsByTagName(b || "*") : typeof a.querySelectorAll !== L ? a.querySelectorAll(b || "*") : void 0;
        if (!f) for (f = [], c = a.childNodes || a; null != (d = c[e]); e++) !b || n.nodeName(d, b) ? f.push(d) : n.merge(f, vb(d, b));
        return void 0 === b || b && n.nodeName(a, b) ? n.merge([a], f) : f
    }

    function wb(a) {
        X.test(a.type) && (a.defaultChecked = a.checked)
    }

    function xb(a, b) {
        return n.nodeName(a, "table") && n.nodeName(11 !== b.nodeType ? b : b.firstChild, "tr") ? a.getElementsByTagName("tbody")[0] || a.appendChild(a.ownerDocument.createElement("tbody")) : a
    }

    function yb(a) {
        return a.type = (null !== n.find.attr(a, "type")) + "/" + a.type, a
    }

    function zb(a) {
        var b = qb.exec(a.type);
        return b ? a.type = b[1] : a.removeAttribute("type"), a
    }

    function Ab(a, b) {
        for (var c, d = 0; null != (c = a[d]); d++) n._data(c, "globalEval", !b || n._data(b[d], "globalEval"))
    }

    function Bb(a, b) {
        if (1 === b.nodeType && n.hasData(a)) {
            var c, d, e, f = n._data(a), g = n._data(b, f), h = f.events;
            if (h) {
                delete g.handle, g.events = {};
                for (c in h) for (d = 0, e = h[c].length; e > d; d++) n.event.add(b, c, h[c][d])
            }
            g.data && (g.data = n.extend({}, g.data))
        }
    }

    function Cb(a, b) {
        var c, d, e;
        if (1 === b.nodeType) {
            if (c = b.nodeName.toLowerCase(), !l.noCloneEvent && b[n.expando]) {
                e = n._data(b);
                for (d in e.events) n.removeEvent(b, d, e.handle);
                b.removeAttribute(n.expando)
            }
            "script" === c && b.text !== a.text ? (yb(b).text = a.text, zb(b)) : "object" === c ? (b.parentNode && (b.outerHTML = a.outerHTML), l.html5Clone && a.innerHTML && !n.trim(b.innerHTML) && (b.innerHTML = a.innerHTML)) : "input" === c && X.test(a.type) ? (b.defaultChecked = b.checked = a.checked, b.value !== a.value && (b.value = a.value)) : "option" === c ? b.defaultSelected = b.selected = a.defaultSelected : ("input" === c || "textarea" === c) && (b.defaultValue = a.defaultValue)
        }
    }

    n.extend({
        clone: function (a, b, c) {
            var d, e, f, g, h, i = n.contains(a.ownerDocument, a);
            if (l.html5Clone || n.isXMLDoc(a) || !hb.test("<" + a.nodeName + ">") ? f = a.cloneNode(!0) : (ub.innerHTML = a.outerHTML, ub.removeChild(f = ub.firstChild)), !(l.noCloneEvent && l.noCloneChecked || 1 !== a.nodeType && 11 !== a.nodeType || n.isXMLDoc(a))) for (d = vb(f), h = vb(a), g = 0; null != (e = h[g]); ++g) d[g] && Cb(e, d[g]);
            if (b) if (c) for (h = h || vb(a), d = d || vb(f), g = 0; null != (e = h[g]); g++) Bb(e, d[g]); else Bb(a, f);
            return d = vb(f, "script"), d.length > 0 && Ab(d, !i && vb(a, "script")), d = h = e = null, f
        }, buildFragment: function (a, b, c, d) {
            for (var e, f, g, h, i, j, k, m = a.length, o = eb(b), p = [], q = 0; m > q; q++) if (f = a[q], f || 0 === f) if ("object" === n.type(f)) n.merge(p, f.nodeType ? [f] : f); else if (mb.test(f)) {
                h = h || o.appendChild(b.createElement("div")), i = (kb.exec(f) || ["", ""])[1].toLowerCase(), k = sb[i] || sb._default, h.innerHTML = k[1] + f.replace(jb, "<$1></$2>") + k[2], e = k[0];
                while (e--) h = h.lastChild;
                if (!l.leadingWhitespace && ib.test(f) && p.push(b.createTextNode(ib.exec(f)[0])), !l.tbody) {
                    f = "table" !== i || lb.test(f) ? "<table>" !== k[1] || lb.test(f) ? 0 : h : h.firstChild, e = f && f.childNodes.length;
                    while (e--) n.nodeName(j = f.childNodes[e], "tbody") && !j.childNodes.length && f.removeChild(j)
                }
                n.merge(p, h.childNodes), h.textContent = "";
                while (h.firstChild) h.removeChild(h.firstChild);
                h = o.lastChild
            } else p.push(b.createTextNode(f));
            h && o.removeChild(h), l.appendChecked || n.grep(vb(p, "input"), wb), q = 0;
            while (f = p[q++]) if ((!d || -1 === n.inArray(f, d)) && (g = n.contains(f.ownerDocument, f), h = vb(o.appendChild(f), "script"), g && Ab(h), c)) {
                e = 0;
                while (f = h[e++]) pb.test(f.type || "") && c.push(f)
            }
            return h = null, o
        }, cleanData: function (a, b) {
            for (var d, e, f, g, h = 0, i = n.expando, j = n.cache, k = l.deleteExpando, m = n.event.special; null != (d = a[h]); h++) if ((b || n.acceptData(d)) && (f = d[i], g = f && j[f])) {
                if (g.events) for (e in g.events) m[e] ? n.event.remove(d, e) : n.removeEvent(d, e, g.handle);
                j[f] && (delete j[f], k ? delete d[i] : typeof d.removeAttribute !== L ? d.removeAttribute(i) : d[i] = null, c.push(f))
            }
        }
    }), n.fn.extend({
        text: function (a) {
            return W(this, function (a) {
                return void 0 === a ? n.text(this) : this.empty().append((this[0] && this[0].ownerDocument || z).createTextNode(a))
            }, null, a, arguments.length)
        }, append: function () {
            return this.domManip(arguments, function (a) {
                if (1 === this.nodeType || 11 === this.nodeType || 9 === this.nodeType) {
                    var b = xb(this, a);
                    b.appendChild(a)
                }
            })
        }, prepend: function () {
            return this.domManip(arguments, function (a) {
                if (1 === this.nodeType || 11 === this.nodeType || 9 === this.nodeType) {
                    var b = xb(this, a);
                    b.insertBefore(a, b.firstChild)
                }
            })
        }, before: function () {
            return this.domManip(arguments, function (a) {
                this.parentNode && this.parentNode.insertBefore(a, this)
            })
        }, after: function () {
            return this.domManip(arguments, function (a) {
                this.parentNode && this.parentNode.insertBefore(a, this.nextSibling)
            })
        }, remove: function (a, b) {
            for (var c, d = a ? n.filter(a, this) : this, e = 0; null != (c = d[e]); e++) b || 1 !== c.nodeType || n.cleanData(vb(c)), c.parentNode && (b && n.contains(c.ownerDocument, c) && Ab(vb(c, "script")), c.parentNode.removeChild(c));
            return this
        }, empty: function () {
            for (var a, b = 0; null != (a = this[b]); b++) {
                1 === a.nodeType && n.cleanData(vb(a, !1));
                while (a.firstChild) a.removeChild(a.firstChild);
                a.options && n.nodeName(a, "select") && (a.options.length = 0)
            }
            return this
        }, clone: function (a, b) {
            return a = null == a ? !1 : a, b = null == b ? a : b, this.map(function () {
                return n.clone(this, a, b)
            })
        }, html: function (a) {
            return W(this, function (a) {
                var b = this[0] || {}, c = 0, d = this.length;
                if (void 0 === a) return 1 === b.nodeType ? b.innerHTML.replace(gb, "") : void 0;
                if (!("string" != typeof a || nb.test(a) || !l.htmlSerialize && hb.test(a) || !l.leadingWhitespace && ib.test(a) || sb[(kb.exec(a) || ["", ""])[1].toLowerCase()])) {
                    a = a.replace(jb, "<$1></$2>");
                    try {
                        for (; d > c; c++) b = this[c] || {}, 1 === b.nodeType && (n.cleanData(vb(b, !1)), b.innerHTML = a);
                        b = 0
                    } catch (e) {
                    }
                }
                b && this.empty().append(a)
            }, null, a, arguments.length)
        }, replaceWith: function () {
            var a = arguments[0];
            return this.domManip(arguments, function (b) {
                a = this.parentNode, n.cleanData(vb(this)), a && a.replaceChild(b, this)
            }), a && (a.length || a.nodeType) ? this : this.remove()
        }, detach: function (a) {
            return this.remove(a, !0)
        }, domManip: function (a, b) {
            a = e.apply([], a);
            var c, d, f, g, h, i, j = 0, k = this.length, m = this, o = k - 1, p = a[0], q = n.isFunction(p);
            if (q || k > 1 && "string" == typeof p && !l.checkClone && ob.test(p)) return this.each(function (c) {
                var d = m.eq(c);
                q && (a[0] = p.call(this, c, d.html())), d.domManip(a, b)
            });
            if (k && (i = n.buildFragment(a, this[0].ownerDocument, !1, this), c = i.firstChild, 1 === i.childNodes.length && (i = c), c)) {
                for (g = n.map(vb(i, "script"), yb), f = g.length; k > j; j++) d = i, j !== o && (d = n.clone(d, !0, !0), f && n.merge(g, vb(d, "script"))), b.call(this[j], d, j);
                if (f) for (h = g[g.length - 1].ownerDocument, n.map(g, zb), j = 0; f > j; j++) d = g[j], pb.test(d.type || "") && !n._data(d, "globalEval") && n.contains(h, d) && (d.src ? n._evalUrl && n._evalUrl(d.src) : n.globalEval((d.text || d.textContent || d.innerHTML || "").replace(rb, "")));
                i = c = null
            }
            return this
        }
    }), n.each({
        appendTo: "append",
        prependTo: "prepend",
        insertBefore: "before",
        insertAfter: "after",
        replaceAll: "replaceWith"
    }, function (a, b) {
        n.fn[a] = function (a) {
            for (var c, d = 0, e = [], g = n(a), h = g.length - 1; h >= d; d++) c = d === h ? this : this.clone(!0), n(g[d])[b](c), f.apply(e, c.get());
            return this.pushStack(e)
        }
    });
    var Db, Eb = {};

    function Fb(b, c) {
        var d = n(c.createElement(b)).appendTo(c.body),
            e = a.getDefaultComputedStyle ? a.getDefaultComputedStyle(d[0]).display : n.css(d[0], "display");
        return d.detach(), e
    }

    function Gb(a) {
        var b = z, c = Eb[a];
        return c || (c = Fb(a, b), "none" !== c && c || (Db = (Db || n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement), b = (Db[0].contentWindow || Db[0].contentDocument).document, b.write(), b.close(), c = Fb(a, b), Db.detach()), Eb[a] = c), c
    }

    !function () {
        var a, b, c = z.createElement("div"),
            d = "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;padding:0;margin:0;border:0";
        c.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>", a = c.getElementsByTagName("a")[0], a.style.cssText = "float:left;opacity:.5", l.opacity = /^0.5/.test(a.style.opacity), l.cssFloat = !!a.style.cssFloat, c.style.backgroundClip = "content-box", c.cloneNode(!0).style.backgroundClip = "", l.clearCloneStyle = "content-box" === c.style.backgroundClip, a = c = null, l.shrinkWrapBlocks = function () {
            var a, c, e, f;
            if (null == b) {
                if (a = z.getElementsByTagName("body")[0], !a) return;
                f = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px", c = z.createElement("div"), e = z.createElement("div"), a.appendChild(c).appendChild(e), b = !1, typeof e.style.zoom !== L && (e.style.cssText = d + ";width:1px;padding:1px;zoom:1", e.innerHTML = "<div></div>", e.firstChild.style.width = "5px", b = 3 !== e.offsetWidth), a.removeChild(c), a = c = e = null
            }
            return b
        }
    }();
    var Hb = /^margin/, Ib = new RegExp("^(" + T + ")(?!px)[a-z%]+$", "i"), Jb, Kb, Lb = /^(top|right|bottom|left)$/;
    a.getComputedStyle ? (Jb = function (a) {
        return a.ownerDocument.defaultView.getComputedStyle(a, null)
    }, Kb = function (a, b, c) {
        var d, e, f, g, h = a.style;
        return c = c || Jb(a), g = c ? c.getPropertyValue(b) || c[b] : void 0, c && ("" !== g || n.contains(a.ownerDocument, a) || (g = n.style(a, b)), Ib.test(g) && Hb.test(b) && (d = h.width, e = h.minWidth, f = h.maxWidth, h.minWidth = h.maxWidth = h.width = g, g = c.width, h.width = d, h.minWidth = e, h.maxWidth = f)), void 0 === g ? g : g + ""
    }) : z.documentElement.currentStyle && (Jb = function (a) {
        return a.currentStyle
    }, Kb = function (a, b, c) {
        var d, e, f, g, h = a.style;
        return c = c || Jb(a), g = c ? c[b] : void 0, null == g && h && h[b] && (g = h[b]), Ib.test(g) && !Lb.test(b) && (d = h.left, e = a.runtimeStyle, f = e && e.left, f && (e.left = a.currentStyle.left), h.left = "fontSize" === b ? "1em" : g, g = h.pixelLeft + "px", h.left = d, f && (e.left = f)), void 0 === g ? g : g + "" || "auto"
    });

    function Mb(a, b) {
        return {
            get: function () {
                var c = a();
                if (null != c) return c ? void delete this.get : (this.get = b).apply(this, arguments)
            }
        }
    }

    !function () {
        var b, c, d, e, f, g, h = z.createElement("div"),
            i = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px",
            j = "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;padding:0;margin:0;border:0";
        h.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>", b = h.getElementsByTagName("a")[0], b.style.cssText = "float:left;opacity:.5", l.opacity = /^0.5/.test(b.style.opacity), l.cssFloat = !!b.style.cssFloat, h.style.backgroundClip = "content-box", h.cloneNode(!0).style.backgroundClip = "", l.clearCloneStyle = "content-box" === h.style.backgroundClip, b = h = null, n.extend(l, {
            reliableHiddenOffsets: function () {
                if (null != c) return c;
                var a, b, d, e = z.createElement("div"), f = z.getElementsByTagName("body")[0];
                if (f) return e.setAttribute("className", "t"), e.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>", a = z.createElement("div"), a.style.cssText = i, f.appendChild(a).appendChild(e), e.innerHTML = "<table><tr><td></td><td>t</td></tr></table>", b = e.getElementsByTagName("td"), b[0].style.cssText = "padding:0;margin:0;border:0;display:none", d = 0 === b[0].offsetHeight, b[0].style.display = "", b[1].style.display = "none", c = d && 0 === b[0].offsetHeight, f.removeChild(a), e = f = null, c
            }, boxSizing: function () {
                return null == d && k(), d
            }, boxSizingReliable: function () {
                return null == e && k(), e
            }, pixelPosition: function () {
                return null == f && k(), f
            }, reliableMarginRight: function () {
                var b, c, d, e;
                if (null == g && a.getComputedStyle) {
                    if (b = z.getElementsByTagName("body")[0], !b) return;
                    c = z.createElement("div"), d = z.createElement("div"), c.style.cssText = i, b.appendChild(c).appendChild(d), e = d.appendChild(z.createElement("div")), e.style.cssText = d.style.cssText = j, e.style.marginRight = e.style.width = "0", d.style.width = "1px", g = !parseFloat((a.getComputedStyle(e, null) || {}).marginRight), b.removeChild(c)
                }
                return g
            }
        });

        function k() {
            var b, c, h = z.getElementsByTagName("body")[0];
            h && (b = z.createElement("div"), c = z.createElement("div"), b.style.cssText = i, h.appendChild(b).appendChild(c), c.style.cssText = "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:absolute;display:block;padding:1px;border:1px;width:4px;margin-top:1%;top:1%", n.swap(h, null != h.style.zoom ? {zoom: 1} : {}, function () {
                d = 4 === c.offsetWidth
            }), e = !0, f = !1, g = !0, a.getComputedStyle && (f = "1%" !== (a.getComputedStyle(c, null) || {}).top, e = "4px" === (a.getComputedStyle(c, null) || {width: "4px"}).width), h.removeChild(b), c = h = null)
        }
    }(), n.swap = function (a, b, c, d) {
        var e, f, g = {};
        for (f in b) g[f] = a.style[f], a.style[f] = b[f];
        e = c.apply(a, d || []);
        for (f in b) a.style[f] = g[f];
        return e
    };
    var Nb = /alpha\([^)]*\)/i, Ob = /opacity\s*=\s*([^)]*)/, Pb = /^(none|table(?!-c[ea]).+)/,
        Qb = new RegExp("^(" + T + ")(.*)$", "i"), Rb = new RegExp("^([+-])=(" + T + ")", "i"),
        Sb = {position: "absolute", visibility: "hidden", display: "block"}, Tb = {letterSpacing: 0, fontWeight: 400},
        Ub = ["Webkit", "O", "Moz", "ms"];

    function Vb(a, b) {
        if (b in a) return b;
        var c = b.charAt(0).toUpperCase() + b.slice(1), d = b, e = Ub.length;
        while (e--) if (b = Ub[e] + c, b in a) return b;
        return d
    }

    function Wb(a, b) {
        for (var c, d, e, f = [], g = 0, h = a.length; h > g; g++) d = a[g], d.style && (f[g] = n._data(d, "olddisplay"), c = d.style.display, b ? (f[g] || "none" !== c || (d.style.display = ""), "" === d.style.display && V(d) && (f[g] = n._data(d, "olddisplay", Gb(d.nodeName)))) : f[g] || (e = V(d), (c && "none" !== c || !e) && n._data(d, "olddisplay", e ? c : n.css(d, "display"))));
        for (g = 0; h > g; g++) d = a[g], d.style && (b && "none" !== d.style.display && "" !== d.style.display || (d.style.display = b ? f[g] || "" : "none"));
        return a
    }

    function Xb(a, b, c) {
        var d = Qb.exec(b);
        return d ? Math.max(0, d[1] - (c || 0)) + (d[2] || "px") : b
    }

    function Yb(a, b, c, d, e) {
        for (var f = c === (d ? "border" : "content") ? 4 : "width" === b ? 1 : 0, g = 0; 4 > f; f += 2) "margin" === c && (g += n.css(a, c + U[f], !0, e)), d ? ("content" === c && (g -= n.css(a, "padding" + U[f], !0, e)), "margin" !== c && (g -= n.css(a, "border" + U[f] + "Width", !0, e))) : (g += n.css(a, "padding" + U[f], !0, e), "padding" !== c && (g += n.css(a, "border" + U[f] + "Width", !0, e)));
        return g
    }

    function Zb(a, b, c) {
        var d = !0, e = "width" === b ? a.offsetWidth : a.offsetHeight, f = Jb(a),
            g = l.boxSizing() && "border-box" === n.css(a, "boxSizing", !1, f);
        if (0 >= e || null == e) {
            if (e = Kb(a, b, f), (0 > e || null == e) && (e = a.style[b]), Ib.test(e)) return e;
            d = g && (l.boxSizingReliable() || e === a.style[b]), e = parseFloat(e) || 0
        }
        return e + Yb(a, b, c || (g ? "border" : "content"), d, f) + "px"
    }

    n.extend({
        cssHooks: {
            opacity: {
                get: function (a, b) {
                    if (b) {
                        var c = Kb(a, "opacity");
                        return "" === c ? "1" : c
                    }
                }
            }
        },
        cssNumber: {
            columnCount: !0,
            fillOpacity: !0,
            fontWeight: !0,
            lineHeight: !0,
            opacity: !0,
            order: !0,
            orphans: !0,
            widows: !0,
            zIndex: !0,
            zoom: !0
        },
        cssProps: {"float": l.cssFloat ? "cssFloat" : "styleFloat"},
        style: function (a, b, c, d) {
            if (a && 3 !== a.nodeType && 8 !== a.nodeType && a.style) {
                var e, f, g, h = n.camelCase(b), i = a.style;
                if (b = n.cssProps[h] || (n.cssProps[h] = Vb(i, h)), g = n.cssHooks[b] || n.cssHooks[h], void 0 === c) return g && "get" in g && void 0 !== (e = g.get(a, !1, d)) ? e : i[b];
                if (f = typeof c, "string" === f && (e = Rb.exec(c)) && (c = (e[1] + 1) * e[2] + parseFloat(n.css(a, b)), f = "number"), null != c && c === c && ("number" !== f || n.cssNumber[h] || (c += "px"), l.clearCloneStyle || "" !== c || 0 !== b.indexOf("background") || (i[b] = "inherit"), !(g && "set" in g && void 0 === (c = g.set(a, c, d))))) try {
                    i[b] = "", i[b] = c
                } catch (j) {
                }
            }
        },
        css: function (a, b, c, d) {
            var e, f, g, h = n.camelCase(b);
            return b = n.cssProps[h] || (n.cssProps[h] = Vb(a.style, h)), g = n.cssHooks[b] || n.cssHooks[h], g && "get" in g && (f = g.get(a, !0, c)), void 0 === f && (f = Kb(a, b, d)), "normal" === f && b in Tb && (f = Tb[b]), "" === c || c ? (e = parseFloat(f), c === !0 || n.isNumeric(e) ? e || 0 : f) : f
        }
    }), n.each(["height", "width"], function (a, b) {
        n.cssHooks[b] = {
            get: function (a, c, d) {
                return c ? 0 === a.offsetWidth && Pb.test(n.css(a, "display")) ? n.swap(a, Sb, function () {
                    return Zb(a, b, d)
                }) : Zb(a, b, d) : void 0
            }, set: function (a, c, d) {
                var e = d && Jb(a);
                return Xb(a, c, d ? Yb(a, b, d, l.boxSizing() && "border-box" === n.css(a, "boxSizing", !1, e), e) : 0)
            }
        }
    }), l.opacity || (n.cssHooks.opacity = {
        get: function (a, b) {
            return Ob.test((b && a.currentStyle ? a.currentStyle.filter : a.style.filter) || "") ? .01 * parseFloat(RegExp.$1) + "" : b ? "1" : ""
        }, set: function (a, b) {
            var c = a.style, d = a.currentStyle, e = n.isNumeric(b) ? "alpha(opacity=" + 100 * b + ")" : "",
                f = d && d.filter || c.filter || "";
            c.zoom = 1, (b >= 1 || "" === b) && "" === n.trim(f.replace(Nb, "")) && c.removeAttribute && (c.removeAttribute("filter"), "" === b || d && !d.filter) || (c.filter = Nb.test(f) ? f.replace(Nb, e) : f + " " + e)
        }
    }), n.cssHooks.marginRight = Mb(l.reliableMarginRight, function (a, b) {
        return b ? n.swap(a, {display: "inline-block"}, Kb, [a, "marginRight"]) : void 0
    }), n.each({margin: "", padding: "", border: "Width"}, function (a, b) {
        n.cssHooks[a + b] = {
            expand: function (c) {
                for (var d = 0, e = {}, f = "string" == typeof c ? c.split(" ") : [c]; 4 > d; d++) e[a + U[d] + b] = f[d] || f[d - 2] || f[0];
                return e
            }
        }, Hb.test(a) || (n.cssHooks[a + b].set = Xb)
    }), n.fn.extend({
        css: function (a, b) {
            return W(this, function (a, b, c) {
                var d, e, f = {}, g = 0;
                if (n.isArray(b)) {
                    for (d = Jb(a), e = b.length; e > g; g++) f[b[g]] = n.css(a, b[g], !1, d);
                    return f
                }
                return void 0 !== c ? n.style(a, b, c) : n.css(a, b)
            }, a, b, arguments.length > 1)
        }, show: function () {
            return Wb(this, !0)
        }, hide: function () {
            return Wb(this)
        }, toggle: function (a) {
            return "boolean" == typeof a ? a ? this.show() : this.hide() : this.each(function () {
                V(this) ? n(this).show() : n(this).hide()
            })
        }
    });

    function $b(a, b, c, d, e) {
        return new $b.prototype.init(a, b, c, d, e)
    }

    n.Tween = $b, $b.prototype = {
        constructor: $b, init: function (a, b, c, d, e, f) {
            this.elem = a, this.prop = c, this.easing = e || "swing", this.options = b, this.start = this.now = this.cur(), this.end = d, this.unit = f || (n.cssNumber[c] ? "" : "px")
        }, cur: function () {
            var a = $b.propHooks[this.prop];
            return a && a.get ? a.get(this) : $b.propHooks._default.get(this)
        }, run: function (a) {
            var b, c = $b.propHooks[this.prop];
            return this.pos = b = this.options.duration ? n.easing[this.easing](a, this.options.duration * a, 0, 1, this.options.duration) : a, this.now = (this.end - this.start) * b + this.start, this.options.step && this.options.step.call(this.elem, this.now, this), c && c.set ? c.set(this) : $b.propHooks._default.set(this), this
        }
    }, $b.prototype.init.prototype = $b.prototype, $b.propHooks = {
        _default: {
            get: function (a) {
                var b;
                return null == a.elem[a.prop] || a.elem.style && null != a.elem.style[a.prop] ? (b = n.css(a.elem, a.prop, ""), b && "auto" !== b ? b : 0) : a.elem[a.prop]
            }, set: function (a) {
                n.fx.step[a.prop] ? n.fx.step[a.prop](a) : a.elem.style && (null != a.elem.style[n.cssProps[a.prop]] || n.cssHooks[a.prop]) ? n.style(a.elem, a.prop, a.now + a.unit) : a.elem[a.prop] = a.now
            }
        }
    }, $b.propHooks.scrollTop = $b.propHooks.scrollLeft = {
        set: function (a) {
            a.elem.nodeType && a.elem.parentNode && (a.elem[a.prop] = a.now)
        }
    }, n.easing = {
        linear: function (a) {
            return a
        }, swing: function (a) {
            return .5 - Math.cos(a * Math.PI) / 2
        }
    }, n.fx = $b.prototype.init, n.fx.step = {};
    var _b, ac, bc = /^(?:toggle|show|hide)$/, cc = new RegExp("^(?:([+-])=|)(" + T + ")([a-z%]*)$", "i"),
        dc = /queueHooks$/, ec = [jc], fc = {
            "*": [function (a, b) {
                var c = this.createTween(a, b), d = c.cur(), e = cc.exec(b), f = e && e[3] || (n.cssNumber[a] ? "" : "px"),
                    g = (n.cssNumber[a] || "px" !== f && +d) && cc.exec(n.css(c.elem, a)), h = 1, i = 20;
                if (g && g[3] !== f) {
                    f = f || g[3], e = e || [], g = +d || 1;
                    do h = h || ".5", g /= h, n.style(c.elem, a, g + f); while (h !== (h = c.cur() / d) && 1 !== h && --i)
                }
                return e && (g = c.start = +g || +d || 0, c.unit = f, c.end = e[1] ? g + (e[1] + 1) * e[2] : +e[2]), c
            }]
        };

    function gc() {
        return setTimeout(function () {
            _b = void 0
        }), _b = n.now()
    }

    function hc(a, b) {
        var c, d = {height: a}, e = 0;
        for (b = b ? 1 : 0; 4 > e; e += 2 - b) c = U[e], d["margin" + c] = d["padding" + c] = a;
        return b && (d.opacity = d.width = a), d
    }

    function ic(a, b, c) {
        for (var d, e = (fc[b] || []).concat(fc["*"]), f = 0, g = e.length; g > f; f++) if (d = e[f].call(c, b, a)) return d
    }

    function jc(a, b, c) {
        var d, e, f, g, h, i, j, k, m = this, o = {}, p = a.style, q = a.nodeType && V(a), r = n._data(a, "fxshow");
        c.queue || (h = n._queueHooks(a, "fx"), null == h.unqueued && (h.unqueued = 0, i = h.empty.fire, h.empty.fire = function () {
            h.unqueued || i()
        }), h.unqueued++, m.always(function () {
            m.always(function () {
                h.unqueued--, n.queue(a, "fx").length || h.empty.fire()
            })
        })), 1 === a.nodeType && ("height" in b || "width" in b) && (c.overflow = [p.overflow, p.overflowX, p.overflowY], j = n.css(a, "display"), k = Gb(a.nodeName), "none" === j && (j = k), "inline" === j && "none" === n.css(a, "float") && (l.inlineBlockNeedsLayout && "inline" !== k ? p.zoom = 1 : p.display = "inline-block")), c.overflow && (p.overflow = "hidden", l.shrinkWrapBlocks() || m.always(function () {
            p.overflow = c.overflow[0], p.overflowX = c.overflow[1], p.overflowY = c.overflow[2]
        }));
        for (d in b) if (e = b[d], bc.exec(e)) {
            if (delete b[d], f = f || "toggle" === e, e === (q ? "hide" : "show")) {
                if ("show" !== e || !r || void 0 === r[d]) continue;
                q = !0
            }
            o[d] = r && r[d] || n.style(a, d)
        }
        if (!n.isEmptyObject(o)) {
            r ? "hidden" in r && (q = r.hidden) : r = n._data(a, "fxshow", {}), f && (r.hidden = !q), q ? n(a).show() : m.done(function () {
                n(a).hide()
            }), m.done(function () {
                var b;
                n._removeData(a, "fxshow");
                for (b in o) n.style(a, b, o[b])
            });
            for (d in o) g = ic(q ? r[d] : 0, d, m), d in r || (r[d] = g.start, q && (g.end = g.start, g.start = "width" === d || "height" === d ? 1 : 0))
        }
    }

    function kc(a, b) {
        var c, d, e, f, g;
        for (c in a) if (d = n.camelCase(c), e = b[d], f = a[c], n.isArray(f) && (e = f[1], f = a[c] = f[0]), c !== d && (a[d] = f, delete a[c]), g = n.cssHooks[d], g && "expand" in g) {
            f = g.expand(f), delete a[d];
            for (c in f) c in a || (a[c] = f[c], b[c] = e)
        } else b[d] = e
    }

    function lc(a, b, c) {
        var d, e, f = 0, g = ec.length, h = n.Deferred().always(function () {
            delete i.elem
        }), i = function () {
            if (e) return !1;
            for (var b = _b || gc(), c = Math.max(0, j.startTime + j.duration - b), d = c / j.duration || 0, f = 1 - d, g = 0, i = j.tweens.length; i > g; g++) j.tweens[g].run(f);
            return h.notifyWith(a, [j, f, c]), 1 > f && i ? c : (h.resolveWith(a, [j]), !1)
        }, j = h.promise({
            elem: a,
            props: n.extend({}, b),
            opts: n.extend(!0, {specialEasing: {}}, c),
            originalProperties: b,
            originalOptions: c,
            startTime: _b || gc(),
            duration: c.duration,
            tweens: [],
            createTween: function (b, c) {
                var d = n.Tween(a, j.opts, b, c, j.opts.specialEasing[b] || j.opts.easing);
                return j.tweens.push(d), d
            },
            stop: function (b) {
                var c = 0, d = b ? j.tweens.length : 0;
                if (e) return this;
                for (e = !0; d > c; c++) j.tweens[c].run(1);
                return b ? h.resolveWith(a, [j, b]) : h.rejectWith(a, [j, b]), this
            }
        }), k = j.props;
        for (kc(k, j.opts.specialEasing); g > f; f++) if (d = ec[f].call(j, a, k, j.opts)) return d;
        return n.map(k, ic, j), n.isFunction(j.opts.start) && j.opts.start.call(a, j), n.fx.timer(n.extend(i, {
            elem: a,
            anim: j,
            queue: j.opts.queue
        })), j.progress(j.opts.progress).done(j.opts.done, j.opts.complete).fail(j.opts.fail).always(j.opts.always)
    }

    n.Animation = n.extend(lc, {
        tweener: function (a, b) {
            n.isFunction(a) ? (b = a, a = ["*"]) : a = a.split(" ");
            for (var c, d = 0, e = a.length; e > d; d++) c = a[d], fc[c] = fc[c] || [], fc[c].unshift(b)
        }, prefilter: function (a, b) {
            b ? ec.unshift(a) : ec.push(a)
        }
    }), n.speed = function (a, b, c) {
        var d = a && "object" == typeof a ? n.extend({}, a) : {
            complete: c || !c && b || n.isFunction(a) && a,
            duration: a,
            easing: c && b || b && !n.isFunction(b) && b
        };
        return d.duration = n.fx.off ? 0 : "number" == typeof d.duration ? d.duration : d.duration in n.fx.speeds ? n.fx.speeds[d.duration] : n.fx.speeds._default, (null == d.queue || d.queue === !0) && (d.queue = "fx"), d.old = d.complete, d.complete = function () {
            n.isFunction(d.old) && d.old.call(this), d.queue && n.dequeue(this, d.queue)
        }, d
    }, n.fn.extend({
        fadeTo: function (a, b, c, d) {
            return this.filter(V).css("opacity", 0).show().end().animate({opacity: b}, a, c, d)
        }, animate: function (a, b, c, d) {
            var e = n.isEmptyObject(a), f = n.speed(b, c, d), g = function () {
                var b = lc(this, n.extend({}, a), f);
                (e || n._data(this, "finish")) && b.stop(!0)
            };
            return g.finish = g, e || f.queue === !1 ? this.each(g) : this.queue(f.queue, g)
        }, stop: function (a, b, c) {
            var d = function (a) {
                var b = a.stop;
                delete a.stop, b(c)
            };
            return "string" != typeof a && (c = b, b = a, a = void 0), b && a !== !1 && this.queue(a || "fx", []), this.each(function () {
                var b = !0, e = null != a && a + "queueHooks", f = n.timers, g = n._data(this);
                if (e) g[e] && g[e].stop && d(g[e]); else for (e in g) g[e] && g[e].stop && dc.test(e) && d(g[e]);
                for (e = f.length; e--;) f[e].elem !== this || null != a && f[e].queue !== a || (f[e].anim.stop(c), b = !1, f.splice(e, 1));
                (b || !c) && n.dequeue(this, a)
            })
        }, finish: function (a) {
            return a !== !1 && (a = a || "fx"), this.each(function () {
                var b, c = n._data(this), d = c[a + "queue"], e = c[a + "queueHooks"], f = n.timers,
                    g = d ? d.length : 0;
                for (c.finish = !0, n.queue(this, a, []), e && e.stop && e.stop.call(this, !0), b = f.length; b--;) f[b].elem === this && f[b].queue === a && (f[b].anim.stop(!0), f.splice(b, 1));
                for (b = 0; g > b; b++) d[b] && d[b].finish && d[b].finish.call(this);
                delete c.finish
            })
        }
    }), n.each(["toggle", "show", "hide"], function (a, b) {
        var c = n.fn[b];
        n.fn[b] = function (a, d, e) {
            return null == a || "boolean" == typeof a ? c.apply(this, arguments) : this.animate(hc(b, !0), a, d, e)
        }
    }), n.each({
        slideDown: hc("show"),
        slideUp: hc("hide"),
        slideToggle: hc("toggle"),
        fadeIn: {opacity: "show"},
        fadeOut: {opacity: "hide"},
        fadeToggle: {opacity: "toggle"}
    }, function (a, b) {
        n.fn[a] = function (a, c, d) {
            return this.animate(b, a, c, d)
        }
    }), n.timers = [], n.fx.tick = function () {
        var a, b = n.timers, c = 0;
        for (_b = n.now(); c < b.length; c++) a = b[c], a() || b[c] !== a || b.splice(c--, 1);
        b.length || n.fx.stop(), _b = void 0
    }, n.fx.timer = function (a) {
        n.timers.push(a), a() ? n.fx.start() : n.timers.pop()
    }, n.fx.interval = 13, n.fx.start = function () {
        ac || (ac = setInterval(n.fx.tick, n.fx.interval))
    }, n.fx.stop = function () {
        clearInterval(ac), ac = null
    }, n.fx.speeds = {slow: 600, fast: 200, _default: 400}, n.fn.delay = function (a, b) {
        return a = n.fx ? n.fx.speeds[a] || a : a, b = b || "fx", this.queue(b, function (b, c) {
            var d = setTimeout(b, a);
            c.stop = function () {
                clearTimeout(d)
            }
        })
    }, function () {
        var a, b, c, d, e = z.createElement("div");
        e.setAttribute("className", "t"), e.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>", a = e.getElementsByTagName("a")[0], c = z.createElement("select"), d = c.appendChild(z.createElement("option")), b = e.getElementsByTagName("input")[0], a.style.cssText = "top:1px", l.getSetAttribute = "t" !== e.className, l.style = /top/.test(a.getAttribute("style")), l.hrefNormalized = "/a" === a.getAttribute("href"), l.checkOn = !!b.value, l.optSelected = d.selected, l.enctype = !!z.createElement("form").enctype, c.disabled = !0, l.optDisabled = !d.disabled, b = z.createElement("input"), b.setAttribute("value", ""), l.input = "" === b.getAttribute("value"), b.value = "t", b.setAttribute("type", "radio"), l.radioValue = "t" === b.value, a = b = c = d = e = null
    }();
    var mc = /\r/g;
    n.fn.extend({
        val: function (a) {
            var b, c, d, e = this[0];
            {
                if (arguments.length) return d = n.isFunction(a), this.each(function (c) {
                    var e;
                    1 === this.nodeType && (e = d ? a.call(this, c, n(this).val()) : a, null == e ? e = "" : "number" == typeof e ? e += "" : n.isArray(e) && (e = n.map(e, function (a) {
                        return null == a ? "" : a + ""
                    })), b = n.valHooks[this.type] || n.valHooks[this.nodeName.toLowerCase()], b && "set" in b && void 0 !== b.set(this, e, "value") || (this.value = e))
                });
                if (e) return b = n.valHooks[e.type] || n.valHooks[e.nodeName.toLowerCase()], b && "get" in b && void 0 !== (c = b.get(e, "value")) ? c : (c = e.value, "string" == typeof c ? c.replace(mc, "") : null == c ? "" : c)
            }
        }
    }), n.extend({
        valHooks: {
            option: {
                get: function (a) {
                    var b = n.find.attr(a, "value");
                    return null != b ? b : n.text(a)
                }
            }, select: {
                get: function (a) {
                    for (var b, c, d = a.options, e = a.selectedIndex, f = "select-one" === a.type || 0 > e, g = f ? null : [], h = f ? e + 1 : d.length, i = 0 > e ? h : f ? e : 0; h > i; i++) if (c = d[i], !(!c.selected && i !== e || (l.optDisabled ? c.disabled : null !== c.getAttribute("disabled")) || c.parentNode.disabled && n.nodeName(c.parentNode, "optgroup"))) {
                        if (b = n(c).val(), f) return b;
                        g.push(b)
                    }
                    return g
                }, set: function (a, b) {
                    var c, d, e = a.options, f = n.makeArray(b), g = e.length;
                    while (g--) if (d = e[g], n.inArray(n.valHooks.option.get(d), f) >= 0) try {
                        d.selected = c = !0
                    } catch (h) {
                        d.scrollHeight
                    } else d.selected = !1;
                    return c || (a.selectedIndex = -1), e
                }
            }
        }
    }), n.each(["radio", "checkbox"], function () {
        n.valHooks[this] = {
            set: function (a, b) {
                return n.isArray(b) ? a.checked = n.inArray(n(a).val(), b) >= 0 : void 0
            }
        }, l.checkOn || (n.valHooks[this].get = function (a) {
            return null === a.getAttribute("value") ? "on" : a.value
        })
    });
    var nc, oc, pc = n.expr.attrHandle, qc = /^(?:checked|selected)$/i, rc = l.getSetAttribute, sc = l.input;
    n.fn.extend({
        attr: function (a, b) {
            return W(this, n.attr, a, b, arguments.length > 1)
        }, removeAttr: function (a) {
            return this.each(function () {
                n.removeAttr(this, a)
            })
        }
    }), n.extend({
        attr: function (a, b, c) {
            var d, e, f = a.nodeType;
            if (a && 3 !== f && 8 !== f && 2 !== f) return typeof a.getAttribute === L ? n.prop(a, b, c) : (1 === f && n.isXMLDoc(a) || (b = b.toLowerCase(), d = n.attrHooks[b] || (n.expr.match.bool.test(b) ? oc : nc)), void 0 === c ? d && "get" in d && null !== (e = d.get(a, b)) ? e : (e = n.find.attr(a, b), null == e ? void 0 : e) : null !== c ? d && "set" in d && void 0 !== (e = d.set(a, c, b)) ? e : (a.setAttribute(b, c + ""), c) : void n.removeAttr(a, b))
        }, removeAttr: function (a, b) {
            var c, d, e = 0, f = b && b.match(F);
            if (f && 1 === a.nodeType) while (c = f[e++]) d = n.propFix[c] || c, n.expr.match.bool.test(c) ? sc && rc || !qc.test(c) ? a[d] = !1 : a[n.camelCase("default-" + c)] = a[d] = !1 : n.attr(a, c, ""), a.removeAttribute(rc ? c : d)
        }, attrHooks: {
            type: {
                set: function (a, b) {
                    if (!l.radioValue && "radio" === b && n.nodeName(a, "input")) {
                        var c = a.value;
                        return a.setAttribute("type", b), c && (a.value = c), b
                    }
                }
            }
        }
    }), oc = {
        set: function (a, b, c) {
            return b === !1 ? n.removeAttr(a, c) : sc && rc || !qc.test(c) ? a.setAttribute(!rc && n.propFix[c] || c, c) : a[n.camelCase("default-" + c)] = a[c] = !0, c
        }
    }, n.each(n.expr.match.bool.source.match(/\w+/g), function (a, b) {
        var c = pc[b] || n.find.attr;
        pc[b] = sc && rc || !qc.test(b) ? function (a, b, d) {
            var e, f;
            return d || (f = pc[b], pc[b] = e, e = null != c(a, b, d) ? b.toLowerCase() : null, pc[b] = f), e
        } : function (a, b, c) {
            return c ? void 0 : a[n.camelCase("default-" + b)] ? b.toLowerCase() : null
        }
    }), sc && rc || (n.attrHooks.value = {
        set: function (a, b, c) {
            return n.nodeName(a, "input") ? void(a.defaultValue = b) : nc && nc.set(a, b, c)
        }
    }), rc || (nc = {
        set: function (a, b, c) {
            var d = a.getAttributeNode(c);
            return d || a.setAttributeNode(d = a.ownerDocument.createAttribute(c)), d.value = b += "", "value" === c || b === a.getAttribute(c) ? b : void 0
        }
    }, pc.id = pc.name = pc.coords = function (a, b, c) {
        var d;
        return c ? void 0 : (d = a.getAttributeNode(b)) && "" !== d.value ? d.value : null
    }, n.valHooks.button = {
        get: function (a, b) {
            var c = a.getAttributeNode(b);
            return c && c.specified ? c.value : void 0
        }, set: nc.set
    }, n.attrHooks.contenteditable = {
        set: function (a, b, c) {
            nc.set(a, "" === b ? !1 : b, c)
        }
    }, n.each(["width", "height"], function (a, b) {
        n.attrHooks[b] = {
            set: function (a, c) {
                return "" === c ? (a.setAttribute(b, "auto"), c) : void 0
            }
        }
    })), l.style || (n.attrHooks.style = {
        get: function (a) {
            return a.style.cssText || void 0
        }, set: function (a, b) {
            return a.style.cssText = b + ""
        }
    });
    var tc = /^(?:input|select|textarea|button|object)$/i, uc = /^(?:a|area)$/i;
    n.fn.extend({
        prop: function (a, b) {
            return W(this, n.prop, a, b, arguments.length > 1)
        }, removeProp: function (a) {
            return a = n.propFix[a] || a, this.each(function () {
                try {
                    this[a] = void 0, delete this[a]
                } catch (b) {
                }
            })
        }
    }), n.extend({
        propFix: {"for": "htmlFor", "class": "className"}, prop: function (a, b, c) {
            var d, e, f, g = a.nodeType;
            if (a && 3 !== g && 8 !== g && 2 !== g) return f = 1 !== g || !n.isXMLDoc(a), f && (b = n.propFix[b] || b, e = n.propHooks[b]), void 0 !== c ? e && "set" in e && void 0 !== (d = e.set(a, c, b)) ? d : a[b] = c : e && "get" in e && null !== (d = e.get(a, b)) ? d : a[b]
        }, propHooks: {
            tabIndex: {
                get: function (a) {
                    var b = n.find.attr(a, "tabindex");
                    return b ? parseInt(b, 10) : tc.test(a.nodeName) || uc.test(a.nodeName) && a.href ? 0 : -1
                }
            }
        }
    }), l.hrefNormalized || n.each(["href", "src"], function (a, b) {
        n.propHooks[b] = {
            get: function (a) {
                return a.getAttribute(b, 4)
            }
        }
    }), l.optSelected || (n.propHooks.selected = {
        get: function (a) {
            var b = a.parentNode;
            return b && (b.selectedIndex, b.parentNode && b.parentNode.selectedIndex), null
        }
    }), n.each(["tabIndex", "readOnly", "maxLength", "cellSpacing", "cellPadding", "rowSpan", "colSpan", "useMap", "frameBorder", "contentEditable"], function () {
        n.propFix[this.toLowerCase()] = this
    }), l.enctype || (n.propFix.enctype = "encoding");
    var vc = /[\t\r\n\f]/g;
    n.fn.extend({
        addClass: function (a) {
            var b, c, d, e, f, g, h = 0, i = this.length, j = "string" == typeof a && a;
            if (n.isFunction(a)) return this.each(function (b) {
                n(this).addClass(a.call(this, b, this.className))
            });
            if (j) for (b = (a || "").match(F) || []; i > h; h++) if (c = this[h], d = 1 === c.nodeType && (c.className ? (" " + c.className + " ").replace(vc, " ") : " ")) {
                f = 0;
                while (e = b[f++]) d.indexOf(" " + e + " ") < 0 && (d += e + " ");
                g = n.trim(d), c.className !== g && (c.className = g)
            }
            return this
        }, removeClass: function (a) {
            var b, c, d, e, f, g, h = 0, i = this.length, j = 0 === arguments.length || "string" == typeof a && a;
            if (n.isFunction(a)) return this.each(function (b) {
                n(this).removeClass(a.call(this, b, this.className))
            });
            if (j) for (b = (a || "").match(F) || []; i > h; h++) if (c = this[h], d = 1 === c.nodeType && (c.className ? (" " + c.className + " ").replace(vc, " ") : "")) {
                f = 0;
                while (e = b[f++]) while (d.indexOf(" " + e + " ") >= 0) d = d.replace(" " + e + " ", " ");
                g = a ? n.trim(d) : "", c.className !== g && (c.className = g)
            }
            return this
        }, toggleClass: function (a, b) {
            var c = typeof a;
            return "boolean" == typeof b && "string" === c ? b ? this.addClass(a) : this.removeClass(a) : this.each(n.isFunction(a) ? function (c) {
                n(this).toggleClass(a.call(this, c, this.className, b), b)
            } : function () {
                if ("string" === c) {
                    var b, d = 0, e = n(this), f = a.match(F) || [];
                    while (b = f[d++]) e.hasClass(b) ? e.removeClass(b) : e.addClass(b)
                } else (c === L || "boolean" === c) && (this.className && n._data(this, "__className__", this.className), this.className = this.className || a === !1 ? "" : n._data(this, "__className__") || "")
            })
        }, hasClass: function (a) {
            for (var b = " " + a + " ", c = 0, d = this.length; d > c; c++) if (1 === this[c].nodeType && (" " + this[c].className + " ").replace(vc, " ").indexOf(b) >= 0) return !0;
            return !1
        }
    }), n.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 (a, b) {
        n.fn[b] = function (a, c) {
            return arguments.length > 0 ? this.on(b, null, a, c) : this.trigger(b)
        }
    }), n.fn.extend({
        hover: function (a, b) {
            return this.mouseenter(a).mouseleave(b || a)
        }, bind: function (a, b, c) {
            return this.on(a, null, b, c)
        }, unbind: function (a, b) {
            return this.off(a, null, b)
        }, delegate: function (a, b, c, d) {
            return this.on(b, a, c, d)
        }, undelegate: function (a, b, c) {
            return 1 === arguments.length ? this.off(a, "**") : this.off(b, a || "**", c)
        }
    });
    var wc = n.now(), xc = /\?/,
        yc = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;
    n.parseJSON = function (b) {
        if (a.JSON && a.JSON.parse) return a.JSON.parse(b + "");
        var c, d = null, e = n.trim(b + "");
        return e && !n.trim(e.replace(yc, function (a, b, e, f) {
            return c && b && (d = 0), 0 === d ? a : (c = e || b, d += !f - !e, "")
        })) ? Function("return " + e)() : n.error("Invalid JSON: " + b)
    }, n.parseXML = function (b) {
        var c, d;
        if (!b || "string" != typeof b) return null;
        try {
            a.DOMParser ? (d = new DOMParser, c = d.parseFromString(b, "text/xml")) : (c = new ActiveXObject("Microsoft.XMLDOM"), c.async = "false", c.loadXML(b))
        } catch (e) {
            c = void 0
        }
        return c && c.documentElement && !c.getElementsByTagName("parsererror").length || n.error("Invalid XML: " + b), c
    };
    var zc, Ac, Bc = /#.*$/, Cc = /([?&])_=[^&]*/, Dc = /^(.*?):[ \t]*([^\r\n]*)\r?$/gm,
        Ec = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, Fc = /^(?:GET|HEAD)$/, Gc = /^\/\//,
        Hc = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/, Ic = {}, Jc = {}, Kc = "*/".concat("*");
    try {
        Ac = location.href
    } catch (Lc) {
        Ac = z.createElement("a"), Ac.href = "", Ac = Ac.href
    }
    zc = Hc.exec(Ac.toLowerCase()) || [];

    function Mc(a) {
        return function (b, c) {
            "string" != typeof b && (c = b, b = "*");
            var d, e = 0, f = b.toLowerCase().match(F) || [];
            if (n.isFunction(c)) while (d = f[e++]) "+" === d.charAt(0) ? (d = d.slice(1) || "*", (a[d] = a[d] || []).unshift(c)) : (a[d] = a[d] || []).push(c)
        }
    }

    function Nc(a, b, c, d) {
        var e = {}, f = a === Jc;

        function g(h) {
            var i;
            return e[h] = !0, n.each(a[h] || [], function (a, h) {
                var j = h(b, c, d);
                return "string" != typeof j || f || e[j] ? f ? !(i = j) : void 0 : (b.dataTypes.unshift(j), g(j), !1)
            }), i
        }

        return g(b.dataTypes[0]) || !e["*"] && g("*")
    }

    function Oc(a, b) {
        var c, d, e = n.ajaxSettings.flatOptions || {};
        for (d in b) void 0 !== b[d] && ((e[d] ? a : c || (c = {}))[d] = b[d]);
        return c && n.extend(!0, a, c), a
    }

    function Pc(a, b, c) {
        var d, e, f, g, h = a.contents, i = a.dataTypes;
        while ("*" === i[0]) i.shift(), void 0 === e && (e = a.mimeType || b.getResponseHeader("Content-Type"));
        if (e) for (g in h) if (h[g] && h[g].test(e)) {
            i.unshift(g);
            break
        }
        if (i[0] in c) f = i[0]; else {
            for (g in c) {
                if (!i[0] || a.converters[g + " " + i[0]]) {
                    f = g;
                    break
                }
                d || (d = g)
            }
            f = f || d
        }
        return f ? (f !== i[0] && i.unshift(f), c[f]) : void 0
    }

    function Qc(a, b, c, d) {
        var e, f, g, h, i, j = {}, k = a.dataTypes.slice();
        if (k[1]) for (g in a.converters) j[g.toLowerCase()] = a.converters[g];
        f = k.shift();
        while (f) if (a.responseFields[f] && (c[a.responseFields[f]] = b), !i && d && a.dataFilter && (b = a.dataFilter(b, a.dataType)), i = f, f = k.shift()) if ("*" === f) f = i; else if ("*" !== i && i !== f) {
            if (g = j[i + " " + f] || j["* " + f], !g) for (e in j) if (h = e.split(" "), h[1] === f && (g = j[i + " " + h[0]] || j["* " + h[0]])) {
                g === !0 ? g = j[e] : j[e] !== !0 && (f = h[0], k.unshift(h[1]));
                break
            }
            if (g !== !0) if (g && a["throws"]) b = g(b); else try {
                b = g(b)
            } catch (l) {
                return {state: "parsererror", error: g ? l : "No conversion from " + i + " to " + f}
            }
        }
        return {state: "success", data: b}
    }

    n.extend({
        active: 0,
        lastModified: {},
        etag: {},
        ajaxSettings: {
            url: Ac,
            type: "GET",
            isLocal: Ec.test(zc[1]),
            global: !0,
            processData: !0,
            async: !0,
            contentType: "application/x-www-form-urlencoded; charset=UTF-8",
            accepts: {
                "*": Kc,
                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"},
            converters: {"* text": String, "text html": !0, "text json": n.parseJSON, "text xml": n.parseXML},
            flatOptions: {url: !0, context: !0}
        },
        ajaxSetup: function (a, b) {
            return b ? Oc(Oc(a, n.ajaxSettings), b) : Oc(n.ajaxSettings, a)
        },
        ajaxPrefilter: Mc(Ic),
        ajaxTransport: Mc(Jc),
        ajax: function (a, b) {
            "object" == typeof a && (b = a, a = void 0), b = b || {};
            var c, d, e, f, g, h, i, j, k = n.ajaxSetup({}, b), l = k.context || k,
                m = k.context && (l.nodeType || l.jquery) ? n(l) : n.event, o = n.Deferred(),
                p = n.Callbacks("once memory"), q = k.statusCode || {}, r = {}, s = {}, t = 0, u = "canceled", v = {
                    readyState: 0, getResponseHeader: function (a) {
                        var b;
                        if (2 === t) {
                            if (!j) {
                                j = {};
                                while (b = Dc.exec(f)) j[b[1].toLowerCase()] = b[2]
                            }
                            b = j[a.toLowerCase()]
                        }
                        return null == b ? null : b
                    }, getAllResponseHeaders: function () {
                        return 2 === t ? f : null
                    }, setRequestHeader: function (a, b) {
                        var c = a.toLowerCase();
                        return t || (a = s[c] = s[c] || a, r[a] = b), this
                    }, overrideMimeType: function (a) {
                        return t || (k.mimeType = a), this
                    }, statusCode: function (a) {
                        var b;
                        if (a) if (2 > t) for (b in a) q[b] = [q[b], a[b]]; else v.always(a[v.status]);
                        return this
                    }, abort: function (a) {
                        var b = a || u;
                        return i && i.abort(b), x(0, b), this
                    }
                };
            if (o.promise(v).complete = p.add, v.success = v.done, v.error = v.fail, k.url = ((a || k.url || Ac) + "").replace(Bc, "").replace(Gc, zc[1] + "//"), k.type = b.method || b.type || k.method || k.type, k.dataTypes = n.trim(k.dataType || "*").toLowerCase().match(F) || [""], null == k.crossDomain && (c = Hc.exec(k.url.toLowerCase()), k.crossDomain = !(!c || c[1] === zc[1] && c[2] === zc[2] && (c[3] || ("http:" === c[1] ? "80" : "443")) === (zc[3] || ("http:" === zc[1] ? "80" : "443")))), k.data && k.processData && "string" != typeof k.data && (k.data = n.param(k.data, k.traditional)), Nc(Ic, k, b, v), 2 === t) return v;
            h = k.global, h && 0 === n.active++ && n.event.trigger("ajaxStart"), k.type = k.type.toUpperCase(), k.hasContent = !Fc.test(k.type), e = k.url, k.hasContent || (k.data && (e = k.url += (xc.test(e) ? "&" : "?") + k.data, delete k.data), k.cache === !1 && (k.url = Cc.test(e) ? e.replace(Cc, "$1_=" + wc++) : e + (xc.test(e) ? "&" : "?") + "_=" + wc++)), k.ifModified && (n.lastModified[e] && v.setRequestHeader("If-Modified-Since", n.lastModified[e]), n.etag[e] && v.setRequestHeader("If-None-Match", n.etag[e])), (k.data && k.hasContent && k.contentType !== !1 || b.contentType) && v.setRequestHeader("Content-Type", k.contentType), v.setRequestHeader("Accept", k.dataTypes[0] && k.accepts[k.dataTypes[0]] ? k.accepts[k.dataTypes[0]] + ("*" !== k.dataTypes[0] ? ", " + Kc + "; q=0.01" : "") : k.accepts["*"]);
            for (d in k.headers) v.setRequestHeader(d, k.headers[d]);
            if (k.beforeSend && (k.beforeSend.call(l, v, k) === !1 || 2 === t)) return v.abort();
            u = "abort";
            for (d in{success: 1, error: 1, complete: 1}) v[d](k[d]);
            if (i = Nc(Jc, k, b, v)) {
                v.readyState = 1, h && m.trigger("ajaxSend", [v, k]), k.async && k.timeout > 0 && (g = setTimeout(function () {
                    v.abort("timeout")
                }, k.timeout));
                try {
                    t = 1, i.send(r, x)
                } catch (w) {
                    if (!(2 > t)) throw w;
                    x(-1, w)
                }
            } else x(-1, "No Transport");

            function x(a, b, c, d) {
                var j, r, s, u, w, x = b;
                2 !== t && (t = 2, g && clearTimeout(g), i = void 0, f = d || "", v.readyState = a > 0 ? 4 : 0, j = a >= 200 && 300 > a || 304 === a, c && (u = Pc(k, v, c)), u = Qc(k, u, v, j), j ? (k.ifModified && (w = v.getResponseHeader("Last-Modified"), w && (n.lastModified[e] = w), w = v.getResponseHeader("etag"), w && (n.etag[e] = w)), 204 === a || "HEAD" === k.type ? x = "nocontent" : 304 === a ? x = "notmodified" : (x = u.state, r = u.data, s = u.error, j = !s)) : (s = x, (a || !x) && (x = "error", 0 > a && (a = 0))), v.status = a, v.statusText = (b || x) + "", j ? o.resolveWith(l, [r, x, v]) : o.rejectWith(l, [v, x, s]), v.statusCode(q), q = void 0, h && m.trigger(j ? "ajaxSuccess" : "ajaxError", [v, k, j ? r : s]), p.fireWith(l, [v, x]), h && (m.trigger("ajaxComplete", [v, k]), --n.active || n.event.trigger("ajaxStop")))
            }

            return v
        },
        getJSON: function (a, b, c) {
            return n.get(a, b, c, "json")
        },
        getScript: function (a, b) {
            return n.get(a, void 0, b, "script")
        }
    }), n.each(["get", "post"], function (a, b) {
        n[b] = function (a, c, d, e) {
            return n.isFunction(c) && (e = e || d, d = c, c = void 0), n.ajax({
                url: a,
                type: b,
                dataType: e,
                data: c,
                success: d
            })
        }
    }), n.each(["ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend"], function (a, b) {
        n.fn[b] = function (a) {
            return this.on(b, a)
        }
    }), n._evalUrl = function (a) {
        return n.ajax({url: a, type: "GET", dataType: "script", async: !1, global: !1, "throws": !0})
    }, n.fn.extend({
        wrapAll: function (a) {
            if (n.isFunction(a)) return this.each(function (b) {
                n(this).wrapAll(a.call(this, b))
            });
            if (this[0]) {
                var b = n(a, this[0].ownerDocument).eq(0).clone(!0);
                this[0].parentNode && b.insertBefore(this[0]), b.map(function () {
                    var a = this;
                    while (a.firstChild && 1 === a.firstChild.nodeType) a = a.firstChild;
                    return a
                }).append(this)
            }
            return this
        }, wrapInner: function (a) {
            return this.each(n.isFunction(a) ? function (b) {
                n(this).wrapInner(a.call(this, b))
            } : function () {
                var b = n(this), c = b.contents();
                c.length ? c.wrapAll(a) : b.append(a)
            })
        }, wrap: function (a) {
            var b = n.isFunction(a);
            return this.each(function (c) {
                n(this).wrapAll(b ? a.call(this, c) : a)
            })
        }, unwrap: function () {
            return this.parent().each(function () {
                n.nodeName(this, "body") || n(this).replaceWith(this.childNodes)
            }).end()
        }
    }), n.expr.filters.hidden = function (a) {
        return a.offsetWidth <= 0 && a.offsetHeight <= 0 || !l.reliableHiddenOffsets() && "none" === (a.style && a.style.display || n.css(a, "display"))
    }, n.expr.filters.visible = function (a) {
        return !n.expr.filters.hidden(a)
    };
    var Rc = /%20/g, Sc = /\[\]$/, Tc = /\r?\n/g, Uc = /^(?:submit|button|image|reset|file)$/i,
        Vc = /^(?:input|select|textarea|keygen)/i;

    function Wc(a, b, c, d) {
        var e;
        if (n.isArray(b)) n.each(b, function (b, e) {
            c || Sc.test(a) ? d(a, e) : Wc(a + "[" + ("object" == typeof e ? b : "") + "]", e, c, d)
        }); else if (c || "object" !== n.type(b)) d(a, b); else for (e in b) Wc(a + "[" + e + "]", b[e], c, d)
    }

    n.param = function (a, b) {
        var c, d = [], e = function (a, b) {
            b = n.isFunction(b) ? b() : null == b ? "" : b, d[d.length] = encodeURIComponent(a) + "=" + encodeURIComponent(b)
        };
        if (void 0 === b && (b = n.ajaxSettings && n.ajaxSettings.traditional), n.isArray(a) || a.jquery && !n.isPlainObject(a)) n.each(a, function () {
            e(this.name, this.value)
        }); else for (c in a) Wc(c, a[c], b, e);
        return d.join("&").replace(Rc, "+")
    }, n.fn.extend({
        serialize: function () {
            return n.param(this.serializeArray())
        }, serializeArray: function () {
            return this.map(function () {
                var a = n.prop(this, "elements");
                return a ? n.makeArray(a) : this
            }).filter(function () {
                var a = this.type;
                return this.name && !n(this).is(":disabled") && Vc.test(this.nodeName) && !Uc.test(a) && (this.checked || !X.test(a))
            }).map(function (a, b) {
                var c = n(this).val();
                return null == c ? null : n.isArray(c) ? n.map(c, function (a) {
                    return {name: b.name, value: a.replace(Tc, "\r\n")}
                }) : {name: b.name, value: c.replace(Tc, "\r\n")}
            }).get()
        }
    }), n.ajaxSettings.xhr = void 0 !== a.ActiveXObject ? function () {
        return !this.isLocal && /^(get|post|head|put|delete|options)$/i.test(this.type) && $c() || _c()
    } : $c;
    var Xc = 0, Yc = {}, Zc = n.ajaxSettings.xhr();
    a.ActiveXObject && n(a).on("unload", function () {
        for (var a in Yc) Yc[a](void 0, !0)
    }), l.cors = !!Zc && "withCredentials" in Zc, Zc = l.ajax = !!Zc, Zc && n.ajaxTransport(function (a) {
        if (!a.crossDomain || l.cors) {
            var b;
            return {
                send: function (c, d) {
                    var e, f = a.xhr(), g = ++Xc;
                    if (f.open(a.type, a.url, a.async, a.username, a.password), a.xhrFields) for (e in a.xhrFields) f[e] = a.xhrFields[e];
                    a.mimeType && f.overrideMimeType && f.overrideMimeType(a.mimeType), a.crossDomain || c["X-Requested-With"] || (c["X-Requested-With"] = "XMLHttpRequest");
                    for (e in c) void 0 !== c[e] && f.setRequestHeader(e, c[e] + "");
                    f.send(a.hasContent && a.data || null), b = function (c, e) {
                        var h, i, j;
                        if (b && (e || 4 === f.readyState)) if (delete Yc[g], b = void 0, f.onreadystatechange = n.noop, e) 4 !== f.readyState && f.abort(); else {
                            j = {}, h = f.status, "string" == typeof f.responseText && (j.text = f.responseText);
                            try {
                                i = f.statusText
                            } catch (k) {
                                i = ""
                            }
                            h || !a.isLocal || a.crossDomain ? 1223 === h && (h = 204) : h = j.text ? 200 : 404
                        }
                        j && d(h, i, j, f.getAllResponseHeaders())
                    }, a.async ? 4 === f.readyState ? setTimeout(b) : f.onreadystatechange = Yc[g] = b : b()
                }, abort: function () {
                    b && b(void 0, !0)
                }
            }
        }
    });

    function $c() {
        try {
            return new a.XMLHttpRequest
        } catch (b) {
        }
    }

    function _c() {
        try {
            return new a.ActiveXObject("Microsoft.XMLHTTP")
        } catch (b) {
        }
    }

    n.ajaxSetup({
        accepts: {script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},
        contents: {script: /(?:java|ecma)script/},
        converters: {
            "text script": function (a) {
                return n.globalEval(a), a
            }
        }
    }), n.ajaxPrefilter("script", function (a) {
        void 0 === a.cache && (a.cache = !1), a.crossDomain && (a.type = "GET", a.global = !1)
    }), n.ajaxTransport("script", function (a) {
        if (a.crossDomain) {
            var b, c = z.head || n("head")[0] || z.documentElement;
            return {
                send: function (d, e) {
                    b = z.createElement("script"), b.async = !0, a.scriptCharset && (b.charset = a.scriptCharset), b.src = a.url, b.onload = b.onreadystatechange = function (a, c) {
                        (c || !b.readyState || /loaded|complete/.test(b.readyState)) && (b.onload = b.onreadystatechange = null, b.parentNode && b.parentNode.removeChild(b), b = null, c || e(200, "success"))
                    }, c.insertBefore(b, c.firstChild)
                }, abort: function () {
                    b && b.onload(void 0, !0)
                }
            }
        }
    });
    var ad = [], bd = /(=)\?(?=&|$)|\?\?/;
    n.ajaxSetup({
        jsonp: "callback", jsonpCallback: function () {
            var a = ad.pop() || n.expando + "_" + wc++;
            return this[a] = !0, a
        }
    }), n.ajaxPrefilter("json jsonp", function (b, c, d) {
        var e, f, g,
            h = b.jsonp !== !1 && (bd.test(b.url) ? "url" : "string" == typeof b.data && !(b.contentType || "").indexOf("application/x-www-form-urlencoded") && bd.test(b.data) && "data");
        return h || "jsonp" === b.dataTypes[0] ? (e = b.jsonpCallback = n.isFunction(b.jsonpCallback) ? b.jsonpCallback() : b.jsonpCallback, h ? b[h] = b[h].replace(bd, "$1" + e) : b.jsonp !== !1 && (b.url += (xc.test(b.url) ? "&" : "?") + b.jsonp + "=" + e), b.converters["script json"] = function () {
            return g || n.error(e + " was not called"), g[0]
        }, b.dataTypes[0] = "json", f = a[e], a[e] = function () {
            g = arguments
        }, d.always(function () {
            a[e] = f, b[e] && (b.jsonpCallback = c.jsonpCallback, ad.push(e)), g && n.isFunction(f) && f(g[0]), g = f = void 0
        }), "script") : void 0
    }), n.parseHTML = function (a, b, c) {
        if (!a || "string" != typeof a) return null;
        "boolean" == typeof b && (c = b, b = !1), b = b || z;
        var d = v.exec(a), e = !c && [];
        return d ? [b.createElement(d[1])] : (d = n.buildFragment([a], b, e), e && e.length && n(e).remove(), n.merge([], d.childNodes))
    };
    var cd = n.fn.load;
    n.fn.load = function (a, b, c) {
        if ("string" != typeof a && cd) return cd.apply(this, arguments);
        var d, e, f, g = this, h = a.indexOf(" ");
        return h >= 0 && (d = a.slice(h, a.length), a = a.slice(0, h)), n.isFunction(b) ? (c = b, b = void 0) : b && "object" == typeof b && (f = "POST"), g.length > 0 && n.ajax({
            url: a,
            type: f,
            dataType: "html",
            data: b
        }).done(function (a) {
            e = arguments, g.html(d ? n("<div>").append(n.parseHTML(a)).find(d) : a)
        }).complete(c && function (a, b) {
            g.each(c, e || [a.responseText, b, a])
        }), this
    }, n.expr.filters.animated = function (a) {
        return n.grep(n.timers, function (b) {
            return a === b.elem
        }).length
    };
    var dd = a.document.documentElement;

    function ed(a) {
        return n.isWindow(a) ? a : 9 === a.nodeType ? a.defaultView || a.parentWindow : !1
    }

    n.offset = {
        setOffset: function (a, b, c) {
            var d, e, f, g, h, i, j, k = n.css(a, "position"), l = n(a), m = {};
            "static" === k && (a.style.position = "relative"), h = l.offset(), f = n.css(a, "top"), i = n.css(a, "left"), j = ("absolute" === k || "fixed" === k) && n.inArray("auto", [f, i]) > -1, j ? (d = l.position(), g = d.top, e = d.left) : (g = parseFloat(f) || 0, e = parseFloat(i) || 0), n.isFunction(b) && (b = b.call(a, c, h)), null != b.top && (m.top = b.top - h.top + g), null != b.left && (m.left = b.left - h.left + e), "using" in b ? b.using.call(a, m) : l.css(m)
        }
    }, n.fn.extend({
        offset: function (a) {
            if (arguments.length) return void 0 === a ? this : this.each(function (b) {
                n.offset.setOffset(this, a, b)
            });
            var b, c, d = {top: 0, left: 0}, e = this[0], f = e && e.ownerDocument;
            if (f) return b = f.documentElement, n.contains(b, e) ? (typeof e.getBoundingClientRect !== L && (d = e.getBoundingClientRect()), c = ed(f), {
                top: d.top + (c.pageYOffset || b.scrollTop) - (b.clientTop || 0),
                left: d.left + (c.pageXOffset || b.scrollLeft) - (b.clientLeft || 0)
            }) : d
        }, position: function () {
            if (this[0]) {
                var a, b, c = {top: 0, left: 0}, d = this[0];
                return "fixed" === n.css(d, "position") ? b = d.getBoundingClientRect() : (a = this.offsetParent(), b = this.offset(), n.nodeName(a[0], "html") || (c = a.offset()), c.top += n.css(a[0], "borderTopWidth", !0), c.left += n.css(a[0], "borderLeftWidth", !0)), {
                    top: b.top - c.top - n.css(d, "marginTop", !0),
                    left: b.left - c.left - n.css(d, "marginLeft", !0)
                }
            }
        }, offsetParent: function () {
            return this.map(function () {
                var a = this.offsetParent || dd;
                while (a && !n.nodeName(a, "html") && "static" === n.css(a, "position")) a = a.offsetParent;
                return a || dd
            })
        }
    }), n.each({scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function (a, b) {
        var c = /Y/.test(b);
        n.fn[a] = function (d) {
            return W(this, function (a, d, e) {
                var f = ed(a);
                return void 0 === e ? f ? b in f ? f[b] : f.document.documentElement[d] : a[d] : void(f ? f.scrollTo(c ? n(f).scrollLeft() : e, c ? e : n(f).scrollTop()) : a[d] = e)
            }, a, d, arguments.length, null)
        }
    }), n.each(["top", "left"], function (a, b) {
        n.cssHooks[b] = Mb(l.pixelPosition, function (a, c) {
            return c ? (c = Kb(a, b), Ib.test(c) ? n(a).position()[b] + "px" : c) : void 0
        })
    }), n.each({Height: "height", Width: "width"}, function (a, b) {
        n.each({padding: "inner" + a, content: b, "": "outer" + a}, function (c, d) {
            n.fn[d] = function (d, e) {
                var f = arguments.length && (c || "boolean" != typeof d),
                    g = c || (d === !0 || e === !0 ? "margin" : "border");
                return W(this, function (b, c, d) {
                    var e;
                    return n.isWindow(b) ? b.document.documentElement["client" + a] : 9 === b.nodeType ? (e = b.documentElement, Math.max(b.body["scroll" + a], e["scroll" + a], b.body["offset" + a], e["offset" + a], e["client" + a])) : void 0 === d ? n.css(b, c, g) : n.style(b, c, d, g)
                }, b, f ? d : void 0, f, null)
            }
        })
    }), n.fn.size = function () {
        return this.length
    }, n.fn.andSelf = n.fn.addBack, "function" == typeof define && define.amd && define("jquery", [], function () {
        return n
    });
    var fd = a.jQuery, gd = a.$;
    return n.noConflict = function (b) {
        return a.$ === n && (a.$ = gd), b && a.jQuery === n && (a.jQuery = fd), n
    }, typeof b === L && (a.jQuery = a.$ = n), n
});
</script>

<!-- Underscore + Knockout -->
<script type="text/javascript">//     Underscore.js 1.8.3
//     http://underscorejs.org
//     (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
//     Underscore may be freely distributed under the MIT license.
(function () {
    function n(n) {
        function t(t, r, e, u, i, o) {
            for (; i >= 0 && o > i; i += n) {
                var a = u ? u[i] : i;
                e = r(e, t[a], a, t)
            }
            return e
        }

        return function (r, e, u, i) {
            e = b(e, i, 4);
            var o = !k(r) && m.keys(r), a = (o || r).length, c = n > 0 ? 0 : a - 1;
            return arguments.length < 3 && (u = r[o ? o[c] : c], c += n), t(r, e, u, o, c, a)
        }
    }

    function t(n) {
        return function (t, r, e) {
            r = x(r, e);
            for (var u = O(t), i = n > 0 ? 0 : u - 1; i >= 0 && u > i; i += n) if (r(t[i], i, t)) return i;
            return -1
        }
    }

    function r(n, t, r) {
        return function (e, u, i) {
            var o = 0, a = O(e);
            if ("number" == typeof i) n > 0 ? o = i >= 0 ? i : Math.max(i + a, o) : a = i >= 0 ? Math.min(i + 1, a) : i + a + 1; else if (r && i && a) return i = r(e, u), e[i] === u ? i : -1;
            if (u !== u) return i = t(l.call(e, o, a), m.isNaN), i >= 0 ? i + o : -1;
            for (i = n > 0 ? o : a - 1; i >= 0 && a > i; i += n) if (e[i] === u) return i;
            return -1
        }
    }

    function e(n, t) {
        var r = I.length, e = n.constructor, u = m.isFunction(e) && e.prototype || a, i = "constructor";
        for (m.has(n, i) && !m.contains(t, i) && t.push(i); r--;) i = I[r], i in n && n[i] !== u[i] && !m.contains(t, i) && t.push(i)
    }

    var u = this, i = u._, o = Array.prototype, a = Object.prototype, c = Function.prototype, f = o.push, l = o.slice,
        s = a.toString, p = a.hasOwnProperty, h = Array.isArray, v = Object.keys, g = c.bind, y = Object.create,
        d = function () {
        }, m = function (n) {
            return n instanceof m ? n : this instanceof m ? void(this._wrapped = n) : new m(n)
        };
    "undefined" != typeof exports ? ("undefined" != typeof module && module.exports && (exports = module.exports = m), exports._ = m) : u._ = m, m.VERSION = "1.8.3";
    var b = function (n, t, r) {
        if (t === void 0) return n;
        switch (null == r ? 3 : r) {
            case 1:
                return function (r) {
                    return n.call(t, r)
                };
            case 2:
                return function (r, e) {
                    return n.call(t, r, e)
                };
            case 3:
                return function (r, e, u) {
                    return n.call(t, r, e, u)
                };
            case 4:
                return function (r, e, u, i) {
                    return n.call(t, r, e, u, i)
                }
        }
        return function () {
            return n.apply(t, arguments)
        }
    }, x = function (n, t, r) {
        return null == n ? m.identity : m.isFunction(n) ? b(n, t, r) : m.isObject(n) ? m.matcher(n) : m.property(n)
    };
    m.iteratee = function (n, t) {
        return x(n, t, 1 / 0)
    };
    var _ = function (n, t) {
        return function (r) {
            var e = arguments.length;
            if (2 > e || null == r) return r;
            for (var u = 1; e > u; u++) for (var i = arguments[u], o = n(i), a = o.length, c = 0; a > c; c++) {
                var f = o[c];
                t && r[f] !== void 0 || (r[f] = i[f])
            }
            return r
        }
    }, j = function (n) {
        if (!m.isObject(n)) return {};
        if (y) return y(n);
        d.prototype = n;
        var t = new d;
        return d.prototype = null, t
    }, w = function (n) {
        return function (t) {
            return null == t ? void 0 : t[n]
        }
    }, A = Math.pow(2, 53) - 1, O = w("length"), k = function (n) {
        var t = O(n);
        return "number" == typeof t && t >= 0 && A >= t
    };
    m.each = m.forEach = function (n, t, r) {
        t = b(t, r);
        var e, u;
        if (k(n)) for (e = 0, u = n.length; u > e; e++) t(n[e], e, n); else {
            var i = m.keys(n);
            for (e = 0, u = i.length; u > e; e++) t(n[i[e]], i[e], n)
        }
        return n
    }, m.map = m.collect = function (n, t, r) {
        t = x(t, r);
        for (var e = !k(n) && m.keys(n), u = (e || n).length, i = Array(u), o = 0; u > o; o++) {
            var a = e ? e[o] : o;
            i[o] = t(n[a], a, n)
        }
        return i
    }, m.reduce = m.foldl = m.inject = n(1), m.reduceRight = m.foldr = n(-1), m.find = m.detect = function (n, t, r) {
        var e;
        return e = k(n) ? m.findIndex(n, t, r) : m.findKey(n, t, r), e !== void 0 && e !== -1 ? n[e] : void 0
    }, m.filter = m.select = function (n, t, r) {
        var e = [];
        return t = x(t, r), m.each(n, function (n, r, u) {
            t(n, r, u) && e.push(n)
        }), e
    }, m.reject = function (n, t, r) {
        return m.filter(n, m.negate(x(t)), r)
    }, m.every = m.all = function (n, t, r) {
        t = x(t, r);
        for (var e = !k(n) && m.keys(n), u = (e || n).length, i = 0; u > i; i++) {
            var o = e ? e[i] : i;
            if (!t(n[o], o, n)) return !1
        }
        return !0
    }, m.some = m.any = function (n, t, r) {
        t = x(t, r);
        for (var e = !k(n) && m.keys(n), u = (e || n).length, i = 0; u > i; i++) {
            var o = e ? e[i] : i;
            if (t(n[o], o, n)) return !0
        }
        return !1
    }, m.contains = m.includes = m.include = function (n, t, r, e) {
        return k(n) || (n = m.values(n)), ("number" != typeof r || e) && (r = 0), m.indexOf(n, t, r) >= 0
    }, m.invoke = function (n, t) {
        var r = l.call(arguments, 2), e = m.isFunction(t);
        return m.map(n, function (n) {
            var u = e ? t : n[t];
            return null == u ? u : u.apply(n, r)
        })
    }, m.pluck = function (n, t) {
        return m.map(n, m.property(t))
    }, m.where = function (n, t) {
        return m.filter(n, m.matcher(t))
    }, m.findWhere = function (n, t) {
        return m.find(n, m.matcher(t))
    }, m.max = function (n, t, r) {
        var e, u, i = -1 / 0, o = -1 / 0;
        if (null == t && null != n) {
            n = k(n) ? n : m.values(n);
            for (var a = 0, c = n.length; c > a; a++) e = n[a], e > i && (i = e)
        } else t = x(t, r), m.each(n, function (n, r, e) {
            u = t(n, r, e), (u > o || u === -1 / 0 && i === -1 / 0) && (i = n, o = u)
        });
        return i
    }, m.min = function (n, t, r) {
        var e, u, i = 1 / 0, o = 1 / 0;
        if (null == t && null != n) {
            n = k(n) ? n : m.values(n);
            for (var a = 0, c = n.length; c > a; a++) e = n[a], i > e && (i = e)
        } else t = x(t, r), m.each(n, function (n, r, e) {
            u = t(n, r, e), (o > u || 1 / 0 === u && 1 / 0 === i) && (i = n, o = u)
        });
        return i
    }, m.shuffle = function (n) {
        for (var t, r = k(n) ? n : m.values(n), e = r.length, u = Array(e), i = 0; e > i; i++) t = m.random(0, i), t !== i && (u[i] = u[t]), u[t] = r[i];
        return u
    }, m.sample = function (n, t, r) {
        return null == t || r ? (k(n) || (n = m.values(n)), n[m.random(n.length - 1)]) : m.shuffle(n).slice(0, Math.max(0, t))
    }, m.sortBy = function (n, t, r) {
        return t = x(t, r), m.pluck(m.map(n, function (n, r, e) {
            return {value: n, index: r, criteria: t(n, r, e)}
        }).sort(function (n, t) {
            var r = n.criteria, e = t.criteria;
            if (r !== e) {
                if (r > e || r === void 0) return 1;
                if (e > r || e === void 0) return -1
            }
            return n.index - t.index
        }), "value")
    };
    var F = function (n) {
        return function (t, r, e) {
            var u = {};
            return r = x(r, e), m.each(t, function (e, i) {
                var o = r(e, i, t);
                n(u, e, o)
            }), u
        }
    };
    m.groupBy = F(function (n, t, r) {
        m.has(n, r) ? n[r].push(t) : n[r] = [t]
    }), m.indexBy = F(function (n, t, r) {
        n[r] = t
    }), m.countBy = F(function (n, t, r) {
        m.has(n, r) ? n[r]++ : n[r] = 1
    }), m.toArray = function (n) {
        return n ? m.isArray(n) ? l.call(n) : k(n) ? m.map(n, m.identity) : m.values(n) : []
    }, m.size = function (n) {
        return null == n ? 0 : k(n) ? n.length : m.keys(n).length
    }, m.partition = function (n, t, r) {
        t = x(t, r);
        var e = [], u = [];
        return m.each(n, function (n, r, i) {
            (t(n, r, i) ? e : u).push(n)
        }), [e, u]
    }, m.first = m.head = m.take = function (n, t, r) {
        return null == n ? void 0 : null == t || r ? n[0] : m.initial(n, n.length - t)
    }, m.initial = function (n, t, r) {
        return l.call(n, 0, Math.max(0, n.length - (null == t || r ? 1 : t)))
    }, m.last = function (n, t, r) {
        return null == n ? void 0 : null == t || r ? n[n.length - 1] : m.rest(n, Math.max(0, n.length - t))
    }, m.rest = m.tail = m.drop = function (n, t, r) {
        return l.call(n, null == t || r ? 1 : t)
    }, m.compact = function (n) {
        return m.filter(n, m.identity)
    };
    var S = function (n, t, r, e) {
        for (var u = [], i = 0, o = e || 0, a = O(n); a > o; o++) {
            var c = n[o];
            if (k(c) && (m.isArray(c) || m.isArguments(c))) {
                t || (c = S(c, t, r));
                var f = 0, l = c.length;
                for (u.length += l; l > f;) u[i++] = c[f++]
            } else r || (u[i++] = c)
        }
        return u
    };
    m.flatten = function (n, t) {
        return S(n, t, !1)
    }, m.without = function (n) {
        return m.difference(n, l.call(arguments, 1))
    }, m.uniq = m.unique = function (n, t, r, e) {
        m.isBoolean(t) || (e = r, r = t, t = !1), null != r && (r = x(r, e));
        for (var u = [], i = [], o = 0, a = O(n); a > o; o++) {
            var c = n[o], f = r ? r(c, o, n) : c;
            t ? (o && i === f || u.push(c), i = f) : r ? m.contains(i, f) || (i.push(f), u.push(c)) : m.contains(u, c) || u.push(c)
        }
        return u
    }, m.union = function () {
        return m.uniq(S(arguments, !0, !0))
    }, m.intersection = function (n) {
        for (var t = [], r = arguments.length, e = 0, u = O(n); u > e; e++) {
            var i = n[e];
            if (!m.contains(t, i)) {
                for (var o = 1; r > o && m.contains(arguments[o], i); o++) ;
                o === r && t.push(i)
            }
        }
        return t
    }, m.difference = function (n) {
        var t = S(arguments, !0, !0, 1);
        return m.filter(n, function (n) {
            return !m.contains(t, n)
        })
    }, m.zip = function () {
        return m.unzip(arguments)
    }, m.unzip = function (n) {
        for (var t = n && m.max(n, O).length || 0, r = Array(t), e = 0; t > e; e++) r[e] = m.pluck(n, e);
        return r
    }, m.object = function (n, t) {
        for (var r = {}, e = 0, u = O(n); u > e; e++) t ? r[n[e]] = t[e] : r[n[e][0]] = n[e][1];
        return r
    }, m.findIndex = t(1), m.findLastIndex = t(-1), m.sortedIndex = function (n, t, r, e) {
        r = x(r, e, 1);
        for (var u = r(t), i = 0, o = O(n); o > i;) {
            var a = Math.floor((i + o) / 2);
            r(n[a]) < u ? i = a + 1 : o = a
        }
        return i
    }, m.indexOf = r(1, m.findIndex, m.sortedIndex), m.lastIndexOf = r(-1, m.findLastIndex), m.range = function (n, t, r) {
        null == t && (t = n || 0, n = 0), r = r || 1;
        for (var e = Math.max(Math.ceil((t - n) / r), 0), u = Array(e), i = 0; e > i; i++, n += r) u[i] = n;
        return u
    };
    var E = function (n, t, r, e, u) {
        if (!(e instanceof t)) return n.apply(r, u);
        var i = j(n.prototype), o = n.apply(i, u);
        return m.isObject(o) ? o : i
    };
    m.bind = function (n, t) {
        if (g && n.bind === g) return g.apply(n, l.call(arguments, 1));
        if (!m.isFunction(n)) throw new TypeError("Bind must be called on a function");
        var r = l.call(arguments, 2), e = function () {
            return E(n, e, t, this, r.concat(l.call(arguments)))
        };
        return e
    }, m.partial = function (n) {
        var t = l.call(arguments, 1), r = function () {
            for (var e = 0, u = t.length, i = Array(u), o = 0; u > o; o++) i[o] = t[o] === m ? arguments[e++] : t[o];
            for (; e < arguments.length;) i.push(arguments[e++]);
            return E(n, r, this, this, i)
        };
        return r
    }, m.bindAll = function (n) {
        var t, r, e = arguments.length;
        if (1 >= e) throw new Error("bindAll must be passed function names");
        for (t = 1; e > t; t++) r = arguments[t], n[r] = m.bind(n[r], n);
        return n
    }, m.memoize = function (n, t) {
        var r = function (e) {
            var u = r.cache, i = "" + (t ? t.apply(this, arguments) : e);
            return m.has(u, i) || (u[i] = n.apply(this, arguments)), u[i]
        };
        return r.cache = {}, r
    }, m.delay = function (n, t) {
        var r = l.call(arguments, 2);
        return setTimeout(function () {
            return n.apply(null, r)
        }, t)
    }, m.defer = m.partial(m.delay, m, 1), m.throttle = function (n, t, r) {
        var e, u, i, o = null, a = 0;
        r || (r = {});
        var c = function () {
            a = r.leading === !1 ? 0 : m.now(), o = null, i = n.apply(e, u), o || (e = u = null)
        };
        return function () {
            var f = m.now();
            a || r.leading !== !1 || (a = f);
            var l = t - (f - a);
            return e = this, u = arguments, 0 >= l || l > t ? (o && (clearTimeout(o), o = null), a = f, i = n.apply(e, u), o || (e = u = null)) : o || r.trailing === !1 || (o = setTimeout(c, l)), i
        }
    }, m.debounce = function (n, t, r) {
        var e, u, i, o, a, c = function () {
            var f = m.now() - o;
            t > f && f >= 0 ? e = setTimeout(c, t - f) : (e = null, r || (a = n.apply(i, u), e || (i = u = null)))
        };
        return function () {
            i = this, u = arguments, o = m.now();
            var f = r && !e;
            return e || (e = setTimeout(c, t)), f && (a = n.apply(i, u), i = u = null), a
        }
    }, m.wrap = function (n, t) {
        return m.partial(t, n)
    }, m.negate = function (n) {
        return function () {
            return !n.apply(this, arguments)
        }
    }, m.compose = function () {
        var n = arguments, t = n.length - 1;
        return function () {
            for (var r = t, e = n[t].apply(this, arguments); r--;) e = n[r].call(this, e);
            return e
        }
    }, m.after = function (n, t) {
        return function () {
            return --n < 1 ? t.apply(this, arguments) : void 0
        }
    }, m.before = function (n, t) {
        var r;
        return function () {
            return --n > 0 && (r = t.apply(this, arguments)), 1 >= n && (t = null), r
        }
    }, m.once = m.partial(m.before, 2);
    var M = !{toString: null}.propertyIsEnumerable("toString"),
        I = ["valueOf", "isPrototypeOf", "toString", "propertyIsEnumerable", "hasOwnProperty", "toLocaleString"];
    m.keys = function (n) {
        if (!m.isObject(n)) return [];
        if (v) return v(n);
        var t = [];
        for (var r in n) m.has(n, r) && t.push(r);
        return M && e(n, t), t
    }, m.allKeys = function (n) {
        if (!m.isObject(n)) return [];
        var t = [];
        for (var r in n) t.push(r);
        return M && e(n, t), t
    }, m.values = function (n) {
        for (var t = m.keys(n), r = t.length, e = Array(r), u = 0; r > u; u++) e[u] = n[t[u]];
        return e
    }, m.mapObject = function (n, t, r) {
        t = x(t, r);
        for (var e, u = m.keys(n), i = u.length, o = {}, a = 0; i > a; a++) e = u[a], o[e] = t(n[e], e, n);
        return o
    }, m.pairs = function (n) {
        for (var t = m.keys(n), r = t.length, e = Array(r), u = 0; r > u; u++) e[u] = [t[u], n[t[u]]];
        return e
    }, m.invert = function (n) {
        for (var t = {}, r = m.keys(n), e = 0, u = r.length; u > e; e++) t[n[r[e]]] = r[e];
        return t
    }, m.functions = m.methods = function (n) {
        var t = [];
        for (var r in n) m.isFunction(n[r]) && t.push(r);
        return t.sort()
    }, m.extend = _(m.allKeys), m.extendOwn = m.assign = _(m.keys), m.findKey = function (n, t, r) {
        t = x(t, r);
        for (var e, u = m.keys(n), i = 0, o = u.length; o > i; i++) if (e = u[i], t(n[e], e, n)) return e
    }, m.pick = function (n, t, r) {
        var e, u, i = {}, o = n;
        if (null == o) return i;
        m.isFunction(t) ? (u = m.allKeys(o), e = b(t, r)) : (u = S(arguments, !1, !1, 1), e = function (n, t, r) {
            return t in r
        }, o = Object(o));
        for (var a = 0, c = u.length; c > a; a++) {
            var f = u[a], l = o[f];
            e(l, f, o) && (i[f] = l)
        }
        return i
    }, m.omit = function (n, t, r) {
        if (m.isFunction(t)) t = m.negate(t); else {
            var e = m.map(S(arguments, !1, !1, 1), String);
            t = function (n, t) {
                return !m.contains(e, t)
            }
        }
        return m.pick(n, t, r)
    }, m.defaults = _(m.allKeys, !0), m.create = function (n, t) {
        var r = j(n);
        return t && m.extendOwn(r, t), r
    }, m.clone = function (n) {
        return m.isObject(n) ? m.isArray(n) ? n.slice() : m.extend({}, n) : n
    }, m.tap = function (n, t) {
        return t(n), n
    }, m.isMatch = function (n, t) {
        var r = m.keys(t), e = r.length;
        if (null == n) return !e;
        for (var u = Object(n), i = 0; e > i; i++) {
            var o = r[i];
            if (t[o] !== u[o] || !(o in u)) return !1
        }
        return !0
    };
    var N = function (n, t, r, e) {
        if (n === t) return 0 !== n || 1 / n === 1 / t;
        if (null == n || null == t) return n === t;
        n instanceof m && (n = n._wrapped), t instanceof m && (t = t._wrapped);
        var u = s.call(n);
        if (u !== s.call(t)) return !1;
        switch (u) {
            case"[object RegExp]":
            case"[object String]":
                return "" + n == "" + t;
            case"[object Number]":
                return +n !== +n ? +t !== +t : 0 === +n ? 1 / +n === 1 / t : +n === +t;
            case"[object Date]":
            case"[object Boolean]":
                return +n === +t
        }
        var i = "[object Array]" === u;
        if (!i) {
            if ("object" != typeof n || "object" != typeof t) return !1;
            var o = n.constructor, a = t.constructor;
            if (o !== a && !(m.isFunction(o) && o instanceof o && m.isFunction(a) && a instanceof a) && "constructor" in n && "constructor" in t) return !1
        }
        r = r || [], e = e || [];
        for (var c = r.length; c--;) if (r[c] === n) return e[c] === t;
        if (r.push(n), e.push(t), i) {
            if (c = n.length, c !== t.length) return !1;
            for (; c--;) if (!N(n[c], t[c], r, e)) return !1
        } else {
            var f, l = m.keys(n);
            if (c = l.length, m.keys(t).length !== c) return !1;
            for (; c--;) if (f = l[c], !m.has(t, f) || !N(n[f], t[f], r, e)) return !1
        }
        return r.pop(), e.pop(), !0
    };
    m.isEqual = function (n, t) {
        return N(n, t)
    }, m.isEmpty = function (n) {
        return null == n ? !0 : k(n) && (m.isArray(n) || m.isString(n) || m.isArguments(n)) ? 0 === n.length : 0 === m.keys(n).length
    }, m.isElement = function (n) {
        return !(!n || 1 !== n.nodeType)
    }, m.isArray = h || function (n) {
        return "[object Array]" === s.call(n)
    }, m.isObject = function (n) {
        var t = typeof n;
        return "function" === t || "object" === t && !!n
    }, m.each(["Arguments", "Function", "String", "Number", "Date", "RegExp", "Error"], function (n) {
        m["is" + n] = function (t) {
            return s.call(t) === "[object " + n + "]"
        }
    }), m.isArguments(arguments) || (m.isArguments = function (n) {
        return m.has(n, "callee")
    }), "function" != typeof/./ && "object" != typeof Int8Array && (m.isFunction = function (n) {
        return "function" == typeof n || !1
    }), m.isFinite = function (n) {
        return isFinite(n) && !isNaN(parseFloat(n))
    }, m.isNaN = function (n) {
        return m.isNumber(n) && n !== +n
    }, m.isBoolean = function (n) {
        return n === !0 || n === !1 || "[object Boolean]" === s.call(n)
    }, m.isNull = function (n) {
        return null === n
    }, m.isUndefined = function (n) {
        return n === void 0
    }, m.has = function (n, t) {
        return null != n && p.call(n, t)
    }, m.noConflict = function () {
        return u._ = i, this
    }, m.identity = function (n) {
        return n
    }, m.constant = function (n) {
        return function () {
            return n
        }
    }, m.noop = function () {
    }, m.property = w, m.propertyOf = function (n) {
        return null == n ? function () {
        } : function (t) {
            return n[t]
        }
    }, m.matcher = m.matches = function (n) {
        return n = m.extendOwn({}, n), function (t) {
            return m.isMatch(t, n)
        }
    }, m.times = function (n, t, r) {
        var e = Array(Math.max(0, n));
        t = b(t, r, 1);
        for (var u = 0; n > u; u++) e[u] = t(u);
        return e
    }, m.random = function (n, t) {
        return null == t && (t = n, n = 0), n + Math.floor(Math.random() * (t - n + 1))
    }, m.now = Date.now || function () {
        return (new Date).getTime()
    };
    var B = {"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "'", "`": "`"}, T = m.invert(B),
        R = function (n) {
            var t = function (t) {
                return n[t]
            }, r = "(?:" + m.keys(n).join("|") + ")", e = RegExp(r), u = RegExp(r, "g");
            return function (n) {
                return n = null == n ? "" : "" + n, e.test(n) ? n.replace(u, t) : n
            }
        };
    m.escape = R(B), m.unescape = R(T), m.result = function (n, t, r) {
        var e = null == n ? void 0 : n[t];
        return e === void 0 && (e = r), m.isFunction(e) ? e.call(n) : e
    };
    var q = 0;
    m.uniqueId = function (n) {
        var t = ++q + "";
        return n ? n + t : t
    }, m.templateSettings = {evaluate: /<%([\s\S]+?)%>/g, interpolate: /<%=([\s\S]+?)%>/g, escape: /<%-([\s\S]+?)%>/g};
    var K = /(.)^/, z = {"'": "'", "\\": "\\", "\r": "r", "\n": "n", "\u2028": "u2028", "\u2029": "u2029"},
        D = /\\|'|\r|\n|\u2028|\u2029/g, L = function (n) {
            return "\\" + z[n]
        };
    m.template = function (n, t, r) {
        !t && r && (t = r), t = m.defaults({}, t, m.templateSettings);
        var e = RegExp([(t.escape || K).source, (t.interpolate || K).source, (t.evaluate || K).source].join("|") + "|$", "g"),
            u = 0, i = "__p+='";
        n.replace(e, function (t, r, e, o, a) {
            return i += n.slice(u, a).replace(D, L), u = a + t.length, r ? i += "'+\n((__t=(" + r + "))==null?'':_.escape(__t))+\n'" : e ? i += "'+\n((__t=(" + e + "))==null?'':__t)+\n'" : o && (i += "';\n" + o + "\n__p+='"), t
        }), i += "';\n", t.variable || (i = "with(obj||{}){\n" + i + "}\n"), i = "var __t,__p='',__j=Array.prototype.join," + "print=function(){__p+=__j.call(arguments,'');};\n" + i + "return __p;\n";
        try {
            var o = new Function(t.variable || "obj", "_", i)
        } catch (a) {
            throw a.source = i, a
        }
        var c = function (n) {
            return o.call(this, n, m)
        }, f = t.variable || "obj";
        return c.source = "function(" + f + "){\n" + i + "}", c
    }, m.chain = function (n) {
        var t = m(n);
        return t._chain = !0, t
    };
    var P = function (n, t) {
        return n._chain ? m(t).chain() : t
    };
    m.mixin = function (n) {
        m.each(m.functions(n), function (t) {
            var r = m[t] = n[t];
            m.prototype[t] = function () {
                var n = [this._wrapped];
                return f.apply(n, arguments), P(this, r.apply(m, n))
            }
        })
    }, m.mixin(m), m.each(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (n) {
        var t = o[n];
        m.prototype[n] = function () {
            var r = this._wrapped;
            return t.apply(r, arguments), "shift" !== n && "splice" !== n || 0 !== r.length || delete r[0], P(this, r)
        }
    }), m.each(["concat", "join", "slice"], function (n) {
        var t = o[n];
        m.prototype[n] = function () {
            return P(this, t.apply(this._wrapped, arguments))
        }
    }), m.prototype.value = function () {
        return this._wrapped
    }, m.prototype.valueOf = m.prototype.toJSON = m.prototype.value, m.prototype.toString = function () {
        return "" + this._wrapped
    }, "function" == typeof define && define.amd && define("underscore", [], function () {
        return m
    })
}).call(this);
//# sourceMappingURL=underscore-min.map</script>
<script type="text/javascript">(function () {
    var t = this;
    var e = t.Backbone;
    var i = [];
    var r = i.push;
    var s = i.slice;
    var n = i.splice;
    var a;
    if (typeof exports !== "undefined") {
        a = exports
    } else {
        a = t.Backbone = {}
    }
    a.VERSION = "1.1.0";
    var h = t._;
    if (!h && typeof require !== "undefined") h = require("underscore");
    a.$ = t.jQuery || t.Zepto || t.ender || t.$;
    a.noConflict = function () {
        t.Backbone = e;
        return this
    };
    a.emulateHTTP = false;
    a.emulateJSON = false;
    var o = a.Events = {
        on: function (t, e, i) {
            if (!l(this, "on", t, [e, i]) || !e) return this;
            this._events || (this._events = {});
            var r = this._events[t] || (this._events[t] = []);
            r.push({callback: e, context: i, ctx: i || this});
            return this
        }, once: function (t, e, i) {
            if (!l(this, "once", t, [e, i]) || !e) return this;
            var r = this;
            var s = h.once(function () {
                r.off(t, s);
                e.apply(this, arguments)
            });
            s._callback = e;
            return this.on(t, s, i)
        }, off: function (t, e, i) {
            var r, s, n, a, o, u, c, f;
            if (!this._events || !l(this, "off", t, [e, i])) return this;
            if (!t && !e && !i) {
                this._events = {};
                return this
            }
            a = t ? [t] : h.keys(this._events);
            for (o = 0, u = a.length; o < u; o++) {
                t = a[o];
                if (n = this._events[t]) {
                    this._events[t] = r = [];
                    if (e || i) {
                        for (c = 0, f = n.length; c < f; c++) {
                            s = n[c];
                            if (e && e !== s.callback && e !== s.callback._callback || i && i !== s.context) {
                                r.push(s)
                            }
                        }
                    }
                    if (!r.length) delete this._events[t]
                }
            }
            return this
        }, trigger: function (t) {
            if (!this._events) return this;
            var e = s.call(arguments, 1);
            if (!l(this, "trigger", t, e)) return this;
            var i = this._events[t];
            var r = this._events.all;
            if (i) c(i, e);
            if (r) c(r, arguments);
            return this
        }, stopListening: function (t, e, i) {
            var r = this._listeningTo;
            if (!r) return this;
            var s = !e && !i;
            if (!i && typeof e === "object") i = this;
            if (t) (r = {})[t._listenId] = t;
            for (var n in r) {
                t = r[n];
                t.off(e, i, this);
                if (s || h.isEmpty(t._events)) delete this._listeningTo[n]
            }
            return this
        }
    };
    var u = /\s+/;
    var l = function (t, e, i, r) {
        if (!i) return true;
        if (typeof i === "object") {
            for (var s in i) {
                t[e].apply(t, [s, i[s]].concat(r))
            }
            return false
        }
        if (u.test(i)) {
            var n = i.split(u);
            for (var a = 0, h = n.length; a < h; a++) {
                t[e].apply(t, [n[a]].concat(r))
            }
            return false
        }
        return true
    };
    var c = function (t, e) {
        var i, r = -1, s = t.length, n = e[0], a = e[1], h = e[2];
        switch (e.length) {
            case 0:
                while (++r < s) (i = t[r]).callback.call(i.ctx);
                return;
            case 1:
                while (++r < s) (i = t[r]).callback.call(i.ctx, n);
                return;
            case 2:
                while (++r < s) (i = t[r]).callback.call(i.ctx, n, a);
                return;
            case 3:
                while (++r < s) (i = t[r]).callback.call(i.ctx, n, a, h);
                return;
            default:
                while (++r < s) (i = t[r]).callback.apply(i.ctx, e)
        }
    };
    var f = {listenTo: "on", listenToOnce: "once"};
    h.each(f, function (t, e) {
        o[e] = function (e, i, r) {
            var s = this._listeningTo || (this._listeningTo = {});
            var n = e._listenId || (e._listenId = h.uniqueId("l"));
            s[n] = e;
            if (!r && typeof i === "object") r = this;
            e[t](i, r, this);
            return this
        }
    });
    o.bind = o.on;
    o.unbind = o.off;
    h.extend(a, o);
    var d = a.Model = function (t, e) {
        var i = t || {};
        e || (e = {});
        this.cid = h.uniqueId("c");
        this.attributes = {};
        if (e.collection) this.collection = e.collection;
        if (e.parse) i = this.parse(i, e) || {};
        i = h.defaults({}, i, h.result(this, "defaults"));
        this.set(i, e);
        this.changed = {};
        this.initialize.apply(this, arguments)
    };
    h.extend(d.prototype, o, {
        changed: null, validationError: null, idAttribute: "id", initialize: function () {
        }, toJSON: function (t) {
            return h.clone(this.attributes)
        }, sync: function () {
            return a.sync.apply(this, arguments)
        }, get: function (t) {
            return this.attributes[t]
        }, escape: function (t) {
            return h.escape(this.get(t))
        }, has: function (t) {
            return this.get(t) != null
        }, set: function (t, e, i) {
            var r, s, n, a, o, u, l, c;
            if (t == null) return this;
            if (typeof t === "object") {
                s = t;
                i = e
            } else {
                (s = {})[t] = e
            }
            i || (i = {});
            if (!this._validate(s, i)) return false;
            n = i.unset;
            o = i.silent;
            a = [];
            u = this._changing;
            this._changing = true;
            if (!u) {
                this._previousAttributes = h.clone(this.attributes);
                this.changed = {}
            }
            c = this.attributes, l = this._previousAttributes;
            if (this.idAttribute in s) this.id = s[this.idAttribute];
            for (r in s) {
                e = s[r];
                if (!h.isEqual(c[r], e)) a.push(r);
                if (!h.isEqual(l[r], e)) {
                    this.changed[r] = e
                } else {
                    delete this.changed[r]
                }
                n ? delete c[r] : c[r] = e
            }
            if (!o) {
                if (a.length) this._pending = true;
                for (var f = 0, d = a.length; f < d; f++) {
                    this.trigger("change:" + a[f], this, c[a[f]], i)
                }
            }
            if (u) return this;
            if (!o) {
                while (this._pending) {
                    this._pending = false;
                    this.trigger("change", this, i)
                }
            }
            this._pending = false;
            this._changing = false;
            return this
        }, unset: function (t, e) {
            return this.set(t, void 0, h.extend({}, e, {unset: true}))
        }, clear: function (t) {
            var e = {};
            for (var i in this.attributes) e[i] = void 0;
            return this.set(e, h.extend({}, t, {unset: true}))
        }, hasChanged: function (t) {
            if (t == null) return !h.isEmpty(this.changed);
            return h.has(this.changed, t)
        }, changedAttributes: function (t) {
            if (!t) return this.hasChanged() ? h.clone(this.changed) : false;
            var e, i = false;
            var r = this._changing ? this._previousAttributes : this.attributes;
            for (var s in t) {
                if (h.isEqual(r[s], e = t[s])) continue;
                (i || (i = {}))[s] = e
            }
            return i
        }, previous: function (t) {
            if (t == null || !this._previousAttributes) return null;
            return this._previousAttributes[t]
        }, previousAttributes: function () {
            return h.clone(this._previousAttributes)
        }, fetch: function (t) {
            t = t ? h.clone(t) : {};
            if (t.parse === void 0) t.parse = true;
            var e = this;
            var i = t.success;
            t.success = function (r) {
                if (!e.set(e.parse(r, t), t)) return false;
                if (i) i(e, r, t);
                e.trigger("sync", e, r, t)
            };
            M(this, t);
            return this.sync("read", this, t)
        }, save: function (t, e, i) {
            var r, s, n, a = this.attributes;
            if (t == null || typeof t === "object") {
                r = t;
                i = e
            } else {
                (r = {})[t] = e
            }
            i = h.extend({validate: true}, i);
            if (r && !i.wait) {
                if (!this.set(r, i)) return false
            } else {
                if (!this._validate(r, i)) return false
            }
            if (r && i.wait) {
                this.attributes = h.extend({}, a, r)
            }
            if (i.parse === void 0) i.parse = true;
            var o = this;
            var u = i.success;
            i.success = function (t) {
                o.attributes = a;
                var e = o.parse(t, i);
                if (i.wait) e = h.extend(r || {}, e);
                if (h.isObject(e) && !o.set(e, i)) {
                    return false
                }
                if (u) u(o, t, i);
                o.trigger("sync", o, t, i)
            };
            M(this, i);
            s = this.isNew() ? "create" : i.patch ? "patch" : "update";
            if (s === "patch") i.attrs = r;
            n = this.sync(s, this, i);
            if (r && i.wait) this.attributes = a;
            return n
        }, destroy: function (t) {
            t = t ? h.clone(t) : {};
            var e = this;
            var i = t.success;
            var r = function () {
                e.trigger("destroy", e, e.collection, t)
            };
            t.success = function (s) {
                if (t.wait || e.isNew()) r();
                if (i) i(e, s, t);
                if (!e.isNew()) e.trigger("sync", e, s, t)
            };
            if (this.isNew()) {
                t.success();
                return false
            }
            M(this, t);
            var s = this.sync("delete", this, t);
            if (!t.wait) r();
            return s
        }, url: function () {
            var t = h.result(this, "urlRoot") || h.result(this.collection, "url") || U();
            if (this.isNew()) return t;
            return t + (t.charAt(t.length - 1) === "/" ? "" : "/") + encodeURIComponent(this.id)
        }, parse: function (t, e) {
            return t
        }, clone: function () {
            return new this.constructor(this.attributes)
        }, isNew: function () {
            return this.id == null
        }, isValid: function (t) {
            return this._validate({}, h.extend(t || {}, {validate: true}))
        }, _validate: function (t, e) {
            if (!e.validate || !this.validate) return true;
            t = h.extend({}, this.attributes, t);
            var i = this.validationError = this.validate(t, e) || null;
            if (!i) return true;
            this.trigger("invalid", this, i, h.extend(e, {validationError: i}));
            return false
        }
    });
    var p = ["keys", "values", "pairs", "invert", "pick", "omit"];
    h.each(p, function (t) {
        d.prototype[t] = function () {
            var e = s.call(arguments);
            e.unshift(this.attributes);
            return h[t].apply(h, e)
        }
    });
    var v = a.Collection = function (t, e) {
        e || (e = {});
        if (e.model) this.model = e.model;
        if (e.comparator !== void 0) this.comparator = e.comparator;
        this._reset();
        this.initialize.apply(this, arguments);
        if (t) this.reset(t, h.extend({silent: true}, e))
    };
    var g = {add: true, remove: true, merge: true};
    var m = {add: true, remove: false};
    h.extend(v.prototype, o, {
        model: d, initialize: function () {
        }, toJSON: function (t) {
            return this.map(function (e) {
                return e.toJSON(t)
            })
        }, sync: function () {
            return a.sync.apply(this, arguments)
        }, add: function (t, e) {
            return this.set(t, h.extend({merge: false}, e, m))
        }, remove: function (t, e) {
            var i = !h.isArray(t);
            t = i ? [t] : h.clone(t);
            e || (e = {});
            var r, s, n, a;
            for (r = 0, s = t.length; r < s; r++) {
                a = t[r] = this.get(t[r]);
                if (!a) continue;
                delete this._byId[a.id];
                delete this._byId[a.cid];
                n = this.indexOf(a);
                this.models.splice(n, 1);
                this.length--;
                if (!e.silent) {
                    e.index = n;
                    a.trigger("remove", a, this, e)
                }
                this._removeReference(a)
            }
            return i ? t[0] : t
        }, set: function (t, e) {
            e = h.defaults({}, e, g);
            if (e.parse) t = this.parse(t, e);
            var i = !h.isArray(t);
            t = i ? t ? [t] : [] : h.clone(t);
            var r, s, n, a, o, u, l;
            var c = e.at;
            var f = this.model;
            var p = this.comparator && c == null && e.sort !== false;
            var v = h.isString(this.comparator) ? this.comparator : null;
            var m = [], y = [], _ = {};
            var w = e.add, b = e.merge, x = e.remove;
            var E = !p && w && x ? [] : false;
            for (r = 0, s = t.length; r < s; r++) {
                o = t[r];
                if (o instanceof d) {
                    n = a = o
                } else {
                    n = o[f.prototype.idAttribute]
                }
                if (u = this.get(n)) {
                    if (x) _[u.cid] = true;
                    if (b) {
                        o = o === a ? a.attributes : o;
                        if (e.parse) o = u.parse(o, e);
                        u.set(o, e);
                        if (p && !l && u.hasChanged(v)) l = true
                    }
                    t[r] = u
                } else if (w) {
                    a = t[r] = this._prepareModel(o, e);
                    if (!a) continue;
                    m.push(a);
                    a.on("all", this._onModelEvent, this);
                    this._byId[a.cid] = a;
                    if (a.id != null) this._byId[a.id] = a
                }
                if (E) E.push(u || a)
            }
            if (x) {
                for (r = 0, s = this.length; r < s; ++r) {
                    if (!_[(a = this.models[r]).cid]) y.push(a)
                }
                if (y.length) this.remove(y, e)
            }
            if (m.length || E && E.length) {
                if (p) l = true;
                this.length += m.length;
                if (c != null) {
                    for (r = 0, s = m.length; r < s; r++) {
                        this.models.splice(c + r, 0, m[r])
                    }
                } else {
                    if (E) this.models.length = 0;
                    var T = E || m;
                    for (r = 0, s = T.length; r < s; r++) {
                        this.models.push(T[r])
                    }
                }
            }
            if (l) this.sort({silent: true});
            if (!e.silent) {
                for (r = 0, s = m.length; r < s; r++) {
                    (a = m[r]).trigger("add", a, this, e)
                }
                if (l || E && E.length) this.trigger("sort", this, e)
            }
            return i ? t[0] : t
        }, reset: function (t, e) {
            e || (e = {});
            for (var i = 0, r = this.models.length; i < r; i++) {
                this._removeReference(this.models[i])
            }
            e.previousModels = this.models;
            this._reset();
            t = this.add(t, h.extend({silent: true}, e));
            if (!e.silent) this.trigger("reset", this, e);
            return t
        }, push: function (t, e) {
            return this.add(t, h.extend({at: this.length}, e))
        }, pop: function (t) {
            var e = this.at(this.length - 1);
            this.remove(e, t);
            return e
        }, unshift: function (t, e) {
            return this.add(t, h.extend({at: 0}, e))
        }, shift: function (t) {
            var e = this.at(0);
            this.remove(e, t);
            return e
        }, slice: function () {
            return s.apply(this.models, arguments)
        }, get: function (t) {
            if (t == null) return void 0;
            return this._byId[t.id] || this._byId[t.cid] || this._byId[t]
        }, at: function (t) {
            return this.models[t]
        }, where: function (t, e) {
            if (h.isEmpty(t)) return e ? void 0 : [];
            return this[e ? "find" : "filter"](function (e) {
                for (var i in t) {
                    if (t[i] !== e.get(i)) return false
                }
                return true
            })
        }, findWhere: function (t) {
            return this.where(t, true)
        }, sort: function (t) {
            if (!this.comparator) throw new Error("Cannot sort a set without a comparator");
            t || (t = {});
            if (h.isString(this.comparator) || this.comparator.length === 1) {
                this.models = this.sortBy(this.comparator, this)
            } else {
                this.models.sort(h.bind(this.comparator, this))
            }
            if (!t.silent) this.trigger("sort", this, t);
            return this
        }, pluck: function (t) {
            return h.invoke(this.models, "get", t)
        }, fetch: function (t) {
            t = t ? h.clone(t) : {};
            if (t.parse === void 0) t.parse = true;
            var e = t.success;
            var i = this;
            t.success = function (r) {
                var s = t.reset ? "reset" : "set";
                i[s](r, t);
                if (e) e(i, r, t);
                i.trigger("sync", i, r, t)
            };
            M(this, t);
            return this.sync("read", this, t)
        }, create: function (t, e) {
            e = e ? h.clone(e) : {};
            if (!(t = this._prepareModel(t, e))) return false;
            if (!e.wait) this.add(t, e);
            var i = this;
            var r = e.success;
            e.success = function (t, e, s) {
                if (s.wait) i.add(t, s);
                if (r) r(t, e, s)
            };
            t.save(null, e);
            return t
        }, parse: function (t, e) {
            return t
        }, clone: function () {
            return new this.constructor(this.models)
        }, _reset: function () {
            this.length = 0;
            this.models = [];
            this._byId = {}
        }, _prepareModel: function (t, e) {
            if (t instanceof d) {
                if (!t.collection) t.collection = this;
                return t
            }
            e = e ? h.clone(e) : {};
            e.collection = this;
            var i = new this.model(t, e);
            if (!i.validationError) return i;
            this.trigger("invalid", this, i.validationError, e);
            return false
        }, _removeReference: function (t) {
            if (this === t.collection) delete t.collection;
            t.off("all", this._onModelEvent, this)
        }, _onModelEvent: function (t, e, i, r) {
            if ((t === "add" || t === "remove") && i !== this) return;
            if (t === "destroy") this.remove(e, r);
            if (e && t === "change:" + e.idAttribute) {
                delete this._byId[e.previous(e.idAttribute)];
                if (e.id != null) this._byId[e.id] = e
            }
            this.trigger.apply(this, arguments)
        }
    });
    var y = ["forEach", "each", "map", "collect", "reduce", "foldl", "inject", "reduceRight", "foldr", "find", "detect", "filter", "select", "reject", "every", "all", "some", "any", "include", "contains", "invoke", "max", "min", "toArray", "size", "first", "head", "take", "initial", "rest", "tail", "drop", "last", "without", "difference", "indexOf", "shuffle", "lastIndexOf", "isEmpty", "chain"];
    h.each(y, function (t) {
        v.prototype[t] = function () {
            var e = s.call(arguments);
            e.unshift(this.models);
            return h[t].apply(h, e)
        }
    });
    var _ = ["groupBy", "countBy", "sortBy"];
    h.each(_, function (t) {
        v.prototype[t] = function (e, i) {
            var r = h.isFunction(e) ? e : function (t) {
                return t.get(e)
            };
            return h[t](this.models, r, i)
        }
    });
    var w = a.View = function (t) {
        this.cid = h.uniqueId("view");
        t || (t = {});
        h.extend(this, h.pick(t, x));
        this._ensureElement();
        this.initialize.apply(this, arguments);
        this.delegateEvents()
    };
    var b = /^(\S+)\s*(.*)$/;
    var x = ["model", "collection", "el", "id", "attributes", "className", "tagName", "events"];
    h.extend(w.prototype, o, {
        tagName: "div", $: function (t) {
            return this.$el.find(t)
        }, initialize: function () {
        }, render: function () {
            return this
        }, remove: function () {
            this.$el.remove();
            this.stopListening();
            return this
        }, setElement: function (t, e) {
            if (this.$el) this.undelegateEvents();
            this.$el = t instanceof a.$ ? t : a.$(t);
            this.el = this.$el[0];
            if (e !== false) this.delegateEvents();
            return this
        }, delegateEvents: function (t) {
            if (!(t || (t = h.result(this, "events")))) return this;
            this.undelegateEvents();
            for (var e in t) {
                var i = t[e];
                if (!h.isFunction(i)) i = this[t[e]];
                if (!i) continue;
                var r = e.match(b);
                var s = r[1], n = r[2];
                i = h.bind(i, this);
                s += ".delegateEvents" + this.cid;
                if (n === "") {
                    this.$el.on(s, i)
                } else {
                    this.$el.on(s, n, i)
                }
            }
            return this
        }, undelegateEvents: function () {
            this.$el.off(".delegateEvents" + this.cid);
            return this
        }, _ensureElement: function () {
            if (!this.el) {
                var t = h.extend({}, h.result(this, "attributes"));
                if (this.id) t.id = h.result(this, "id");
                if (this.className) t["class"] = h.result(this, "className");
                var e = a.$("<" + h.result(this, "tagName") + ">").attr(t);
                this.setElement(e, false)
            } else {
                this.setElement(h.result(this, "el"), false)
            }
        }
    });
    a.sync = function (t, e, i) {
        var r = T[t];
        h.defaults(i || (i = {}), {emulateHTTP: a.emulateHTTP, emulateJSON: a.emulateJSON});
        var s = {type: r, dataType: "json"};
        if (!i.url) {
            s.url = h.result(e, "url") || U()
        }
        if (i.data == null && e && (t === "create" || t === "update" || t === "patch")) {
            s.contentType = "application/json";
            s.data = JSON.stringify(i.attrs || e.toJSON(i))
        }
        if (i.emulateJSON) {
            s.contentType = "application/x-www-form-urlencoded";
            s.data = s.data ? {model: s.data} : {}
        }
        if (i.emulateHTTP && (r === "PUT" || r === "DELETE" || r === "PATCH")) {
            s.type = "POST";
            if (i.emulateJSON) s.data._method = r;
            var n = i.beforeSend;
            i.beforeSend = function (t) {
                t.setRequestHeader("X-HTTP-Method-Override", r);
                if (n) return n.apply(this, arguments)
            }
        }
        if (s.type !== "GET" && !i.emulateJSON) {
            s.processData = false
        }
        if (s.type === "PATCH" && E) {
            s.xhr = function () {
                return new ActiveXObject("Microsoft.XMLHTTP")
            }
        }
        var o = i.xhr = a.ajax(h.extend(s, i));
        e.trigger("request", e, o, i);
        return o
    };
    var E = typeof window !== "undefined" && !!window.ActiveXObject && !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent);
    var T = {create: "POST", update: "PUT", patch: "PATCH", "delete": "DELETE", read: "GET"};
    a.ajax = function () {
        return a.$.ajax.apply(a.$, arguments)
    };
    var k = a.Router = function (t) {
        t || (t = {});
        if (t.routes) this.routes = t.routes;
        this._bindRoutes();
        this.initialize.apply(this, arguments)
    };
    var S = /\((.*?)\)/g;
    var $ = /(\(\?)?:\w+/g;
    var H = /\*\w+/g;
    var A = /[\-{}\[\]+?.,\\\^$|#\s]/g;
    h.extend(k.prototype, o, {
        initialize: function () {
        }, route: function (t, e, i) {
            if (!h.isRegExp(t)) t = this._routeToRegExp(t);
            if (h.isFunction(e)) {
                i = e;
                e = ""
            }
            if (!i) i = this[e];
            var r = this;
            a.history.route(t, function (s) {
                var n = r._extractParameters(t, s);
                i && i.apply(r, n);
                r.trigger.apply(r, ["route:" + e].concat(n));
                r.trigger("route", e, n);
                a.history.trigger("route", r, e, n)
            });
            return this
        }, navigate: function (t, e) {
            a.history.navigate(t, e);
            return this
        }, _bindRoutes: function () {
            if (!this.routes) return;
            this.routes = h.result(this, "routes");
            var t, e = h.keys(this.routes);
            while ((t = e.pop()) != null) {
                this.route(t, this.routes[t])
            }
        }, _routeToRegExp: function (t) {
            t = t.replace(A, "\\$&").replace(S, "(?:$1)?").replace($, function (t, e) {
                return e ? t : "([^/]+)"
            }).replace(H, "(.*?)");
            return new RegExp("^" + t + "$")
        }, _extractParameters: function (t, e) {
            var i = t.exec(e).slice(1);
            return h.map(i, function (t) {
                return t ? decodeURIComponent(t) : null
            })
        }
    });
    var I = a.History = function () {
        this.handlers = [];
        h.bindAll(this, "checkUrl");
        if (typeof window !== "undefined") {
            this.location = window.location;
            this.history = window.history
        }
    };
    var N = /^[#\/]|\s+$/g;
    var O = /^\/+|\/+$/g;
    var P = /msie [\w.]+/;
    var C = /\/$/;
    var j = /[?#].*$/;
    I.started = false;
    h.extend(I.prototype, o, {
        interval: 50, getHash: function (t) {
            var e = (t || this).location.href.match(/#(.*)$/);
            return e ? e[1] : ""
        }, getFragment: function (t, e) {
            if (t == null) {
                if (this._hasPushState || !this._wantsHashChange || e) {
                    t = this.location.pathname;
                    var i = this.root.replace(C, "");
                    if (!t.indexOf(i)) t = t.slice(i.length)
                } else {
                    t = this.getHash()
                }
            }
            return t.replace(N, "")
        }, start: function (t) {
            if (I.started) throw new Error("Backbone.history has already been started");
            I.started = true;
            this.options = h.extend({root: "/"}, this.options, t);
            this.root = this.options.root;
            this._wantsHashChange = this.options.hashChange !== false;
            this._wantsPushState = !!this.options.pushState;
            this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState);
            var e = this.getFragment();
            var i = document.documentMode;
            var r = P.exec(navigator.userAgent.toLowerCase()) && (!i || i <= 7);
            this.root = ("/" + this.root + "/").replace(O, "/");
            if (r && this._wantsHashChange) {
                this.iframe = a.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow;
                this.navigate(e)
            }
            if (this._hasPushState) {
                a.$(window).on("popstate", this.checkUrl)
            } else if (this._wantsHashChange && "onhashchange" in window && !r) {
                a.$(window).on("hashchange", this.checkUrl)
            } else if (this._wantsHashChange) {
                this._checkUrlInterval = setInterval(this.checkUrl, this.interval)
            }
            this.fragment = e;
            var s = this.location;
            var n = s.pathname.replace(/[^\/]$/, "$&/") === this.root;
            if (this._wantsHashChange && this._wantsPushState) {
                if (!this._hasPushState && !n) {
                    this.fragment = this.getFragment(null, true);
                    this.location.replace(this.root + this.location.search + "#" + this.fragment);
                    return true
                } else if (this._hasPushState && n && s.hash) {
                    this.fragment = this.getHash().replace(N, "");
                    this.history.replaceState({}, document.title, this.root + this.fragment + s.search)
                }
            }
            if (!this.options.silent) return this.loadUrl()
        }, stop: function () {
            a.$(window).off("popstate", this.checkUrl).off("hashchange", this.checkUrl);
            clearInterval(this._checkUrlInterval);
            I.started = false
        }, route: function (t, e) {
            this.handlers.unshift({route: t, callback: e})
        }, checkUrl: function (t) {
            var e = this.getFragment();
            if (e === this.fragment && this.iframe) {
                e = this.getFragment(this.getHash(this.iframe))
            }
            if (e === this.fragment) return false;
            if (this.iframe) this.navigate(e);
            this.loadUrl()
        }, loadUrl: function (t) {
            t = this.fragment = this.getFragment(t);
            return h.any(this.handlers, function (e) {
                if (e.route.test(t)) {
                    e.callback(t);
                    return true
                }
            })
        }, navigate: function (t, e) {
            if (!I.started) return false;
            if (!e || e === true) e = {trigger: !!e};
            var i = this.root + (t = this.getFragment(t || ""));
            t = t.replace(j, "");
            if (this.fragment === t) return;
            this.fragment = t;
            if (t === "" && i !== "/") i = i.slice(0, -1);
            if (this._hasPushState) {
                this.history[e.replace ? "replaceState" : "pushState"]({}, document.title, i)
            } else if (this._wantsHashChange) {
                this._updateHash(this.location, t, e.replace);
                if (this.iframe && t !== this.getFragment(this.getHash(this.iframe))) {
                    if (!e.replace) this.iframe.document.open().close();
                    this._updateHash(this.iframe.location, t, e.replace)
                }
            } else {
                return this.location.assign(i)
            }
            if (e.trigger) return this.loadUrl(t)
        }, _updateHash: function (t, e, i) {
            if (i) {
                var r = t.href.replace(/(javascript:|#).*$/, "");
                t.replace(r + "#" + e)
            } else {
                t.hash = "#" + e
            }
        }
    });
    a.history = new I;
    var R = function (t, e) {
        var i = this;
        var r;
        if (t && h.has(t, "constructor")) {
            r = t.constructor
        } else {
            r = function () {
                return i.apply(this, arguments)
            }
        }
        h.extend(r, i, e);
        var s = function () {
            this.constructor = r
        };
        s.prototype = i.prototype;
        r.prototype = new s;
        if (t) h.extend(r.prototype, t);
        r.__super__ = i.prototype;
        return r
    };
    d.extend = v.extend = k.extend = w.extend = I.extend = R;
    var U = function () {
        throw new Error('A "url" property or function must be specified')
    };
    var M = function (t, e) {
        var i = e.error;
        e.error = function (r) {
            if (i) i(t, r, e);
            t.trigger("error", t, r, e)
        }
    }
}).call(this);
//# sourceMappingURL=backbone-min.map</script>
<script type="text/javascript">// Knockout JavaScript library v3.1.0
// (c) Steven Sanderson - http://knockoutjs.com/
// License: MIT (http://www.opensource.org/licenses/mit-license.php)

(function () {
    (function (p) {
        var A = this || (0, eval)("this"), w = A.document, K = A.navigator, t = A.jQuery, C = A.JSON;
        (function (p) {
            "function" === typeof require && "object" === typeof exports && "object" === typeof module ? p(module.exports || exports) : "function" === typeof define && define.amd ? define(["exports"], p) : p(A.ko = {})
        })(function (z) {
            function G(a, c) {
                return null === a || typeof a in M ? a === c : !1
            }

            function N(a, c) {
                var d;
                return function () {
                    d || (d = setTimeout(function () {
                        d = p;
                        a()
                    }, c))
                }
            }

            function O(a, c) {
                var d;
                return function () {
                    clearTimeout(d);
                    d = setTimeout(a,
                        c)
                }
            }

            function H(b, c, d, e) {
                a.d[b] = {
                    init: function (b, h, g, k, l) {
                        var n, r;
                        a.ba(function () {
                            var g = a.a.c(h()), k = !d !== !g, s = !r;
                            if (s || c || k !== n) s && a.ca.fa() && (r = a.a.lb(a.e.childNodes(b), !0)), k ? (s || a.e.U(b, a.a.lb(r)), a.gb(e ? e(l, g) : l, b)) : a.e.da(b), n = k
                        }, null, {G: b});
                        return {controlsDescendantBindings: !0}
                    }
                };
                a.g.aa[b] = !1;
                a.e.Q[b] = !0
            }

            var a = "undefined" !== typeof z ? z : {};
            a.b = function (b, c) {
                for (var d = b.split("."), e = a, f = 0; f < d.length - 1; f++) e = e[d[f]];
                e[d[d.length - 1]] = c
            };
            a.s = function (a, c, d) {
                a[c] = d
            };
            a.version = "3.1.0";
            a.b("version",
                a.version);
            a.a = function () {
                function b(a, b) {
                    for (var c in a) a.hasOwnProperty(c) && b(c, a[c])
                }

                function c(a, b) {
                    if (b) for (var c in b) b.hasOwnProperty(c) && (a[c] = b[c]);
                    return a
                }

                function d(a, b) {
                    a.__proto__ = b;
                    return a
                }

                var e = {__proto__: []} instanceof Array, f = {}, h = {};
                f[K && /Firefox\/2/i.test(K.userAgent) ? "KeyboardEvent" : "UIEvents"] = ["keyup", "keydown", "keypress"];
                f.MouseEvents = "click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave".split(" ");
                b(f, function (a, b) {
                    if (b.length) for (var c = 0,
                                           d = b.length; c < d; c++) h[b[c]] = a
                });
                var g = {propertychange: !0}, k = w && function () {
                    for (var a = 3, b = w.createElement("div"), c = b.getElementsByTagName("i"); b.innerHTML = "\x3c!--[if gt IE " + ++a + "]><i></i><![endif]--\x3e", c[0];) ;
                    return 4 < a ? a : p
                }();
                return {
                    mb: ["authenticity_token", /^__RequestVerificationToken(_.*)?$/], r: function (a, b) {
                        for (var c = 0, d = a.length; c < d; c++) b(a[c], c)
                    }, l: function (a, b) {
                        if ("function" == typeof Array.prototype.indexOf) return Array.prototype.indexOf.call(a, b);
                        for (var c = 0, d = a.length; c < d; c++) if (a[c] ===
                            b) return c;
                        return -1
                    }, hb: function (a, b, c) {
                        for (var d = 0, e = a.length; d < e; d++) if (b.call(c, a[d], d)) return a[d];
                        return null
                    }, ma: function (b, c) {
                        var d = a.a.l(b, c);
                        0 < d ? b.splice(d, 1) : 0 === d && b.shift()
                    }, ib: function (b) {
                        b = b || [];
                        for (var c = [], d = 0, e = b.length; d < e; d++) 0 > a.a.l(c, b[d]) && c.push(b[d]);
                        return c
                    }, ya: function (a, b) {
                        a = a || [];
                        for (var c = [], d = 0, e = a.length; d < e; d++) c.push(b(a[d], d));
                        return c
                    }, la: function (a, b) {
                        a = a || [];
                        for (var c = [], d = 0, e = a.length; d < e; d++) b(a[d], d) && c.push(a[d]);
                        return c
                    }, $: function (a, b) {
                        if (b instanceof Array) a.push.apply(a,
                            b); else for (var c = 0, d = b.length; c < d; c++) a.push(b[c]);
                        return a
                    }, Y: function (b, c, d) {
                        var e = a.a.l(a.a.Sa(b), c);
                        0 > e ? d && b.push(c) : d || b.splice(e, 1)
                    }, na: e, extend: c, ra: d, sa: e ? d : c, A: b, Oa: function (a, b) {
                        if (!a) return a;
                        var c = {}, d;
                        for (d in a) a.hasOwnProperty(d) && (c[d] = b(a[d], d, a));
                        return c
                    }, Fa: function (b) {
                        for (; b.firstChild;) a.removeNode(b.firstChild)
                    }, ec: function (b) {
                        b = a.a.R(b);
                        for (var c = w.createElement("div"), d = 0, e = b.length; d < e; d++) c.appendChild(a.M(b[d]));
                        return c
                    }, lb: function (b, c) {
                        for (var d = 0, e = b.length, g = []; d <
                        e; d++) {
                            var k = b[d].cloneNode(!0);
                            g.push(c ? a.M(k) : k)
                        }
                        return g
                    }, U: function (b, c) {
                        a.a.Fa(b);
                        if (c) for (var d = 0, e = c.length; d < e; d++) b.appendChild(c[d])
                    }, Bb: function (b, c) {
                        var d = b.nodeType ? [b] : b;
                        if (0 < d.length) {
                            for (var e = d[0], g = e.parentNode, k = 0, h = c.length; k < h; k++) g.insertBefore(c[k], e);
                            k = 0;
                            for (h = d.length; k < h; k++) a.removeNode(d[k])
                        }
                    }, ea: function (a, b) {
                        if (a.length) {
                            for (b = 8 === b.nodeType && b.parentNode || b; a.length && a[0].parentNode !== b;) a.shift();
                            if (1 < a.length) {
                                var c = a[0], d = a[a.length - 1];
                                for (a.length = 0; c !== d;) if (a.push(c),
                                        c = c.nextSibling, !c) return;
                                a.push(d)
                            }
                        }
                        return a
                    }, Db: function (a, b) {
                        7 > k ? a.setAttribute("selected", b) : a.selected = b
                    }, ta: function (a) {
                        return null === a || a === p ? "" : a.trim ? a.trim() : a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g, "")
                    }, oc: function (b, c) {
                        for (var d = [], e = (b || "").split(c), g = 0, k = e.length; g < k; g++) {
                            var h = a.a.ta(e[g]);
                            "" !== h && d.push(h)
                        }
                        return d
                    }, kc: function (a, b) {
                        a = a || "";
                        return b.length > a.length ? !1 : a.substring(0, b.length) === b
                    }, Sb: function (a, b) {
                        if (a === b) return !0;
                        if (11 === a.nodeType) return !1;
                        if (b.contains) return b.contains(3 ===
                        a.nodeType ? a.parentNode : a);
                        if (b.compareDocumentPosition) return 16 == (b.compareDocumentPosition(a) & 16);
                        for (; a && a != b;) a = a.parentNode;
                        return !!a
                    }, Ea: function (b) {
                        return a.a.Sb(b, b.ownerDocument.documentElement)
                    }, eb: function (b) {
                        return !!a.a.hb(b, a.a.Ea)
                    }, B: function (a) {
                        return a && a.tagName && a.tagName.toLowerCase()
                    }, q: function (b, c, d) {
                        var e = k && g[c];
                        if (!e && t) t(b).bind(c, d); else if (e || "function" != typeof b.addEventListener) if ("undefined" != typeof b.attachEvent) {
                            var h = function (a) {
                                d.call(b, a)
                            }, f = "on" + c;
                            b.attachEvent(f,
                                h);
                            a.a.u.ja(b, function () {
                                b.detachEvent(f, h)
                            })
                        } else throw Error("Browser doesn't support addEventListener or attachEvent"); else b.addEventListener(c, d, !1)
                    }, ha: function (b, c) {
                        if (!b || !b.nodeType) throw Error("element must be a DOM node when calling triggerEvent");
                        var d;
                        "input" === a.a.B(b) && b.type && "click" == c.toLowerCase() ? (d = b.type, d = "checkbox" == d || "radio" == d) : d = !1;
                        if (t && !d) t(b).trigger(c); else if ("function" == typeof w.createEvent) if ("function" == typeof b.dispatchEvent) d = w.createEvent(h[c] || "HTMLEvents"),
                            d.initEvent(c, !0, !0, A, 0, 0, 0, 0, 0, !1, !1, !1, !1, 0, b), b.dispatchEvent(d); else throw Error("The supplied element doesn't support dispatchEvent"); else if (d && b.click) b.click(); else if ("undefined" != typeof b.fireEvent) b.fireEvent("on" + c); else throw Error("Browser doesn't support triggering events");
                    }, c: function (b) {
                        return a.v(b) ? b() : b
                    }, Sa: function (b) {
                        return a.v(b) ? b.o() : b
                    }, ua: function (b, c, d) {
                        if (c) {
                            var e = /\S+/g, g = b.className.match(e) || [];
                            a.a.r(c.match(e), function (b) {
                                a.a.Y(g, b, d)
                            });
                            b.className = g.join(" ")
                        }
                    }, Xa: function (b,
                                     c) {
                        var d = a.a.c(c);
                        if (null === d || d === p) d = "";
                        var e = a.e.firstChild(b);
                        !e || 3 != e.nodeType || a.e.nextSibling(e) ? a.e.U(b, [b.ownerDocument.createTextNode(d)]) : e.data = d;
                        a.a.Vb(b)
                    }, Cb: function (a, b) {
                        a.name = b;
                        if (7 >= k) try {
                            a.mergeAttributes(w.createElement("<input name='" + a.name + "'/>"), !1)
                        } catch (c) {
                        }
                    }, Vb: function (a) {
                        9 <= k && (a = 1 == a.nodeType ? a : a.parentNode, a.style && (a.style.zoom = a.style.zoom))
                    }, Tb: function (a) {
                        if (k) {
                            var b = a.style.width;
                            a.style.width = 0;
                            a.style.width = b
                        }
                    }, ic: function (b, c) {
                        b = a.a.c(b);
                        c = a.a.c(c);
                        for (var d =
                            [], e = b; e <= c; e++) d.push(e);
                        return d
                    }, R: function (a) {
                        for (var b = [], c = 0, d = a.length; c < d; c++) b.push(a[c]);
                        return b
                    }, mc: 6 === k, nc: 7 === k, oa: k, ob: function (b, c) {
                        for (var d = a.a.R(b.getElementsByTagName("input")).concat(a.a.R(b.getElementsByTagName("textarea"))), e = "string" == typeof c ? function (a) {
                            return a.name === c
                        } : function (a) {
                            return c.test(a.name)
                        }, g = [], k = d.length - 1; 0 <= k; k--) e(d[k]) && g.push(d[k]);
                        return g
                    }, fc: function (b) {
                        return "string" == typeof b && (b = a.a.ta(b)) ? C && C.parse ? C.parse(b) : (new Function("return " + b))() :
                            null
                    }, Ya: function (b, c, d) {
                        if (!C || !C.stringify) throw Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
                        return C.stringify(a.a.c(b), c, d)
                    }, gc: function (c, d, e) {
                        e = e || {};
                        var g = e.params || {}, k = e.includeFields || this.mb, h = c;
                        if ("object" == typeof c && "form" === a.a.B(c)) for (var h = c.action, f = k.length - 1; 0 <= f; f--) for (var u = a.a.ob(c, k[f]), D = u.length - 1; 0 <= D; D--) g[u[D].name] =
                            u[D].value;
                        d = a.a.c(d);
                        var y = w.createElement("form");
                        y.style.display = "none";
                        y.action = h;
                        y.method = "post";
                        for (var p in d) c = w.createElement("input"), c.name = p, c.value = a.a.Ya(a.a.c(d[p])), y.appendChild(c);
                        b(g, function (a, b) {
                            var c = w.createElement("input");
                            c.name = a;
                            c.value = b;
                            y.appendChild(c)
                        });
                        w.body.appendChild(y);
                        e.submitter ? e.submitter(y) : y.submit();
                        setTimeout(function () {
                            y.parentNode.removeChild(y)
                        }, 0)
                    }
                }
            }();
            a.b("utils", a.a);
            a.b("utils.arrayForEach", a.a.r);
            a.b("utils.arrayFirst", a.a.hb);
            a.b("utils.arrayFilter",
                a.a.la);
            a.b("utils.arrayGetDistinctValues", a.a.ib);
            a.b("utils.arrayIndexOf", a.a.l);
            a.b("utils.arrayMap", a.a.ya);
            a.b("utils.arrayPushAll", a.a.$);
            a.b("utils.arrayRemoveItem", a.a.ma);
            a.b("utils.extend", a.a.extend);
            a.b("utils.fieldsIncludedWithJsonPost", a.a.mb);
            a.b("utils.getFormFields", a.a.ob);
            a.b("utils.peekObservable", a.a.Sa);
            a.b("utils.postJson", a.a.gc);
            a.b("utils.parseJson", a.a.fc);
            a.b("utils.registerEventHandler", a.a.q);
            a.b("utils.stringifyJson", a.a.Ya);
            a.b("utils.range", a.a.ic);
            a.b("utils.toggleDomNodeCssClass",
                a.a.ua);
            a.b("utils.triggerEvent", a.a.ha);
            a.b("utils.unwrapObservable", a.a.c);
            a.b("utils.objectForEach", a.a.A);
            a.b("utils.addOrRemoveItem", a.a.Y);
            a.b("unwrap", a.a.c);
            Function.prototype.bind || (Function.prototype.bind = function (a) {
                var c = this, d = Array.prototype.slice.call(arguments);
                a = d.shift();
                return function () {
                    return c.apply(a, d.concat(Array.prototype.slice.call(arguments)))
                }
            });
            a.a.f = new function () {
                function a(b, h) {
                    var g = b[d];
                    if (!g || "null" === g || !e[g]) {
                        if (!h) return p;
                        g = b[d] = "ko" + c++;
                        e[g] = {}
                    }
                    return e[g]
                }

                var c = 0, d = "__ko__" + (new Date).getTime(), e = {};
                return {
                    get: function (c, d) {
                        var e = a(c, !1);
                        return e === p ? p : e[d]
                    }, set: function (c, d, e) {
                        if (e !== p || a(c, !1) !== p) a(c, !0)[d] = e
                    }, clear: function (a) {
                        var b = a[d];
                        return b ? (delete e[b], a[d] = null, !0) : !1
                    }, L: function () {
                        return c++ + d
                    }
                }
            };
            a.b("utils.domData", a.a.f);
            a.b("utils.domData.clear", a.a.f.clear);
            a.a.u = new function () {
                function b(b, c) {
                    var e = a.a.f.get(b, d);
                    e === p && c && (e = [], a.a.f.set(b, d, e));
                    return e
                }

                function c(d) {
                    var e = b(d, !1);
                    if (e) for (var e = e.slice(0), k = 0; k < e.length; k++) e[k](d);
                    a.a.f.clear(d);
                    a.a.u.cleanExternalData(d);
                    if (f[d.nodeType]) for (e = d.firstChild; d = e;) e = d.nextSibling, 8 === d.nodeType && c(d)
                }

                var d = a.a.f.L(), e = {1: !0, 8: !0, 9: !0}, f = {1: !0, 9: !0};
                return {
                    ja: function (a, c) {
                        if ("function" != typeof c) throw Error("Callback must be a function");
                        b(a, !0).push(c)
                    }, Ab: function (c, e) {
                        var k = b(c, !1);
                        k && (a.a.ma(k, e), 0 == k.length && a.a.f.set(c, d, p))
                    }, M: function (b) {
                        if (e[b.nodeType] && (c(b), f[b.nodeType])) {
                            var d = [];
                            a.a.$(d, b.getElementsByTagName("*"));
                            for (var k = 0, l = d.length; k < l; k++) c(d[k])
                        }
                        return b
                    },
                    removeNode: function (b) {
                        a.M(b);
                        b.parentNode && b.parentNode.removeChild(b)
                    }, cleanExternalData: function (a) {
                        t && "function" == typeof t.cleanData && t.cleanData([a])
                    }
                }
            };
            a.M = a.a.u.M;
            a.removeNode = a.a.u.removeNode;
            a.b("cleanNode", a.M);
            a.b("removeNode", a.removeNode);
            a.b("utils.domNodeDisposal", a.a.u);
            a.b("utils.domNodeDisposal.addDisposeCallback", a.a.u.ja);
            a.b("utils.domNodeDisposal.removeDisposeCallback", a.a.u.Ab);
            (function () {
                a.a.Qa = function (b) {
                    var c;
                    if (t) if (t.parseHTML) c = t.parseHTML(b) || []; else {
                        if ((c = t.clean([b])) &&
                            c[0]) {
                            for (b = c[0]; b.parentNode && 11 !== b.parentNode.nodeType;) b = b.parentNode;
                            b.parentNode && b.parentNode.removeChild(b)
                        }
                    } else {
                        var d = a.a.ta(b).toLowerCase();
                        c = w.createElement("div");
                        d = d.match(/^<(thead|tbody|tfoot)/) && [1, "<table>", "</table>"] || !d.indexOf("<tr") && [2, "<table><tbody>", "</tbody></table>"] || (!d.indexOf("<td") || !d.indexOf("<th")) && [3, "<table><tbody><tr>", "</tr></tbody></table>"] || [0, "", ""];
                        b = "ignored<div>" + d[1] + b + d[2] + "</div>";
                        for ("function" == typeof A.innerShiv ? c.appendChild(A.innerShiv(b)) :
                                 c.innerHTML = b; d[0]--;) c = c.lastChild;
                        c = a.a.R(c.lastChild.childNodes)
                    }
                    return c
                };
                a.a.Va = function (b, c) {
                    a.a.Fa(b);
                    c = a.a.c(c);
                    if (null !== c && c !== p) if ("string" != typeof c && (c = c.toString()), t) t(b).html(c); else for (var d = a.a.Qa(c), e = 0; e < d.length; e++) b.appendChild(d[e])
                }
            })();
            a.b("utils.parseHtmlFragment", a.a.Qa);
            a.b("utils.setHtml", a.a.Va);
            a.w = function () {
                function b(c, e) {
                    if (c) if (8 == c.nodeType) {
                        var f = a.w.xb(c.nodeValue);
                        null != f && e.push({Rb: c, cc: f})
                    } else if (1 == c.nodeType) for (var f = 0, h = c.childNodes, g = h.length; f < g; f++) b(h[f],
                        e)
                }

                var c = {};
                return {
                    Na: function (a) {
                        if ("function" != typeof a) throw Error("You can only pass a function to ko.memoization.memoize()");
                        var b = (4294967296 * (1 + Math.random()) | 0).toString(16).substring(1) + (4294967296 * (1 + Math.random()) | 0).toString(16).substring(1);
                        c[b] = a;
                        return "\x3c!--[ko_memo:" + b + "]--\x3e"
                    }, Hb: function (a, b) {
                        var f = c[a];
                        if (f === p) throw Error("Couldn't find any memo with ID " + a + ". Perhaps it's already been unmemoized.");
                        try {
                            return f.apply(null, b || []), !0
                        } finally {
                            delete c[a]
                        }
                    }, Ib: function (c, e) {
                        var f =
                            [];
                        b(c, f);
                        for (var h = 0, g = f.length; h < g; h++) {
                            var k = f[h].Rb, l = [k];
                            e && a.a.$(l, e);
                            a.w.Hb(f[h].cc, l);
                            k.nodeValue = "";
                            k.parentNode && k.parentNode.removeChild(k)
                        }
                    }, xb: function (a) {
                        return (a = a.match(/^\[ko_memo\:(.*?)\]$/)) ? a[1] : null
                    }
                }
            }();
            a.b("memoization", a.w);
            a.b("memoization.memoize", a.w.Na);
            a.b("memoization.unmemoize", a.w.Hb);
            a.b("memoization.parseMemoText", a.w.xb);
            a.b("memoization.unmemoizeDomNodeAndDescendants", a.w.Ib);
            a.Ga = {
                throttle: function (b, c) {
                    b.throttleEvaluation = c;
                    var d = null;
                    return a.h({
                        read: b, write: function (a) {
                            clearTimeout(d);
                            d = setTimeout(function () {
                                b(a)
                            }, c)
                        }
                    })
                }, rateLimit: function (a, c) {
                    var d, e, f;
                    "number" == typeof c ? d = c : (d = c.timeout, e = c.method);
                    f = "notifyWhenChangesStop" == e ? O : N;
                    a.Ma(function (a) {
                        return f(a, d)
                    })
                }, notify: function (a, c) {
                    a.equalityComparer = "always" == c ? null : G
                }
            };
            var M = {undefined: 1, "boolean": 1, number: 1, string: 1};
            a.b("extenders", a.Ga);
            a.Fb = function (b, c, d) {
                this.target = b;
                this.za = c;
                this.Qb = d;
                this.sb = !1;
                a.s(this, "dispose", this.F)
            };
            a.Fb.prototype.F = function () {
                this.sb = !0;
                this.Qb()
            };
            a.N = function () {
                a.a.sa(this, a.N.fn);
                this.H =
                    {}
            };
            var F = "change";
            z = {
                V: function (b, c, d) {
                    var e = this;
                    d = d || F;
                    var f = new a.Fb(e, c ? b.bind(c) : b, function () {
                        a.a.ma(e.H[d], f)
                    });
                    e.o && e.o();
                    e.H[d] || (e.H[d] = []);
                    e.H[d].push(f);
                    return f
                }, notifySubscribers: function (b, c) {
                    c = c || F;
                    if (this.qb(c)) try {
                        a.k.jb();
                        for (var d = this.H[c].slice(0), e = 0, f; f = d[e]; ++e) f.sb || f.za(b)
                    } finally {
                        a.k.end()
                    }
                }, Ma: function (b) {
                    var c = this, d = a.v(c), e, f, h;
                    c.ia || (c.ia = c.notifySubscribers, c.notifySubscribers = function (a, b) {
                        b && b !== F ? "beforeChange" === b ? c.bb(a) : c.ia(a, b) : c.cb(a)
                    });
                    var g = b(function () {
                        d &&
                        h === c && (h = c());
                        e = !1;
                        c.Ka(f, h) && c.ia(f = h)
                    });
                    c.cb = function (a) {
                        e = !0;
                        h = a;
                        g()
                    };
                    c.bb = function (a) {
                        e || (f = a, c.ia(a, "beforeChange"))
                    }
                }, qb: function (a) {
                    return this.H[a] && this.H[a].length
                }, Wb: function () {
                    var b = 0;
                    a.a.A(this.H, function (a, d) {
                        b += d.length
                    });
                    return b
                }, Ka: function (a, c) {
                    return !this.equalityComparer || !this.equalityComparer(a, c)
                }, extend: function (b) {
                    var c = this;
                    b && a.a.A(b, function (b, e) {
                        var f = a.Ga[b];
                        "function" == typeof f && (c = f(c, e) || c)
                    });
                    return c
                }
            };
            a.s(z, "subscribe", z.V);
            a.s(z, "extend", z.extend);
            a.s(z, "getSubscriptionsCount",
                z.Wb);
            a.a.na && a.a.ra(z, Function.prototype);
            a.N.fn = z;
            a.tb = function (a) {
                return null != a && "function" == typeof a.V && "function" == typeof a.notifySubscribers
            };
            a.b("subscribable", a.N);
            a.b("isSubscribable", a.tb);
            a.ca = a.k = function () {
                function b(a) {
                    d.push(e);
                    e = a
                }

                function c() {
                    e = d.pop()
                }

                var d = [], e, f = 0;
                return {
                    jb: b, end: c, zb: function (b) {
                        if (e) {
                            if (!a.tb(b)) throw Error("Only subscribable things can act as dependencies");
                            e.za(b, b.Kb || (b.Kb = ++f))
                        }
                    }, t: function (a, d, e) {
                        try {
                            return b(), a.apply(d, e || [])
                        } finally {
                            c()
                        }
                    }, fa: function () {
                        if (e) return e.ba.fa()
                    },
                    pa: function () {
                        if (e) return e.pa
                    }
                }
            }();
            a.b("computedContext", a.ca);
            a.b("computedContext.getDependenciesCount", a.ca.fa);
            a.b("computedContext.isInitial", a.ca.pa);
            a.m = function (b) {
                function c() {
                    if (0 < arguments.length) return c.Ka(d, arguments[0]) && (c.P(), d = arguments[0], c.O()), this;
                    a.k.zb(c);
                    return d
                }

                var d = b;
                a.N.call(c);
                a.a.sa(c, a.m.fn);
                c.o = function () {
                    return d
                };
                c.O = function () {
                    c.notifySubscribers(d)
                };
                c.P = function () {
                    c.notifySubscribers(d, "beforeChange")
                };
                a.s(c, "peek", c.o);
                a.s(c, "valueHasMutated", c.O);
                a.s(c, "valueWillMutate",
                    c.P);
                return c
            };
            a.m.fn = {equalityComparer: G};
            var E = a.m.hc = "__ko_proto__";
            a.m.fn[E] = a.m;
            a.a.na && a.a.ra(a.m.fn, a.N.fn);
            a.Ha = function (b, c) {
                return null === b || b === p || b[E] === p ? !1 : b[E] === c ? !0 : a.Ha(b[E], c)
            };
            a.v = function (b) {
                return a.Ha(b, a.m)
            };
            a.ub = function (b) {
                return "function" == typeof b && b[E] === a.m || "function" == typeof b && b[E] === a.h && b.Yb ? !0 : !1
            };
            a.b("observable", a.m);
            a.b("isObservable", a.v);
            a.b("isWriteableObservable", a.ub);
            a.T = function (b) {
                b = b || [];
                if ("object" != typeof b || !("length" in b)) throw Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");
                b = a.m(b);
                a.a.sa(b, a.T.fn);
                return b.extend({trackArrayChanges: !0})
            };
            a.T.fn = {
                remove: function (b) {
                    for (var c = this.o(), d = [], e = "function" != typeof b || a.v(b) ? function (a) {
                        return a === b
                    } : b, f = 0; f < c.length; f++) {
                        var h = c[f];
                        e(h) && (0 === d.length && this.P(), d.push(h), c.splice(f, 1), f--)
                    }
                    d.length && this.O();
                    return d
                }, removeAll: function (b) {
                    if (b === p) {
                        var c = this.o(), d = c.slice(0);
                        this.P();
                        c.splice(0, c.length);
                        this.O();
                        return d
                    }
                    return b ? this.remove(function (c) {
                        return 0 <= a.a.l(b, c)
                    }) : []
                }, destroy: function (b) {
                    var c = this.o(), d =
                        "function" != typeof b || a.v(b) ? function (a) {
                            return a === b
                        } : b;
                    this.P();
                    for (var e = c.length - 1; 0 <= e; e--) d(c[e]) && (c[e]._destroy = !0);
                    this.O()
                }, destroyAll: function (b) {
                    return b === p ? this.destroy(function () {
                        return !0
                    }) : b ? this.destroy(function (c) {
                        return 0 <= a.a.l(b, c)
                    }) : []
                }, indexOf: function (b) {
                    var c = this();
                    return a.a.l(c, b)
                }, replace: function (a, c) {
                    var d = this.indexOf(a);
                    0 <= d && (this.P(), this.o()[d] = c, this.O())
                }
            };
            a.a.r("pop push reverse shift sort splice unshift".split(" "), function (b) {
                a.T.fn[b] = function () {
                    var a = this.o();
                    this.P();
                    this.kb(a, b, arguments);
                    a = a[b].apply(a, arguments);
                    this.O();
                    return a
                }
            });
            a.a.r(["slice"], function (b) {
                a.T.fn[b] = function () {
                    var a = this();
                    return a[b].apply(a, arguments)
                }
            });
            a.a.na && a.a.ra(a.T.fn, a.m.fn);
            a.b("observableArray", a.T);
            var I = "arrayChange";
            a.Ga.trackArrayChanges = function (b) {
                function c() {
                    if (!d) {
                        d = !0;
                        var c = b.notifySubscribers;
                        b.notifySubscribers = function (a, b) {
                            b && b !== F || ++f;
                            return c.apply(this, arguments)
                        };
                        var k = [].concat(b.o() || []);
                        e = null;
                        b.V(function (c) {
                            c = [].concat(c || []);
                            if (b.qb(I)) {
                                var d;
                                if (!e || 1 < f) e = a.a.Aa(k, c, {sparse: !0});
                                d = e;
                                d.length && b.notifySubscribers(d, I)
                            }
                            k = c;
                            e = null;
                            f = 0
                        })
                    }
                }

                if (!b.kb) {
                    var d = !1, e = null, f = 0, h = b.V;
                    b.V = b.subscribe = function (a, b, d) {
                        d === I && c();
                        return h.apply(this, arguments)
                    };
                    b.kb = function (b, c, l) {
                        function h(a, b, c) {
                            return r[r.length] = {status: a, value: b, index: c}
                        }

                        if (d && !f) {
                            var r = [], m = b.length, q = l.length, s = 0;
                            switch (c) {
                                case "push":
                                    s = m;
                                case "unshift":
                                    for (c = 0; c < q; c++) h("added", l[c], s + c);
                                    break;
                                case "pop":
                                    s = m - 1;
                                case "shift":
                                    m && h("deleted", b[s], s);
                                    break;
                                case "splice":
                                    c = Math.min(Math.max(0,
                                        0 > l[0] ? m + l[0] : l[0]), m);
                                    for (var m = 1 === q ? m : Math.min(c + (l[1] || 0), m), q = c + q - 2, s = Math.max(m, q), B = [], u = [], D = 2; c < s; ++c, ++D) c < m && u.push(h("deleted", b[c], c)), c < q && B.push(h("added", l[D], c));
                                    a.a.nb(u, B);
                                    break;
                                default:
                                    return
                            }
                            e = r
                        }
                    }
                }
            };
            a.ba = a.h = function (b, c, d) {
                function e() {
                    q = !0;
                    a.a.A(v, function (a, b) {
                        b.F()
                    });
                    v = {};
                    x = 0;
                    n = !1
                }

                function f() {
                    var a = g.throttleEvaluation;
                    a && 0 <= a ? (clearTimeout(t), t = setTimeout(h, a)) : g.wa ? g.wa() : h()
                }

                function h() {
                    if (!r && !q) {
                        if (y && y()) {
                            if (!m) {
                                p();
                                return
                            }
                        } else m = !1;
                        r = !0;
                        try {
                            var b = v, d = x;
                            a.k.jb({
                                za: function (a,
                                              c) {
                                    q || (d && b[c] ? (v[c] = b[c], ++x, delete b[c], --d) : v[c] || (v[c] = a.V(f), ++x))
                                }, ba: g, pa: !x
                            });
                            v = {};
                            x = 0;
                            try {
                                var e = c ? s.call(c) : s()
                            } finally {
                                a.k.end(), d && a.a.A(b, function (a, b) {
                                    b.F()
                                }), n = !1
                            }
                            g.Ka(l, e) && (g.notifySubscribers(l, "beforeChange"), l = e, g.wa && !g.throttleEvaluation || g.notifySubscribers(l))
                        } finally {
                            r = !1
                        }
                        x || p()
                    }
                }

                function g() {
                    if (0 < arguments.length) {
                        if ("function" === typeof B) B.apply(c, arguments); else throw Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
                        return this
                    }
                    n && h();
                    a.k.zb(g);
                    return l
                }

                function k() {
                    return n || 0 < x
                }

                var l, n = !0, r = !1, m = !1, q = !1, s = b;
                s && "object" == typeof s ? (d = s, s = d.read) : (d = d || {}, s || (s = d.read));
                if ("function" != typeof s) throw Error("Pass a function that returns the value of the ko.computed");
                var B = d.write, u = d.disposeWhenNodeIsRemoved || d.G || null, D = d.disposeWhen || d.Da, y = D, p = e,
                    v = {}, x = 0, t = null;
                c || (c = d.owner);
                a.N.call(g);
                a.a.sa(g, a.h.fn);
                g.o = function () {
                    n && !x && h();
                    return l
                };
                g.fa = function () {
                    return x
                };
                g.Yb = "function" === typeof d.write;
                g.F = function () {
                    p()
                };
                g.ga = k;
                var w = g.Ma;
                g.Ma = function (a) {
                    w.call(g, a);
                    g.wa = function () {
                        g.bb(l);
                        n = !0;
                        g.cb(g)
                    }
                };
                a.s(g, "peek", g.o);
                a.s(g, "dispose", g.F);
                a.s(g, "isActive", g.ga);
                a.s(g, "getDependenciesCount", g.fa);
                u && (m = !0, u.nodeType && (y = function () {
                    return !a.a.Ea(u) || D && D()
                }));
                !0 !== d.deferEvaluation && h();
                u && k() && u.nodeType && (p = function () {
                    a.a.u.Ab(u, p);
                    e()
                }, a.a.u.ja(u, p));
                return g
            };
            a.$b = function (b) {
                return a.Ha(b, a.h)
            };
            z = a.m.hc;
            a.h[z] = a.m;
            a.h.fn = {equalityComparer: G};
            a.h.fn[z] = a.h;
            a.a.na && a.a.ra(a.h.fn, a.N.fn);
            a.b("dependentObservable",
                a.h);
            a.b("computed", a.h);
            a.b("isComputed", a.$b);
            (function () {
                function b(a, f, h) {
                    h = h || new d;
                    a = f(a);
                    if ("object" != typeof a || null === a || a === p || a instanceof Date || a instanceof String || a instanceof Number || a instanceof Boolean) return a;
                    var g = a instanceof Array ? [] : {};
                    h.save(a, g);
                    c(a, function (c) {
                        var d = f(a[c]);
                        switch (typeof d) {
                            case "boolean":
                            case "number":
                            case "string":
                            case "function":
                                g[c] = d;
                                break;
                            case "object":
                            case "undefined":
                                var n = h.get(d);
                                g[c] = n !== p ? n : b(d, f, h)
                        }
                    });
                    return g
                }

                function c(a, b) {
                    if (a instanceof Array) {
                        for (var c =
                            0; c < a.length; c++) b(c);
                        "function" == typeof a.toJSON && b("toJSON")
                    } else for (c in a) b(c)
                }

                function d() {
                    this.keys = [];
                    this.ab = []
                }

                a.Gb = function (c) {
                    if (0 == arguments.length) throw Error("When calling ko.toJS, pass the object you want to convert.");
                    return b(c, function (b) {
                        for (var c = 0; a.v(b) && 10 > c; c++) b = b();
                        return b
                    })
                };
                a.toJSON = function (b, c, d) {
                    b = a.Gb(b);
                    return a.a.Ya(b, c, d)
                };
                d.prototype = {
                    save: function (b, c) {
                        var d = a.a.l(this.keys, b);
                        0 <= d ? this.ab[d] = c : (this.keys.push(b), this.ab.push(c))
                    }, get: function (b) {
                        b = a.a.l(this.keys,
                            b);
                        return 0 <= b ? this.ab[b] : p
                    }
                }
            })();
            a.b("toJS", a.Gb);
            a.b("toJSON", a.toJSON);
            (function () {
                a.i = {
                    p: function (b) {
                        switch (a.a.B(b)) {
                            case "option":
                                return !0 === b.__ko__hasDomDataOptionValue__ ? a.a.f.get(b, a.d.options.Pa) : 7 >= a.a.oa ? b.getAttributeNode("value") && b.getAttributeNode("value").specified ? b.value : b.text : b.value;
                            case "select":
                                return 0 <= b.selectedIndex ? a.i.p(b.options[b.selectedIndex]) : p;
                            default:
                                return b.value
                        }
                    }, X: function (b, c, d) {
                        switch (a.a.B(b)) {
                            case "option":
                                switch (typeof c) {
                                    case "string":
                                        a.a.f.set(b, a.d.options.Pa,
                                            p);
                                        "__ko__hasDomDataOptionValue__" in b && delete b.__ko__hasDomDataOptionValue__;
                                        b.value = c;
                                        break;
                                    default:
                                        a.a.f.set(b, a.d.options.Pa, c), b.__ko__hasDomDataOptionValue__ = !0, b.value = "number" === typeof c ? c : ""
                                }
                                break;
                            case "select":
                                if ("" === c || null === c) c = p;
                                for (var e = -1, f = 0, h = b.options.length, g; f < h; ++f) if (g = a.i.p(b.options[f]), g == c || "" == g && c === p) {
                                    e = f;
                                    break
                                }
                                if (d || 0 <= e || c === p && 1 < b.size) b.selectedIndex = e;
                                break;
                            default:
                                if (null === c || c === p) c = "";
                                b.value = c
                        }
                    }
                }
            })();
            a.b("selectExtensions", a.i);
            a.b("selectExtensions.readValue",
                a.i.p);
            a.b("selectExtensions.writeValue", a.i.X);
            a.g = function () {
                function b(b) {
                    b = a.a.ta(b);
                    123 === b.charCodeAt(0) && (b = b.slice(1, -1));
                    var c = [], d = b.match(e), g, m, q = 0;
                    if (d) {
                        d.push(",");
                        for (var s = 0, B; B = d[s]; ++s) {
                            var u = B.charCodeAt(0);
                            if (44 === u) {
                                if (0 >= q) {
                                    g && c.push(m ? {key: g, value: m.join("")} : {unknown: g});
                                    g = m = q = 0;
                                    continue
                                }
                            } else if (58 === u) {
                                if (!m) continue
                            } else if (47 === u && s && 1 < B.length) (u = d[s - 1].match(f)) && !h[u[0]] && (b = b.substr(b.indexOf(B) + 1), d = b.match(e), d.push(","), s = -1, B = "/"); else if (40 === u || 123 === u || 91 ===
                                u) ++q; else if (41 === u || 125 === u || 93 === u) --q; else if (!g && !m) {
                                g = 34 === u || 39 === u ? B.slice(1, -1) : B;
                                continue
                            }
                            m ? m.push(B) : m = [B]
                        }
                    }
                    return c
                }

                var c = ["true", "false", "null", "undefined"],
                    d = /^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i,
                    e = RegExp("\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'|/(?:[^/\\\\]|\\\\.)*/w*|[^\\s:,/][^,\"'{}()/:[\\]]*[^\\s,\"'{}()/:[\\]]|[^\\s]", "g"),
                    f = /[\])"'A-Za-z0-9_$]+$/, h = {"in": 1, "return": 1, "typeof": 1}, g = {};
                return {
                    aa: [], W: g, Ra: b, qa: function (e, l) {
                        function f(b, e) {
                            var l, k = a.getBindingHandler(b);
                            if (k && k.preprocess ? e = k.preprocess(e, b, f) : 1) {
                                if (k = g[b]) l = e, 0 <= a.a.l(c, l) ? l = !1 : (k = l.match(d), l = null === k ? !1 : k[1] ? "Object(" + k[1] + ")" + k[2] : l), k = l;
                                k && m.push("'" + b + "':function(_z){" + l + "=_z}");
                                q && (e = "function(){return " + e + " }");
                                h.push("'" + b + "':" + e)
                            }
                        }

                        l = l || {};
                        var h = [], m = [], q = l.valueAccessors, s = "string" === typeof e ? b(e) : e;
                        a.a.r(s, function (a) {
                            f(a.key || a.unknown, a.value)
                        });
                        m.length && f("_ko_property_writers", "{" + m.join(",") + " }");
                        return h.join(",")
                    }, bc: function (a, b) {
                        for (var c = 0; c < a.length; c++) if (a[c].key == b) return !0;
                        return !1
                    }, va: function (b, c, d, e, g) {
                        if (b && a.v(b)) !a.ub(b) || g && b.o() === e || b(e); else if ((b = c.get("_ko_property_writers")) && b[d]) b[d](e)
                    }
                }
            }();
            a.b("expressionRewriting", a.g);
            a.b("expressionRewriting.bindingRewriteValidators", a.g.aa);
            a.b("expressionRewriting.parseObjectLiteral", a.g.Ra);
            a.b("expressionRewriting.preProcessBindings", a.g.qa);
            a.b("expressionRewriting._twoWayBindings", a.g.W);
            a.b("jsonExpressionRewriting", a.g);
            a.b("jsonExpressionRewriting.insertPropertyAccessorsIntoJson", a.g.qa);
            (function () {
                function b(a) {
                    return 8 ==
                        a.nodeType && h.test(f ? a.text : a.nodeValue)
                }

                function c(a) {
                    return 8 == a.nodeType && g.test(f ? a.text : a.nodeValue)
                }

                function d(a, d) {
                    for (var e = a, g = 1, k = []; e = e.nextSibling;) {
                        if (c(e) && (g--, 0 === g)) return k;
                        k.push(e);
                        b(e) && g++
                    }
                    if (!d) throw Error("Cannot find closing comment tag to match: " + a.nodeValue);
                    return null
                }

                function e(a, b) {
                    var c = d(a, b);
                    return c ? 0 < c.length ? c[c.length - 1].nextSibling : a.nextSibling : null
                }

                var f = w && "\x3c!--test--\x3e" === w.createComment("test").text,
                    h = f ? /^\x3c!--\s*ko(?:\s+([\s\S]+))?\s*--\x3e$/ : /^\s*ko(?:\s+([\s\S]+))?\s*$/,
                    g = f ? /^\x3c!--\s*\/ko\s*--\x3e$/ : /^\s*\/ko\s*$/, k = {ul: !0, ol: !0};
                a.e = {
                    Q: {}, childNodes: function (a) {
                        return b(a) ? d(a) : a.childNodes
                    }, da: function (c) {
                        if (b(c)) {
                            c = a.e.childNodes(c);
                            for (var d = 0, e = c.length; d < e; d++) a.removeNode(c[d])
                        } else a.a.Fa(c)
                    }, U: function (c, d) {
                        if (b(c)) {
                            a.e.da(c);
                            for (var e = c.nextSibling, g = 0, k = d.length; g < k; g++) e.parentNode.insertBefore(d[g], e)
                        } else a.a.U(c, d)
                    }, yb: function (a, c) {
                        b(a) ? a.parentNode.insertBefore(c, a.nextSibling) : a.firstChild ? a.insertBefore(c, a.firstChild) : a.appendChild(c)
                    }, rb: function (c,
                                     d, e) {
                        e ? b(c) ? c.parentNode.insertBefore(d, e.nextSibling) : e.nextSibling ? c.insertBefore(d, e.nextSibling) : c.appendChild(d) : a.e.yb(c, d)
                    }, firstChild: function (a) {
                        return b(a) ? !a.nextSibling || c(a.nextSibling) ? null : a.nextSibling : a.firstChild
                    }, nextSibling: function (a) {
                        b(a) && (a = e(a));
                        return a.nextSibling && c(a.nextSibling) ? null : a.nextSibling
                    }, Xb: b, lc: function (a) {
                        return (a = (f ? a.text : a.nodeValue).match(h)) ? a[1] : null
                    }, wb: function (d) {
                        if (k[a.a.B(d)]) {
                            var g = d.firstChild;
                            if (g) {
                                do if (1 === g.nodeType) {
                                    var f;
                                    f = g.firstChild;
                                    var h = null;
                                    if (f) {
                                        do if (h) h.push(f); else if (b(f)) {
                                            var q = e(f, !0);
                                            q ? f = q : h = [f]
                                        } else c(f) && (h = [f]); while (f = f.nextSibling)
                                    }
                                    if (f = h) for (h = g.nextSibling, q = 0; q < f.length; q++) h ? d.insertBefore(f[q], h) : d.appendChild(f[q])
                                } while (g = g.nextSibling)
                            }
                        }
                    }
                }
            })();
            a.b("virtualElements", a.e);
            a.b("virtualElements.allowedBindings", a.e.Q);
            a.b("virtualElements.emptyNode", a.e.da);
            a.b("virtualElements.insertAfter", a.e.rb);
            a.b("virtualElements.prepend", a.e.yb);
            a.b("virtualElements.setDomNodeChildren", a.e.U);
            (function () {
                a.J = function () {
                    this.Nb =
                        {}
                };
                a.a.extend(a.J.prototype, {
                    nodeHasBindings: function (b) {
                        switch (b.nodeType) {
                            case 1:
                                return null != b.getAttribute("data-bind");
                            case 8:
                                return a.e.Xb(b);
                            default:
                                return !1
                        }
                    }, getBindings: function (a, c) {
                        var d = this.getBindingsString(a, c);
                        return d ? this.parseBindingsString(d, c, a) : null
                    }, getBindingAccessors: function (a, c) {
                        var d = this.getBindingsString(a, c);
                        return d ? this.parseBindingsString(d, c, a, {valueAccessors: !0}) : null
                    }, getBindingsString: function (b) {
                        switch (b.nodeType) {
                            case 1:
                                return b.getAttribute("data-bind");
                            case 8:
                                return a.e.lc(b);
                            default:
                                return null
                        }
                    }, parseBindingsString: function (b, c, d, e) {
                        try {
                            var f = this.Nb, h = b + (e && e.valueAccessors || ""), g;
                            if (!(g = f[h])) {
                                var k, l = "with($context){with($data||{}){return{" + a.g.qa(b, e) + "}}}";
                                k = new Function("$context", "$element", l);
                                g = f[h] = k
                            }
                            return g(c, d)
                        } catch (n) {
                            throw n.message = "Unable to parse bindings.\nBindings value: " + b + "\nMessage: " + n.message, n;
                        }
                    }
                });
                a.J.instance = new a.J
            })();
            a.b("bindingProvider", a.J);
            (function () {
                function b(a) {
                    return function () {
                        return a
                    }
                }

                function c(a) {
                    return a()
                }

                function d(b) {
                    return a.a.Oa(a.k.t(b), function (a, c) {
                        return function () {
                            return b()[c]
                        }
                    })
                }

                function e(a, b) {
                    return d(this.getBindings.bind(this, a, b))
                }

                function f(b, c, d) {
                    var e, g = a.e.firstChild(c), k = a.J.instance, f = k.preprocessNode;
                    if (f) {
                        for (; e = g;) g = a.e.nextSibling(e), f.call(k, e);
                        g = a.e.firstChild(c)
                    }
                    for (; e = g;) g = a.e.nextSibling(e), h(b, e, d)
                }

                function h(b, c, d) {
                    var e = !0, g = 1 === c.nodeType;
                    g && a.e.wb(c);
                    if (g && d || a.J.instance.nodeHasBindings(c)) e = k(c, null, b, d).shouldBindDescendants;
                    e && !n[a.a.B(c)] && f(b, c, !g)
                }

                function g(b) {
                    var c =
                        [], d = {}, e = [];
                    a.a.A(b, function y(g) {
                        if (!d[g]) {
                            var k = a.getBindingHandler(g);
                            k && (k.after && (e.push(g), a.a.r(k.after, function (c) {
                                if (b[c]) {
                                    if (-1 !== a.a.l(e, c)) throw Error("Cannot combine the following bindings, because they have a cyclic dependency: " + e.join(", "));
                                    y(c)
                                }
                            }), e.length--), c.push({key: g, pb: k}));
                            d[g] = !0
                        }
                    });
                    return c
                }

                function k(b, d, k, f) {
                    var h = a.a.f.get(b, r);
                    if (!d) {
                        if (h) throw Error("You cannot apply bindings multiple times to the same element.");
                        a.a.f.set(b, r, !0)
                    }
                    !h && f && a.Eb(b, k);
                    var l;
                    if (d && "function" !==
                        typeof d) l = d; else {
                        var n = a.J.instance, m = n.getBindingAccessors || e, x = a.h(function () {
                            (l = d ? d(k, b) : m.call(n, b, k)) && k.D && k.D();
                            return l
                        }, null, {G: b});
                        l && x.ga() || (x = null)
                    }
                    var t;
                    if (l) {
                        var w = x ? function (a) {
                            return function () {
                                return c(x()[a])
                            }
                        } : function (a) {
                            return l[a]
                        }, z = function () {
                            return a.a.Oa(x ? x() : l, c)
                        };
                        z.get = function (a) {
                            return l[a] && c(w(a))
                        };
                        z.has = function (a) {
                            return a in l
                        };
                        f = g(l);
                        a.a.r(f, function (c) {
                            var d = c.pb.init, e = c.pb.update, g = c.key;
                            if (8 === b.nodeType && !a.e.Q[g]) throw Error("The binding '" + g + "' cannot be used with virtual elements");
                            try {
                                "function" == typeof d && a.k.t(function () {
                                    var a = d(b, w(g), z, k.$data, k);
                                    if (a && a.controlsDescendantBindings) {
                                        if (t !== p) throw Error("Multiple bindings (" + t + " and " + g + ") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");
                                        t = g
                                    }
                                }), "function" == typeof e && a.h(function () {
                                    e(b, w(g), z, k.$data, k)
                                }, null, {G: b})
                            } catch (f) {
                                throw f.message = 'Unable to process binding "' + g + ": " + l[g] + '"\nMessage: ' + f.message, f;
                            }
                        })
                    }
                    return {shouldBindDescendants: t === p}
                }

                function l(b) {
                    return b && b instanceof a.I ? b : new a.I(b)
                }

                a.d = {};
                var n = {script: !0};
                a.getBindingHandler = function (b) {
                    return a.d[b]
                };
                a.I = function (b, c, d, e) {
                    var g = this, k = "function" == typeof b && !a.v(b), f, h = a.h(function () {
                        var f = k ? b() : b, l = a.a.c(f);
                        c ? (c.D && c.D(), a.a.extend(g, c), h && (g.D = h)) : (g.$parents = [], g.$root = l, g.ko = a);
                        g.$rawData = f;
                        g.$data = l;
                        d && (g[d] = l);
                        e && e(g, c, l);
                        return g.$data
                    }, null, {
                        Da: function () {
                            return f && !a.a.eb(f)
                        }, G: !0
                    });
                    h.ga() && (g.D = h, h.equalityComparer = null, f = [], h.Jb = function (b) {
                        f.push(b);
                        a.a.u.ja(b,
                            function (b) {
                                a.a.ma(f, b);
                                f.length || (h.F(), g.D = h = p)
                            })
                    })
                };
                a.I.prototype.createChildContext = function (b, c, d) {
                    return new a.I(b, this, c, function (a, b) {
                        a.$parentContext = b;
                        a.$parent = b.$data;
                        a.$parents = (b.$parents || []).slice(0);
                        a.$parents.unshift(a.$parent);
                        d && d(a)
                    })
                };
                a.I.prototype.extend = function (b) {
                    return new a.I(this.D || this.$data, this, null, function (c, d) {
                        c.$rawData = d.$rawData;
                        a.a.extend(c, "function" == typeof b ? b() : b)
                    })
                };
                var r = a.a.f.L(), m = a.a.f.L();
                a.Eb = function (b, c) {
                    if (2 == arguments.length) a.a.f.set(b, m, c),
                    c.D && c.D.Jb(b); else return a.a.f.get(b, m)
                };
                a.xa = function (b, c, d) {
                    1 === b.nodeType && a.e.wb(b);
                    return k(b, c, l(d), !0)
                };
                a.Lb = function (c, e, g) {
                    g = l(g);
                    return a.xa(c, "function" === typeof e ? d(e.bind(null, g, c)) : a.a.Oa(e, b), g)
                };
                a.gb = function (a, b) {
                    1 !== b.nodeType && 8 !== b.nodeType || f(l(a), b, !0)
                };
                a.fb = function (a, b) {
                    !t && A.jQuery && (t = A.jQuery);
                    if (b && 1 !== b.nodeType && 8 !== b.nodeType) throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");
                    b = b || A.document.body;
                    h(l(a),
                        b, !0)
                };
                a.Ca = function (b) {
                    switch (b.nodeType) {
                        case 1:
                        case 8:
                            var c = a.Eb(b);
                            if (c) return c;
                            if (b.parentNode) return a.Ca(b.parentNode)
                    }
                    return p
                };
                a.Pb = function (b) {
                    return (b = a.Ca(b)) ? b.$data : p
                };
                a.b("bindingHandlers", a.d);
                a.b("applyBindings", a.fb);
                a.b("applyBindingsToDescendants", a.gb);
                a.b("applyBindingAccessorsToNode", a.xa);
                a.b("applyBindingsToNode", a.Lb);
                a.b("contextFor", a.Ca);
                a.b("dataFor", a.Pb)
            })();
            var L = {"class": "className", "for": "htmlFor"};
            a.d.attr = {
                update: function (b, c) {
                    var d = a.a.c(c()) || {};
                    a.a.A(d, function (c,
                                       d) {
                        d = a.a.c(d);
                        var h = !1 === d || null === d || d === p;
                        h && b.removeAttribute(c);
                        8 >= a.a.oa && c in L ? (c = L[c], h ? b.removeAttribute(c) : b[c] = d) : h || b.setAttribute(c, d.toString());
                        "name" === c && a.a.Cb(b, h ? "" : d.toString())
                    })
                }
            };
            (function () {
                a.d.checked = {
                    after: ["value", "attr"], init: function (b, c, d) {
                        function e() {
                            return d.has("checkedValue") ? a.a.c(d.get("checkedValue")) : b.value
                        }

                        function f() {
                            var g = b.checked, f = r ? e() : g;
                            if (!a.ca.pa() && (!k || g)) {
                                var h = a.k.t(c);
                                l ? n !== f ? (g && (a.a.Y(h, f, !0), a.a.Y(h, n, !1)), n = f) : a.a.Y(h, f, g) : a.g.va(h, d, "checked",
                                    f, !0)
                            }
                        }

                        function h() {
                            var d = a.a.c(c());
                            b.checked = l ? 0 <= a.a.l(d, e()) : g ? d : e() === d
                        }

                        var g = "checkbox" == b.type, k = "radio" == b.type;
                        if (g || k) {
                            var l = g && a.a.c(c()) instanceof Array, n = l ? e() : p, r = k || l;
                            k && !b.name && a.d.uniqueName.init(b, function () {
                                return !0
                            });
                            a.ba(f, null, {G: b});
                            a.a.q(b, "click", f);
                            a.ba(h, null, {G: b})
                        }
                    }
                };
                a.g.W.checked = !0;
                a.d.checkedValue = {
                    update: function (b, c) {
                        b.value = a.a.c(c())
                    }
                }
            })();
            a.d.css = {
                update: function (b, c) {
                    var d = a.a.c(c());
                    "object" == typeof d ? a.a.A(d, function (c, d) {
                        d = a.a.c(d);
                        a.a.ua(b, c, d)
                    }) : (d = String(d ||
                        ""), a.a.ua(b, b.__ko__cssValue, !1), b.__ko__cssValue = d, a.a.ua(b, d, !0))
                }
            };
            a.d.enable = {
                update: function (b, c) {
                    var d = a.a.c(c());
                    d && b.disabled ? b.removeAttribute("disabled") : d || b.disabled || (b.disabled = !0)
                }
            };
            a.d.disable = {
                update: function (b, c) {
                    a.d.enable.update(b, function () {
                        return !a.a.c(c())
                    })
                }
            };
            a.d.event = {
                init: function (b, c, d, e, f) {
                    var h = c() || {};
                    a.a.A(h, function (g) {
                        "string" == typeof g && a.a.q(b, g, function (b) {
                            var h, n = c()[g];
                            if (n) {
                                try {
                                    var r = a.a.R(arguments);
                                    e = f.$data;
                                    r.unshift(e);
                                    h = n.apply(e, r)
                                } finally {
                                    !0 !== h && (b.preventDefault ?
                                        b.preventDefault() : b.returnValue = !1)
                                }
                                !1 === d.get(g + "Bubble") && (b.cancelBubble = !0, b.stopPropagation && b.stopPropagation())
                            }
                        })
                    })
                }
            };
            a.d.foreach = {
                vb: function (b) {
                    return function () {
                        var c = b(), d = a.a.Sa(c);
                        if (!d || "number" == typeof d.length) return {foreach: c, templateEngine: a.K.Ja};
                        a.a.c(c);
                        return {
                            foreach: d.data,
                            as: d.as,
                            includeDestroyed: d.includeDestroyed,
                            afterAdd: d.afterAdd,
                            beforeRemove: d.beforeRemove,
                            afterRender: d.afterRender,
                            beforeMove: d.beforeMove,
                            afterMove: d.afterMove,
                            templateEngine: a.K.Ja
                        }
                    }
                }, init: function (b,
                                   c) {
                    return a.d.template.init(b, a.d.foreach.vb(c))
                }, update: function (b, c, d, e, f) {
                    return a.d.template.update(b, a.d.foreach.vb(c), d, e, f)
                }
            };
            a.g.aa.foreach = !1;
            a.e.Q.foreach = !0;
            a.d.hasfocus = {
                init: function (b, c, d) {
                    function e(e) {
                        b.__ko_hasfocusUpdating = !0;
                        var k = b.ownerDocument;
                        if ("activeElement" in k) {
                            var f;
                            try {
                                f = k.activeElement
                            } catch (h) {
                                f = k.body
                            }
                            e = f === b
                        }
                        k = c();
                        a.g.va(k, d, "hasfocus", e, !0);
                        b.__ko_hasfocusLastValue = e;
                        b.__ko_hasfocusUpdating = !1
                    }

                    var f = e.bind(null, !0), h = e.bind(null, !1);
                    a.a.q(b, "focus", f);
                    a.a.q(b, "focusin",
                        f);
                    a.a.q(b, "blur", h);
                    a.a.q(b, "focusout", h)
                }, update: function (b, c) {
                    var d = !!a.a.c(c());
                    b.__ko_hasfocusUpdating || b.__ko_hasfocusLastValue === d || (d ? b.focus() : b.blur(), a.k.t(a.a.ha, null, [b, d ? "focusin" : "focusout"]))
                }
            };
            a.g.W.hasfocus = !0;
            a.d.hasFocus = a.d.hasfocus;
            a.g.W.hasFocus = !0;
            a.d.html = {
                init: function () {
                    return {controlsDescendantBindings: !0}
                }, update: function (b, c) {
                    a.a.Va(b, c())
                }
            };
            H("if");
            H("ifnot", !1, !0);
            H("with", !0, !1, function (a, c) {
                return a.createChildContext(c)
            });
            var J = {};
            a.d.options = {
                init: function (b) {
                    if ("select" !==
                        a.a.B(b)) throw Error("options binding applies only to SELECT elements");
                    for (; 0 < b.length;) b.remove(0);
                    return {controlsDescendantBindings: !0}
                }, update: function (b, c, d) {
                    function e() {
                        return a.a.la(b.options, function (a) {
                            return a.selected
                        })
                    }

                    function f(a, b, c) {
                        var d = typeof b;
                        return "function" == d ? b(a) : "string" == d ? a[b] : c
                    }

                    function h(c, d) {
                        if (r.length) {
                            var e = 0 <= a.a.l(r, a.i.p(d[0]));
                            a.a.Db(d[0], e);
                            m && !e && a.k.t(a.a.ha, null, [b, "change"])
                        }
                    }

                    var g = 0 != b.length && b.multiple ? b.scrollTop : null, k = a.a.c(c()),
                        l = d.get("optionsIncludeDestroyed");
                    c = {};
                    var n, r;
                    r = b.multiple ? a.a.ya(e(), a.i.p) : 0 <= b.selectedIndex ? [a.i.p(b.options[b.selectedIndex])] : [];
                    k && ("undefined" == typeof k.length && (k = [k]), n = a.a.la(k, function (b) {
                        return l || b === p || null === b || !a.a.c(b._destroy)
                    }), d.has("optionsCaption") && (k = a.a.c(d.get("optionsCaption")), null !== k && k !== p && n.unshift(J)));
                    var m = !1;
                    c.beforeRemove = function (a) {
                        b.removeChild(a)
                    };
                    k = h;
                    d.has("optionsAfterRender") && (k = function (b, c) {
                        h(0, c);
                        a.k.t(d.get("optionsAfterRender"), null, [c[0], b !== J ? b : p])
                    });
                    a.a.Ua(b, n, function (c, e, g) {
                        g.length &&
                        (r = g[0].selected ? [a.i.p(g[0])] : [], m = !0);
                        e = b.ownerDocument.createElement("option");
                        c === J ? (a.a.Xa(e, d.get("optionsCaption")), a.i.X(e, p)) : (g = f(c, d.get("optionsValue"), c), a.i.X(e, a.a.c(g)), c = f(c, d.get("optionsText"), g), a.a.Xa(e, c));
                        return [e]
                    }, c, k);
                    a.k.t(function () {
                        d.get("valueAllowUnset") && d.has("value") ? a.i.X(b, a.a.c(d.get("value")), !0) : (b.multiple ? r.length && e().length < r.length : r.length && 0 <= b.selectedIndex ? a.i.p(b.options[b.selectedIndex]) !== r[0] : r.length || 0 <= b.selectedIndex) && a.a.ha(b, "change")
                    });
                    a.a.Tb(b);
                    g && 20 < Math.abs(g - b.scrollTop) && (b.scrollTop = g)
                }
            };
            a.d.options.Pa = a.a.f.L();
            a.d.selectedOptions = {
                after: ["options", "foreach"], init: function (b, c, d) {
                    a.a.q(b, "change", function () {
                        var e = c(), f = [];
                        a.a.r(b.getElementsByTagName("option"), function (b) {
                            b.selected && f.push(a.i.p(b))
                        });
                        a.g.va(e, d, "selectedOptions", f)
                    })
                }, update: function (b, c) {
                    if ("select" != a.a.B(b)) throw Error("values binding applies only to SELECT elements");
                    var d = a.a.c(c());
                    d && "number" == typeof d.length && a.a.r(b.getElementsByTagName("option"), function (b) {
                        var c =
                            0 <= a.a.l(d, a.i.p(b));
                        a.a.Db(b, c)
                    })
                }
            };
            a.g.W.selectedOptions = !0;
            a.d.style = {
                update: function (b, c) {
                    var d = a.a.c(c() || {});
                    a.a.A(d, function (c, d) {
                        d = a.a.c(d);
                        b.style[c] = d || ""
                    })
                }
            };
            a.d.submit = {
                init: function (b, c, d, e, f) {
                    if ("function" != typeof c()) throw Error("The value for a submit binding must be a function");
                    a.a.q(b, "submit", function (a) {
                        var d, e = c();
                        try {
                            d = e.call(f.$data, b)
                        } finally {
                            !0 !== d && (a.preventDefault ? a.preventDefault() : a.returnValue = !1)
                        }
                    })
                }
            };
            a.d.text = {
                init: function () {
                    return {controlsDescendantBindings: !0}
                },
                update: function (b, c) {
                    a.a.Xa(b, c())
                }
            };
            a.e.Q.text = !0;
            a.d.uniqueName = {
                init: function (b, c) {
                    if (c()) {
                        var d = "ko_unique_" + ++a.d.uniqueName.Ob;
                        a.a.Cb(b, d)
                    }
                }
            };
            a.d.uniqueName.Ob = 0;
            a.d.value = {
                after: ["options", "foreach"], init: function (b, c, d) {
                    function e() {
                        g = !1;
                        var e = c(), f = a.i.p(b);
                        a.g.va(e, d, "value", f)
                    }

                    var f = ["change"], h = d.get("valueUpdate"), g = !1;
                    h && ("string" == typeof h && (h = [h]), a.a.$(f, h), f = a.a.ib(f));
                    !a.a.oa || "input" != b.tagName.toLowerCase() || "text" != b.type || "off" == b.autocomplete || b.form && "off" == b.form.autocomplete ||
                    -1 != a.a.l(f, "propertychange") || (a.a.q(b, "propertychange", function () {
                        g = !0
                    }), a.a.q(b, "focus", function () {
                        g = !1
                    }), a.a.q(b, "blur", function () {
                        g && e()
                    }));
                    a.a.r(f, function (c) {
                        var d = e;
                        a.a.kc(c, "after") && (d = function () {
                            setTimeout(e, 0)
                        }, c = c.substring(5));
                        a.a.q(b, c, d)
                    })
                }, update: function (b, c, d) {
                    var e = a.a.c(c());
                    c = a.i.p(b);
                    if (e !== c) if ("select" === a.a.B(b)) {
                        var f = d.get("valueAllowUnset");
                        d = function () {
                            a.i.X(b, e, f)
                        };
                        d();
                        f || e === a.i.p(b) ? setTimeout(d, 0) : a.k.t(a.a.ha, null, [b, "change"])
                    } else a.i.X(b, e)
                }
            };
            a.g.W.value = !0;
            a.d.visible =
                {
                    update: function (b, c) {
                        var d = a.a.c(c()), e = "none" != b.style.display;
                        d && !e ? b.style.display = "" : !d && e && (b.style.display = "none")
                    }
                };
            (function (b) {
                a.d[b] = {
                    init: function (c, d, e, f, h) {
                        return a.d.event.init.call(this, c, function () {
                            var a = {};
                            a[b] = d();
                            return a
                        }, e, f, h)
                    }
                }
            })("click");
            a.C = function () {
            };
            a.C.prototype.renderTemplateSource = function () {
                throw Error("Override renderTemplateSource");
            };
            a.C.prototype.createJavaScriptEvaluatorBlock = function () {
                throw Error("Override createJavaScriptEvaluatorBlock");
            };
            a.C.prototype.makeTemplateSource =
                function (b, c) {
                    if ("string" == typeof b) {
                        c = c || w;
                        var d = c.getElementById(b);
                        if (!d) throw Error("Cannot find template with ID " + b);
                        return new a.n.j(d)
                    }
                    if (1 == b.nodeType || 8 == b.nodeType) return new a.n.Z(b);
                    throw Error("Unknown template type: " + b);
                };
            a.C.prototype.renderTemplate = function (a, c, d, e) {
                a = this.makeTemplateSource(a, e);
                return this.renderTemplateSource(a, c, d)
            };
            a.C.prototype.isTemplateRewritten = function (a, c) {
                return !1 === this.allowTemplateRewriting ? !0 : this.makeTemplateSource(a, c).data("isRewritten")
            };
            a.C.prototype.rewriteTemplate =
                function (a, c, d) {
                    a = this.makeTemplateSource(a, d);
                    c = c(a.text());
                    a.text(c);
                    a.data("isRewritten", !0)
                };
            a.b("templateEngine", a.C);
            a.Za = function () {
                function b(b, c, d, g) {
                    b = a.g.Ra(b);
                    for (var k = a.g.aa, l = 0; l < b.length; l++) {
                        var n = b[l].key;
                        if (k.hasOwnProperty(n)) {
                            var r = k[n];
                            if ("function" === typeof r) {
                                if (n = r(b[l].value)) throw Error(n);
                            } else if (!r) throw Error("This template engine does not support the '" + n + "' binding within its templates");
                        }
                    }
                    d = "ko.__tr_ambtns(function($context,$element){return(function(){return{ " + a.g.qa(b,
                        {valueAccessors: !0}) + " } })()},'" + d.toLowerCase() + "')";
                    return g.createJavaScriptEvaluatorBlock(d) + c
                }

                var c = /(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,
                    d = /\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;
                return {
                    Ub: function (b, c, d) {
                        c.isTemplateRewritten(b, d) || c.rewriteTemplate(b, function (b) {
                            return a.Za.dc(b, c)
                        }, d)
                    }, dc: function (a, f) {
                        return a.replace(c, function (a, c, d, e, n) {
                            return b(n, c, d, f)
                        }).replace(d, function (a, c) {
                            return b(c, "\x3c!-- ko --\x3e",
                                "#comment", f)
                        })
                    }, Mb: function (b, c) {
                        return a.w.Na(function (d, g) {
                            var k = d.nextSibling;
                            k && k.nodeName.toLowerCase() === c && a.xa(k, b, g)
                        })
                    }
                }
            }();
            a.b("__tr_ambtns", a.Za.Mb);
            (function () {
                a.n = {};
                a.n.j = function (a) {
                    this.j = a
                };
                a.n.j.prototype.text = function () {
                    var b = a.a.B(this.j), b = "script" === b ? "text" : "textarea" === b ? "value" : "innerHTML";
                    if (0 == arguments.length) return this.j[b];
                    var c = arguments[0];
                    "innerHTML" === b ? a.a.Va(this.j, c) : this.j[b] = c
                };
                var b = a.a.f.L() + "_";
                a.n.j.prototype.data = function (c) {
                    if (1 === arguments.length) return a.a.f.get(this.j,
                        b + c);
                    a.a.f.set(this.j, b + c, arguments[1])
                };
                var c = a.a.f.L();
                a.n.Z = function (a) {
                    this.j = a
                };
                a.n.Z.prototype = new a.n.j;
                a.n.Z.prototype.text = function () {
                    if (0 == arguments.length) {
                        var b = a.a.f.get(this.j, c) || {};
                        b.$a === p && b.Ba && (b.$a = b.Ba.innerHTML);
                        return b.$a
                    }
                    a.a.f.set(this.j, c, {$a: arguments[0]})
                };
                a.n.j.prototype.nodes = function () {
                    if (0 == arguments.length) return (a.a.f.get(this.j, c) || {}).Ba;
                    a.a.f.set(this.j, c, {Ba: arguments[0]})
                };
                a.b("templateSources", a.n);
                a.b("templateSources.domElement", a.n.j);
                a.b("templateSources.anonymousTemplate",
                    a.n.Z)
            })();
            (function () {
                function b(b, c, d) {
                    var e;
                    for (c = a.e.nextSibling(c); b && (e = b) !== c;) b = a.e.nextSibling(e), d(e, b)
                }

                function c(c, d) {
                    if (c.length) {
                        var e = c[0], f = c[c.length - 1], h = e.parentNode, m = a.J.instance, q = m.preprocessNode;
                        if (q) {
                            b(e, f, function (a, b) {
                                var c = a.previousSibling, d = q.call(m, a);
                                d && (a === e && (e = d[0] || b), a === f && (f = d[d.length - 1] || c))
                            });
                            c.length = 0;
                            if (!e) return;
                            e === f ? c.push(e) : (c.push(e, f), a.a.ea(c, h))
                        }
                        b(e, f, function (b) {
                            1 !== b.nodeType && 8 !== b.nodeType || a.fb(d, b)
                        });
                        b(e, f, function (b) {
                            1 !== b.nodeType && 8 !==
                            b.nodeType || a.w.Ib(b, [d])
                        });
                        a.a.ea(c, h)
                    }
                }

                function d(a) {
                    return a.nodeType ? a : 0 < a.length ? a[0] : null
                }

                function e(b, e, h, n, r) {
                    r = r || {};
                    var m = b && d(b), m = m && m.ownerDocument, q = r.templateEngine || f;
                    a.Za.Ub(h, q, m);
                    h = q.renderTemplate(h, n, r, m);
                    if ("number" != typeof h.length || 0 < h.length && "number" != typeof h[0].nodeType) throw Error("Template engine must return an array of DOM nodes");
                    m = !1;
                    switch (e) {
                        case "replaceChildren":
                            a.e.U(b, h);
                            m = !0;
                            break;
                        case "replaceNode":
                            a.a.Bb(b, h);
                            m = !0;
                            break;
                        case "ignoreTargetNode":
                            break;
                        default:
                            throw Error("Unknown renderMode: " +
                                e);
                    }
                    m && (c(h, n), r.afterRender && a.k.t(r.afterRender, null, [h, n.$data]));
                    return h
                }

                var f;
                a.Wa = function (b) {
                    if (b != p && !(b instanceof a.C)) throw Error("templateEngine must inherit from ko.templateEngine");
                    f = b
                };
                a.Ta = function (b, c, h, n, r) {
                    h = h || {};
                    if ((h.templateEngine || f) == p) throw Error("Set a template engine before calling renderTemplate");
                    r = r || "replaceChildren";
                    if (n) {
                        var m = d(n);
                        return a.h(function () {
                            var f = c && c instanceof a.I ? c : new a.I(a.a.c(c)),
                                p = a.v(b) ? b() : "function" == typeof b ? b(f.$data, f) : b, f = e(n, r, p, f, h);
                            "replaceNode" == r && (n = f, m = d(n))
                        }, null, {
                            Da: function () {
                                return !m || !a.a.Ea(m)
                            }, G: m && "replaceNode" == r ? m.parentNode : m
                        })
                    }
                    return a.w.Na(function (d) {
                        a.Ta(b, c, h, d, "replaceNode")
                    })
                };
                a.jc = function (b, d, f, h, r) {
                    function m(a, b) {
                        c(b, s);
                        f.afterRender && f.afterRender(b, a)
                    }

                    function q(a, c) {
                        s = r.createChildContext(a, f.as, function (a) {
                            a.$index = c
                        });
                        var d = "function" == typeof b ? b(a, s) : b;
                        return e(null, "ignoreTargetNode", d, s, f)
                    }

                    var s;
                    return a.h(function () {
                        var b = a.a.c(d) || [];
                        "undefined" == typeof b.length && (b = [b]);
                        b = a.a.la(b, function (b) {
                            return f.includeDestroyed ||
                                b === p || null === b || !a.a.c(b._destroy)
                        });
                        a.k.t(a.a.Ua, null, [h, b, q, f, m])
                    }, null, {G: h})
                };
                var h = a.a.f.L();
                a.d.template = {
                    init: function (b, c) {
                        var d = a.a.c(c());
                        "string" == typeof d || d.name ? a.e.da(b) : (d = a.e.childNodes(b), d = a.a.ec(d), (new a.n.Z(b)).nodes(d));
                        return {controlsDescendantBindings: !0}
                    }, update: function (b, c, d, e, f) {
                        var m = c(), q;
                        c = a.a.c(m);
                        d = !0;
                        e = null;
                        "string" == typeof c ? c = {} : (m = c.name, "if" in c && (d = a.a.c(c["if"])), d && "ifnot" in c && (d = !a.a.c(c.ifnot)), q = a.a.c(c.data));
                        "foreach" in c ? e = a.jc(m || b, d && c.foreach ||
                            [], c, b, f) : d ? (f = "data" in c ? f.createChildContext(q, c.as) : f, e = a.Ta(m || b, f, c, b)) : a.e.da(b);
                        f = e;
                        (q = a.a.f.get(b, h)) && "function" == typeof q.F && q.F();
                        a.a.f.set(b, h, f && f.ga() ? f : p)
                    }
                };
                a.g.aa.template = function (b) {
                    b = a.g.Ra(b);
                    return 1 == b.length && b[0].unknown || a.g.bc(b, "name") ? null : "This template engine does not support anonymous templates nested within its templates"
                };
                a.e.Q.template = !0
            })();
            a.b("setTemplateEngine", a.Wa);
            a.b("renderTemplate", a.Ta);
            a.a.nb = function (a, c, d) {
                if (a.length && c.length) {
                    var e, f, h, g, k;
                    for (e =
                             f = 0; (!d || e < d) && (g = a[f]); ++f) {
                        for (h = 0; k = c[h]; ++h) if (g.value === k.value) {
                            g.moved = k.index;
                            k.moved = g.index;
                            c.splice(h, 1);
                            e = h = 0;
                            break
                        }
                        e += h
                    }
                }
            };
            a.a.Aa = function () {
                function b(b, d, e, f, h) {
                    var g = Math.min, k = Math.max, l = [], n, p = b.length, m, q = d.length, s = q - p || 1,
                        t = p + q + 1, u, w, y;
                    for (n = 0; n <= p; n++) for (w = u, l.push(u = []), y = g(q, n + s), m = k(0, n - 1); m <= y; m++) u[m] = m ? n ? b[n - 1] === d[m - 1] ? w[m - 1] : g(w[m] || t, u[m - 1] || t) + 1 : m + 1 : n + 1;
                    g = [];
                    k = [];
                    s = [];
                    n = p;
                    for (m = q; n || m;) q = l[n][m] - 1, m && q === l[n][m - 1] ? k.push(g[g.length] = {
                            status: e,
                            value: d[--m],
                            index: m
                        }) :
                        n && q === l[n - 1][m] ? s.push(g[g.length] = {
                            status: f,
                            value: b[--n],
                            index: n
                        }) : (--m, --n, h.sparse || g.push({status: "retained", value: d[m]}));
                    a.a.nb(k, s, 10 * p);
                    return g.reverse()
                }

                return function (a, d, e) {
                    e = "boolean" === typeof e ? {dontLimitMoves: e} : e || {};
                    a = a || [];
                    d = d || [];
                    return a.length <= d.length ? b(a, d, "added", "deleted", e) : b(d, a, "deleted", "added", e)
                }
            }();
            a.b("utils.compareArrays", a.a.Aa);
            (function () {
                function b(b, c, f, h, g) {
                    var k = [], l = a.h(function () {
                        var l = c(f, g, a.a.ea(k, b)) || [];
                        0 < k.length && (a.a.Bb(k, l), h && a.k.t(h, null, [f,
                            l, g]));
                        k.length = 0;
                        a.a.$(k, l)
                    }, null, {
                        G: b, Da: function () {
                            return !a.a.eb(k)
                        }
                    });
                    return {S: k, h: l.ga() ? l : p}
                }

                var c = a.a.f.L();
                a.a.Ua = function (d, e, f, h, g) {
                    function k(b, c) {
                        v = r[c];
                        u !== c && (z[b] = v);
                        v.Ia(u++);
                        a.a.ea(v.S, d);
                        s.push(v);
                        y.push(v)
                    }

                    function l(b, c) {
                        if (b) for (var d = 0, e = c.length; d < e; d++) c[d] && a.a.r(c[d].S, function (a) {
                            b(a, d, c[d].ka)
                        })
                    }

                    e = e || [];
                    h = h || {};
                    var n = a.a.f.get(d, c) === p, r = a.a.f.get(d, c) || [], m = a.a.ya(r, function (a) {
                        return a.ka
                    }), q = a.a.Aa(m, e, h.dontLimitMoves), s = [], t = 0, u = 0, w = [], y = [];
                    e = [];
                    for (var z = [], m = [],
                             v, x = 0, A, C; A = q[x]; x++) switch (C = A.moved, A.status) {
                        case "deleted":
                            C === p && (v = r[t], v.h && v.h.F(), w.push.apply(w, a.a.ea(v.S, d)), h.beforeRemove && (e[x] = v, y.push(v)));
                            t++;
                            break;
                        case "retained":
                            k(x, t++);
                            break;
                        case "added":
                            C !== p ? k(x, C) : (v = {ka: A.value, Ia: a.m(u++)}, s.push(v), y.push(v), n || (m[x] = v))
                    }
                    l(h.beforeMove, z);
                    a.a.r(w, h.beforeRemove ? a.M : a.removeNode);
                    for (var x = 0, n = a.e.firstChild(d), E; v = y[x]; x++) {
                        v.S || a.a.extend(v, b(d, f, v.ka, g, v.Ia));
                        for (t = 0; q = v.S[t]; n = q.nextSibling, E = q, t++) q !== n && a.e.rb(d, q, E);
                        !v.Zb && g && (g(v.ka,
                            v.S, v.Ia), v.Zb = !0)
                    }
                    l(h.beforeRemove, e);
                    l(h.afterMove, z);
                    l(h.afterAdd, m);
                    a.a.f.set(d, c, s)
                }
            })();
            a.b("utils.setDomNodeChildrenFromArrayMapping", a.a.Ua);
            a.K = function () {
                this.allowTemplateRewriting = !1
            };
            a.K.prototype = new a.C;
            a.K.prototype.renderTemplateSource = function (b) {
                var c = (9 > a.a.oa ? 0 : b.nodes) ? b.nodes() : null;
                if (c) return a.a.R(c.cloneNode(!0).childNodes);
                b = b.text();
                return a.a.Qa(b)
            };
            a.K.Ja = new a.K;
            a.Wa(a.K.Ja);
            a.b("nativeTemplateEngine", a.K);
            (function () {
                a.La = function () {
                    var a = this.ac = function () {
                        if (!t ||
                            !t.tmpl) return 0;
                        try {
                            if (0 <= t.tmpl.tag.tmpl.open.toString().indexOf("__")) return 2
                        } catch (a) {
                        }
                        return 1
                    }();
                    this.renderTemplateSource = function (b, e, f) {
                        f = f || {};
                        if (2 > a) throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");
                        var h = b.data("precompiled");
                        h || (h = b.text() || "", h = t.template(null, "{{ko_with $item.koBindingContext}}" + h + "{{/ko_with}}"), b.data("precompiled", h));
                        b = [e.$data];
                        e = t.extend({koBindingContext: e}, f.templateOptions);
                        e = t.tmpl(h, b, e);
                        e.appendTo(w.createElement("div"));
                        t.fragments = {};
                        return e
                    };
                    this.createJavaScriptEvaluatorBlock = function (a) {
                        return "{{ko_code ((function() { return " + a + " })()) }}"
                    };
                    this.addTemplate = function (a, b) {
                        w.write("<script type='text/html' id='" + a + "'>" + b + "\x3c/script>")
                    };
                    0 < a && (t.tmpl.tag.ko_code = {open: "__.push($1 || '');"}, t.tmpl.tag.ko_with = {
                        open: "with($1) {",
                        close: "} "
                    })
                };
                a.La.prototype = new a.C;
                var b = new a.La;
                0 < b.ac && a.Wa(b);
                a.b("jqueryTmplTemplateEngine", a.La)
            })()
        })
    })();
})();
</script>

<!-- Three.js (3d canvas engine) -->

<!--script type="text/javascript" src="http://cdn.jsdelivr.net/threejs/r62/three.js"></script-->
<!--script type="text/javascript" src="http://cdn.strategiqcommerce.com/ajax/libs/three.js/r57/three.min.js"></script>-->
<!--script type="text/javascript" src="http://www.rootcdn.com/libs/three.js/r57/three.min.js"></script>-->
<script type="text/javascript">// three.js - http://github.com/mrdoob/three.js
'use strict';
var THREE = THREE || {REVISION: "57"};
self.console = self.console || {
    info: function () {
    }, log: function () {
    }, debug: function () {
    }, warn: function () {
    }, error: function () {
    }
};
self.Int32Array = self.Int32Array || Array;
self.Float32Array = self.Float32Array || Array;
String.prototype.trim = String.prototype.trim || function () {
    return this.replace(/^\s+|\s+$/g, "")
};
THREE.extend = function (a, b) {
    if (Object.keys) for (var c = Object.keys(b), d = 0, e = c.length; d < e; d++) {
        var f = c[d];
        Object.defineProperty(a, f, Object.getOwnPropertyDescriptor(b, f))
    } else for (f in c = {}.hasOwnProperty, b) c.call(b, f) && (a[f] = b[f]);
    return a
};
(function () {
    for (var a = 0, b = ["ms", "moz", "webkit", "o"], c = 0; c < b.length && !window.requestAnimationFrame; ++c) window.requestAnimationFrame = window[b[c] + "RequestAnimationFrame"], window.cancelAnimationFrame = window[b[c] + "CancelAnimationFrame"] || window[b[c] + "CancelRequestAnimationFrame"];
    void 0 === window.requestAnimationFrame && (window.requestAnimationFrame = function (b) {
        var c = Date.now(), f = Math.max(0, 16 - (c - a)), g = window.setTimeout(function () {
            b(c + f)
        }, f);
        a = c + f;
        return g
    });
    window.cancelAnimationFrame = window.cancelAnimationFrame ||
        function (a) {
            window.clearTimeout(a)
        }
})();
THREE.CullFaceNone = 0;
THREE.CullFaceBack = 1;
THREE.CullFaceFront = 2;
THREE.CullFaceFrontBack = 3;
THREE.FrontFaceDirectionCW = 0;
THREE.FrontFaceDirectionCCW = 1;
THREE.BasicShadowMap = 0;
THREE.PCFShadowMap = 1;
THREE.PCFSoftShadowMap = 2;
THREE.FrontSide = 0;
THREE.BackSide = 1;
THREE.DoubleSide = 2;
THREE.NoShading = 0;
THREE.FlatShading = 1;
THREE.SmoothShading = 2;
THREE.NoColors = 0;
THREE.FaceColors = 1;
THREE.VertexColors = 2;
THREE.NoBlending = 0;
THREE.NormalBlending = 1;
THREE.AdditiveBlending = 2;
THREE.SubtractiveBlending = 3;
THREE.MultiplyBlending = 4;
THREE.CustomBlending = 5;
THREE.AddEquation = 100;
THREE.SubtractEquation = 101;
THREE.ReverseSubtractEquation = 102;
THREE.ZeroFactor = 200;
THREE.OneFactor = 201;
THREE.SrcColorFactor = 202;
THREE.OneMinusSrcColorFactor = 203;
THREE.SrcAlphaFactor = 204;
THREE.OneMinusSrcAlphaFactor = 205;
THREE.DstAlphaFactor = 206;
THREE.OneMinusDstAlphaFactor = 207;
THREE.DstColorFactor = 208;
THREE.OneMinusDstColorFactor = 209;
THREE.SrcAlphaSaturateFactor = 210;
THREE.MultiplyOperation = 0;
THREE.MixOperation = 1;
THREE.AddOperation = 2;
THREE.UVMapping = function () {
};
THREE.CubeReflectionMapping = function () {
};
THREE.CubeRefractionMapping = function () {
};
THREE.SphericalReflectionMapping = function () {
};
THREE.SphericalRefractionMapping = function () {
};
THREE.RepeatWrapping = 1E3;
THREE.ClampToEdgeWrapping = 1001;
THREE.MirroredRepeatWrapping = 1002;
THREE.NearestFilter = 1003;
THREE.NearestMipMapNearestFilter = 1004;
THREE.NearestMipMapLinearFilter = 1005;
THREE.LinearFilter = 1006;
THREE.LinearMipMapNearestFilter = 1007;
THREE.LinearMipMapLinearFilter = 1008;
THREE.UnsignedByteType = 1009;
THREE.ByteType = 1010;
THREE.ShortType = 1011;
THREE.UnsignedShortType = 1012;
THREE.IntType = 1013;
THREE.UnsignedIntType = 1014;
THREE.FloatType = 1015;
THREE.UnsignedShort4444Type = 1016;
THREE.UnsignedShort5551Type = 1017;
THREE.UnsignedShort565Type = 1018;
THREE.AlphaFormat = 1019;
THREE.RGBFormat = 1020;
THREE.RGBAFormat = 1021;
THREE.LuminanceFormat = 1022;
THREE.LuminanceAlphaFormat = 1023;
THREE.RGB_S3TC_DXT1_Format = 2001;
THREE.RGBA_S3TC_DXT1_Format = 2002;
THREE.RGBA_S3TC_DXT3_Format = 2003;
THREE.RGBA_S3TC_DXT5_Format = 2004;
THREE.Color = function (a) {
    void 0 !== a && this.set(a);
    return this
};
THREE.Color.prototype = {
    constructor: THREE.Color, r: 1, g: 1, b: 1, set: function (a) {
        switch (typeof a) {
            case "number":
                this.setHex(a);
                break;
            case "string":
                this.setStyle(a)
        }
    }, setHex: function (a) {
        a = Math.floor(a);
        this.r = (a >> 16 & 255) / 255;
        this.g = (a >> 8 & 255) / 255;
        this.b = (a & 255) / 255;
        return this
    }, setRGB: function (a, b, c) {
        this.r = a;
        this.g = b;
        this.b = c;
        return this
    }, setHSL: function (a, b, c) {
        if (0 === b) this.r = this.g = this.b = c; else {
            var d = function (a, b, c) {
                    0 > c && (c += 1);
                    1 < c && (c -= 1);
                    return c < 1 / 6 ? a + 6 * (b - a) * c : 0.5 > c ? b : c < 2 / 3 ? a + 6 * (b - a) * (2 / 3 - c) : a
                },
                b = 0.5 >= c ? c * (1 + b) : c + b - c * b, c = 2 * c - b;
            this.r = d(c, b, a + 1 / 3);
            this.g = d(c, b, a);
            this.b = d(c, b, a - 1 / 3)
        }
        return this
    }, setStyle: function (a) {
        if (/^rgb\((\d+),(\d+),(\d+)\)$/i.test(a)) return a = /^rgb\((\d+),(\d+),(\d+)\)$/i.exec(a), this.r = Math.min(255, parseInt(a[1], 10)) / 255, this.g = Math.min(255, parseInt(a[2], 10)) / 255, this.b = Math.min(255, parseInt(a[3], 10)) / 255, this;
        if (/^rgb\((\d+)\%,(\d+)\%,(\d+)\%\)$/i.test(a)) return a = /^rgb\((\d+)\%,(\d+)\%,(\d+)\%\)$/i.exec(a), this.r = Math.min(100, parseInt(a[1], 10)) / 100, this.g = Math.min(100,
            parseInt(a[2], 10)) / 100, this.b = Math.min(100, parseInt(a[3], 10)) / 100, this;
        if (/^\#([0-9a-f]{6})$/i.test(a)) return a = /^\#([0-9a-f]{6})$/i.exec(a), this.setHex(parseInt(a[1], 16)), this;
        if (/^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.test(a)) return a = /^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(a), this.setHex(parseInt(a[1] + a[1] + a[2] + a[2] + a[3] + a[3], 16)), this;
        if (/^(\w+)$/i.test(a)) return this.setHex(THREE.ColorKeywords[a]), this
    }, copy: function (a) {
        this.r = a.r;
        this.g = a.g;
        this.b = a.b;
        return this
    }, copyGammaToLinear: function (a) {
        this.r =
            a.r * a.r;
        this.g = a.g * a.g;
        this.b = a.b * a.b;
        return this
    }, copyLinearToGamma: function (a) {
        this.r = Math.sqrt(a.r);
        this.g = Math.sqrt(a.g);
        this.b = Math.sqrt(a.b);
        return this
    }, convertGammaToLinear: function () {
        var a = this.r, b = this.g, c = this.b;
        this.r = a * a;
        this.g = b * b;
        this.b = c * c;
        return this
    }, convertLinearToGamma: function () {
        this.r = Math.sqrt(this.r);
        this.g = Math.sqrt(this.g);
        this.b = Math.sqrt(this.b);
        return this
    }, getHex: function () {
        return 255 * this.r << 16 ^ 255 * this.g << 8 ^ 255 * this.b << 0
    }, getHexString: function () {
        return ("000000" + this.getHex().toString(16)).slice(-6)
    },
    getHSL: function () {
        var a = {h: 0, s: 0, l: 0};
        return function () {
            var b = this.r, c = this.g, d = this.b, e = Math.max(b, c, d), f = Math.min(b, c, d), g, h = (f + e) / 2;
            if (f === e) f = g = 0; else {
                var i = e - f, f = 0.5 >= h ? i / (e + f) : i / (2 - e - f);
                switch (e) {
                    case b:
                        g = (c - d) / i + (c < d ? 6 : 0);
                        break;
                    case c:
                        g = (d - b) / i + 2;
                        break;
                    case d:
                        g = (b - c) / i + 4
                }
                g /= 6
            }
            a.h = g;
            a.s = f;
            a.l = h;
            return a
        }
    }(), getStyle: function () {
        return "rgb(" + (255 * this.r | 0) + "," + (255 * this.g | 0) + "," + (255 * this.b | 0) + ")"
    }, offsetHSL: function (a, b, c) {
        var d = this.getHSL();
        d.h += a;
        d.s += b;
        d.l += c;
        this.setHSL(d.h, d.s, d.l);
        return this
    },
    add: function (a) {
        this.r += a.r;
        this.g += a.g;
        this.b += a.b;
        return this
    }, addColors: function (a, b) {
        this.r = a.r + b.r;
        this.g = a.g + b.g;
        this.b = a.b + b.b;
        return this
    }, addScalar: function (a) {
        this.r += a;
        this.g += a;
        this.b += a;
        return this
    }, multiply: function (a) {
        this.r *= a.r;
        this.g *= a.g;
        this.b *= a.b;
        return this
    }, multiplyScalar: function (a) {
        this.r *= a;
        this.g *= a;
        this.b *= a;
        return this
    }, lerp: function (a, b) {
        this.r += (a.r - this.r) * b;
        this.g += (a.g - this.g) * b;
        this.b += (a.b - this.b) * b;
        return this
    }, equals: function (a) {
        return a.r === this.r && a.g === this.g &&
            a.b === this.b
    }, clone: function () {
        return (new THREE.Color).setRGB(this.r, this.g, this.b)
    }
};
THREE.ColorKeywords = {
    aliceblue: 15792383,
    antiquewhite: 16444375,
    aqua: 65535,
    aquamarine: 8388564,
    azure: 15794175,
    beige: 16119260,
    bisque: 16770244,
    black: 0,
    blanchedalmond: 16772045,
    blue: 255,
    blueviolet: 9055202,
    brown: 10824234,
    burlywood: 14596231,
    cadetblue: 6266528,
    chartreuse: 8388352,
    chocolate: 13789470,
    coral: 16744272,
    cornflowerblue: 6591981,
    cornsilk: 16775388,
    crimson: 14423100,
    cyan: 65535,
    darkblue: 139,
    darkcyan: 35723,
    darkgoldenrod: 12092939,
    darkgray: 11119017,
    darkgreen: 25600,
    darkgrey: 11119017,
    darkkhaki: 12433259,
    darkmagenta: 9109643,
    darkolivegreen: 5597999,
    darkorange: 16747520,
    darkorchid: 10040012,
    darkred: 9109504,
    darksalmon: 15308410,
    darkseagreen: 9419919,
    darkslateblue: 4734347,
    darkslategray: 3100495,
    darkslategrey: 3100495,
    darkturquoise: 52945,
    darkviolet: 9699539,
    deeppink: 16716947,
    deepskyblue: 49151,
    dimgray: 6908265,
    dimgrey: 6908265,
    dodgerblue: 2003199,
    firebrick: 11674146,
    floralwhite: 16775920,
    forestgreen: 2263842,
    fuchsia: 16711935,
    gainsboro: 14474460,
    ghostwhite: 16316671,
    gold: 16766720,
    goldenrod: 14329120,
    gray: 8421504,
    green: 32768,
    greenyellow: 11403055,
    grey: 8421504,
    honeydew: 15794160,
    hotpink: 16738740,
    indianred: 13458524,
    indigo: 4915330,
    ivory: 16777200,
    khaki: 15787660,
    lavender: 15132410,
    lavenderblush: 16773365,
    lawngreen: 8190976,
    lemonchiffon: 16775885,
    lightblue: 11393254,
    lightcoral: 15761536,
    lightcyan: 14745599,
    lightgoldenrodyellow: 16448210,
    lightgray: 13882323,
    lightgreen: 9498256,
    lightgrey: 13882323,
    lightpink: 16758465,
    lightsalmon: 16752762,
    lightseagreen: 2142890,
    lightskyblue: 8900346,
    lightslategray: 7833753,
    lightslategrey: 7833753,
    lightsteelblue: 11584734,
    lightyellow: 16777184,
    lime: 65280,
    limegreen: 3329330,
    linen: 16445670,
    magenta: 16711935,
    maroon: 8388608,
    mediumaquamarine: 6737322,
    mediumblue: 205,
    mediumorchid: 12211667,
    mediumpurple: 9662683,
    mediumseagreen: 3978097,
    mediumslateblue: 8087790,
    mediumspringgreen: 64154,
    mediumturquoise: 4772300,
    mediumvioletred: 13047173,
    midnightblue: 1644912,
    mintcream: 16121850,
    mistyrose: 16770273,
    moccasin: 16770229,
    navajowhite: 16768685,
    navy: 128,
    oldlace: 16643558,
    olive: 8421376,
    olivedrab: 7048739,
    orange: 16753920,
    orangered: 16729344,
    orchid: 14315734,
    palegoldenrod: 15657130,
    palegreen: 10025880,
    paleturquoise: 11529966,
    palevioletred: 14381203,
    papayawhip: 16773077,
    peachpuff: 16767673,
    peru: 13468991,
    pink: 16761035,
    plum: 14524637,
    powderblue: 11591910,
    purple: 8388736,
    red: 16711680,
    rosybrown: 12357519,
    royalblue: 4286945,
    saddlebrown: 9127187,
    salmon: 16416882,
    sandybrown: 16032864,
    seagreen: 3050327,
    seashell: 16774638,
    sienna: 10506797,
    silver: 12632256,
    skyblue: 8900331,
    slateblue: 6970061,
    slategray: 7372944,
    slategrey: 7372944,
    snow: 16775930,
    springgreen: 65407,
    steelblue: 4620980,
    tan: 13808780,
    teal: 32896,
    thistle: 14204888,
    tomato: 16737095,
    turquoise: 4251856,
    violet: 15631086,
    wheat: 16113331,
    white: 16777215,
    whitesmoke: 16119285,
    yellow: 16776960,
    yellowgreen: 10145074
};
THREE.Quaternion = function (a, b, c, d) {
    this.x = a || 0;
    this.y = b || 0;
    this.z = c || 0;
    this.w = void 0 !== d ? d : 1
};
THREE.Quaternion.prototype = {
    constructor: THREE.Quaternion, set: function (a, b, c, d) {
        this.x = a;
        this.y = b;
        this.z = c;
        this.w = d;
        return this
    }, copy: function (a) {
        this.x = a.x;
        this.y = a.y;
        this.z = a.z;
        this.w = a.w;
        return this
    }, setFromEuler: function (a, b) {
        var c = Math.cos(a.x / 2), d = Math.cos(a.y / 2), e = Math.cos(a.z / 2), f = Math.sin(a.x / 2),
            g = Math.sin(a.y / 2), h = Math.sin(a.z / 2);
        void 0 === b || "XYZ" === b ? (this.x = f * d * e + c * g * h, this.y = c * g * e - f * d * h, this.z = c * d * h + f * g * e, this.w = c * d * e - f * g * h) : "YXZ" === b ? (this.x = f * d * e + c * g * h, this.y = c * g * e - f * d * h, this.z = c * d *
            h - f * g * e, this.w = c * d * e + f * g * h) : "ZXY" === b ? (this.x = f * d * e - c * g * h, this.y = c * g * e + f * d * h, this.z = c * d * h + f * g * e, this.w = c * d * e - f * g * h) : "ZYX" === b ? (this.x = f * d * e - c * g * h, this.y = c * g * e + f * d * h, this.z = c * d * h - f * g * e, this.w = c * d * e + f * g * h) : "YZX" === b ? (this.x = f * d * e + c * g * h, this.y = c * g * e + f * d * h, this.z = c * d * h - f * g * e, this.w = c * d * e - f * g * h) : "XZY" === b && (this.x = f * d * e - c * g * h, this.y = c * g * e - f * d * h, this.z = c * d * h + f * g * e, this.w = c * d * e + f * g * h);
        return this
    }, setFromAxisAngle: function (a, b) {
        var c = b / 2, d = Math.sin(c);
        this.x = a.x * d;
        this.y = a.y * d;
        this.z = a.z * d;
        this.w = Math.cos(c);
        return this
    }, setFromRotationMatrix: function (a) {
        var b = a.elements, c = b[0], a = b[4], d = b[8], e = b[1], f = b[5], g = b[9], h = b[2], i = b[6], b = b[10],
            k = c + f + b;
        0 < k ? (c = 0.5 / Math.sqrt(k + 1), this.w = 0.25 / c, this.x = (i - g) * c, this.y = (d - h) * c, this.z = (e - a) * c) : c > f && c > b ? (c = 2 * Math.sqrt(1 + c - f - b), this.w = (i - g) / c, this.x = 0.25 * c, this.y = (a + e) / c, this.z = (d + h) / c) : f > b ? (c = 2 * Math.sqrt(1 + f - c - b), this.w = (d - h) / c, this.x = (a + e) / c, this.y = 0.25 * c, this.z = (g + i) / c) : (c = 2 * Math.sqrt(1 + b - c - f), this.w = (e - a) / c, this.x = (d + h) / c, this.y = (g + i) / c, this.z = 0.25 * c);
        return this
    }, inverse: function () {
        this.conjugate().normalize();
        return this
    }, conjugate: function () {
        this.x *= -1;
        this.y *= -1;
        this.z *= -1;
        return this
    }, lengthSq: function () {
        return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w
    }, length: function () {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w)
    }, normalize: function () {
        var a = this.length();
        0 === a ? (this.z = this.y = this.x = 0, this.w = 1) : (a = 1 / a, this.x *= a, this.y *= a, this.z *= a, this.w *= a);
        return this
    }, multiply: function (a, b) {
        return void 0 !== b ? (console.warn("DEPRECATED: Quaternion's .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),
            this.multiplyQuaternions(a, b)) : this.multiplyQuaternions(this, a)
    }, multiplyQuaternions: function (a, b) {
        var c = a.x, d = a.y, e = a.z, f = a.w, g = b.x, h = b.y, i = b.z, k = b.w;
        this.x = c * k + f * g + d * i - e * h;
        this.y = d * k + f * h + e * g - c * i;
        this.z = e * k + f * i + c * h - d * g;
        this.w = f * k - c * g - d * h - e * i;
        return this
    }, multiplyVector3: function (a) {
        console.warn("DEPRECATED: Quaternion's .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.");
        return a.applyQuaternion(this)
    }, slerp: function (a, b) {
        var c = this.x, d = this.y, e = this.z,
            f = this.w, g = f * a.w + c * a.x + d * a.y + e * a.z;
        0 > g ? (this.w = -a.w, this.x = -a.x, this.y = -a.y, this.z = -a.z, g = -g) : this.copy(a);
        if (1 <= g) return this.w = f, this.x = c, this.y = d, this.z = e, this;
        var h = Math.acos(g), i = Math.sqrt(1 - g * g);
        if (0.001 > Math.abs(i)) return this.w = 0.5 * (f + this.w), this.x = 0.5 * (c + this.x), this.y = 0.5 * (d + this.y), this.z = 0.5 * (e + this.z), this;
        g = Math.sin((1 - b) * h) / i;
        h = Math.sin(b * h) / i;
        this.w = f * g + this.w * h;
        this.x = c * g + this.x * h;
        this.y = d * g + this.y * h;
        this.z = e * g + this.z * h;
        return this
    }, equals: function (a) {
        return a.x === this.x && a.y ===
            this.y && a.z === this.z && a.w === this.w
    }, clone: function () {
        return new THREE.Quaternion(this.x, this.y, this.z, this.w)
    }
};
THREE.Quaternion.slerp = function (a, b, c, d) {
    return c.copy(a).slerp(b, d)
};
THREE.Vector2 = function (a, b) {
    this.x = a || 0;
    this.y = b || 0
};
THREE.Vector2.prototype = {
    constructor: THREE.Vector2, set: function (a, b) {
        this.x = a;
        this.y = b;
        return this
    }, setX: function (a) {
        this.x = a;
        return this
    }, setY: function (a) {
        this.y = a;
        return this
    }, setComponent: function (a, b) {
        switch (a) {
            case 0:
                this.x = b;
                break;
            case 1:
                this.y = b;
                break;
            default:
                throw Error("index is out of range: " + a);
        }
    }, getComponent: function (a) {
        switch (a) {
            case 0:
                return this.x;
            case 1:
                return this.y;
            default:
                throw Error("index is out of range: " + a);
        }
    }, copy: function (a) {
        this.x = a.x;
        this.y = a.y;
        return this
    }, add: function (a,
                      b) {
        if (void 0 !== b) return console.warn("DEPRECATED: Vector2's .add() now only accepts one argument. Use .addVectors( a, b ) instead."), this.addVectors(a, b);
        this.x += a.x;
        this.y += a.y;
        return this
    }, addVectors: function (a, b) {
        this.x = a.x + b.x;
        this.y = a.y + b.y;
        return this
    }, addScalar: function (a) {
        this.x += a;
        this.y += a;
        return this
    }, sub: function (a, b) {
        if (void 0 !== b) return console.warn("DEPRECATED: Vector2's .sub() now only accepts one argument. Use .subVectors( a, b ) instead."), this.subVectors(a, b);
        this.x -= a.x;
        this.y -=
            a.y;
        return this
    }, subVectors: function (a, b) {
        this.x = a.x - b.x;
        this.y = a.y - b.y;
        return this
    }, multiplyScalar: function (a) {
        this.x *= a;
        this.y *= a;
        return this
    }, divideScalar: function (a) {
        0 !== a ? (this.x /= a, this.y /= a) : this.set(0, 0);
        return this
    }, min: function (a) {
        this.x > a.x && (this.x = a.x);
        this.y > a.y && (this.y = a.y);
        return this
    }, max: function (a) {
        this.x < a.x && (this.x = a.x);
        this.y < a.y && (this.y = a.y);
        return this
    }, clamp: function (a, b) {
        this.x < a.x ? this.x = a.x : this.x > b.x && (this.x = b.x);
        this.y < a.y ? this.y = a.y : this.y > b.y && (this.y = b.y);
        return this
    },
    negate: function () {
        return this.multiplyScalar(-1)
    }, dot: function (a) {
        return this.x * a.x + this.y * a.y
    }, lengthSq: function () {
        return this.x * this.x + this.y * this.y
    }, length: function () {
        return Math.sqrt(this.x * this.x + this.y * this.y)
    }, normalize: function () {
        return this.divideScalar(this.length())
    }, distanceTo: function (a) {
        return Math.sqrt(this.distanceToSquared(a))
    }, distanceToSquared: function (a) {
        var b = this.x - a.x, a = this.y - a.y;
        return b * b + a * a
    }, setLength: function (a) {
        var b = this.length();
        0 !== b && a !== b && this.multiplyScalar(a /
            b);
        return this
    }, lerp: function (a, b) {
        this.x += (a.x - this.x) * b;
        this.y += (a.y - this.y) * b;
        return this
    }, equals: function (a) {
        return a.x === this.x && a.y === this.y
    }, toArray: function () {
        return [this.x, this.y]
    }, clone: function () {
        return new THREE.Vector2(this.x, this.y)
    }
};
THREE.Vector3 = function (a, b, c) {
    this.x = a || 0;
    this.y = b || 0;
    this.z = c || 0
};
THREE.Vector3.prototype = {
    constructor: THREE.Vector3, set: function (a, b, c) {
        this.x = a;
        this.y = b;
        this.z = c;
        return this
    }, setX: function (a) {
        this.x = a;
        return this
    }, setY: function (a) {
        this.y = a;
        return this
    }, setZ: function (a) {
        this.z = a;
        return this
    }, setComponent: function (a, b) {
        switch (a) {
            case 0:
                this.x = b;
                break;
            case 1:
                this.y = b;
                break;
            case 2:
                this.z = b;
                break;
            default:
                throw Error("index is out of range: " + a);
        }
    }, getComponent: function (a) {
        switch (a) {
            case 0:
                return this.x;
            case 1:
                return this.y;
            case 2:
                return this.z;
            default:
                throw Error("index is out of range: " +
                    a);
        }
    }, copy: function (a) {
        this.x = a.x;
        this.y = a.y;
        this.z = a.z;
        return this
    }, add: function (a, b) {
        if (void 0 !== b) return console.warn("DEPRECATED: Vector3's .add() now only accepts one argument. Use .addVectors( a, b ) instead."), this.addVectors(a, b);
        this.x += a.x;
        this.y += a.y;
        this.z += a.z;
        return this
    }, addScalar: function (a) {
        this.x += a;
        this.y += a;
        this.z += a;
        return this
    }, addVectors: function (a, b) {
        this.x = a.x + b.x;
        this.y = a.y + b.y;
        this.z = a.z + b.z;
        return this
    }, sub: function (a, b) {
        if (void 0 !== b) return console.warn("DEPRECATED: Vector3's .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),
            this.subVectors(a, b);
        this.x -= a.x;
        this.y -= a.y;
        this.z -= a.z;
        return this
    }, subVectors: function (a, b) {
        this.x = a.x - b.x;
        this.y = a.y - b.y;
        this.z = a.z - b.z;
        return this
    }, multiply: function (a, b) {
        if (void 0 !== b) return console.warn("DEPRECATED: Vector3's .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."), this.multiplyVectors(a, b);
        this.x *= a.x;
        this.y *= a.y;
        this.z *= a.z;
        return this
    }, multiplyScalar: function (a) {
        this.x *= a;
        this.y *= a;
        this.z *= a;
        return this
    }, multiplyVectors: function (a, b) {
        this.x = a.x *
            b.x;
        this.y = a.y * b.y;
        this.z = a.z * b.z;
        return this
    }, applyMatrix3: function (a) {
        var b = this.x, c = this.y, d = this.z, a = a.elements;
        this.x = a[0] * b + a[3] * c + a[6] * d;
        this.y = a[1] * b + a[4] * c + a[7] * d;
        this.z = a[2] * b + a[5] * c + a[8] * d;
        return this
    }, applyMatrix4: function (a) {
        var b = this.x, c = this.y, d = this.z, a = a.elements;
        this.x = a[0] * b + a[4] * c + a[8] * d + a[12];
        this.y = a[1] * b + a[5] * c + a[9] * d + a[13];
        this.z = a[2] * b + a[6] * c + a[10] * d + a[14];
        return this
    }, applyProjection: function (a) {
        var b = this.x, c = this.y, d = this.z, a = a.elements, e = 1 / (a[3] * b + a[7] * c + a[11] * d + a[15]);
        this.x = (a[0] * b + a[4] * c + a[8] * d + a[12]) * e;
        this.y = (a[1] * b + a[5] * c + a[9] * d + a[13]) * e;
        this.z = (a[2] * b + a[6] * c + a[10] * d + a[14]) * e;
        return this
    }, applyQuaternion: function (a) {
        var b = this.x, c = this.y, d = this.z, e = a.x, f = a.y, g = a.z, a = a.w, h = a * b + f * d - g * c,
            i = a * c + g * b - e * d, k = a * d + e * c - f * b, b = -e * b - f * c - g * d;
        this.x = h * a + b * -e + i * -g - k * -f;
        this.y = i * a + b * -f + k * -e - h * -g;
        this.z = k * a + b * -g + h * -f - i * -e;
        return this
    }, transformDirection: function (a) {
        var b = this.x, c = this.y, d = this.z, a = a.elements;
        this.x = a[0] * b + a[4] * c + a[8] * d;
        this.y = a[1] * b + a[5] * c + a[9] * d;
        this.z = a[2] *
            b + a[6] * c + a[10] * d;
        this.normalize();
        return this
    }, divide: function (a) {
        this.x /= a.x;
        this.y /= a.y;
        this.z /= a.z;
        return this
    }, divideScalar: function (a) {
        0 !== a ? (this.x /= a, this.y /= a, this.z /= a) : this.z = this.y = this.x = 0;
        return this
    }, min: function (a) {
        this.x > a.x && (this.x = a.x);
        this.y > a.y && (this.y = a.y);
        this.z > a.z && (this.z = a.z);
        return this
    }, max: function (a) {
        this.x < a.x && (this.x = a.x);
        this.y < a.y && (this.y = a.y);
        this.z < a.z && (this.z = a.z);
        return this
    }, clamp: function (a, b) {
        this.x < a.x ? this.x = a.x : this.x > b.x && (this.x = b.x);
        this.y < a.y ? this.y =
            a.y : this.y > b.y && (this.y = b.y);
        this.z < a.z ? this.z = a.z : this.z > b.z && (this.z = b.z);
        return this
    }, negate: function () {
        return this.multiplyScalar(-1)
    }, dot: function (a) {
        return this.x * a.x + this.y * a.y + this.z * a.z
    }, lengthSq: function () {
        return this.x * this.x + this.y * this.y + this.z * this.z
    }, length: function () {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)
    }, lengthManhattan: function () {
        return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z)
    }, normalize: function () {
        return this.divideScalar(this.length())
    }, setLength: function (a) {
        var b =
            this.length();
        0 !== b && a !== b && this.multiplyScalar(a / b);
        return this
    }, lerp: function (a, b) {
        this.x += (a.x - this.x) * b;
        this.y += (a.y - this.y) * b;
        this.z += (a.z - this.z) * b;
        return this
    }, cross: function (a, b) {
        if (void 0 !== b) return console.warn("DEPRECATED: Vector3's .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."), this.crossVectors(a, b);
        var c = this.x, d = this.y, e = this.z;
        this.x = d * a.z - e * a.y;
        this.y = e * a.x - c * a.z;
        this.z = c * a.y - d * a.x;
        return this
    }, crossVectors: function (a, b) {
        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
    }, angleTo: function (a) {
        a = this.dot(a) / (this.length() * a.length());
        return Math.acos(THREE.Math.clamp(a, -1, 1))
    }, distanceTo: function (a) {
        return Math.sqrt(this.distanceToSquared(a))
    }, distanceToSquared: function (a) {
        var b = this.x - a.x, c = this.y - a.y, a = this.z - a.z;
        return b * b + c * c + a * a
    }, setEulerFromRotationMatrix: function (a, b) {
        function c(a) {
            return Math.min(Math.max(a, -1), 1)
        }

        var d = a.elements, e = d[0], f = d[4], g = d[8], h = d[1], i = d[5], k = d[9], l = d[2], m = d[6], d = d[10];
        void 0 === b || "XYZ" ===
        b ? (this.y = Math.asin(c(g)), 0.99999 > Math.abs(g) ? (this.x = Math.atan2(-k, d), this.z = Math.atan2(-f, e)) : (this.x = Math.atan2(m, i), this.z = 0)) : "YXZ" === b ? (this.x = Math.asin(-c(k)), 0.99999 > Math.abs(k) ? (this.y = Math.atan2(g, d), this.z = Math.atan2(h, i)) : (this.y = Math.atan2(-l, e), this.z = 0)) : "ZXY" === b ? (this.x = Math.asin(c(m)), 0.99999 > Math.abs(m) ? (this.y = Math.atan2(-l, d), this.z = Math.atan2(-f, i)) : (this.y = 0, this.z = Math.atan2(h, e))) : "ZYX" === b ? (this.y = Math.asin(-c(l)), 0.99999 > Math.abs(l) ? (this.x = Math.atan2(m, d), this.z = Math.atan2(h,
            e)) : (this.x = 0, this.z = Math.atan2(-f, i))) : "YZX" === b ? (this.z = Math.asin(c(h)), 0.99999 > Math.abs(h) ? (this.x = Math.atan2(-k, i), this.y = Math.atan2(-l, e)) : (this.x = 0, this.y = Math.atan2(g, d))) : "XZY" === b && (this.z = Math.asin(-c(f)), 0.99999 > Math.abs(f) ? (this.x = Math.atan2(m, i), this.y = Math.atan2(g, e)) : (this.x = Math.atan2(-k, d), this.y = 0));
        return this
    }, setEulerFromQuaternion: function (a, b) {
        function c(a) {
            return Math.min(Math.max(a, -1), 1)
        }

        var d = a.x * a.x, e = a.y * a.y, f = a.z * a.z, g = a.w * a.w;
        void 0 === b || "XYZ" === b ? (this.x = Math.atan2(2 *
            (a.x * a.w - a.y * a.z), g - d - e + f), this.y = Math.asin(c(2 * (a.x * a.z + a.y * a.w))), this.z = Math.atan2(2 * (a.z * a.w - a.x * a.y), g + d - e - f)) : "YXZ" === b ? (this.x = Math.asin(c(2 * (a.x * a.w - a.y * a.z))), this.y = Math.atan2(2 * (a.x * a.z + a.y * a.w), g - d - e + f), this.z = Math.atan2(2 * (a.x * a.y + a.z * a.w), g - d + e - f)) : "ZXY" === b ? (this.x = Math.asin(c(2 * (a.x * a.w + a.y * a.z))), this.y = Math.atan2(2 * (a.y * a.w - a.z * a.x), g - d - e + f), this.z = Math.atan2(2 * (a.z * a.w - a.x * a.y), g - d + e - f)) : "ZYX" === b ? (this.x = Math.atan2(2 * (a.x * a.w + a.z * a.y), g - d - e + f), this.y = Math.asin(c(2 * (a.y * a.w - a.x *
            a.z))), this.z = Math.atan2(2 * (a.x * a.y + a.z * a.w), g + d - e - f)) : "YZX" === b ? (this.x = Math.atan2(2 * (a.x * a.w - a.z * a.y), g - d + e - f), this.y = Math.atan2(2 * (a.y * a.w - a.x * a.z), g + d - e - f), this.z = Math.asin(c(2 * (a.x * a.y + a.z * a.w)))) : "XZY" === b && (this.x = Math.atan2(2 * (a.x * a.w + a.y * a.z), g - d + e - f), this.y = Math.atan2(2 * (a.x * a.z + a.y * a.w), g + d - e - f), this.z = Math.asin(c(2 * (a.z * a.w - a.x * a.y))));
        return this
    }, getPositionFromMatrix: function (a) {
        this.x = a.elements[12];
        this.y = a.elements[13];
        this.z = a.elements[14];
        return this
    }, getScaleFromMatrix: function (a) {
        var b =
                this.set(a.elements[0], a.elements[1], a.elements[2]).length(),
            c = this.set(a.elements[4], a.elements[5], a.elements[6]).length(),
            a = this.set(a.elements[8], a.elements[9], a.elements[10]).length();
        this.x = b;
        this.y = c;
        this.z = a;
        return this
    }, getColumnFromMatrix: function (a, b) {
        var c = 4 * a, d = b.elements;
        this.x = d[c];
        this.y = d[c + 1];
        this.z = d[c + 2];
        return this
    }, equals: function (a) {
        return a.x === this.x && a.y === this.y && a.z === this.z
    }, toArray: function () {
        return [this.x, this.y, this.z]
    }, clone: function () {
        return new THREE.Vector3(this.x,
            this.y, this.z)
    }
};
THREE.extend(THREE.Vector3.prototype, {
    applyEuler: function () {
        var a = new THREE.Quaternion;
        return function (b, c) {
            var d = a.setFromEuler(b, c);
            this.applyQuaternion(d);
            return this
        }
    }(), applyAxisAngle: function () {
        var a = new THREE.Quaternion;
        return function (b, c) {
            var d = a.setFromAxisAngle(b, c);
            this.applyQuaternion(d);
            return this
        }
    }(), projectOnVector: function () {
        var a = new THREE.Vector3;
        return function (b) {
            a.copy(b).normalize();
            b = this.dot(a);
            return this.copy(a).multiplyScalar(b)
        }
    }(), projectOnPlane: function () {
        var a = new THREE.Vector3;
        return function (b) {
            a.copy(this).projectOnVector(b);
            return this.sub(a)
        }
    }(), reflect: function () {
        var a = new THREE.Vector3;
        return function (b) {
            a.copy(this).projectOnVector(b).multiplyScalar(2);
            return this.subVectors(a, this)
        }
    }()
});
THREE.Vector4 = function (a, b, c, d) {
    this.x = a || 0;
    this.y = b || 0;
    this.z = c || 0;
    this.w = void 0 !== d ? d : 1
};
THREE.Vector4.prototype = {
    constructor: THREE.Vector4, set: function (a, b, c, d) {
        this.x = a;
        this.y = b;
        this.z = c;
        this.w = d;
        return this
    }, setX: function (a) {
        this.x = a;
        return this
    }, setY: function (a) {
        this.y = a;
        return this
    }, setZ: function (a) {
        this.z = a;
        return this
    }, setW: function (a) {
        this.w = a;
        return this
    }, setComponent: function (a, b) {
        switch (a) {
            case 0:
                this.x = b;
                break;
            case 1:
                this.y = b;
                break;
            case 2:
                this.z = b;
                break;
            case 3:
                this.w = b;
                break;
            default:
                throw Error("index is out of range: " + a);
        }
    }, getComponent: function (a) {
        switch (a) {
            case 0:
                return this.x;
            case 1:
                return this.y;
            case 2:
                return this.z;
            case 3:
                return this.w;
            default:
                throw Error("index is out of range: " + a);
        }
    }, copy: function (a) {
        this.x = a.x;
        this.y = a.y;
        this.z = a.z;
        this.w = void 0 !== a.w ? a.w : 1;
        return this
    }, add: function (a, b) {
        if (void 0 !== b) return console.warn("DEPRECATED: Vector4's .add() now only accepts one argument. Use .addVectors( a, b ) instead."), this.addVectors(a, b);
        this.x += a.x;
        this.y += a.y;
        this.z += a.z;
        this.w += a.w;
        return this
    }, addScalar: function (a) {
        this.x += a;
        this.y += a;
        this.z += a;
        this.w += a;
        return this
    },
    addVectors: function (a, b) {
        this.x = a.x + b.x;
        this.y = a.y + b.y;
        this.z = a.z + b.z;
        this.w = a.w + b.w;
        return this
    }, sub: function (a, b) {
        if (void 0 !== b) return console.warn("DEPRECATED: Vector4's .sub() now only accepts one argument. Use .subVectors( a, b ) instead."), this.subVectors(a, b);
        this.x -= a.x;
        this.y -= a.y;
        this.z -= a.z;
        this.w -= a.w;
        return this
    }, subVectors: function (a, b) {
        this.x = a.x - b.x;
        this.y = a.y - b.y;
        this.z = a.z - b.z;
        this.w = a.w - b.w;
        return this
    }, multiplyScalar: function (a) {
        this.x *= a;
        this.y *= a;
        this.z *= a;
        this.w *= a;
        return this
    },
    applyMatrix4: function (a) {
        var b = this.x, c = this.y, d = this.z, e = this.w, a = a.elements;
        this.x = a[0] * b + a[4] * c + a[8] * d + a[12] * e;
        this.y = a[1] * b + a[5] * c + a[9] * d + a[13] * e;
        this.z = a[2] * b + a[6] * c + a[10] * d + a[14] * e;
        this.w = a[3] * b + a[7] * c + a[11] * d + a[15] * e;
        return this
    }, divideScalar: function (a) {
        0 !== a ? (this.x /= a, this.y /= a, this.z /= a, this.w /= a) : (this.z = this.y = this.x = 0, this.w = 1);
        return this
    }, setAxisAngleFromQuaternion: function (a) {
        this.w = 2 * Math.acos(a.w);
        var b = Math.sqrt(1 - a.w * a.w);
        1E-4 > b ? (this.x = 1, this.z = this.y = 0) : (this.x = a.x / b, this.y =
            a.y / b, this.z = a.z / b);
        return this
    }, setAxisAngleFromRotationMatrix: function (a) {
        var b, c, d, a = a.elements, e = a[0];
        d = a[4];
        var f = a[8], g = a[1], h = a[5], i = a[9];
        c = a[2];
        b = a[6];
        var k = a[10];
        if (0.01 > Math.abs(d - g) && 0.01 > Math.abs(f - c) && 0.01 > Math.abs(i - b)) {
            if (0.1 > Math.abs(d + g) && 0.1 > Math.abs(f + c) && 0.1 > Math.abs(i + b) && 0.1 > Math.abs(e + h + k - 3)) return this.set(1, 0, 0, 0), this;
            a = Math.PI;
            e = (e + 1) / 2;
            h = (h + 1) / 2;
            k = (k + 1) / 2;
            d = (d + g) / 4;
            f = (f + c) / 4;
            i = (i + b) / 4;
            e > h && e > k ? 0.01 > e ? (b = 0, d = c = 0.707106781) : (b = Math.sqrt(e), c = d / b, d = f / b) : h > k ? 0.01 > h ? (b = 0.707106781,
                c = 0, d = 0.707106781) : (c = Math.sqrt(h), b = d / c, d = i / c) : 0.01 > k ? (c = b = 0.707106781, d = 0) : (d = Math.sqrt(k), b = f / d, c = i / d);
            this.set(b, c, d, a);
            return this
        }
        a = Math.sqrt((b - i) * (b - i) + (f - c) * (f - c) + (g - d) * (g - d));
        0.001 > Math.abs(a) && (a = 1);
        this.x = (b - i) / a;
        this.y = (f - c) / a;
        this.z = (g - d) / a;
        this.w = Math.acos((e + h + k - 1) / 2);
        return this
    }, min: function (a) {
        this.x > a.x && (this.x = a.x);
        this.y > a.y && (this.y = a.y);
        this.z > a.z && (this.z = a.z);
        this.w > a.w && (this.w = a.w);
        return this
    }, max: function (a) {
        this.x < a.x && (this.x = a.x);
        this.y < a.y && (this.y = a.y);
        this.z < a.z &&
        (this.z = a.z);
        this.w < a.w && (this.w = a.w);
        return this
    }, clamp: function (a, b) {
        this.x < a.x ? this.x = a.x : this.x > b.x && (this.x = b.x);
        this.y < a.y ? this.y = a.y : this.y > b.y && (this.y = b.y);
        this.z < a.z ? this.z = a.z : this.z > b.z && (this.z = b.z);
        this.w < a.w ? this.w = a.w : this.w > b.w && (this.w = b.w);
        return this
    }, negate: function () {
        return this.multiplyScalar(-1)
    }, dot: function (a) {
        return this.x * a.x + this.y * a.y + this.z * a.z + this.w * a.w
    }, lengthSq: function () {
        return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w
    }, length: function () {
        return Math.sqrt(this.x *
            this.x + this.y * this.y + this.z * this.z + this.w * this.w)
    }, lengthManhattan: function () {
        return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w)
    }, normalize: function () {
        return this.divideScalar(this.length())
    }, setLength: function (a) {
        var b = this.length();
        0 !== b && a !== b && this.multiplyScalar(a / b);
        return this
    }, lerp: function (a, b) {
        this.x += (a.x - this.x) * b;
        this.y += (a.y - this.y) * b;
        this.z += (a.z - this.z) * b;
        this.w += (a.w - this.w) * b;
        return this
    }, equals: function (a) {
        return a.x === this.x && a.y === this.y && a.z === this.z &&
            a.w === this.w
    }, toArray: function () {
        return [this.x, this.y, this.z, this.w]
    }, clone: function () {
        return new THREE.Vector4(this.x, this.y, this.z, this.w)
    }
};
THREE.Line3 = function (a, b) {
    this.start = void 0 !== a ? a : new THREE.Vector3;
    this.end = void 0 !== b ? b : new THREE.Vector3
};
THREE.Line3.prototype = {
    constructor: THREE.Line3, set: function (a, b) {
        this.start.copy(a);
        this.end.copy(b);
        return this
    }, copy: function (a) {
        this.start.copy(a.start);
        this.end.copy(a.end);
        return this
    }, center: function (a) {
        return (a || new THREE.Vector3).addVectors(this.start, this.end).multiplyScalar(0.5)
    }, delta: function (a) {
        return (a || new THREE.Vector3).subVectors(this.end, this.start)
    }, distanceSq: function () {
        return this.start.distanceToSquared(this.end)
    }, distance: function () {
        return this.start.distanceTo(this.end)
    }, at: function (a,
                     b) {
        var c = b || new THREE.Vector3;
        return this.delta(c).multiplyScalar(a).add(this.start)
    }, closestPointToPointParameter: function () {
        var a = new THREE.Vector3, b = new THREE.Vector3;
        return function (c, d) {
            a.subVectors(c, this.start);
            b.subVectors(this.end, this.start);
            var e = b.dot(b), e = b.dot(a) / e;
            d && (e = THREE.Math.clamp(e, 0, 1));
            return e
        }
    }(), closestPointToPoint: function (a, b, c) {
        a = this.closestPointToPointParameter(a, b);
        c = c || new THREE.Vector3;
        return this.delta(c).multiplyScalar(a).add(this.start)
    }, applyMatrix4: function (a) {
        this.start.applyMatrix4(a);
        this.end.applyMatrix4(a);
        return this
    }, equals: function (a) {
        return a.start.equals(this.start) && a.end.equals(this.end)
    }, clone: function () {
        return (new THREE.Line3).copy(this)
    }
};
THREE.Box2 = function (a, b) {
    this.min = void 0 !== a ? a : new THREE.Vector2(Infinity, Infinity);
    this.max = void 0 !== b ? b : new THREE.Vector2(-Infinity, -Infinity)
};
THREE.Box2.prototype = {
    constructor: THREE.Box2, set: function (a, b) {
        this.min.copy(a);
        this.max.copy(b);
        return this
    }, setFromPoints: function (a) {
        if (0 < a.length) {
            var b = a[0];
            this.min.copy(b);
            this.max.copy(b);
            for (var c = 1, d = a.length; c < d; c++) b = a[c], b.x < this.min.x ? this.min.x = b.x : b.x > this.max.x && (this.max.x = b.x), b.y < this.min.y ? this.min.y = b.y : b.y > this.max.y && (this.max.y = b.y)
        } else this.makeEmpty();
        return this
    }, setFromCenterAndSize: function () {
        var a = new THREE.Vector2;
        return function (b, c) {
            var d = a.copy(c).multiplyScalar(0.5);
            this.min.copy(b).sub(d);
            this.max.copy(b).add(d);
            return this
        }
    }(), copy: function (a) {
        this.min.copy(a.min);
        this.max.copy(a.max);
        return this
    }, makeEmpty: function () {
        this.min.x = this.min.y = Infinity;
        this.max.x = this.max.y = -Infinity;
        return this
    }, empty: function () {
        return this.max.x < this.min.x || this.max.y < this.min.y
    }, center: function (a) {
        return (a || new THREE.Vector2).addVectors(this.min, this.max).multiplyScalar(0.5)
    }, size: function (a) {
        return (a || new THREE.Vector2).subVectors(this.max, this.min)
    }, expandByPoint: function (a) {
        this.min.min(a);
        this.max.max(a);
        return this
    }, expandByVector: function (a) {
        this.min.sub(a);
        this.max.add(a);
        return this
    }, expandByScalar: function (a) {
        this.min.addScalar(-a);
        this.max.addScalar(a);
        return this
    }, containsPoint: function (a) {
        return a.x < this.min.x || a.x > this.max.x || a.y < this.min.y || a.y > this.max.y ? !1 : !0
    }, containsBox: function (a) {
        return this.min.x <= a.min.x && a.max.x <= this.max.x && this.min.y <= a.min.y && a.max.y <= this.max.y ? !0 : !1
    }, getParameter: function (a) {
        return new THREE.Vector2((a.x - this.min.x) / (this.max.x - this.min.x),
            (a.y - this.min.y) / (this.max.y - this.min.y))
    }, isIntersectionBox: function (a) {
        return a.max.x < this.min.x || a.min.x > this.max.x || a.max.y < this.min.y || a.min.y > this.max.y ? !1 : !0
    }, clampPoint: function (a, b) {
        return (b || new THREE.Vector2).copy(a).clamp(this.min, this.max)
    }, distanceToPoint: function () {
        var a = new THREE.Vector2;
        return function (b) {
            return a.copy(b).clamp(this.min, this.max).sub(b).length()
        }
    }(), intersect: function (a) {
        this.min.max(a.min);
        this.max.min(a.max);
        return this
    }, union: function (a) {
        this.min.min(a.min);
        this.max.max(a.max);
        return this
    }, translate: function (a) {
        this.min.add(a);
        this.max.add(a);
        return this
    }, equals: function (a) {
        return a.min.equals(this.min) && a.max.equals(this.max)
    }, clone: function () {
        return (new THREE.Box2).copy(this)
    }
};
THREE.Box3 = function (a, b) {
    this.min = void 0 !== a ? a : new THREE.Vector3(Infinity, Infinity, Infinity);
    this.max = void 0 !== b ? b : new THREE.Vector3(-Infinity, -Infinity, -Infinity)
};
THREE.Box3.prototype = {
    constructor: THREE.Box3, set: function (a, b) {
        this.min.copy(a);
        this.max.copy(b);
        return this
    }, setFromPoints: function (a) {
        if (0 < a.length) {
            var b = a[0];
            this.min.copy(b);
            this.max.copy(b);
            for (var c = 1, d = a.length; c < d; c++) b = a[c], b.x < this.min.x ? this.min.x = b.x : b.x > this.max.x && (this.max.x = b.x), b.y < this.min.y ? this.min.y = b.y : b.y > this.max.y && (this.max.y = b.y), b.z < this.min.z ? this.min.z = b.z : b.z > this.max.z && (this.max.z = b.z)
        } else this.makeEmpty();
        return this
    }, setFromCenterAndSize: function () {
        var a = new THREE.Vector3;
        return function (b, c) {
            var d = a.copy(c).multiplyScalar(0.5);
            this.min.copy(b).sub(d);
            this.max.copy(b).add(d);
            return this
        }
    }(), copy: function (a) {
        this.min.copy(a.min);
        this.max.copy(a.max);
        return this
    }, makeEmpty: function () {
        this.min.x = this.min.y = this.min.z = Infinity;
        this.max.x = this.max.y = this.max.z = -Infinity;
        return this
    }, empty: function () {
        return this.max.x < this.min.x || this.max.y < this.min.y || this.max.z < this.min.z
    }, center: function (a) {
        return (a || new THREE.Vector3).addVectors(this.min, this.max).multiplyScalar(0.5)
    },
    size: function (a) {
        return (a || new THREE.Vector3).subVectors(this.max, this.min)
    }, expandByPoint: function (a) {
        this.min.min(a);
        this.max.max(a);
        return this
    }, expandByVector: function (a) {
        this.min.sub(a);
        this.max.add(a);
        return this
    }, expandByScalar: function (a) {
        this.min.addScalar(-a);
        this.max.addScalar(a);
        return this
    }, containsPoint: function (a) {
        return a.x < this.min.x || a.x > this.max.x || a.y < this.min.y || a.y > this.max.y || a.z < this.min.z || a.z > this.max.z ? !1 : !0
    }, containsBox: function (a) {
        return this.min.x <= a.min.x && a.max.x <=
        this.max.x && this.min.y <= a.min.y && a.max.y <= this.max.y && this.min.z <= a.min.z && a.max.z <= this.max.z ? !0 : !1
    }, getParameter: function (a) {
        return new THREE.Vector3((a.x - this.min.x) / (this.max.x - this.min.x), (a.y - this.min.y) / (this.max.y - this.min.y), (a.z - this.min.z) / (this.max.z - this.min.z))
    }, isIntersectionBox: function (a) {
        return a.max.x < this.min.x || a.min.x > this.max.x || a.max.y < this.min.y || a.min.y > this.max.y || a.max.z < this.min.z || a.min.z > this.max.z ? !1 : !0
    }, clampPoint: function (a, b) {
        return (b || new THREE.Vector3).copy(a).clamp(this.min,
            this.max)
    }, distanceToPoint: function () {
        var a = new THREE.Vector3;
        return function (b) {
            return a.copy(b).clamp(this.min, this.max).sub(b).length()
        }
    }(), getBoundingSphere: function () {
        var a = new THREE.Vector3;
        return function (b) {
            b = b || new THREE.Sphere;
            b.center = this.center();
            b.radius = 0.5 * this.size(a).length();
            return b
        }
    }(), intersect: function (a) {
        this.min.max(a.min);
        this.max.min(a.max);
        return this
    }, union: function (a) {
        this.min.min(a.min);
        this.max.max(a.max);
        return this
    }, applyMatrix4: function () {
        var a = [new THREE.Vector3,
            new THREE.Vector3, new THREE.Vector3, new THREE.Vector3, new THREE.Vector3, new THREE.Vector3, new THREE.Vector3, new THREE.Vector3];
        return function (b) {
            a[0].set(this.min.x, this.min.y, this.min.z).applyMatrix4(b);
            a[1].set(this.min.x, this.min.y, this.max.z).applyMatrix4(b);
            a[2].set(this.min.x, this.max.y, this.min.z).applyMatrix4(b);
            a[3].set(this.min.x, this.max.y, this.max.z).applyMatrix4(b);
            a[4].set(this.max.x, this.min.y, this.min.z).applyMatrix4(b);
            a[5].set(this.max.x, this.min.y, this.max.z).applyMatrix4(b);
            a[6].set(this.max.x,
                this.max.y, this.min.z).applyMatrix4(b);
            a[7].set(this.max.x, this.max.y, this.max.z).applyMatrix4(b);
            this.makeEmpty();
            this.setFromPoints(a);
            return this
        }
    }(), translate: function (a) {
        this.min.add(a);
        this.max.add(a);
        return this
    }, equals: function (a) {
        return a.min.equals(this.min) && a.max.equals(this.max)
    }, clone: function () {
        return (new THREE.Box3).copy(this)
    }
};
THREE.Matrix3 = function (a, b, c, d, e, f, g, h, i) {
    this.elements = new Float32Array(9);
    this.set(void 0 !== a ? a : 1, b || 0, c || 0, d || 0, void 0 !== e ? e : 1, f || 0, g || 0, h || 0, void 0 !== i ? i : 1)
};
THREE.Matrix3.prototype = {
    constructor: THREE.Matrix3, set: function (a, b, c, d, e, f, g, h, i) {
        var k = this.elements;
        k[0] = a;
        k[3] = b;
        k[6] = c;
        k[1] = d;
        k[4] = e;
        k[7] = f;
        k[2] = g;
        k[5] = h;
        k[8] = i;
        return this
    }, identity: function () {
        this.set(1, 0, 0, 0, 1, 0, 0, 0, 1);
        return this
    }, copy: function (a) {
        a = a.elements;
        this.set(a[0], a[3], a[6], a[1], a[4], a[7], a[2], a[5], a[8]);
        return this
    }, multiplyVector3: function (a) {
        console.warn("DEPRECATED: Matrix3's .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.");
        return a.applyMatrix3(this)
    },
    multiplyVector3Array: function () {
        var a = new THREE.Vector3;
        return function (b) {
            for (var c = 0, d = b.length; c < d; c += 3) a.x = b[c], a.y = b[c + 1], a.z = b[c + 2], a.applyMatrix3(this), b[c] = a.x, b[c + 1] = a.y, b[c + 2] = a.z;
            return b
        }
    }(), multiplyScalar: function (a) {
        var b = this.elements;
        b[0] *= a;
        b[3] *= a;
        b[6] *= a;
        b[1] *= a;
        b[4] *= a;
        b[7] *= a;
        b[2] *= a;
        b[5] *= a;
        b[8] *= a;
        return this
    }, determinant: function () {
        var a = this.elements, b = a[0], c = a[1], d = a[2], e = a[3], f = a[4], g = a[5], h = a[6], i = a[7], a = a[8];
        return b * f * a - b * g * i - c * e * a + c * g * h + d * e * i - d * f * h
    }, getInverse: function (a,
                             b) {
        var c = a.elements, d = this.elements;
        d[0] = c[10] * c[5] - c[6] * c[9];
        d[1] = -c[10] * c[1] + c[2] * c[9];
        d[2] = c[6] * c[1] - c[2] * c[5];
        d[3] = -c[10] * c[4] + c[6] * c[8];
        d[4] = c[10] * c[0] - c[2] * c[8];
        d[5] = -c[6] * c[0] + c[2] * c[4];
        d[6] = c[9] * c[4] - c[5] * c[8];
        d[7] = -c[9] * c[0] + c[1] * c[8];
        d[8] = c[5] * c[0] - c[1] * c[4];
        c = c[0] * d[0] + c[1] * d[3] + c[2] * d[6];
        if (0 === c) {
            if (b) throw Error("Matrix3.getInverse(): can't invert matrix, determinant is 0");
            console.warn("Matrix3.getInverse(): can't invert matrix, determinant is 0");
            this.identity();
            return this
        }
        this.multiplyScalar(1 /
            c);
        return this
    }, transpose: function () {
        var a, b = this.elements;
        a = b[1];
        b[1] = b[3];
        b[3] = a;
        a = b[2];
        b[2] = b[6];
        b[6] = a;
        a = b[5];
        b[5] = b[7];
        b[7] = a;
        return this
    }, getNormalMatrix: function (a) {
        this.getInverse(a).transpose();
        return this
    }, transposeIntoArray: function (a) {
        var b = this.elements;
        a[0] = b[0];
        a[1] = b[3];
        a[2] = b[6];
        a[3] = b[1];
        a[4] = b[4];
        a[5] = b[7];
        a[6] = b[2];
        a[7] = b[5];
        a[8] = b[8];
        return this
    }, clone: function () {
        var a = this.elements;
        return new THREE.Matrix3(a[0], a[3], a[6], a[1], a[4], a[7], a[2], a[5], a[8])
    }
};
THREE.Matrix4 = function (a, b, c, d, e, f, g, h, i, k, l, m, p, s, r, n) {
    var q = this.elements = new Float32Array(16);
    q[0] = void 0 !== a ? a : 1;
    q[4] = b || 0;
    q[8] = c || 0;
    q[12] = d || 0;
    q[1] = e || 0;
    q[5] = void 0 !== f ? f : 1;
    q[9] = g || 0;
    q[13] = h || 0;
    q[2] = i || 0;
    q[6] = k || 0;
    q[10] = void 0 !== l ? l : 1;
    q[14] = m || 0;
    q[3] = p || 0;
    q[7] = s || 0;
    q[11] = r || 0;
    q[15] = void 0 !== n ? n : 1
};
THREE.Matrix4.prototype = {
    constructor: THREE.Matrix4, set: function (a, b, c, d, e, f, g, h, i, k, l, m, p, s, r, n) {
        var q = this.elements;
        q[0] = a;
        q[4] = b;
        q[8] = c;
        q[12] = d;
        q[1] = e;
        q[5] = f;
        q[9] = g;
        q[13] = h;
        q[2] = i;
        q[6] = k;
        q[10] = l;
        q[14] = m;
        q[3] = p;
        q[7] = s;
        q[11] = r;
        q[15] = n;
        return this
    }, identity: function () {
        this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
        return this
    }, copy: function (a) {
        a = a.elements;
        this.set(a[0], a[4], a[8], a[12], a[1], a[5], a[9], a[13], a[2], a[6], a[10], a[14], a[3], a[7], a[11], a[15]);
        return this
    }, setRotationFromEuler: function (a, b) {
        var c =
                this.elements, d = a.x, e = a.y, f = a.z, g = Math.cos(d), d = Math.sin(d), h = Math.cos(e),
            e = Math.sin(e), i = Math.cos(f), f = Math.sin(f);
        if (void 0 === b || "XYZ" === b) {
            var k = g * i, l = g * f, m = d * i, p = d * f;
            c[0] = h * i;
            c[4] = -h * f;
            c[8] = e;
            c[1] = l + m * e;
            c[5] = k - p * e;
            c[9] = -d * h;
            c[2] = p - k * e;
            c[6] = m + l * e;
            c[10] = g * h
        } else "YXZ" === b ? (k = h * i, l = h * f, m = e * i, p = e * f, c[0] = k + p * d, c[4] = m * d - l, c[8] = g * e, c[1] = g * f, c[5] = g * i, c[9] = -d, c[2] = l * d - m, c[6] = p + k * d, c[10] = g * h) : "ZXY" === b ? (k = h * i, l = h * f, m = e * i, p = e * f, c[0] = k - p * d, c[4] = -g * f, c[8] = m + l * d, c[1] = l + m * d, c[5] = g * i, c[9] = p - k * d, c[2] = -g * e, c[6] =
            d, c[10] = g * h) : "ZYX" === b ? (k = g * i, l = g * f, m = d * i, p = d * f, c[0] = h * i, c[4] = m * e - l, c[8] = k * e + p, c[1] = h * f, c[5] = p * e + k, c[9] = l * e - m, c[2] = -e, c[6] = d * h, c[10] = g * h) : "YZX" === b ? (k = g * h, l = g * e, m = d * h, p = d * e, c[0] = h * i, c[4] = p - k * f, c[8] = m * f + l, c[1] = f, c[5] = g * i, c[9] = -d * i, c[2] = -e * i, c[6] = l * f + m, c[10] = k - p * f) : "XZY" === b && (k = g * h, l = g * e, m = d * h, p = d * e, c[0] = h * i, c[4] = -f, c[8] = e * i, c[1] = k * f + p, c[5] = g * i, c[9] = l * f - m, c[2] = m * f - l, c[6] = d * i, c[10] = p * f + k);
        return this
    }, setRotationFromQuaternion: function (a) {
        var b = this.elements, c = a.x, d = a.y, e = a.z, f = a.w, g = c + c, h = d + d, i = e +
            e, a = c * g, k = c * h, c = c * i, l = d * h, d = d * i, e = e * i, g = f * g, h = f * h, f = f * i;
        b[0] = 1 - (l + e);
        b[4] = k - f;
        b[8] = c + h;
        b[1] = k + f;
        b[5] = 1 - (a + e);
        b[9] = d - g;
        b[2] = c - h;
        b[6] = d + g;
        b[10] = 1 - (a + l);
        return this
    }, lookAt: function () {
        var a = new THREE.Vector3, b = new THREE.Vector3, c = new THREE.Vector3;
        return function (d, e, f) {
            var g = this.elements;
            c.subVectors(d, e).normalize();
            0 === c.length() && (c.z = 1);
            a.crossVectors(f, c).normalize();
            0 === a.length() && (c.x += 1E-4, a.crossVectors(f, c).normalize());
            b.crossVectors(c, a);
            g[0] = a.x;
            g[4] = b.x;
            g[8] = c.x;
            g[1] = a.y;
            g[5] = b.y;
            g[9] =
                c.y;
            g[2] = a.z;
            g[6] = b.z;
            g[10] = c.z;
            return this
        }
    }(), multiply: function (a, b) {
        return void 0 !== b ? (console.warn("DEPRECATED: Matrix4's .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."), this.multiplyMatrices(a, b)) : this.multiplyMatrices(this, a)
    }, multiplyMatrices: function (a, b) {
        var c = a.elements, d = b.elements, e = this.elements, f = c[0], g = c[4], h = c[8], i = c[12], k = c[1],
            l = c[5], m = c[9], p = c[13], s = c[2], r = c[6], n = c[10], q = c[14], z = c[3], t = c[7], x = c[11],
            c = c[15], u = d[0], B = d[4], G = d[8], D = d[12], w = d[1], I =
                d[5], J = d[9], E = d[13], Z = d[2], A = d[6], S = d[10], F = d[14], H = d[3], K = d[7], N = d[11],
            d = d[15];
        e[0] = f * u + g * w + h * Z + i * H;
        e[4] = f * B + g * I + h * A + i * K;
        e[8] = f * G + g * J + h * S + i * N;
        e[12] = f * D + g * E + h * F + i * d;
        e[1] = k * u + l * w + m * Z + p * H;
        e[5] = k * B + l * I + m * A + p * K;
        e[9] = k * G + l * J + m * S + p * N;
        e[13] = k * D + l * E + m * F + p * d;
        e[2] = s * u + r * w + n * Z + q * H;
        e[6] = s * B + r * I + n * A + q * K;
        e[10] = s * G + r * J + n * S + q * N;
        e[14] = s * D + r * E + n * F + q * d;
        e[3] = z * u + t * w + x * Z + c * H;
        e[7] = z * B + t * I + x * A + c * K;
        e[11] = z * G + t * J + x * S + c * N;
        e[15] = z * D + t * E + x * F + c * d;
        return this
    }, multiplyToArray: function (a, b, c) {
        var d = this.elements;
        this.multiplyMatrices(a,
            b);
        c[0] = d[0];
        c[1] = d[1];
        c[2] = d[2];
        c[3] = d[3];
        c[4] = d[4];
        c[5] = d[5];
        c[6] = d[6];
        c[7] = d[7];
        c[8] = d[8];
        c[9] = d[9];
        c[10] = d[10];
        c[11] = d[11];
        c[12] = d[12];
        c[13] = d[13];
        c[14] = d[14];
        c[15] = d[15];
        return this
    }, multiplyScalar: function (a) {
        var b = this.elements;
        b[0] *= a;
        b[4] *= a;
        b[8] *= a;
        b[12] *= a;
        b[1] *= a;
        b[5] *= a;
        b[9] *= a;
        b[13] *= a;
        b[2] *= a;
        b[6] *= a;
        b[10] *= a;
        b[14] *= a;
        b[3] *= a;
        b[7] *= a;
        b[11] *= a;
        b[15] *= a;
        return this
    }, multiplyVector3: function (a) {
        console.warn("DEPRECATED: Matrix4's .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.");
        return a.applyProjection(this)
    }, multiplyVector4: function (a) {
        console.warn("DEPRECATED: Matrix4's .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.");
        return a.applyMatrix4(this)
    }, multiplyVector3Array: function () {
        var a = new THREE.Vector3;
        return function (b) {
            for (var c = 0, d = b.length; c < d; c += 3) a.x = b[c], a.y = b[c + 1], a.z = b[c + 2], a.applyProjection(this), b[c] = a.x, b[c + 1] = a.y, b[c + 2] = a.z;
            return b
        }
    }(), rotateAxis: function (a) {
        console.warn("DEPRECATED: Matrix4's .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.");
        a.transformDirection(this)
    }, crossVector: function (a) {
        var b = this.elements, c = new THREE.Vector4;
        c.x = b[0] * a.x + b[4] * a.y + b[8] * a.z + b[12] * a.w;
        c.y = b[1] * a.x + b[5] * a.y + b[9] * a.z + b[13] * a.w;
        c.z = b[2] * a.x + b[6] * a.y + b[10] * a.z + b[14] * a.w;
        c.w = a.w ? b[3] * a.x + b[7] * a.y + b[11] * a.z + b[15] * a.w : 1;
        return c
    }, determinant: function () {
        var a = this.elements, b = a[0], c = a[4], d = a[8], e = a[12], f = a[1], g = a[5], h = a[9], i = a[13],
            k = a[2], l = a[6], m = a[10], p = a[14];
        return a[3] * (+e * h * l - d * i * l - e * g * m + c * i * m + d * g * p - c * h * p) + a[7] * (+b * h * p - b * i * m + e * f * m - d * f * p + d * i * k - e * h * k) + a[11] *
            (+b * i * l - b * g * p - e * f * l + c * f * p + e * g * k - c * i * k) + a[15] * (-d * g * k - b * h * l + b * g * m + d * f * l - c * f * m + c * h * k)
    }, transpose: function () {
        var a = this.elements, b;
        b = a[1];
        a[1] = a[4];
        a[4] = b;
        b = a[2];
        a[2] = a[8];
        a[8] = b;
        b = a[6];
        a[6] = a[9];
        a[9] = b;
        b = a[3];
        a[3] = a[12];
        a[12] = b;
        b = a[7];
        a[7] = a[13];
        a[13] = b;
        b = a[11];
        a[11] = a[14];
        a[14] = b;
        return this
    }, flattenToArray: function (a) {
        var b = this.elements;
        a[0] = b[0];
        a[1] = b[1];
        a[2] = b[2];
        a[3] = b[3];
        a[4] = b[4];
        a[5] = b[5];
        a[6] = b[6];
        a[7] = b[7];
        a[8] = b[8];
        a[9] = b[9];
        a[10] = b[10];
        a[11] = b[11];
        a[12] = b[12];
        a[13] = b[13];
        a[14] = b[14];
        a[15] =
            b[15];
        return a
    }, flattenToArrayOffset: function (a, b) {
        var c = this.elements;
        a[b] = c[0];
        a[b + 1] = c[1];
        a[b + 2] = c[2];
        a[b + 3] = c[3];
        a[b + 4] = c[4];
        a[b + 5] = c[5];
        a[b + 6] = c[6];
        a[b + 7] = c[7];
        a[b + 8] = c[8];
        a[b + 9] = c[9];
        a[b + 10] = c[10];
        a[b + 11] = c[11];
        a[b + 12] = c[12];
        a[b + 13] = c[13];
        a[b + 14] = c[14];
        a[b + 15] = c[15];
        return a
    }, getPosition: function () {
        var a = new THREE.Vector3;
        return function () {
            console.warn("DEPRECATED: Matrix4's .getPosition() has been removed. Use Vector3.getPositionFromMatrix( matrix ) instead.");
            var b = this.elements;
            return a.set(b[12],
                b[13], b[14])
        }
    }(), setPosition: function (a) {
        var b = this.elements;
        b[12] = a.x;
        b[13] = a.y;
        b[14] = a.z;
        return this
    }, getInverse: function (a, b) {
        var c = this.elements, d = a.elements, e = d[0], f = d[4], g = d[8], h = d[12], i = d[1], k = d[5], l = d[9],
            m = d[13], p = d[2], s = d[6], r = d[10], n = d[14], q = d[3], z = d[7], t = d[11], x = d[15];
        c[0] = l * n * z - m * r * z + m * s * t - k * n * t - l * s * x + k * r * x;
        c[4] = h * r * z - g * n * z - h * s * t + f * n * t + g * s * x - f * r * x;
        c[8] = g * m * z - h * l * z + h * k * t - f * m * t - g * k * x + f * l * x;
        c[12] = h * l * s - g * m * s - h * k * r + f * m * r + g * k * n - f * l * n;
        c[1] = m * r * q - l * n * q - m * p * t + i * n * t + l * p * x - i * r * x;
        c[5] = g * n * q - h * r *
            q + h * p * t - e * n * t - g * p * x + e * r * x;
        c[9] = h * l * q - g * m * q - h * i * t + e * m * t + g * i * x - e * l * x;
        c[13] = g * m * p - h * l * p + h * i * r - e * m * r - g * i * n + e * l * n;
        c[2] = k * n * q - m * s * q + m * p * z - i * n * z - k * p * x + i * s * x;
        c[6] = h * s * q - f * n * q - h * p * z + e * n * z + f * p * x - e * s * x;
        c[10] = f * m * q - h * k * q + h * i * z - e * m * z - f * i * x + e * k * x;
        c[14] = h * k * p - f * m * p - h * i * s + e * m * s + f * i * n - e * k * n;
        c[3] = l * s * q - k * r * q - l * p * z + i * r * z + k * p * t - i * s * t;
        c[7] = f * r * q - g * s * q + g * p * z - e * r * z - f * p * t + e * s * t;
        c[11] = g * k * q - f * l * q - g * i * z + e * l * z + f * i * t - e * k * t;
        c[15] = f * l * p - g * k * p + g * i * s - e * l * s - f * i * r + e * k * r;
        c = d[0] * c[0] + d[1] * c[4] + d[2] * c[8] + d[3] * c[12];
        if (0 == c) {
            if (b) throw Error("Matrix4.getInverse(): can't invert matrix, determinant is 0");
            console.warn("Matrix4.getInverse(): can't invert matrix, determinant is 0");
            this.identity();
            return this
        }
        this.multiplyScalar(1 / c);
        return this
    }, extractPosition: function (a) {
        var b = this.elements, a = a.elements;
        b[12] = a[12];
        b[13] = a[13];
        b[14] = a[14];
        return this
    }, extractRotation: function () {
        var a = new THREE.Vector3;
        return function (b) {
            var c = this.elements, b = b.elements, d = 1 / a.set(b[0], b[1], b[2]).length(),
                e = 1 / a.set(b[4], b[5], b[6]).length(), f = 1 / a.set(b[8], b[9], b[10]).length();
            c[0] = b[0] * d;
            c[1] = b[1] * d;
            c[2] = b[2] * d;
            c[4] =
                b[4] * e;
            c[5] = b[5] * e;
            c[6] = b[6] * e;
            c[8] = b[8] * f;
            c[9] = b[9] * f;
            c[10] = b[10] * f;
            return this
        }
    }(), translate: function (a) {
        var b = this.elements, c = a.x, d = a.y, a = a.z;
        b[12] = b[0] * c + b[4] * d + b[8] * a + b[12];
        b[13] = b[1] * c + b[5] * d + b[9] * a + b[13];
        b[14] = b[2] * c + b[6] * d + b[10] * a + b[14];
        b[15] = b[3] * c + b[7] * d + b[11] * a + b[15];
        return this
    }, rotateX: function (a) {
        var b = this.elements, c = b[4], d = b[5], e = b[6], f = b[7], g = b[8], h = b[9], i = b[10], k = b[11],
            l = Math.cos(a), a = Math.sin(a);
        b[4] = l * c + a * g;
        b[5] = l * d + a * h;
        b[6] = l * e + a * i;
        b[7] = l * f + a * k;
        b[8] = l * g - a * c;
        b[9] = l * h - a * d;
        b[10] =
            l * i - a * e;
        b[11] = l * k - a * f;
        return this
    }, rotateY: function (a) {
        var b = this.elements, c = b[0], d = b[1], e = b[2], f = b[3], g = b[8], h = b[9], i = b[10], k = b[11],
            l = Math.cos(a), a = Math.sin(a);
        b[0] = l * c - a * g;
        b[1] = l * d - a * h;
        b[2] = l * e - a * i;
        b[3] = l * f - a * k;
        b[8] = l * g + a * c;
        b[9] = l * h + a * d;
        b[10] = l * i + a * e;
        b[11] = l * k + a * f;
        return this
    }, rotateZ: function (a) {
        var b = this.elements, c = b[0], d = b[1], e = b[2], f = b[3], g = b[4], h = b[5], i = b[6], k = b[7],
            l = Math.cos(a), a = Math.sin(a);
        b[0] = l * c + a * g;
        b[1] = l * d + a * h;
        b[2] = l * e + a * i;
        b[3] = l * f + a * k;
        b[4] = l * g - a * c;
        b[5] = l * h - a * d;
        b[6] = l * i - a * e;
        b[7] = l *
            k - a * f;
        return this
    }, rotateByAxis: function (a, b) {
        var c = this.elements;
        if (1 === a.x && 0 === a.y && 0 === a.z) return this.rotateX(b);
        if (0 === a.x && 1 === a.y && 0 === a.z) return this.rotateY(b);
        if (0 === a.x && 0 === a.y && 1 === a.z) return this.rotateZ(b);
        var d = a.x, e = a.y, f = a.z, g = Math.sqrt(d * d + e * e + f * f), d = d / g, e = e / g, f = f / g, g = d * d,
            h = e * e, i = f * f, k = Math.cos(b), l = Math.sin(b), m = 1 - k, p = d * e * m, s = d * f * m,
            m = e * f * m, d = d * l, r = e * l, l = f * l, f = g + (1 - g) * k, g = p + l, e = s - r, p = p - l,
            h = h + (1 - h) * k, l = m + d, s = s + r, m = m - d, i = i + (1 - i) * k, k = c[0], d = c[1], r = c[2],
            n = c[3], q = c[4], z = c[5], t = c[6],
            x = c[7], u = c[8], B = c[9], G = c[10], D = c[11];
        c[0] = f * k + g * q + e * u;
        c[1] = f * d + g * z + e * B;
        c[2] = f * r + g * t + e * G;
        c[3] = f * n + g * x + e * D;
        c[4] = p * k + h * q + l * u;
        c[5] = p * d + h * z + l * B;
        c[6] = p * r + h * t + l * G;
        c[7] = p * n + h * x + l * D;
        c[8] = s * k + m * q + i * u;
        c[9] = s * d + m * z + i * B;
        c[10] = s * r + m * t + i * G;
        c[11] = s * n + m * x + i * D;
        return this
    }, scale: function (a) {
        var b = this.elements, c = a.x, d = a.y, a = a.z;
        b[0] *= c;
        b[4] *= d;
        b[8] *= a;
        b[1] *= c;
        b[5] *= d;
        b[9] *= a;
        b[2] *= c;
        b[6] *= d;
        b[10] *= a;
        b[3] *= c;
        b[7] *= d;
        b[11] *= a;
        return this
    }, getMaxScaleOnAxis: function () {
        var a = this.elements;
        return Math.sqrt(Math.max(a[0] *
            a[0] + a[1] * a[1] + a[2] * a[2], Math.max(a[4] * a[4] + a[5] * a[5] + a[6] * a[6], a[8] * a[8] + a[9] * a[9] + a[10] * a[10])))
    }, makeTranslation: function (a, b, c) {
        this.set(1, 0, 0, a, 0, 1, 0, b, 0, 0, 1, c, 0, 0, 0, 1);
        return this
    }, makeRotationX: function (a) {
        var b = Math.cos(a), a = Math.sin(a);
        this.set(1, 0, 0, 0, 0, b, -a, 0, 0, a, b, 0, 0, 0, 0, 1);
        return this
    }, makeRotationY: function (a) {
        var b = Math.cos(a), a = Math.sin(a);
        this.set(b, 0, a, 0, 0, 1, 0, 0, -a, 0, b, 0, 0, 0, 0, 1);
        return this
    }, makeRotationZ: function (a) {
        var b = Math.cos(a), a = Math.sin(a);
        this.set(b, -a, 0, 0, a, b, 0, 0, 0,
            0, 1, 0, 0, 0, 0, 1);
        return this
    }, makeRotationAxis: function (a, b) {
        var c = Math.cos(b), d = Math.sin(b), e = 1 - c, f = a.x, g = a.y, h = a.z, i = e * f, k = e * g;
        this.set(i * f + c, i * g - d * h, i * h + d * g, 0, i * g + d * h, k * g + c, k * h - d * f, 0, i * h - d * g, k * h + d * f, e * h * h + c, 0, 0, 0, 0, 1);
        return this
    }, makeScale: function (a, b, c) {
        this.set(a, 0, 0, 0, 0, b, 0, 0, 0, 0, c, 0, 0, 0, 0, 1);
        return this
    }, makeFrustum: function (a, b, c, d, e, f) {
        var g = this.elements;
        g[0] = 2 * e / (b - a);
        g[4] = 0;
        g[8] = (b + a) / (b - a);
        g[12] = 0;
        g[1] = 0;
        g[5] = 2 * e / (d - c);
        g[9] = (d + c) / (d - c);
        g[13] = 0;
        g[2] = 0;
        g[6] = 0;
        g[10] = -(f + e) / (f - e);
        g[14] = -2 *
            f * e / (f - e);
        g[3] = 0;
        g[7] = 0;
        g[11] = -1;
        g[15] = 0;
        return this
    }, makePerspective: function (a, b, c, d) {
        var a = c * Math.tan(THREE.Math.degToRad(0.5 * a)), e = -a;
        return this.makeFrustum(e * b, a * b, e, a, c, d)
    }, makeOrthographic: function (a, b, c, d, e, f) {
        var g = this.elements, h = b - a, i = c - d, k = f - e;
        g[0] = 2 / h;
        g[4] = 0;
        g[8] = 0;
        g[12] = -((b + a) / h);
        g[1] = 0;
        g[5] = 2 / i;
        g[9] = 0;
        g[13] = -((c + d) / i);
        g[2] = 0;
        g[6] = 0;
        g[10] = -2 / k;
        g[14] = -((f + e) / k);
        g[3] = 0;
        g[7] = 0;
        g[11] = 0;
        g[15] = 1;
        return this
    }, clone: function () {
        var a = this.elements;
        return new THREE.Matrix4(a[0], a[4], a[8], a[12],
            a[1], a[5], a[9], a[13], a[2], a[6], a[10], a[14], a[3], a[7], a[11], a[15])
    }
};
THREE.extend(THREE.Matrix4.prototype, {
    compose: function () {
        var a = new THREE.Matrix4, b = new THREE.Matrix4;
        return function (c, d, e) {
            var f = this.elements;
            a.identity();
            a.setRotationFromQuaternion(d);
            b.makeScale(e.x, e.y, e.z);
            this.multiplyMatrices(a, b);
            f[12] = c.x;
            f[13] = c.y;
            f[14] = c.z;
            return this
        }
    }(), decompose: function () {
        var a = new THREE.Vector3, b = new THREE.Vector3, c = new THREE.Vector3, d = new THREE.Matrix4;
        return function (e, f, g) {
            var h = this.elements;
            a.set(h[0], h[1], h[2]);
            b.set(h[4], h[5], h[6]);
            c.set(h[8], h[9], h[10]);
            e = e instanceof THREE.Vector3 ? e : new THREE.Vector3;
            f = f instanceof THREE.Quaternion ? f : new THREE.Quaternion;
            g = g instanceof THREE.Vector3 ? g : new THREE.Vector3;
            g.x = a.length();
            g.y = b.length();
            g.z = c.length();
            e.x = h[12];
            e.y = h[13];
            e.z = h[14];
            d.copy(this);
            d.elements[0] /= g.x;
            d.elements[1] /= g.x;
            d.elements[2] /= g.x;
            d.elements[4] /= g.y;
            d.elements[5] /= g.y;
            d.elements[6] /= g.y;
            d.elements[8] /= g.z;
            d.elements[9] /= g.z;
            d.elements[10] /= g.z;
            f.setFromRotationMatrix(d);
            return [e, f, g]
        }
    }()
});
THREE.Ray = function (a, b) {
    this.origin = void 0 !== a ? a : new THREE.Vector3;
    this.direction = void 0 !== b ? b : new THREE.Vector3
};
THREE.Ray.prototype = {
    constructor: THREE.Ray, set: function (a, b) {
        this.origin.copy(a);
        this.direction.copy(b);
        return this
    }, copy: function (a) {
        this.origin.copy(a.origin);
        this.direction.copy(a.direction);
        return this
    }, at: function (a, b) {
        return (b || new THREE.Vector3).copy(this.direction).multiplyScalar(a).add(this.origin)
    }, recast: function () {
        var a = new THREE.Vector3;
        return function (b) {
            this.origin.copy(this.at(b, a));
            return this
        }
    }(), closestPointToPoint: function (a, b) {
        var c = b || new THREE.Vector3;
        c.subVectors(a, this.origin);
        var d = c.dot(this.direction);
        return c.copy(this.direction).multiplyScalar(d).add(this.origin)
    }, distanceToPoint: function () {
        var a = new THREE.Vector3;
        return function (b) {
            var c = a.subVectors(b, this.origin).dot(this.direction);
            a.copy(this.direction).multiplyScalar(c).add(this.origin);
            return a.distanceTo(b)
        }
    }(), isIntersectionSphere: function (a) {
        return this.distanceToPoint(a.center) <= a.radius
    }, isIntersectionPlane: function (a) {
        return 0 != a.normal.dot(this.direction) || 0 == a.distanceToPoint(this.origin) ? !0 : !1
    }, distanceToPlane: function (a) {
        var b =
            a.normal.dot(this.direction);
        if (0 == b) {
            if (0 == a.distanceToPoint(this.origin)) return 0
        } else return -(this.origin.dot(a.normal) + a.constant) / b
    }, intersectPlane: function (a, b) {
        var c = this.distanceToPlane(a);
        return void 0 === c ? void 0 : this.at(c, b)
    }, applyMatrix4: function (a) {
        this.direction.add(this.origin).applyMatrix4(a);
        this.origin.applyMatrix4(a);
        this.direction.sub(this.origin);
        return this
    }, equals: function (a) {
        return a.origin.equals(this.origin) && a.direction.equals(this.direction)
    }, clone: function () {
        return (new THREE.Ray).copy(this)
    }
};
THREE.Sphere = function (a, b) {
    this.center = void 0 !== a ? a : new THREE.Vector3;
    this.radius = void 0 !== b ? b : 0
};
THREE.Sphere.prototype = {
    constructor: THREE.Sphere, set: function (a, b) {
        this.center.copy(a);
        this.radius = b;
        return this
    }, setFromCenterAndPoints: function (a, b) {
        for (var c = 0, d = 0, e = b.length; d < e; d++) var f = a.distanceToSquared(b[d]), c = Math.max(c, f);
        this.center = a;
        this.radius = Math.sqrt(c);
        return this
    }, copy: function (a) {
        this.center.copy(a.center);
        this.radius = a.radius;
        return this
    }, empty: function () {
        return 0 >= this.radius
    }, containsPoint: function (a) {
        return a.distanceToSquared(this.center) <= this.radius * this.radius
    }, distanceToPoint: function (a) {
        return a.distanceTo(this.center) -
            this.radius
    }, intersectsSphere: function (a) {
        var b = this.radius + a.radius;
        return a.center.distanceToSquared(this.center) <= b * b
    }, clampPoint: function (a, b) {
        var c = this.center.distanceToSquared(a), d = b || new THREE.Vector3;
        d.copy(a);
        c > this.radius * this.radius && (d.sub(this.center).normalize(), d.multiplyScalar(this.radius).add(this.center));
        return d
    }, getBoundingBox: function (a) {
        a = a || new THREE.Box3;
        a.set(this.center, this.center);
        a.expandByScalar(this.radius);
        return a
    }, applyMatrix4: function (a) {
        this.center.applyMatrix4(a);
        this.radius *= a.getMaxScaleOnAxis();
        return this
    }, translate: function (a) {
        this.center.add(a);
        return this
    }, equals: function (a) {
        return a.center.equals(this.center) && a.radius === this.radius
    }, clone: function () {
        return (new THREE.Sphere).copy(this)
    }
};
THREE.Frustum = function (a, b, c, d, e, f) {
    this.planes = [void 0 !== a ? a : new THREE.Plane, void 0 !== b ? b : new THREE.Plane, void 0 !== c ? c : new THREE.Plane, void 0 !== d ? d : new THREE.Plane, void 0 !== e ? e : new THREE.Plane, void 0 !== f ? f : new THREE.Plane]
};
THREE.Frustum.prototype = {
    constructor: THREE.Frustum, set: function (a, b, c, d, e, f) {
        var g = this.planes;
        g[0].copy(a);
        g[1].copy(b);
        g[2].copy(c);
        g[3].copy(d);
        g[4].copy(e);
        g[5].copy(f);
        return this
    }, copy: function (a) {
        for (var b = this.planes, c = 0; 6 > c; c++) b[c].copy(a.planes[c]);
        return this
    }, setFromMatrix: function (a) {
        var b = this.planes, c = a.elements, a = c[0], d = c[1], e = c[2], f = c[3], g = c[4], h = c[5], i = c[6],
            k = c[7], l = c[8], m = c[9], p = c[10], s = c[11], r = c[12], n = c[13], q = c[14], c = c[15];
        b[0].setComponents(f - a, k - g, s - l, c - r).normalize();
        b[1].setComponents(f +
            a, k + g, s + l, c + r).normalize();
        b[2].setComponents(f + d, k + h, s + m, c + n).normalize();
        b[3].setComponents(f - d, k - h, s - m, c - n).normalize();
        b[4].setComponents(f - e, k - i, s - p, c - q).normalize();
        b[5].setComponents(f + e, k + i, s + p, c + q).normalize();
        return this
    }, intersectsObject: function () {
        var a = new THREE.Vector3;
        return function (b) {
            var c = b.matrixWorld, d = this.planes, b = -b.geometry.boundingSphere.radius * c.getMaxScaleOnAxis();
            a.getPositionFromMatrix(c);
            for (c = 0; 6 > c; c++) if (d[c].distanceToPoint(a) < b) return !1;
            return !0
        }
    }(), intersectsSphere: function (a) {
        for (var b =
            this.planes, c = a.center, a = -a.radius, d = 0; 6 > d; d++) if (b[d].distanceToPoint(c) < a) return !1;
        return !0
    }, containsPoint: function (a) {
        for (var b = this.planes, c = 0; 6 > c; c++) if (0 > b[c].distanceToPoint(a)) return !1;
        return !0
    }, clone: function () {
        return (new THREE.Frustum).copy(this)
    }
};
THREE.Plane = function (a, b) {
    this.normal = void 0 !== a ? a : new THREE.Vector3(1, 0, 0);
    this.constant = void 0 !== b ? b : 0
};
THREE.Plane.prototype = {
    constructor: THREE.Plane, set: function (a, b) {
        this.normal.copy(a);
        this.constant = b;
        return this
    }, setComponents: function (a, b, c, d) {
        this.normal.set(a, b, c);
        this.constant = d;
        return this
    }, setFromNormalAndCoplanarPoint: function (a, b) {
        this.normal.copy(a);
        this.constant = -b.dot(this.normal);
        return this
    }, setFromCoplanarPoints: function () {
        var a = new THREE.Vector3, b = new THREE.Vector3;
        return function (c, d, e) {
            d = a.subVectors(e, d).cross(b.subVectors(c, d)).normalize();
            this.setFromNormalAndCoplanarPoint(d,
                c);
            return this
        }
    }(), copy: function (a) {
        this.normal.copy(a.normal);
        this.constant = a.constant;
        return this
    }, normalize: function () {
        var a = 1 / this.normal.length();
        this.normal.multiplyScalar(a);
        this.constant *= a;
        return this
    }, negate: function () {
        this.constant *= -1;
        this.normal.negate();
        return this
    }, distanceToPoint: function (a) {
        return this.normal.dot(a) + this.constant
    }, distanceToSphere: function (a) {
        return this.distanceToPoint(a.center) - a.radius
    }, projectPoint: function (a, b) {
        return this.orthoPoint(a, b).sub(a).negate()
    }, orthoPoint: function (a,
                             b) {
        var c = this.distanceToPoint(a);
        return (b || new THREE.Vector3).copy(this.normal).multiplyScalar(c)
    }, isIntersectionLine: function (a) {
        var b = this.distanceToPoint(a.start), a = this.distanceToPoint(a.end);
        return 0 > b && 0 < a || 0 > a && 0 < b
    }, intersectLine: function () {
        var a = new THREE.Vector3;
        return function (b, c) {
            var d = c || new THREE.Vector3, e = b.delta(a), f = this.normal.dot(e);
            if (0 == f) {
                if (0 == this.distanceToPoint(b.start)) return d.copy(b.start)
            } else return f = -(b.start.dot(this.normal) + this.constant) / f, 0 > f || 1 < f ? void 0 : d.copy(e).multiplyScalar(f).add(b.start)
        }
    }(),
    coplanarPoint: function (a) {
        return (a || new THREE.Vector3).copy(this.normal).multiplyScalar(-this.constant)
    }, applyMatrix4: function () {
        var a = new THREE.Vector3, b = new THREE.Vector3;
        return function (c, d) {
            var d = d || (new THREE.Matrix3).getInverse(c).transpose(), e = a.copy(this.normal).applyMatrix3(d),
                f = this.coplanarPoint(b);
            f.applyMatrix4(c);
            this.setFromNormalAndCoplanarPoint(e, f);
            return this
        }
    }(), translate: function (a) {
        this.constant -= a.dot(this.normal);
        return this
    }, equals: function (a) {
        return a.normal.equals(this.normal) &&
            a.constant == this.constant
    }, clone: function () {
        return (new THREE.Plane).copy(this)
    }
};
THREE.Math = {
    clamp: function (a, b, c) {
        return a < b ? b : a > c ? c : a
    }, clampBottom: function (a, b) {
        return a < b ? b : a
    }, mapLinear: function (a, b, c, d, e) {
        return d + (a - b) * (e - d) / (c - b)
    }, smoothstep: function (a, b, c) {
        if (a <= b) return 0;
        if (a >= c) return 1;
        a = (a - b) / (c - b);
        return a * a * (3 - 2 * a)
    }, smootherstep: function (a, b, c) {
        if (a <= b) return 0;
        if (a >= c) return 1;
        a = (a - b) / (c - b);
        return a * a * a * (a * (6 * a - 15) + 10)
    }, random16: function () {
        return (65280 * Math.random() + 255 * Math.random()) / 65535
    }, randInt: function (a, b) {
        return a + Math.floor(Math.random() * (b - a + 1))
    }, randFloat: function (a,
                            b) {
        return a + Math.random() * (b - a)
    }, randFloatSpread: function (a) {
        return a * (0.5 - Math.random())
    }, sign: function (a) {
        return 0 > a ? -1 : 0 < a ? 1 : 0
    }, degToRad: function () {
        var a = Math.PI / 180;
        return function (b) {
            return b * a
        }
    }(), radToDeg: function () {
        var a = 180 / Math.PI;
        return function (b) {
            return b * a
        }
    }()
};
THREE.Spline = function (a) {
    function b(a, b, c, d, e, f, g) {
        a = 0.5 * (c - a);
        d = 0.5 * (d - b);
        return (2 * (b - c) + a + d) * g + (-3 * (b - c) - 2 * a - d) * f + a * e + b
    }

    this.points = a;
    var c = [], d = {x: 0, y: 0, z: 0}, e, f, g, h, i, k, l, m, p;
    this.initFromArray = function (a) {
        this.points = [];
        for (var b = 0; b < a.length; b++) this.points[b] = {x: a[b][0], y: a[b][1], z: a[b][2]}
    };
    this.getPoint = function (a) {
        e = (this.points.length - 1) * a;
        f = Math.floor(e);
        g = e - f;
        c[0] = 0 === f ? f : f - 1;
        c[1] = f;
        c[2] = f > this.points.length - 2 ? this.points.length - 1 : f + 1;
        c[3] = f > this.points.length - 3 ? this.points.length - 1 :
            f + 2;
        k = this.points[c[0]];
        l = this.points[c[1]];
        m = this.points[c[2]];
        p = this.points[c[3]];
        h = g * g;
        i = g * h;
        d.x = b(k.x, l.x, m.x, p.x, g, h, i);
        d.y = b(k.y, l.y, m.y, p.y, g, h, i);
        d.z = b(k.z, l.z, m.z, p.z, g, h, i);
        return d
    };
    this.getControlPointsArray = function () {
        var a, b, c = this.points.length, d = [];
        for (a = 0; a < c; a++) b = this.points[a], d[a] = [b.x, b.y, b.z];
        return d
    };
    this.getLength = function (a) {
        var b, c, d, e = b = b = 0, f = new THREE.Vector3, g = new THREE.Vector3, h = [], i = 0;
        h[0] = 0;
        a || (a = 100);
        c = this.points.length * a;
        f.copy(this.points[0]);
        for (a = 1; a < c; a++) b =
            a / c, d = this.getPoint(b), g.copy(d), i += g.distanceTo(f), f.copy(d), b *= this.points.length - 1, b = Math.floor(b), b != e && (h[b] = i, e = b);
        h[h.length] = i;
        return {chunks: h, total: i}
    };
    this.reparametrizeByArcLength = function (a) {
        var b, c, d, e, f, g, h = [], i = new THREE.Vector3, k = this.getLength();
        h.push(i.copy(this.points[0]).clone());
        for (b = 1; b < this.points.length; b++) {
            c = k.chunks[b] - k.chunks[b - 1];
            g = Math.ceil(a * c / k.total);
            e = (b - 1) / (this.points.length - 1);
            f = b / (this.points.length - 1);
            for (c = 1; c < g - 1; c++) d = e + c * (1 / g) * (f - e), d = this.getPoint(d),
                h.push(i.copy(d).clone());
            h.push(i.copy(this.points[b]).clone())
        }
        this.points = h
    }
};
THREE.Triangle = function (a, b, c) {
    this.a = void 0 !== a ? a : new THREE.Vector3;
    this.b = void 0 !== b ? b : new THREE.Vector3;
    this.c = void 0 !== c ? c : new THREE.Vector3
};
THREE.Triangle.normal = function () {
    var a = new THREE.Vector3;
    return function (b, c, d, e) {
        e = e || new THREE.Vector3;
        e.subVectors(d, c);
        a.subVectors(b, c);
        e.cross(a);
        b = e.lengthSq();
        return 0 < b ? e.multiplyScalar(1 / Math.sqrt(b)) : e.set(0, 0, 0)
    }
}();
THREE.Triangle.barycoordFromPoint = function () {
    var a = new THREE.Vector3, b = new THREE.Vector3, c = new THREE.Vector3;
    return function (d, e, f, g, h) {
        a.subVectors(g, e);
        b.subVectors(f, e);
        c.subVectors(d, e);
        var d = a.dot(a), e = a.dot(b), f = a.dot(c), i = b.dot(b), g = b.dot(c), k = d * i - e * e,
            h = h || new THREE.Vector3;
        if (0 == k) return h.set(-2, -1, -1);
        k = 1 / k;
        i = (i * f - e * g) * k;
        d = (d * g - e * f) * k;
        return h.set(1 - i - d, d, i)
    }
}();
THREE.Triangle.containsPoint = function () {
    var a = new THREE.Vector3;
    return function (b, c, d, e) {
        b = THREE.Triangle.barycoordFromPoint(b, c, d, e, a);
        return 0 <= b.x && 0 <= b.y && 1 >= b.x + b.y
    }
}();
THREE.Triangle.prototype = {
    constructor: THREE.Triangle, set: function (a, b, c) {
        this.a.copy(a);
        this.b.copy(b);
        this.c.copy(c);
        return this
    }, setFromPointsAndIndices: function (a, b, c, d) {
        this.a.copy(a[b]);
        this.b.copy(a[c]);
        this.c.copy(a[d]);
        return this
    }, copy: function (a) {
        this.a.copy(a.a);
        this.b.copy(a.b);
        this.c.copy(a.c);
        return this
    }, area: function () {
        var a = new THREE.Vector3, b = new THREE.Vector3;
        return function () {
            a.subVectors(this.c, this.b);
            b.subVectors(this.a, this.b);
            return 0.5 * a.cross(b).length()
        }
    }(), midpoint: function (a) {
        return (a ||
            new THREE.Vector3).addVectors(this.a, this.b).add(this.c).multiplyScalar(1 / 3)
    }, normal: function (a) {
        return THREE.Triangle.normal(this.a, this.b, this.c, a)
    }, plane: function (a) {
        return (a || new THREE.Plane).setFromCoplanarPoints(this.a, this.b, this.c)
    }, barycoordFromPoint: function (a, b) {
        return THREE.Triangle.barycoordFromPoint(a, this.a, this.b, this.c, b)
    }, containsPoint: function (a) {
        return THREE.Triangle.containsPoint(a, this.a, this.b, this.c)
    }, equals: function (a) {
        return a.a.equals(this.a) && a.b.equals(this.b) && a.c.equals(this.c)
    },
    clone: function () {
        return (new THREE.Triangle).copy(this)
    }
};
THREE.Vertex = function (a) {
    console.warn("THREE.Vertex has been DEPRECATED. Use THREE.Vector3 instead.");
    return a
};
THREE.UV = function (a, b) {
    console.warn("THREE.UV has been DEPRECATED. Use THREE.Vector2 instead.");
    return new THREE.Vector2(a, b)
};
THREE.Clock = function (a) {
    this.autoStart = void 0 !== a ? a : !0;
    this.elapsedTime = this.oldTime = this.startTime = 0;
    this.running = !1
};
THREE.extend(THREE.Clock.prototype, {
    start: function () {
        this.oldTime = this.startTime = void 0 !== window.performance && void 0 !== window.performance.now ? window.performance.now() : Date.now();
        this.running = !0
    }, stop: function () {
        this.getElapsedTime();
        this.running = !1
    }, getElapsedTime: function () {
        this.getDelta();
        return this.elapsedTime
    }, getDelta: function () {
        var a = 0;
        this.autoStart && !this.running && this.start();
        if (this.running) {
            var b = void 0 !== window.performance && void 0 !== window.performance.now ? window.performance.now() : Date.now(),
                a = 0.001 * (b - this.oldTime);
            this.oldTime = b;
            this.elapsedTime += a
        }
        return a
    }
});
THREE.EventDispatcher = function () {
    var a = {};
    this.addEventListener = function (b, c) {
        void 0 === a[b] && (a[b] = []);
        -1 === a[b].indexOf(c) && a[b].push(c)
    };
    this.removeEventListener = function (b, c) {
        var d = a[b].indexOf(c);
        -1 !== d && a[b].splice(d, 1)
    };
    this.dispatchEvent = function (b) {
        var c = a[b.type];
        if (void 0 !== c) {
            b.target = this;
            for (var d = 0, e = c.length; d < e; d++) c[d].call(this, b)
        }
    }
};
(function (a) {
    a.Raycaster = function (b, c, d, e) {
        this.ray = new a.Ray(b, c);
        0 < this.ray.direction.lengthSq() && this.ray.direction.normalize();
        this.near = d || 0;
        this.far = e || Infinity
    };
    var b = new a.Sphere, c = new a.Ray, d = new a.Plane, e = new a.Vector3, f = new a.Vector3, g = new a.Matrix4,
        h = function (a, b) {
            return a.distance - b.distance
        }, i = function (h, i, k) {
            if (h instanceof a.Particle) {
                f.getPositionFromMatrix(h.matrixWorld);
                i = i.ray.distanceToPoint(f);
                if (i > h.scale.x) return k;
                k.push({distance: i, point: h.position, face: null, object: h})
            } else if (h instanceof
                a.Mesh) {
                f.getPositionFromMatrix(h.matrixWorld);
                b.set(f, h.geometry.boundingSphere.radius * h.matrixWorld.getMaxScaleOnAxis());
                if (!i.ray.isIntersectionSphere(b)) return k;
                var s = h.geometry, r = s.vertices, n = h.material instanceof a.MeshFaceMaterial,
                    q = !0 === n ? h.material.materials : null, z = h.material.side, t, x, u, B = i.precision;
                h.matrixRotationWorld.extractRotation(h.matrixWorld);
                g.getInverse(h.matrixWorld);
                c.copy(i.ray).applyMatrix4(g);
                for (var G = 0, D = s.faces.length; G < D; G++) {
                    var w = s.faces[G], z = !0 === n ? q[w.materialIndex] :
                        h.material;
                    if (void 0 !== z) {
                        d.setFromNormalAndCoplanarPoint(w.normal, r[w.a]);
                        var I = c.distanceToPlane(d);
                        if (!(Math.abs(I) < B) && !(0 > I)) {
                            z = z.side;
                            if (z !== a.DoubleSide && (t = c.direction.dot(d.normal), !(z === a.FrontSide ? 0 > t : 0 < t))) continue;
                            if (!(I < i.near || I > i.far)) {
                                e = c.at(I, e);
                                if (w instanceof a.Face3) {
                                    if (z = r[w.a], t = r[w.b], x = r[w.c], !a.Triangle.containsPoint(e, z, t, x)) continue
                                } else if (w instanceof a.Face4) {
                                    if (z = r[w.a], t = r[w.b], x = r[w.c], u = r[w.d], !a.Triangle.containsPoint(e, z, t, u) && !a.Triangle.containsPoint(e, t, x, u)) continue
                                } else throw Error("face type not supported");
                                k.push({distance: I, point: i.ray.at(I), face: w, faceIndex: G, object: h})
                            }
                        }
                    }
                }
            }
        }, k = function (a, b, c) {
            for (var a = a.getDescendants(), d = 0, e = a.length; d < e; d++) i(a[d], b, c)
        };
    a.Raycaster.prototype.precision = 1E-4;
    a.Raycaster.prototype.set = function (a, b) {
        this.ray.set(a, b);
        0 < this.ray.direction.length() && this.ray.direction.normalize()
    };
    a.Raycaster.prototype.intersectObject = function (a, b) {
        var c = [];
        !0 === b && k(a, this, c);
        i(a, this, c);
        c.sort(h);
        return c
    };
    a.Raycaster.prototype.intersectObjects = function (a, b) {
        for (var c = [], d = 0, e = a.length; d <
        e; d++) i(a[d], this, c), !0 === b && k(a[d], this, c);
        c.sort(h);
        return c
    }
})(THREE);
THREE.Object3D = function () {
    this.id = THREE.Object3DIdCount++;
    this.name = "";
    this.parent = void 0;
    this.children = [];
    this.up = new THREE.Vector3(0, 1, 0);
    this.position = new THREE.Vector3;
    this.rotation = new THREE.Vector3;
    this.eulerOrder = THREE.Object3D.defaultEulerOrder;
    this.scale = new THREE.Vector3(1, 1, 1);
    this.renderDepth = null;
    this.rotationAutoUpdate = !0;
    this.matrix = new THREE.Matrix4;
    this.matrixWorld = new THREE.Matrix4;
    this.matrixRotationWorld = new THREE.Matrix4;
    this.matrixWorldNeedsUpdate = this.matrixAutoUpdate = !0;
    this.quaternion =
        new THREE.Quaternion;
    this.useQuaternion = !1;
    this.visible = !0;
    this.receiveShadow = this.castShadow = !1;
    this.frustumCulled = !0;
    this.userData = {}
};
THREE.Object3D.prototype = {
    constructor: THREE.Object3D, applyMatrix: function () {
        var a = new THREE.Matrix4;
        return function (b) {
            this.matrix.multiplyMatrices(b, this.matrix);
            this.position.getPositionFromMatrix(this.matrix);
            this.scale.getScaleFromMatrix(this.matrix);
            a.extractRotation(this.matrix);
            !0 === this.useQuaternion ? this.quaternion.setFromRotationMatrix(a) : this.rotation.setEulerFromRotationMatrix(a, this.eulerOrder)
        }
    }(), translate: function () {
        var a = new THREE.Vector3;
        return function (b, c) {
            a.copy(c);
            !0 === this.useQuaternion ?
                a.applyQuaternion(this.quaternion) : a.applyEuler(this.rotation, this.eulerOrder);
            a.multiplyScalar(b);
            this.position.add(a);
            return this
        }
    }(), translateX: function () {
        var a = new THREE.Vector3(1, 0, 0);
        return function (b) {
            return this.translate(b, a)
        }
    }(), translateY: function () {
        var a = new THREE.Vector3(0, 1, 0);
        return function (b) {
            return this.translate(b, a)
        }
    }(), translateZ: function () {
        var a = new THREE.Vector3(0, 0, 1);
        return function (b) {
            return this.translate(b, a)
        }
    }(), localToWorld: function (a) {
        return a.applyMatrix4(this.matrixWorld)
    },
    worldToLocal: function () {
        var a = new THREE.Matrix4;
        return function (b) {
            return b.applyMatrix4(a.getInverse(this.matrixWorld))
        }
    }(), lookAt: function () {
        var a = new THREE.Matrix4;
        return function (b) {
            a.lookAt(b, this.position, this.up);
            !0 === this.useQuaternion ? this.quaternion.setFromRotationMatrix(a) : this.rotation.setEulerFromRotationMatrix(a, this.eulerOrder)
        }
    }(), add: function (a) {
        if (a === this) console.warn("THREE.Object3D.add: An object can't be added as a child of itself."); else if (a instanceof THREE.Object3D) {
            void 0 !==
            a.parent && a.parent.remove(a);
            a.parent = this;
            this.children.push(a);
            for (var b = this; void 0 !== b.parent;) b = b.parent;
            void 0 !== b && b instanceof THREE.Scene && b.__addObject(a)
        }
    }, remove: function (a) {
        var b = this.children.indexOf(a);
        if (-1 !== b) {
            a.parent = void 0;
            this.children.splice(b, 1);
            for (b = this; void 0 !== b.parent;) b = b.parent;
            void 0 !== b && b instanceof THREE.Scene && b.__removeObject(a)
        }
    }, traverse: function (a) {
        a(this);
        for (var b = 0, c = this.children.length; b < c; b++) this.children[b].traverse(a)
    }, getChildByName: function (a, b) {
        for (var c =
            0, d = this.children.length; c < d; c++) {
            var e = this.children[c];
            if (e.name === a || !0 === b && (e = e.getChildByName(a, b), void 0 !== e)) return e
        }
    }, getDescendants: function (a) {
        void 0 === a && (a = []);
        Array.prototype.push.apply(a, this.children);
        for (var b = 0, c = this.children.length; b < c; b++) this.children[b].getDescendants(a);
        return a
    }, updateMatrix: function () {
        this.matrix.setPosition(this.position);
        !1 === this.useQuaternion ? this.matrix.setRotationFromEuler(this.rotation, this.eulerOrder) : this.matrix.setRotationFromQuaternion(this.quaternion);
        (1 !== this.scale.x || 1 !== this.scale.y || 1 !== this.scale.z) && this.matrix.scale(this.scale);
        this.matrixWorldNeedsUpdate = !0
    }, updateMatrixWorld: function (a) {
        !0 === this.matrixAutoUpdate && this.updateMatrix();
        if (!0 === this.matrixWorldNeedsUpdate || !0 === a) void 0 === this.parent ? this.matrixWorld.copy(this.matrix) : this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix), this.matrixWorldNeedsUpdate = !1, a = !0;
        for (var b = 0, c = this.children.length; b < c; b++) this.children[b].updateMatrixWorld(a)
    }, clone: function (a) {
        void 0 ===
        a && (a = new THREE.Object3D);
        a.name = this.name;
        a.up.copy(this.up);
        a.position.copy(this.position);
        a.rotation instanceof THREE.Vector3 && a.rotation.copy(this.rotation);
        a.eulerOrder = this.eulerOrder;
        a.scale.copy(this.scale);
        a.renderDepth = this.renderDepth;
        a.rotationAutoUpdate = this.rotationAutoUpdate;
        a.matrix.copy(this.matrix);
        a.matrixWorld.copy(this.matrixWorld);
        a.matrixRotationWorld.copy(this.matrixRotationWorld);
        a.matrixAutoUpdate = this.matrixAutoUpdate;
        a.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate;
        a.quaternion.copy(this.quaternion);
        a.useQuaternion = this.useQuaternion;
        a.visible = this.visible;
        a.castShadow = this.castShadow;
        a.receiveShadow = this.receiveShadow;
        a.frustumCulled = this.frustumCulled;
        for (var b = 0; b < this.children.length; b++) a.add(this.children[b].clone());
        return a
    }
};
THREE.Object3D.defaultEulerOrder = "XYZ";
THREE.Object3DIdCount = 0;
THREE.Projector = function () {
    function a() {
        if (f === h) {
            var a = new THREE.RenderableObject;
            g.push(a);
            h++;
            f++;
            return a
        }
        return g[f++]
    }

    function b() {
        if (k === m) {
            var a = new THREE.RenderableVertex;
            l.push(a);
            m++;
            k++;
            return a
        }
        return l[k++]
    }

    function c(a, b) {
        return b.z - a.z
    }

    function d(a, b) {
        var c = 0, d = 1, e = a.z + a.w, f = b.z + b.w, g = -a.z + a.w, h = -b.z + b.w;
        if (0 <= e && 0 <= f && 0 <= g && 0 <= h) return !0;
        if (0 > e && 0 > f || 0 > g && 0 > h) return !1;
        0 > e ? c = Math.max(c, e / (e - f)) : 0 > f && (d = Math.min(d, e / (e - f)));
        0 > g ? c = Math.max(c, g / (g - h)) : 0 > h && (d = Math.min(d, g / (g - h)));
        if (d <
            c) return !1;
        a.lerp(b, c);
        b.lerp(a, 1 - d);
        return !0
    }

    var e, f, g = [], h = 0, i, k, l = [], m = 0, p, s, r = [], n = 0, q, z = [], t = 0, x, u, B = [], G = 0, D, w,
        I = [], J = 0, E = {objects: [], sprites: [], lights: [], elements: []}, Z = new THREE.Vector3,
        A = new THREE.Vector4, S = new THREE.Box3(new THREE.Vector3(-1, -1, -1), new THREE.Vector3(1, 1, 1)),
        F = new THREE.Box3, H = Array(3), K = Array(4), N = new THREE.Matrix4, fa = new THREE.Matrix4, ma,
        eb = new THREE.Matrix4, M = new THREE.Matrix3, U = new THREE.Matrix3, ja = new THREE.Vector3,
        L = new THREE.Frustum, ca = new THREE.Vector4, ta = new THREE.Vector4;
    this.projectVector = function (a, b) {
        b.matrixWorldInverse.getInverse(b.matrixWorld);
        fa.multiplyMatrices(b.projectionMatrix, b.matrixWorldInverse);
        return a.applyProjection(fa)
    };
    this.unprojectVector = function (a, b) {
        b.projectionMatrixInverse.getInverse(b.projectionMatrix);
        fa.multiplyMatrices(b.matrixWorld, b.projectionMatrixInverse);
        return a.applyProjection(fa)
    };
    this.pickingRay = function (a, b) {
        a.z = -1;
        var c = new THREE.Vector3(a.x, a.y, 1);
        this.unprojectVector(a, b);
        this.unprojectVector(c, b);
        c.sub(a).normalize();
        return new THREE.Raycaster(a,
            c)
    };
    this.projectScene = function (g, h, m, db) {
        var La = !1, Oa, ea, ha, V, $, Y, ia, ra, na, Pa, Ba, fb, jb;
        w = u = q = s = 0;
        E.elements.length = 0;
        g.updateMatrixWorld();
        void 0 === h.parent && h.updateMatrixWorld();
        N.copy(h.matrixWorldInverse.getInverse(h.matrixWorld));
        fa.multiplyMatrices(h.projectionMatrix, N);
        U.getInverse(N);
        U.transpose();
        L.setFromMatrix(fa);
        f = 0;
        E.objects.length = 0;
        E.sprites.length = 0;
        E.lights.length = 0;
        var Sa = function (b) {
            for (var c = 0, d = b.children.length; c < d; c++) {
                var f = b.children[c];
                if (!1 !== f.visible) {
                    if (f instanceof
                        THREE.Light) E.lights.push(f); else if (f instanceof THREE.Mesh || f instanceof THREE.Line) {
                        if (!1 === f.frustumCulled || !0 === L.intersectsObject(f)) e = a(), e.object = f, null !== f.renderDepth ? e.z = f.renderDepth : (Z.getPositionFromMatrix(f.matrixWorld), Z.applyProjection(fa), e.z = Z.z), E.objects.push(e)
                    } else f instanceof THREE.Sprite || f instanceof THREE.Particle ? (e = a(), e.object = f, null !== f.renderDepth ? e.z = f.renderDepth : (Z.getPositionFromMatrix(f.matrixWorld), Z.applyProjection(fa), e.z = Z.z), E.sprites.push(e)) : (e = a(), e.object =
                        f, null !== f.renderDepth ? e.z = f.renderDepth : (Z.getPositionFromMatrix(f.matrixWorld), Z.applyProjection(fa), e.z = Z.z), E.objects.push(e));
                    Sa(f)
                }
            }
        };
        Sa(g);
        !0 === m && E.objects.sort(c);
        g = 0;
        for (m = E.objects.length; g < m; g++) if (ra = E.objects[g].object, ma = ra.matrixWorld, k = 0, ra instanceof THREE.Mesh) {
            na = ra.geometry;
            ha = na.vertices;
            Pa = na.faces;
            na = na.faceVertexUvs;
            M.getInverse(ma);
            M.transpose();
            fb = ra.material instanceof THREE.MeshFaceMaterial;
            jb = !0 === fb ? ra.material : null;
            Oa = 0;
            for (ea = ha.length; Oa < ea; Oa++) i = b(), i.positionWorld.copy(ha[Oa]).applyMatrix4(ma),
                i.positionScreen.copy(i.positionWorld).applyMatrix4(fa), i.positionScreen.x /= i.positionScreen.w, i.positionScreen.y /= i.positionScreen.w, i.positionScreen.z /= i.positionScreen.w, i.visible = !(-1 > i.positionScreen.x || 1 < i.positionScreen.x || -1 > i.positionScreen.y || 1 < i.positionScreen.y || -1 > i.positionScreen.z || 1 < i.positionScreen.z);
            ha = 0;
            for (Oa = Pa.length; ha < Oa; ha++) {
                ea = Pa[ha];
                var ob = !0 === fb ? jb.materials[ea.materialIndex] : ra.material;
                if (void 0 !== ob) {
                    Y = ob.side;
                    if (ea instanceof THREE.Face3) if (V = l[ea.a], $ = l[ea.b], ia =
                            l[ea.c], H[0] = V.positionScreen, H[1] = $.positionScreen, H[2] = ia.positionScreen, !0 === V.visible || !0 === $.visible || !0 === ia.visible || S.isIntersectionBox(F.setFromPoints(H))) if (La = 0 > (ia.positionScreen.x - V.positionScreen.x) * ($.positionScreen.y - V.positionScreen.y) - (ia.positionScreen.y - V.positionScreen.y) * ($.positionScreen.x - V.positionScreen.x), Y === THREE.DoubleSide || La === (Y === THREE.FrontSide)) s === n ? (Ba = new THREE.RenderableFace3, r.push(Ba), n++, s++, p = Ba) : p = r[s++], p.v1.copy(V), p.v2.copy($), p.v3.copy(ia); else continue;
                    else continue; else if (ea instanceof THREE.Face4) if (V = l[ea.a], $ = l[ea.b], ia = l[ea.c], Ba = l[ea.d], K[0] = V.positionScreen, K[1] = $.positionScreen, K[2] = ia.positionScreen, K[3] = Ba.positionScreen, !0 === V.visible || !0 === $.visible || !0 === ia.visible || !0 === Ba.visible || S.isIntersectionBox(F.setFromPoints(K))) if (La = 0 > (Ba.positionScreen.x - V.positionScreen.x) * ($.positionScreen.y - V.positionScreen.y) - (Ba.positionScreen.y - V.positionScreen.y) * ($.positionScreen.x - V.positionScreen.x) || 0 > ($.positionScreen.x - ia.positionScreen.x) *
                            (Ba.positionScreen.y - ia.positionScreen.y) - ($.positionScreen.y - ia.positionScreen.y) * (Ba.positionScreen.x - ia.positionScreen.x), Y === THREE.DoubleSide || La === (Y === THREE.FrontSide)) {
                        if (q === t) {
                            var Gb = new THREE.RenderableFace4;
                            z.push(Gb);
                            t++;
                            q++;
                            p = Gb
                        } else p = z[q++];
                        p.v1.copy(V);
                        p.v2.copy($);
                        p.v3.copy(ia);
                        p.v4.copy(Ba)
                    } else continue; else continue;
                    p.normalModel.copy(ea.normal);
                    !1 === La && (Y === THREE.BackSide || Y === THREE.DoubleSide) && p.normalModel.negate();
                    p.normalModel.applyMatrix3(M).normalize();
                    p.normalModelView.copy(p.normalModel).applyMatrix3(U);
                    p.centroidModel.copy(ea.centroid).applyMatrix4(ma);
                    ia = ea.vertexNormals;
                    V = 0;
                    for ($ = ia.length; V < $; V++) Ba = p.vertexNormalsModel[V], Ba.copy(ia[V]), !1 === La && (Y === THREE.BackSide || Y === THREE.DoubleSide) && Ba.negate(), Ba.applyMatrix3(M).normalize(), p.vertexNormalsModelView[V].copy(Ba).applyMatrix3(U);
                    p.vertexNormalsLength = ia.length;
                    V = 0;
                    for ($ = na.length; V < $; V++) if (Ba = na[V][ha], void 0 !== Ba) {
                        Y = 0;
                        for (ia = Ba.length; Y < ia; Y++) p.uvs[V][Y] = Ba[Y]
                    }
                    p.color = ea.color;
                    p.material = ob;
                    ja.copy(p.centroidModel).applyProjection(fa);
                    p.z = ja.z;
                    E.elements.push(p)
                }
            }
        } else if (ra instanceof THREE.Line) {
            eb.multiplyMatrices(fa, ma);
            ha = ra.geometry.vertices;
            V = b();
            V.positionScreen.copy(ha[0]).applyMatrix4(eb);
            Pa = ra.type === THREE.LinePieces ? 2 : 1;
            Oa = 1;
            for (ea = ha.length; Oa < ea; Oa++) V = b(), V.positionScreen.copy(ha[Oa]).applyMatrix4(eb), 0 < (Oa + 1) % Pa || ($ = l[k - 2], ca.copy(V.positionScreen), ta.copy($.positionScreen), !0 === d(ca, ta) && (ca.multiplyScalar(1 / ca.w), ta.multiplyScalar(1 / ta.w), u === G ? (na = new THREE.RenderableLine, B.push(na), G++, u++, x = na) : x = B[u++], x.v1.positionScreen.copy(ca),
                x.v2.positionScreen.copy(ta), x.z = Math.max(ca.z, ta.z), x.material = ra.material, E.elements.push(x)))
        }
        g = 0;
        for (m = E.sprites.length; g < m; g++) ra = E.sprites[g].object, ma = ra.matrixWorld, ra instanceof THREE.Particle && (A.set(ma.elements[12], ma.elements[13], ma.elements[14], 1), A.applyMatrix4(fa), A.z /= A.w, 0 < A.z && 1 > A.z && (w === J ? (La = new THREE.RenderableParticle, I.push(La), J++, w++, D = La) : D = I[w++], D.object = ra, D.x = A.x / A.w, D.y = A.y / A.w, D.z = A.z, D.rotation = ra.rotation.z, D.scale.x = ra.scale.x * Math.abs(D.x - (A.x + h.projectionMatrix.elements[0]) /
            (A.w + h.projectionMatrix.elements[12])), D.scale.y = ra.scale.y * Math.abs(D.y - (A.y + h.projectionMatrix.elements[5]) / (A.w + h.projectionMatrix.elements[13])), D.material = ra.material, E.elements.push(D)));
        !0 === db && E.elements.sort(c);
        return E
    }
};
THREE.Face3 = function (a, b, c, d, e, f) {
    this.a = a;
    this.b = b;
    this.c = c;
    this.normal = d instanceof THREE.Vector3 ? d : new THREE.Vector3;
    this.vertexNormals = d instanceof Array ? d : [];
    this.color = e instanceof THREE.Color ? e : new THREE.Color;
    this.vertexColors = e instanceof Array ? e : [];
    this.vertexTangents = [];
    this.materialIndex = void 0 !== f ? f : 0;
    this.centroid = new THREE.Vector3
};
THREE.Face3.prototype = {
    constructor: THREE.Face3, clone: function () {
        var a = new THREE.Face3(this.a, this.b, this.c);
        a.normal.copy(this.normal);
        a.color.copy(this.color);
        a.centroid.copy(this.centroid);
        a.materialIndex = this.materialIndex;
        var b, c;
        b = 0;
        for (c = this.vertexNormals.length; b < c; b++) a.vertexNormals[b] = this.vertexNormals[b].clone();
        b = 0;
        for (c = this.vertexColors.length; b < c; b++) a.vertexColors[b] = this.vertexColors[b].clone();
        b = 0;
        for (c = this.vertexTangents.length; b < c; b++) a.vertexTangents[b] = this.vertexTangents[b].clone();
        return a
    }
};
THREE.Face4 = function (a, b, c, d, e, f, g) {
    this.a = a;
    this.b = b;
    this.c = c;
    this.d = d;
    this.normal = e instanceof THREE.Vector3 ? e : new THREE.Vector3;
    this.vertexNormals = e instanceof Array ? e : [];
    this.color = f instanceof THREE.Color ? f : new THREE.Color;
    this.vertexColors = f instanceof Array ? f : [];
    this.vertexTangents = [];
    this.materialIndex = void 0 !== g ? g : 0;
    this.centroid = new THREE.Vector3
};
THREE.Face4.prototype = {
    constructor: THREE.Face4, clone: function () {
        var a = new THREE.Face4(this.a, this.b, this.c, this.d);
        a.normal.copy(this.normal);
        a.color.copy(this.color);
        a.centroid.copy(this.centroid);
        a.materialIndex = this.materialIndex;
        var b, c;
        b = 0;
        for (c = this.vertexNormals.length; b < c; b++) a.vertexNormals[b] = this.vertexNormals[b].clone();
        b = 0;
        for (c = this.vertexColors.length; b < c; b++) a.vertexColors[b] = this.vertexColors[b].clone();
        b = 0;
        for (c = this.vertexTangents.length; b < c; b++) a.vertexTangents[b] = this.vertexTangents[b].clone();
        return a
    }
};
THREE.Geometry = function () {
    THREE.EventDispatcher.call(this);
    this.id = THREE.GeometryIdCount++;
    this.name = "";
    this.vertices = [];
    this.colors = [];
    this.normals = [];
    this.faces = [];
    this.faceUvs = [[]];
    this.faceVertexUvs = [[]];
    this.morphTargets = [];
    this.morphColors = [];
    this.morphNormals = [];
    this.skinWeights = [];
    this.skinIndices = [];
    this.lineDistances = [];
    this.boundingSphere = this.boundingBox = null;
    this.hasTangents = !1;
    this.dynamic = !0;
    this.buffersNeedUpdate = this.lineDistancesNeedUpdate = this.colorsNeedUpdate = this.tangentsNeedUpdate =
        this.normalsNeedUpdate = this.uvsNeedUpdate = this.elementsNeedUpdate = this.verticesNeedUpdate = !1
};
THREE.Geometry.prototype = {
    constructor: THREE.Geometry, applyMatrix: function (a) {
        for (var b = (new THREE.Matrix3).getInverse(a).transpose(), c = 0, d = this.vertices.length; c < d; c++) this.vertices[c].applyMatrix4(a);
        c = 0;
        for (d = this.faces.length; c < d; c++) {
            var e = this.faces[c];
            e.normal.applyMatrix3(b).normalize();
            for (var f = 0, g = e.vertexNormals.length; f < g; f++) e.vertexNormals[f].applyMatrix3(b).normalize();
            e.centroid.applyMatrix4(a)
        }
    }, computeCentroids: function () {
        var a, b, c;
        a = 0;
        for (b = this.faces.length; a < b; a++) c = this.faces[a],
            c.centroid.set(0, 0, 0), c instanceof THREE.Face3 ? (c.centroid.add(this.vertices[c.a]), c.centroid.add(this.vertices[c.b]), c.centroid.add(this.vertices[c.c]), c.centroid.divideScalar(3)) : c instanceof THREE.Face4 && (c.centroid.add(this.vertices[c.a]), c.centroid.add(this.vertices[c.b]), c.centroid.add(this.vertices[c.c]), c.centroid.add(this.vertices[c.d]), c.centroid.divideScalar(4))
    }, computeFaceNormals: function () {
        for (var a = new THREE.Vector3, b = new THREE.Vector3, c = 0, d = this.faces.length; c < d; c++) {
            var e = this.faces[c],
                f = this.vertices[e.a], g = this.vertices[e.b];
            a.subVectors(this.vertices[e.c], g);
            b.subVectors(f, g);
            a.cross(b);
            a.normalize();
            e.normal.copy(a)
        }
    }, computeVertexNormals: function (a) {
        var b, c, d, e;
        if (void 0 === this.__tmpVertices) {
            e = this.__tmpVertices = Array(this.vertices.length);
            b = 0;
            for (c = this.vertices.length; b < c; b++) e[b] = new THREE.Vector3;
            b = 0;
            for (c = this.faces.length; b < c; b++) d = this.faces[b], d instanceof THREE.Face3 ? d.vertexNormals = [new THREE.Vector3, new THREE.Vector3, new THREE.Vector3] : d instanceof THREE.Face4 &&
                (d.vertexNormals = [new THREE.Vector3, new THREE.Vector3, new THREE.Vector3, new THREE.Vector3])
        } else {
            e = this.__tmpVertices;
            b = 0;
            for (c = this.vertices.length; b < c; b++) e[b].set(0, 0, 0)
        }
        if (a) {
            var f, g, h, i = new THREE.Vector3, k = new THREE.Vector3, l = new THREE.Vector3, m = new THREE.Vector3,
                p = new THREE.Vector3;
            b = 0;
            for (c = this.faces.length; b < c; b++) d = this.faces[b], d instanceof THREE.Face3 ? (a = this.vertices[d.a], f = this.vertices[d.b], g = this.vertices[d.c], i.subVectors(g, f), k.subVectors(a, f), i.cross(k), e[d.a].add(i), e[d.b].add(i),
                e[d.c].add(i)) : d instanceof THREE.Face4 && (a = this.vertices[d.a], f = this.vertices[d.b], g = this.vertices[d.c], h = this.vertices[d.d], l.subVectors(h, f), k.subVectors(a, f), l.cross(k), e[d.a].add(l), e[d.b].add(l), e[d.d].add(l), m.subVectors(h, g), p.subVectors(f, g), m.cross(p), e[d.b].add(m), e[d.c].add(m), e[d.d].add(m))
        } else {
            b = 0;
            for (c = this.faces.length; b < c; b++) d = this.faces[b], d instanceof THREE.Face3 ? (e[d.a].add(d.normal), e[d.b].add(d.normal), e[d.c].add(d.normal)) : d instanceof THREE.Face4 && (e[d.a].add(d.normal), e[d.b].add(d.normal),
                e[d.c].add(d.normal), e[d.d].add(d.normal))
        }
        b = 0;
        for (c = this.vertices.length; b < c; b++) e[b].normalize();
        b = 0;
        for (c = this.faces.length; b < c; b++) d = this.faces[b], d instanceof THREE.Face3 ? (d.vertexNormals[0].copy(e[d.a]), d.vertexNormals[1].copy(e[d.b]), d.vertexNormals[2].copy(e[d.c])) : d instanceof THREE.Face4 && (d.vertexNormals[0].copy(e[d.a]), d.vertexNormals[1].copy(e[d.b]), d.vertexNormals[2].copy(e[d.c]), d.vertexNormals[3].copy(e[d.d]))
    }, computeMorphNormals: function () {
        var a, b, c, d, e;
        c = 0;
        for (d = this.faces.length; c <
        d; c++) {
            e = this.faces[c];
            e.__originalFaceNormal ? e.__originalFaceNormal.copy(e.normal) : e.__originalFaceNormal = e.normal.clone();
            e.__originalVertexNormals || (e.__originalVertexNormals = []);
            a = 0;
            for (b = e.vertexNormals.length; a < b; a++) e.__originalVertexNormals[a] ? e.__originalVertexNormals[a].copy(e.vertexNormals[a]) : e.__originalVertexNormals[a] = e.vertexNormals[a].clone()
        }
        var f = new THREE.Geometry;
        f.faces = this.faces;
        a = 0;
        for (b = this.morphTargets.length; a < b; a++) {
            if (!this.morphNormals[a]) {
                this.morphNormals[a] = {};
                this.morphNormals[a].faceNormals =
                    [];
                this.morphNormals[a].vertexNormals = [];
                var g = this.morphNormals[a].faceNormals, h = this.morphNormals[a].vertexNormals, i, k;
                c = 0;
                for (d = this.faces.length; c < d; c++) e = this.faces[c], i = new THREE.Vector3, k = e instanceof THREE.Face3 ? {
                    a: new THREE.Vector3,
                    b: new THREE.Vector3,
                    c: new THREE.Vector3
                } : {
                    a: new THREE.Vector3,
                    b: new THREE.Vector3,
                    c: new THREE.Vector3,
                    d: new THREE.Vector3
                }, g.push(i), h.push(k)
            }
            g = this.morphNormals[a];
            f.vertices = this.morphTargets[a].vertices;
            f.computeFaceNormals();
            f.computeVertexNormals();
            c = 0;
            for (d =
                     this.faces.length; c < d; c++) e = this.faces[c], i = g.faceNormals[c], k = g.vertexNormals[c], i.copy(e.normal), e instanceof THREE.Face3 ? (k.a.copy(e.vertexNormals[0]), k.b.copy(e.vertexNormals[1]), k.c.copy(e.vertexNormals[2])) : (k.a.copy(e.vertexNormals[0]), k.b.copy(e.vertexNormals[1]), k.c.copy(e.vertexNormals[2]), k.d.copy(e.vertexNormals[3]))
        }
        c = 0;
        for (d = this.faces.length; c < d; c++) e = this.faces[c], e.normal = e.__originalFaceNormal, e.vertexNormals = e.__originalVertexNormals
    }, computeTangents: function () {
        function a(a, b, c,
                   d, e, f, w) {
            h = a.vertices[b];
            i = a.vertices[c];
            k = a.vertices[d];
            l = g[e];
            m = g[f];
            p = g[w];
            s = i.x - h.x;
            r = k.x - h.x;
            n = i.y - h.y;
            q = k.y - h.y;
            z = i.z - h.z;
            t = k.z - h.z;
            x = m.x - l.x;
            u = p.x - l.x;
            B = m.y - l.y;
            G = p.y - l.y;
            D = 1 / (x * G - u * B);
            E.set((G * s - B * r) * D, (G * n - B * q) * D, (G * z - B * t) * D);
            Z.set((x * r - u * s) * D, (x * q - u * n) * D, (x * t - u * z) * D);
            I[b].add(E);
            I[c].add(E);
            I[d].add(E);
            J[b].add(Z);
            J[c].add(Z);
            J[d].add(Z)
        }

        var b, c, d, e, f, g, h, i, k, l, m, p, s, r, n, q, z, t, x, u, B, G, D, w, I = [], J = [],
            E = new THREE.Vector3, Z = new THREE.Vector3, A = new THREE.Vector3, S = new THREE.Vector3,
            F = new THREE.Vector3;
        b = 0;
        for (c = this.vertices.length; b < c; b++) I[b] = new THREE.Vector3, J[b] = new THREE.Vector3;
        b = 0;
        for (c = this.faces.length; b < c; b++) f = this.faces[b], g = this.faceVertexUvs[0][b], f instanceof THREE.Face3 ? a(this, f.a, f.b, f.c, 0, 1, 2) : f instanceof THREE.Face4 && (a(this, f.a, f.b, f.d, 0, 1, 3), a(this, f.b, f.c, f.d, 1, 2, 3));
        var H = ["a", "b", "c", "d"];
        b = 0;
        for (c = this.faces.length; b < c; b++) {
            f = this.faces[b];
            for (d = 0; d < f.vertexNormals.length; d++) F.copy(f.vertexNormals[d]), e = f[H[d]], w = I[e], A.copy(w), A.sub(F.multiplyScalar(F.dot(w))).normalize(),
                S.crossVectors(f.vertexNormals[d], w), e = S.dot(J[e]), e = 0 > e ? -1 : 1, f.vertexTangents[d] = new THREE.Vector4(A.x, A.y, A.z, e)
        }
        this.hasTangents = !0
    }, computeLineDistances: function () {
        for (var a = 0, b = this.vertices, c = 0, d = b.length; c < d; c++) 0 < c && (a += b[c].distanceTo(b[c - 1])), this.lineDistances[c] = a
    }, computeBoundingBox: function () {
        null === this.boundingBox && (this.boundingBox = new THREE.Box3);
        this.boundingBox.setFromPoints(this.vertices)
    }, computeBoundingSphere: function () {
        null === this.boundingSphere && (this.boundingSphere = new THREE.Sphere);
        this.boundingSphere.setFromCenterAndPoints(this.boundingSphere.center, this.vertices)
    }, mergeVertices: function () {
        var a = {}, b = [], c = [], d, e = Math.pow(10, 4), f, g, h, i, k;
        this.__tmpVertices = void 0;
        f = 0;
        for (g = this.vertices.length; f < g; f++) d = this.vertices[f], d = [Math.round(d.x * e), Math.round(d.y * e), Math.round(d.z * e)].join("_"), void 0 === a[d] ? (a[d] = f, b.push(this.vertices[f]), c[f] = b.length - 1) : c[f] = c[a[d]];
        e = [];
        f = 0;
        for (g = this.faces.length; f < g; f++) if (a = this.faces[f], a instanceof THREE.Face3) {
            a.a = c[a.a];
            a.b = c[a.b];
            a.c = c[a.c];
            h = [a.a, a.b, a.c];
            d = -1;
            for (i = 0; 3 > i; i++) if (h[i] == h[(i + 1) % 3]) {
                e.push(f);
                break
            }
        } else if (a instanceof THREE.Face4) {
            a.a = c[a.a];
            a.b = c[a.b];
            a.c = c[a.c];
            a.d = c[a.d];
            h = [a.a, a.b, a.c, a.d];
            d = -1;
            for (i = 0; 4 > i; i++) h[i] == h[(i + 1) % 4] && (0 <= d && e.push(f), d = i);
            if (0 <= d) {
                h.splice(d, 1);
                var l = new THREE.Face3(h[0], h[1], h[2], a.normal, a.color, a.materialIndex);
                h = 0;
                for (i = this.faceVertexUvs.length; h < i; h++) (k = this.faceVertexUvs[h][f]) && k.splice(d, 1);
                a.vertexNormals && 0 < a.vertexNormals.length && (l.vertexNormals = a.vertexNormals, l.vertexNormals.splice(d,
                    1));
                a.vertexColors && 0 < a.vertexColors.length && (l.vertexColors = a.vertexColors, l.vertexColors.splice(d, 1));
                this.faces[f] = l
            }
        }
        for (f = e.length - 1; 0 <= f; f--) {
            this.faces.splice(f, 1);
            h = 0;
            for (i = this.faceVertexUvs.length; h < i; h++) this.faceVertexUvs[h].splice(f, 1)
        }
        c = this.vertices.length - b.length;
        this.vertices = b;
        return c
    }, clone: function () {
        for (var a = new THREE.Geometry, b = this.vertices, c = 0, d = b.length; c < d; c++) a.vertices.push(b[c].clone());
        b = this.faces;
        c = 0;
        for (d = b.length; c < d; c++) a.faces.push(b[c].clone());
        b = this.faceVertexUvs[0];
        c = 0;
        for (d = b.length; c < d; c++) {
            for (var e = b[c], f = [], g = 0, h = e.length; g < h; g++) f.push(new THREE.Vector2(e[g].x, e[g].y));
            a.faceVertexUvs[0].push(f)
        }
        return a
    }, dispose: function () {
        this.dispatchEvent({type: "dispose"})
    }
};
THREE.GeometryIdCount = 0;
THREE.BufferGeometry = function () {
    THREE.EventDispatcher.call(this);
    this.id = THREE.GeometryIdCount++;
    this.attributes = {};
    this.dynamic = !1;
    this.offsets = [];
    this.boundingSphere = this.boundingBox = null;
    this.hasTangents = !1;
    this.morphTargets = []
};
THREE.BufferGeometry.prototype = {
    constructor: THREE.BufferGeometry, applyMatrix: function (a) {
        var b, c;
        this.attributes.position && (b = this.attributes.position.array);
        this.attributes.normal && (c = this.attributes.normal.array);
        void 0 !== b && (a.multiplyVector3Array(b), this.verticesNeedUpdate = !0);
        void 0 !== c && (b = new THREE.Matrix3, b.getInverse(a).transpose(), b.multiplyVector3Array(c), this.normalizeNormals(), this.normalsNeedUpdate = !0)
    }, computeBoundingBox: function () {
        null === this.boundingBox && (this.boundingBox = new THREE.Box3);
        var a = this.attributes.position.array;
        if (a) {
            var b = this.boundingBox, c, d, e;
            3 <= a.length && (b.min.x = b.max.x = a[0], b.min.y = b.max.y = a[1], b.min.z = b.max.z = a[2]);
            for (var f = 3, g = a.length; f < g; f += 3) c = a[f], d = a[f + 1], e = a[f + 2], c < b.min.x ? b.min.x = c : c > b.max.x && (b.max.x = c), d < b.min.y ? b.min.y = d : d > b.max.y && (b.max.y = d), e < b.min.z ? b.min.z = e : e > b.max.z && (b.max.z = e)
        }
        if (void 0 === a || 0 === a.length) this.boundingBox.min.set(0, 0, 0), this.boundingBox.max.set(0, 0, 0)
    }, computeBoundingSphere: function () {
        null === this.boundingSphere && (this.boundingSphere =
            new THREE.Sphere);
        var a = this.attributes.position.array;
        if (a) {
            for (var b, c = 0, d, e, f = 0, g = a.length; f < g; f += 3) b = a[f], d = a[f + 1], e = a[f + 2], b = b * b + d * d + e * e, b > c && (c = b);
            this.boundingSphere.radius = Math.sqrt(c)
        }
    }, computeVertexNormals: function () {
        if (this.attributes.position) {
            var a, b, c, d;
            a = this.attributes.position.array.length;
            if (void 0 === this.attributes.normal) this.attributes.normal = {
                itemSize: 3,
                array: new Float32Array(a),
                numItems: a
            }; else {
                a = 0;
                for (b = this.attributes.normal.array.length; a < b; a++) this.attributes.normal.array[a] =
                    0
            }
            var e = this.attributes.position.array, f = this.attributes.normal.array, g, h, i, k, l, m,
                p = new THREE.Vector3, s = new THREE.Vector3, r = new THREE.Vector3, n = new THREE.Vector3,
                q = new THREE.Vector3;
            if (this.attributes.index) {
                var z = this.attributes.index.array, t = this.offsets;
                c = 0;
                for (d = t.length; c < d; ++c) {
                    b = t[c].start;
                    g = t[c].count;
                    var x = t[c].index;
                    a = b;
                    for (b += g; a < b; a += 3) g = x + z[a], h = x + z[a + 1], i = x + z[a + 2], k = e[3 * g], l = e[3 * g + 1], m = e[3 * g + 2], p.set(k, l, m), k = e[3 * h], l = e[3 * h + 1], m = e[3 * h + 2], s.set(k, l, m), k = e[3 * i], l = e[3 * i + 1], m = e[3 * i + 2], r.set(k,
                        l, m), n.subVectors(r, s), q.subVectors(p, s), n.cross(q), f[3 * g] += n.x, f[3 * g + 1] += n.y, f[3 * g + 2] += n.z, f[3 * h] += n.x, f[3 * h + 1] += n.y, f[3 * h + 2] += n.z, f[3 * i] += n.x, f[3 * i + 1] += n.y, f[3 * i + 2] += n.z
                }
            } else {
                a = 0;
                for (b = e.length; a < b; a += 9) k = e[a], l = e[a + 1], m = e[a + 2], p.set(k, l, m), k = e[a + 3], l = e[a + 4], m = e[a + 5], s.set(k, l, m), k = e[a + 6], l = e[a + 7], m = e[a + 8], r.set(k, l, m), n.subVectors(r, s), q.subVectors(p, s), n.cross(q), f[a] = n.x, f[a + 1] = n.y, f[a + 2] = n.z, f[a + 3] = n.x, f[a + 4] = n.y, f[a + 5] = n.z, f[a + 6] = n.x, f[a + 7] = n.y, f[a + 8] = n.z
            }
            this.normalizeNormals();
            this.normalsNeedUpdate =
                !0
        }
    }, normalizeNormals: function () {
        for (var a = this.attributes.normal.array, b, c, d, e = 0, f = a.length; e < f; e += 3) b = a[e], c = a[e + 1], d = a[e + 2], b = 1 / Math.sqrt(b * b + c * c + d * d), a[e] *= b, a[e + 1] *= b, a[e + 2] *= b
    }, computeTangents: function () {
        function a(a) {
            ma.x = d[3 * a];
            ma.y = d[3 * a + 1];
            ma.z = d[3 * a + 2];
            eb.copy(ma);
            U = i[a];
            N.copy(U);
            N.sub(ma.multiplyScalar(ma.dot(U))).normalize();
            fa.crossVectors(eb, U);
            ja = fa.dot(k[a]);
            M = 0 > ja ? -1 : 1;
            h[4 * a] = N.x;
            h[4 * a + 1] = N.y;
            h[4 * a + 2] = N.z;
            h[4 * a + 3] = M
        }

        if (void 0 === this.attributes.index || void 0 === this.attributes.position ||
            void 0 === this.attributes.normal || void 0 === this.attributes.uv) console.warn("Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()"); else {
            var b = this.attributes.index.array, c = this.attributes.position.array, d = this.attributes.normal.array,
                e = this.attributes.uv.array, f = c.length / 3;
            if (void 0 === this.attributes.tangent) {
                var g = 4 * f;
                this.attributes.tangent = {itemSize: 4, array: new Float32Array(g), numItems: g}
            }
            for (var h = this.attributes.tangent.array, i = [], k = [], g = 0; g < f; g++) i[g] =
                new THREE.Vector3, k[g] = new THREE.Vector3;
            var l, m, p, s, r, n, q, z, t, x, u, B, G, D, w, f = new THREE.Vector3, g = new THREE.Vector3, I, J, E, Z,
                A, S, F, H = this.offsets;
            E = 0;
            for (Z = H.length; E < Z; ++E) {
                J = H[E].start;
                A = H[E].count;
                var K = H[E].index;
                I = J;
                for (J += A; I < J; I += 3) A = K + b[I], S = K + b[I + 1], F = K + b[I + 2], l = c[3 * A], m = c[3 * A + 1], p = c[3 * A + 2], s = c[3 * S], r = c[3 * S + 1], n = c[3 * S + 2], q = c[3 * F], z = c[3 * F + 1], t = c[3 * F + 2], x = e[2 * A], u = e[2 * A + 1], B = e[2 * S], G = e[2 * S + 1], D = e[2 * F], w = e[2 * F + 1], s -= l, l = q - l, r -= m, m = z - m, n -= p, p = t - p, B -= x, x = D - x, G -= u, u = w - u, w = 1 / (B * u - x * G), f.set((u * s -
                    G * l) * w, (u * r - G * m) * w, (u * n - G * p) * w), g.set((B * l - x * s) * w, (B * m - x * r) * w, (B * p - x * n) * w), i[A].add(f), i[S].add(f), i[F].add(f), k[A].add(g), k[S].add(g), k[F].add(g)
            }
            var N = new THREE.Vector3, fa = new THREE.Vector3, ma = new THREE.Vector3, eb = new THREE.Vector3, M, U, ja;
            E = 0;
            for (Z = H.length; E < Z; ++E) {
                J = H[E].start;
                A = H[E].count;
                K = H[E].index;
                I = J;
                for (J += A; I < J; I += 3) A = K + b[I], S = K + b[I + 1], F = K + b[I + 2], a(A), a(S), a(F)
            }
            this.tangentsNeedUpdate = this.hasTangents = !0
        }
    }, dispose: function () {
        this.dispatchEvent({type: "dispose"})
    }
};
THREE.Camera = function () {
    THREE.Object3D.call(this);
    this.matrixWorldInverse = new THREE.Matrix4;
    this.projectionMatrix = new THREE.Matrix4;
    this.projectionMatrixInverse = new THREE.Matrix4
};
THREE.Camera.prototype = Object.create(THREE.Object3D.prototype);
THREE.Camera.prototype.lookAt = function () {
    var a = new THREE.Matrix4;
    return function (b) {
        a.lookAt(this.position, b, this.up);
        !0 === this.useQuaternion ? this.quaternion.setFromRotationMatrix(a) : this.rotation.setEulerFromRotationMatrix(a, this.eulerOrder)
    }
}();
THREE.OrthographicCamera = function (a, b, c, d, e, f) {
    THREE.Camera.call(this);
    this.left = a;
    this.right = b;
    this.top = c;
    this.bottom = d;
    this.near = void 0 !== e ? e : 0.1;
    this.far = void 0 !== f ? f : 2E3;
    this.updateProjectionMatrix()
};
THREE.OrthographicCamera.prototype = Object.create(THREE.Camera.prototype);
THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () {
    this.projectionMatrix.makeOrthographic(this.left, this.right, this.top, this.bottom, this.near, this.far)
};
THREE.PerspectiveCamera = function (a, b, c, d) {
    THREE.Camera.call(this);
    this.fov = void 0 !== a ? a : 50;
    this.aspect = void 0 !== b ? b : 1;
    this.near = void 0 !== c ? c : 0.1;
    this.far = void 0 !== d ? d : 2E3;
    this.updateProjectionMatrix()
};
THREE.PerspectiveCamera.prototype = Object.create(THREE.Camera.prototype);
THREE.PerspectiveCamera.prototype.setLens = function (a, b) {
    void 0 === b && (b = 24);
    this.fov = 2 * THREE.Math.radToDeg(Math.atan(b / (2 * a)));
    this.updateProjectionMatrix()
};
THREE.PerspectiveCamera.prototype.setViewOffset = function (a, b, c, d, e, f) {
    this.fullWidth = a;
    this.fullHeight = b;
    this.x = c;
    this.y = d;
    this.width = e;
    this.height = f;
    this.updateProjectionMatrix()
};
THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () {
    if (this.fullWidth) {
        var a = this.fullWidth / this.fullHeight, b = Math.tan(THREE.Math.degToRad(0.5 * this.fov)) * this.near, c = -b,
            d = a * c, a = Math.abs(a * b - d), c = Math.abs(b - c);
        this.projectionMatrix.makeFrustum(d + this.x * a / this.fullWidth, d + (this.x + this.width) * a / this.fullWidth, b - (this.y + this.height) * c / this.fullHeight, b - this.y * c / this.fullHeight, this.near, this.far)
    } else this.projectionMatrix.makePerspective(this.fov, this.aspect, this.near, this.far)
};
THREE.Light = function (a) {
    THREE.Object3D.call(this);
    this.color = new THREE.Color(a)
};
THREE.Light.prototype = Object.create(THREE.Object3D.prototype);
THREE.Light.prototype.clone = function (a) {
    void 0 === a && (a = new THREE.Light);
    THREE.Object3D.prototype.clone.call(this, a);
    a.color.copy(this.color);
    return a
};
THREE.AmbientLight = function (a) {
    THREE.Light.call(this, a)
};
THREE.AmbientLight.prototype = Object.create(THREE.Light.prototype);
THREE.AmbientLight.prototype.clone = function () {
    var a = new THREE.AmbientLight;
    THREE.Light.prototype.clone.call(this, a);
    return a
};
THREE.AreaLight = function (a, b) {
    THREE.Light.call(this, a);
    this.normal = new THREE.Vector3(0, -1, 0);
    this.right = new THREE.Vector3(1, 0, 0);
    this.intensity = void 0 !== b ? b : 1;
    this.height = this.width = 1;
    this.constantAttenuation = 1.5;
    this.linearAttenuation = 0.5;
    this.quadraticAttenuation = 0.1
};
THREE.AreaLight.prototype = Object.create(THREE.Light.prototype);
THREE.DirectionalLight = function (a, b) {
    THREE.Light.call(this, a);
    this.position.set(0, 1, 0);
    this.target = new THREE.Object3D;
    this.intensity = void 0 !== b ? b : 1;
    this.onlyShadow = this.castShadow = !1;
    this.shadowCameraNear = 50;
    this.shadowCameraFar = 5E3;
    this.shadowCameraLeft = -500;
    this.shadowCameraTop = this.shadowCameraRight = 500;
    this.shadowCameraBottom = -500;
    this.shadowCameraVisible = !1;
    this.shadowBias = 0;
    this.shadowDarkness = 0.5;
    this.shadowMapHeight = this.shadowMapWidth = 512;
    this.shadowCascade = !1;
    this.shadowCascadeOffset = new THREE.Vector3(0,
        0, -1E3);
    this.shadowCascadeCount = 2;
    this.shadowCascadeBias = [0, 0, 0];
    this.shadowCascadeWidth = [512, 512, 512];
    this.shadowCascadeHeight = [512, 512, 512];
    this.shadowCascadeNearZ = [-1, 0.99, 0.998];
    this.shadowCascadeFarZ = [0.99, 0.998, 1];
    this.shadowCascadeArray = [];
    this.shadowMatrix = this.shadowCamera = this.shadowMapSize = this.shadowMap = null
};
THREE.DirectionalLight.prototype = Object.create(THREE.Light.prototype);
THREE.DirectionalLight.prototype.clone = function () {
    var a = new THREE.DirectionalLight;
    THREE.Light.prototype.clone.call(this, a);
    a.target = this.target.clone();
    a.intensity = this.intensity;
    a.castShadow = this.castShadow;
    a.onlyShadow = this.onlyShadow;
    return a
};
THREE.HemisphereLight = function (a, b, c) {
    THREE.Light.call(this, a);
    this.position.set(0, 100, 0);
    this.groundColor = new THREE.Color(b);
    this.intensity = void 0 !== c ? c : 1
};
THREE.HemisphereLight.prototype = Object.create(THREE.Light.prototype);
THREE.HemisphereLight.prototype.clone = function () {
    var a = new THREE.PointLight;
    THREE.Light.prototype.clone.call(this, a);
    a.groundColor.copy(this.groundColor);
    a.intensity = this.intensity;
    return a
};
THREE.PointLight = function (a, b, c) {
    THREE.Light.call(this, a);
    this.intensity = void 0 !== b ? b : 1;
    this.distance = void 0 !== c ? c : 0
};
THREE.PointLight.prototype = Object.create(THREE.Light.prototype);
THREE.PointLight.prototype.clone = function () {
    var a = new THREE.PointLight;
    THREE.Light.prototype.clone.call(this, a);
    a.intensity = this.intensity;
    a.distance = this.distance;
    return a
};
THREE.SpotLight = function (a, b, c, d, e) {
    THREE.Light.call(this, a);
    this.position.set(0, 1, 0);
    this.target = new THREE.Object3D;
    this.intensity = void 0 !== b ? b : 1;
    this.distance = void 0 !== c ? c : 0;
    this.angle = void 0 !== d ? d : Math.PI / 2;
    this.exponent = void 0 !== e ? e : 10;
    this.onlyShadow = this.castShadow = !1;
    this.shadowCameraNear = 50;
    this.shadowCameraFar = 5E3;
    this.shadowCameraFov = 50;
    this.shadowCameraVisible = !1;
    this.shadowBias = 0;
    this.shadowDarkness = 0.5;
    this.shadowMapHeight = this.shadowMapWidth = 512;
    this.shadowMatrix = this.shadowCamera = this.shadowMapSize =
        this.shadowMap = null
};
THREE.SpotLight.prototype = Object.create(THREE.Light.prototype);
THREE.SpotLight.prototype.clone = function () {
    var a = new THREE.SpotLight;
    THREE.Light.prototype.clone.call(this, a);
    a.target = this.target.clone();
    a.intensity = this.intensity;
    a.distance = this.distance;
    a.angle = this.angle;
    a.exponent = this.exponent;
    a.castShadow = this.castShadow;
    a.onlyShadow = this.onlyShadow;
    return a
};
THREE.Loader = function (a) {
    this.statusDomElement = (this.showStatus = a) ? THREE.Loader.prototype.addStatusElement() : null;
    this.onLoadStart = function () {
    };
    this.onLoadProgress = function () {
    };
    this.onLoadComplete = function () {
    }
};
THREE.Loader.prototype = {
    constructor: THREE.Loader, crossOrigin: "anonymous", addStatusElement: function () {
        var a = document.createElement("div");
        a.style.position = "absolute";
        a.style.right = "0px";
        a.style.top = "0px";
        a.style.fontSize = "0.8em";
        a.style.textAlign = "left";
        a.style.background = "rgba(0,0,0,0.25)";
        a.style.color = "#fff";
        a.style.width = "120px";
        a.style.padding = "0.5em 0.5em 0.5em 0.5em";
        a.style.zIndex = 1E3;
        a.innerHTML = "Loading ...";
        return a
    }, updateProgress: function (a) {
        var b = "Loaded ", b = a.total ? b + ((100 * a.loaded /
            a.total).toFixed(0) + "%") : b + ((a.loaded / 1E3).toFixed(2) + " KB");
        this.statusDomElement.innerHTML = b
    }, extractUrlBase: function (a) {
        a = a.split("/");
        a.pop();
        return (1 > a.length ? "." : a.join("/")) + "/"
    }, initMaterials: function (a, b) {
        for (var c = [], d = 0; d < a.length; ++d) c[d] = THREE.Loader.prototype.createMaterial(a[d], b);
        return c
    }, needsTangents: function (a) {
        for (var b = 0, c = a.length; b < c; b++) if (a[b] instanceof THREE.ShaderMaterial) return !0;
        return !1
    }, createMaterial: function (a, b) {
        function c(a) {
            a = Math.log(a) / Math.LN2;
            return Math.floor(a) ==
                a
        }

        function d(a) {
            a = Math.log(a) / Math.LN2;
            return Math.pow(2, Math.round(a))
        }

        function e(a, e, f, h, i, k, q) {
            var z = /\.dds$/i.test(f), t = b + "/" + f;
            if (z) {
                var x = THREE.ImageUtils.loadCompressedTexture(t);
                a[e] = x
            } else x = document.createElement("canvas"), a[e] = new THREE.Texture(x);
            a[e].sourceFile = f;
            h && (a[e].repeat.set(h[0], h[1]), 1 !== h[0] && (a[e].wrapS = THREE.RepeatWrapping), 1 !== h[1] && (a[e].wrapT = THREE.RepeatWrapping));
            i && a[e].offset.set(i[0], i[1]);
            k && (f = {repeat: THREE.RepeatWrapping, mirror: THREE.MirroredRepeatWrapping}, void 0 !==
            f[k[0]] && (a[e].wrapS = f[k[0]]), void 0 !== f[k[1]] && (a[e].wrapT = f[k[1]]));
            q && (a[e].anisotropy = q);
            if (!z) {
                var u = a[e], a = new Image;
                a.onload = function () {
                    if (!c(this.width) || !c(this.height)) {
                        var a = d(this.width), b = d(this.height);
                        u.image.width = a;
                        u.image.height = b;
                        u.image.getContext("2d").drawImage(this, 0, 0, a, b)
                    } else u.image = this;
                    u.needsUpdate = !0
                };
                a.crossOrigin = g.crossOrigin;
                a.src = t
            }
        }

        function f(a) {
            return (255 * a[0] << 16) + (255 * a[1] << 8) + 255 * a[2]
        }

        var g = this, h = "MeshLambertMaterial", i = {
            color: 15658734, opacity: 1, map: null,
            lightMap: null, normalMap: null, bumpMap: null, wireframe: !1
        };
        if (a.shading) {
            var k = a.shading.toLowerCase();
            "phong" === k ? h = "MeshPhongMaterial" : "basic" === k && (h = "MeshBasicMaterial")
        }
        void 0 !== a.blending && void 0 !== THREE[a.blending] && (i.blending = THREE[a.blending]);
        if (void 0 !== a.transparent || 1 > a.opacity) i.transparent = a.transparent;
        void 0 !== a.depthTest && (i.depthTest = a.depthTest);
        void 0 !== a.depthWrite && (i.depthWrite = a.depthWrite);
        void 0 !== a.visible && (i.visible = a.visible);
        void 0 !== a.flipSided && (i.side = THREE.BackSide);
        void 0 !== a.doubleSided && (i.side = THREE.DoubleSide);
        void 0 !== a.wireframe && (i.wireframe = a.wireframe);
        void 0 !== a.vertexColors && ("face" === a.vertexColors ? i.vertexColors = THREE.FaceColors : a.vertexColors && (i.vertexColors = THREE.VertexColors));
        a.colorDiffuse ? i.color = f(a.colorDiffuse) : a.DbgColor && (i.color = a.DbgColor);
        a.colorSpecular && (i.specular = f(a.colorSpecular));
        a.colorAmbient && (i.ambient = f(a.colorAmbient));
        a.transparency && (i.opacity = a.transparency);
        a.specularCoef && (i.shininess = a.specularCoef);
        a.mapDiffuse &&
        b && e(i, "map", a.mapDiffuse, a.mapDiffuseRepeat, a.mapDiffuseOffset, a.mapDiffuseWrap, a.mapDiffuseAnisotropy);
        a.mapLight && b && e(i, "lightMap", a.mapLight, a.mapLightRepeat, a.mapLightOffset, a.mapLightWrap, a.mapLightAnisotropy);
        a.mapBump && b && e(i, "bumpMap", a.mapBump, a.mapBumpRepeat, a.mapBumpOffset, a.mapBumpWrap, a.mapBumpAnisotropy);
        a.mapNormal && b && e(i, "normalMap", a.mapNormal, a.mapNormalRepeat, a.mapNormalOffset, a.mapNormalWrap, a.mapNormalAnisotropy);
        a.mapSpecular && b && e(i, "specularMap", a.mapSpecular, a.mapSpecularRepeat,
            a.mapSpecularOffset, a.mapSpecularWrap, a.mapSpecularAnisotropy);
        a.mapBumpScale && (i.bumpScale = a.mapBumpScale);
        a.mapNormal ? (h = THREE.ShaderLib.normalmap, k = THREE.UniformsUtils.clone(h.uniforms), k.tNormal.value = i.normalMap, a.mapNormalFactor && k.uNormalScale.value.set(a.mapNormalFactor, a.mapNormalFactor), i.map && (k.tDiffuse.value = i.map, k.enableDiffuse.value = !0), i.specularMap && (k.tSpecular.value = i.specularMap, k.enableSpecular.value = !0), i.lightMap && (k.tAO.value = i.lightMap, k.enableAO.value = !0), k.uDiffuseColor.value.setHex(i.color),
            k.uSpecularColor.value.setHex(i.specular), k.uAmbientColor.value.setHex(i.ambient), k.uShininess.value = i.shininess, void 0 !== i.opacity && (k.uOpacity.value = i.opacity), h = new THREE.ShaderMaterial({
            fragmentShader: h.fragmentShader,
            vertexShader: h.vertexShader,
            uniforms: k,
            lights: !0,
            fog: !0
        }), i.transparent && (h.transparent = !0)) : h = new THREE[h](i);
        void 0 !== a.DbgName && (h.name = a.DbgName);
        return h
    }
};
THREE.ImageLoader = function () {
    THREE.EventDispatcher.call(this);
    this.crossOrigin = null
};
THREE.ImageLoader.prototype = {
    constructor: THREE.ImageLoader, load: function (a, b) {
        var c = this;
        void 0 === b && (b = new Image);
        b.addEventListener("load", function () {
            c.dispatchEvent({type: "load", content: b})
        }, !1);
        b.addEventListener("error", function () {
            c.dispatchEvent({type: "error", message: "Couldn't load URL [" + a + "]"})
        }, !1);
        c.crossOrigin && (b.crossOrigin = c.crossOrigin);
        b.src = a
    }
};
THREE.JSONLoader = function (a) {
    THREE.Loader.call(this, a);
    this.withCredentials = !1
};
THREE.JSONLoader.prototype = Object.create(THREE.Loader.prototype);
THREE.JSONLoader.prototype.load = function (a, b, c) {
    c = c && "string" === typeof c ? c : this.extractUrlBase(a);
    this.onLoadStart();
    this.loadAjaxJSON(this, a, b, c)
};
THREE.JSONLoader.prototype.loadAjaxJSON = function (a, b, c, d, e) {
    var f = new XMLHttpRequest, g = 0;
    f.onreadystatechange = function () {
        if (f.readyState === f.DONE) if (200 === f.status || 0 === f.status) {
            if (f.responseText) {
                var h = JSON.parse(f.responseText), h = a.parse(h, d);
                c(h.geometry, h.materials)
            } else console.warn("THREE.JSONLoader: [" + b + "] seems to be unreachable or file there is empty");
            a.onLoadComplete()
        } else console.error("THREE.JSONLoader: Couldn't load [" + b + "] [" + f.status + "]"); else f.readyState === f.LOADING ? e && (0 === g &&
        (g = f.getResponseHeader("Content-Length")), e({
            total: g,
            loaded: f.responseText.length
        })) : f.readyState === f.HEADERS_RECEIVED && (g = f.getResponseHeader("Content-Length"))
    };
    f.open("GET", b, !0);
    f.withCredentials = this.withCredentials;
    f.send(null)
};
THREE.JSONLoader.prototype.parse = function (a, b) {
    var c = new THREE.Geometry, d = void 0 !== a.scale ? 1 / a.scale : 1, e, f, g, h, i, k, l, m, p, s, r, n, q, z, t,
        x = a.faces;
    s = a.vertices;
    var u = a.normals, B = a.colors, G = 0;
    for (e = 0; e < a.uvs.length; e++) a.uvs[e].length && G++;
    for (e = 0; e < G; e++) c.faceUvs[e] = [], c.faceVertexUvs[e] = [];
    h = 0;
    for (i = s.length; h < i;) k = new THREE.Vector3, k.x = s[h++] * d, k.y = s[h++] * d, k.z = s[h++] * d, c.vertices.push(k);
    h = 0;
    for (i = x.length; h < i;) {
        s = x[h++];
        k = s & 1;
        g = s & 2;
        e = s & 4;
        f = s & 8;
        m = s & 16;
        l = s & 32;
        r = s & 64;
        s &= 128;
        k ? (n = new THREE.Face4, n.a =
            x[h++], n.b = x[h++], n.c = x[h++], n.d = x[h++], k = 4) : (n = new THREE.Face3, n.a = x[h++], n.b = x[h++], n.c = x[h++], k = 3);
        g && (g = x[h++], n.materialIndex = g);
        g = c.faces.length;
        if (e) for (e = 0; e < G; e++) q = a.uvs[e], p = x[h++], t = q[2 * p], p = q[2 * p + 1], c.faceUvs[e][g] = new THREE.Vector2(t, p);
        if (f) for (e = 0; e < G; e++) {
            q = a.uvs[e];
            z = [];
            for (f = 0; f < k; f++) p = x[h++], t = q[2 * p], p = q[2 * p + 1], z[f] = new THREE.Vector2(t, p);
            c.faceVertexUvs[e][g] = z
        }
        m && (m = 3 * x[h++], f = new THREE.Vector3, f.x = u[m++], f.y = u[m++], f.z = u[m], n.normal = f);
        if (l) for (e = 0; e < k; e++) m = 3 * x[h++], f = new THREE.Vector3,
            f.x = u[m++], f.y = u[m++], f.z = u[m], n.vertexNormals.push(f);
        r && (l = x[h++], l = new THREE.Color(B[l]), n.color = l);
        if (s) for (e = 0; e < k; e++) l = x[h++], l = new THREE.Color(B[l]), n.vertexColors.push(l);
        c.faces.push(n)
    }
    if (a.skinWeights) {
        h = 0;
        for (i = a.skinWeights.length; h < i; h += 2) x = a.skinWeights[h], u = a.skinWeights[h + 1], c.skinWeights.push(new THREE.Vector4(x, u, 0, 0))
    }
    if (a.skinIndices) {
        h = 0;
        for (i = a.skinIndices.length; h < i; h += 2) x = a.skinIndices[h], u = a.skinIndices[h + 1], c.skinIndices.push(new THREE.Vector4(x, u, 0, 0))
    }
    c.bones = a.bones;
    c.animation = a.animation;
    if (void 0 !== a.morphTargets) {
        h = 0;
        for (i = a.morphTargets.length; h < i; h++) {
            c.morphTargets[h] = {};
            c.morphTargets[h].name = a.morphTargets[h].name;
            c.morphTargets[h].vertices = [];
            B = c.morphTargets[h].vertices;
            G = a.morphTargets[h].vertices;
            x = 0;
            for (u = G.length; x < u; x += 3) s = new THREE.Vector3, s.x = G[x] * d, s.y = G[x + 1] * d, s.z = G[x + 2] * d, B.push(s)
        }
    }
    if (void 0 !== a.morphColors) {
        h = 0;
        for (i = a.morphColors.length; h < i; h++) {
            c.morphColors[h] = {};
            c.morphColors[h].name = a.morphColors[h].name;
            c.morphColors[h].colors = [];
            u = c.morphColors[h].colors;
            B = a.morphColors[h].colors;
            d = 0;
            for (x = B.length; d < x; d += 3) G = new THREE.Color(16755200), G.setRGB(B[d], B[d + 1], B[d + 2]), u.push(G)
        }
    }
    c.computeCentroids();
    c.computeFaceNormals();
    d = this.initMaterials(a.materials, b);
    this.needsTangents(d) && c.computeTangents();
    return {geometry: c, materials: d}
};
THREE.LoadingMonitor = function () {
    THREE.EventDispatcher.call(this);
    var a = this, b = 0, c = 0, d = function () {
        b++;
        a.dispatchEvent({type: "progress", loaded: b, total: c});
        b === c && a.dispatchEvent({type: "load"})
    };
    this.add = function (a) {
        c++;
        a.addEventListener("load", d, !1)
    }
};
THREE.SceneLoader = function () {
    this.onLoadStart = function () {
    };
    this.onLoadProgress = function () {
    };
    this.onLoadComplete = function () {
    };
    this.callbackSync = function () {
    };
    this.callbackProgress = function () {
    };
    this.geometryHandlerMap = {};
    this.hierarchyHandlerMap = {};
    this.addGeometryHandler("ascii", THREE.JSONLoader)
};
THREE.SceneLoader.prototype.constructor = THREE.SceneLoader;
THREE.SceneLoader.prototype.load = function (a, b) {
    var c = this, d = new XMLHttpRequest;
    d.onreadystatechange = function () {
        if (4 === d.readyState) if (200 === d.status || 0 === d.status) {
            var e = JSON.parse(d.responseText);
            c.parse(e, b, a)
        } else console.error("THREE.SceneLoader: Couldn't load [" + a + "] [" + d.status + "]")
    };
    d.open("GET", a, !0);
    d.send(null)
};
THREE.SceneLoader.prototype.addGeometryHandler = function (a, b) {
    this.geometryHandlerMap[a] = {loaderClass: b}
};
THREE.SceneLoader.prototype.addHierarchyHandler = function (a, b) {
    this.hierarchyHandlerMap[a] = {loaderClass: b}
};
THREE.SceneLoader.prototype.parse = function (a, b, c) {
    function d(a, b) {
        return "relativeToHTML" == b ? a : m + "/" + a
    }

    function e() {
        f(w.scene, J.objects)
    }

    function f(a, b) {
        var c, e, g, i, k, m, n;
        for (n in b) if (void 0 === w.objects[n]) {
            var q = b[n], u = null;
            if (q.type && q.type in l.hierarchyHandlerMap) {
                if (void 0 === q.loading) {
                    e = {
                        type: 1,
                        url: 1,
                        material: 1,
                        position: 1,
                        rotation: 1,
                        scale: 1,
                        visible: 1,
                        children: 1,
                        userData: 1,
                        skin: 1,
                        morph: 1,
                        mirroredLoop: 1,
                        duration: 1
                    };
                    g = {};
                    for (var A in q) A in e || (g[A] = q[A]);
                    s = w.materials[q.material];
                    q.loading = !0;
                    e = l.hierarchyHandlerMap[q.type].loaderObject;
                    e.options ? e.load(d(q.url, J.urlBaseType), h(n, a, s, q)) : e.load(d(q.url, J.urlBaseType), h(n, a, s, q), g)
                }
            } else if (void 0 !== q.geometry) {
                if (p = w.geometries[q.geometry]) {
                    u = !1;
                    s = w.materials[q.material];
                    u = s instanceof THREE.ShaderMaterial;
                    g = q.position;
                    i = q.rotation;
                    k = q.scale;
                    c = q.matrix;
                    m = q.quaternion;
                    q.material || (s = new THREE.MeshFaceMaterial(w.face_materials[q.geometry]));
                    s instanceof THREE.MeshFaceMaterial && 0 === s.materials.length && (s = new THREE.MeshFaceMaterial(w.face_materials[q.geometry]));
                    if (s instanceof THREE.MeshFaceMaterial) for (e = 0; e < s.materials.length; e++) u = u || s.materials[e] instanceof THREE.ShaderMaterial;
                    u && p.computeTangents();
                    q.skin ? u = new THREE.SkinnedMesh(p, s) : q.morph ? (u = new THREE.MorphAnimMesh(p, s), void 0 !== q.duration && (u.duration = q.duration), void 0 !== q.time && (u.time = q.time), void 0 !== q.mirroredLoop && (u.mirroredLoop = q.mirroredLoop), s.morphNormals && p.computeMorphNormals()) : u = new THREE.Mesh(p, s);
                    u.name = n;
                    c ? (u.matrixAutoUpdate = !1, u.matrix.set(c[0], c[1], c[2], c[3], c[4], c[5], c[6],
                        c[7], c[8], c[9], c[10], c[11], c[12], c[13], c[14], c[15])) : (u.position.set(g[0], g[1], g[2]), m ? (u.quaternion.set(m[0], m[1], m[2], m[3]), u.useQuaternion = !0) : u.rotation.set(i[0], i[1], i[2]), u.scale.set(k[0], k[1], k[2]));
                    u.visible = q.visible;
                    u.castShadow = q.castShadow;
                    u.receiveShadow = q.receiveShadow;
                    a.add(u);
                    w.objects[n] = u
                }
            } else "DirectionalLight" === q.type || "PointLight" === q.type || "AmbientLight" === q.type ? (t = void 0 !== q.color ? q.color : 16777215, x = void 0 !== q.intensity ? q.intensity : 1, "DirectionalLight" === q.type ? (g = q.direction,
                z = new THREE.DirectionalLight(t, x), z.position.set(g[0], g[1], g[2]), q.target && (I.push({
                object: z,
                targetName: q.target
            }), z.target = null)) : "PointLight" === q.type ? (g = q.position, e = q.distance, z = new THREE.PointLight(t, x, e), z.position.set(g[0], g[1], g[2])) : "AmbientLight" === q.type && (z = new THREE.AmbientLight(t)), a.add(z), z.name = n, w.lights[n] = z, w.objects[n] = z) : "PerspectiveCamera" === q.type || "OrthographicCamera" === q.type ? ("PerspectiveCamera" === q.type ? r = new THREE.PerspectiveCamera(q.fov, q.aspect, q.near, q.far) : "OrthographicCamera" ===
                q.type && (r = new THREE.OrthographicCamera(q.left, q.right, q.top, q.bottom, q.near, q.far)), g = q.position, r.position.set(g[0], g[1], g[2]), a.add(r), r.name = n, w.cameras[n] = r, w.objects[n] = r) : (g = q.position, i = q.rotation, k = q.scale, m = q.quaternion, u = new THREE.Object3D, u.name = n, u.position.set(g[0], g[1], g[2]), m ? (u.quaternion.set(m[0], m[1], m[2], m[3]), u.useQuaternion = !0) : u.rotation.set(i[0], i[1], i[2]), u.scale.set(k[0], k[1], k[2]), u.visible = void 0 !== q.visible ? q.visible : !1, a.add(u), w.objects[n] = u, w.empties[n] = u);
            if (u) {
                if (void 0 !==
                    q.userData) for (var B in q.userData) u.userData[B] = q.userData[B];
                if (void 0 !== q.groups) for (e = 0; e < q.groups.length; e++) g = q.groups[e], void 0 === w.groups[g] && (w.groups[g] = []), w.groups[g].push(n);
                void 0 !== q.children && f(u, q.children)
            }
        }
    }

    function g(a) {
        return function (b, c) {
            w.geometries[a] = b;
            w.face_materials[a] = c;
            e();
            u -= 1;
            l.onLoadComplete();
            k()
        }
    }

    function h(a, b, c, d) {
        return function (f) {
            var f = f.content ? f.content : f.dae ? f.scene : f, g = d.position, h = d.rotation, i = d.quaternion,
                m = d.scale;
            f.position.set(g[0], g[1], g[2]);
            i ? (f.quaternion.set(i[0],
                i[1], i[2], i[3]), f.useQuaternion = !0) : f.rotation.set(h[0], h[1], h[2]);
            f.scale.set(m[0], m[1], m[2]);
            c && f.traverse(function (a) {
                a.material = c
            });
            var p = void 0 !== d.visible ? d.visible : !0;
            f.traverse(function (a) {
                a.visible = p
            });
            b.add(f);
            f.name = a;
            w.objects[a] = f;
            e();
            u -= 1;
            l.onLoadComplete();
            k()
        }
    }

    function i(a) {
        return function (b, c) {
            w.geometries[a] = b;
            w.face_materials[a] = c
        }
    }

    function k() {
        l.callbackProgress({totalModels: G, totalTextures: D, loadedModels: G - u, loadedTextures: D - B}, w);
        l.onLoadProgress();
        if (0 === u && 0 === B) {
            for (var a =
                0; a < I.length; a++) {
                var c = I[a], d = w.objects[c.targetName];
                d ? c.object.target = d : (c.object.target = new THREE.Object3D, w.scene.add(c.object.target));
                c.object.target.userData.targetInverse = c.object
            }
            b(w)
        }
    }

    var l = this, m = THREE.Loader.prototype.extractUrlBase(c), p, s, r, n, q, z, t, x, u, B, G, D, w, I = [], J = a, E;
    for (E in this.geometryHandlerMap) a = this.geometryHandlerMap[E].loaderClass, this.geometryHandlerMap[E].loaderObject = new a;
    for (E in this.hierarchyHandlerMap) a = this.hierarchyHandlerMap[E].loaderClass, this.hierarchyHandlerMap[E].loaderObject =
        new a;
    B = u = 0;
    w = {
        scene: new THREE.Scene,
        geometries: {},
        face_materials: {},
        materials: {},
        textures: {},
        objects: {},
        cameras: {},
        lights: {},
        fogs: {},
        empties: {},
        groups: {}
    };
    if (J.transform && (E = J.transform.position, a = J.transform.rotation, c = J.transform.scale, E && w.scene.position.set(E[0], E[1], E[2]), a && w.scene.rotation.set(a[0], a[1], a[2]), c && w.scene.scale.set(c[0], c[1], c[2]), E || a || c)) w.scene.updateMatrix(), w.scene.updateMatrixWorld();
    E = function (a) {
        return function () {
            B -= a;
            k();
            l.onLoadComplete()
        }
    };
    for (var Z in J.fogs) a = J.fogs[Z],
        "linear" === a.type ? n = new THREE.Fog(0, a.near, a.far) : "exp2" === a.type && (n = new THREE.FogExp2(0, a.density)), a = a.color, n.color.setRGB(a[0], a[1], a[2]), w.fogs[Z] = n;
    for (var A in J.geometries) n = J.geometries[A], n.type in this.geometryHandlerMap && (u += 1, l.onLoadStart());
    for (var S in J.objects) n = J.objects[S], n.type && n.type in this.hierarchyHandlerMap && (u += 1, l.onLoadStart());
    G = u;
    for (A in J.geometries) if (n = J.geometries[A], "cube" === n.type) p = new THREE.CubeGeometry(n.width, n.height, n.depth, n.widthSegments, n.heightSegments,
        n.depthSegments), w.geometries[A] = p; else if ("plane" === n.type) p = new THREE.PlaneGeometry(n.width, n.height, n.widthSegments, n.heightSegments), w.geometries[A] = p; else if ("sphere" === n.type) p = new THREE.SphereGeometry(n.radius, n.widthSegments, n.heightSegments), w.geometries[A] = p; else if ("cylinder" === n.type) p = new THREE.CylinderGeometry(n.topRad, n.botRad, n.height, n.radSegs, n.heightSegs), w.geometries[A] = p; else if ("torus" === n.type) p = new THREE.TorusGeometry(n.radius, n.tube, n.segmentsR, n.segmentsT), w.geometries[A] =
        p; else if ("icosahedron" === n.type) p = new THREE.IcosahedronGeometry(n.radius, n.subdivisions), w.geometries[A] = p; else if (n.type in this.geometryHandlerMap) {
        S = {};
        for (q in n) "type" !== q && "url" !== q && (S[q] = n[q]);
        this.geometryHandlerMap[n.type].loaderObject.load(d(n.url, J.urlBaseType), g(A), S)
    } else "embedded" === n.type && (S = J.embeds[n.id], S.metadata = J.metadata, S && (S = this.geometryHandlerMap.ascii.loaderObject.parse(S, ""), i(A)(S.geometry, S.materials)));
    for (var F in J.textures) if (A = J.textures[F], A.url instanceof Array) {
        B +=
            A.url.length;
        for (q = 0; q < A.url.length; q++) l.onLoadStart()
    } else B += 1, l.onLoadStart();
    D = B;
    for (F in J.textures) {
        A = J.textures[F];
        void 0 !== A.mapping && void 0 !== THREE[A.mapping] && (A.mapping = new THREE[A.mapping]);
        if (A.url instanceof Array) {
            S = A.url.length;
            n = [];
            for (q = 0; q < S; q++) n[q] = d(A.url[q], J.urlBaseType);
            q = (q = /\.dds$/i.test(n[0])) ? THREE.ImageUtils.loadCompressedTextureCube(n, A.mapping, E(S)) : THREE.ImageUtils.loadTextureCube(n, A.mapping, E(S))
        } else q = /\.dds$/i.test(A.url), S = d(A.url, J.urlBaseType), n = E(1), q = q ?
            THREE.ImageUtils.loadCompressedTexture(S, A.mapping, n) : THREE.ImageUtils.loadTexture(S, A.mapping, n), void 0 !== THREE[A.minFilter] && (q.minFilter = THREE[A.minFilter]), void 0 !== THREE[A.magFilter] && (q.magFilter = THREE[A.magFilter]), A.anisotropy && (q.anisotropy = A.anisotropy), A.repeat && (q.repeat.set(A.repeat[0], A.repeat[1]), 1 !== A.repeat[0] && (q.wrapS = THREE.RepeatWrapping), 1 !== A.repeat[1] && (q.wrapT = THREE.RepeatWrapping)), A.offset && q.offset.set(A.offset[0], A.offset[1]), A.wrap && (S = {
            repeat: THREE.RepeatWrapping,
            mirror: THREE.MirroredRepeatWrapping
        },
        void 0 !== S[A.wrap[0]] && (q.wrapS = S[A.wrap[0]]), void 0 !== S[A.wrap[1]] && (q.wrapT = S[A.wrap[1]]));
        w.textures[F] = q
    }
    var H, K;
    for (H in J.materials) {
        F = J.materials[H];
        for (K in F.parameters) "envMap" === K || "map" === K || "lightMap" === K || "bumpMap" === K ? F.parameters[K] = w.textures[F.parameters[K]] : "shading" === K ? F.parameters[K] = "flat" === F.parameters[K] ? THREE.FlatShading : THREE.SmoothShading : "side" === K ? F.parameters[K] = "double" == F.parameters[K] ? THREE.DoubleSide : "back" == F.parameters[K] ? THREE.BackSide : THREE.FrontSide : "blending" ===
        K ? F.parameters[K] = F.parameters[K] in THREE ? THREE[F.parameters[K]] : THREE.NormalBlending : "combine" === K ? F.parameters[K] = F.parameters[K] in THREE ? THREE[F.parameters[K]] : THREE.MultiplyOperation : "vertexColors" === K ? "face" == F.parameters[K] ? F.parameters[K] = THREE.FaceColors : F.parameters[K] && (F.parameters[K] = THREE.VertexColors) : "wrapRGB" === K && (E = F.parameters[K], F.parameters[K] = new THREE.Vector3(E[0], E[1], E[2]));
        void 0 !== F.parameters.opacity && 1 > F.parameters.opacity && (F.parameters.transparent = !0);
        F.parameters.normalMap ?
            (E = THREE.ShaderLib.normalmap, A = THREE.UniformsUtils.clone(E.uniforms), q = F.parameters.color, S = F.parameters.specular, n = F.parameters.ambient, Z = F.parameters.shininess, A.tNormal.value = w.textures[F.parameters.normalMap], F.parameters.normalScale && A.uNormalScale.value.set(F.parameters.normalScale[0], F.parameters.normalScale[1]), F.parameters.map && (A.tDiffuse.value = F.parameters.map, A.enableDiffuse.value = !0), F.parameters.envMap && (A.tCube.value = F.parameters.envMap, A.enableReflection.value = !0, A.uReflectivity.value =
                F.parameters.reflectivity), F.parameters.lightMap && (A.tAO.value = F.parameters.lightMap, A.enableAO.value = !0), F.parameters.specularMap && (A.tSpecular.value = w.textures[F.parameters.specularMap], A.enableSpecular.value = !0), F.parameters.displacementMap && (A.tDisplacement.value = w.textures[F.parameters.displacementMap], A.enableDisplacement.value = !0, A.uDisplacementBias.value = F.parameters.displacementBias, A.uDisplacementScale.value = F.parameters.displacementScale), A.uDiffuseColor.value.setHex(q), A.uSpecularColor.value.setHex(S),
                A.uAmbientColor.value.setHex(n), A.uShininess.value = Z, F.parameters.opacity && (A.uOpacity.value = F.parameters.opacity), s = new THREE.ShaderMaterial({
                fragmentShader: E.fragmentShader,
                vertexShader: E.vertexShader,
                uniforms: A,
                lights: !0,
                fog: !0
            })) : s = new THREE[F.type](F.parameters);
        w.materials[H] = s
    }
    for (H in J.materials) if (F = J.materials[H], F.parameters.materials) {
        K = [];
        for (q = 0; q < F.parameters.materials.length; q++) K.push(w.materials[F.parameters.materials[q]]);
        w.materials[H].materials = K
    }
    e();
    w.cameras && J.defaults.camera &&
    (w.currentCamera = w.cameras[J.defaults.camera]);
    w.fogs && J.defaults.fog && (w.scene.fog = w.fogs[J.defaults.fog]);
    l.callbackSync(w);
    k()
};
THREE.TextureLoader = function () {
    THREE.EventDispatcher.call(this);
    this.crossOrigin = null
};
THREE.TextureLoader.prototype = {
    constructor: THREE.TextureLoader, load: function (a) {
        var b = this, c = new Image;
        c.addEventListener("load", function () {
            var a = new THREE.Texture(c);
            a.needsUpdate = !0;
            b.dispatchEvent({type: "load", content: a})
        }, !1);
        c.addEventListener("error", function () {
            b.dispatchEvent({type: "error", message: "Couldn't load URL [" + a + "]"})
        }, !1);
        b.crossOrigin && (c.crossOrigin = b.crossOrigin);
        c.src = a
    }
};
THREE.Material = function () {
    THREE.EventDispatcher.call(this);
    this.id = THREE.MaterialIdCount++;
    this.name = "";
    this.side = THREE.FrontSide;
    this.opacity = 1;
    this.transparent = !1;
    this.blending = THREE.NormalBlending;
    this.blendSrc = THREE.SrcAlphaFactor;
    this.blendDst = THREE.OneMinusSrcAlphaFactor;
    this.blendEquation = THREE.AddEquation;
    this.depthWrite = this.depthTest = !0;
    this.polygonOffset = !1;
    this.alphaTest = this.polygonOffsetUnits = this.polygonOffsetFactor = 0;
    this.overdraw = !1;
    this.needsUpdate = this.visible = !0
};
THREE.Material.prototype.setValues = function (a) {
    if (void 0 !== a) for (var b in a) {
        var c = a[b];
        if (void 0 === c) console.warn("THREE.Material: '" + b + "' parameter is undefined."); else if (b in this) {
            var d = this[b];
            d instanceof THREE.Color && c instanceof THREE.Color ? d.copy(c) : d instanceof THREE.Color ? d.set(c) : d instanceof THREE.Vector3 && c instanceof THREE.Vector3 ? d.copy(c) : this[b] = c
        }
    }
};
THREE.Material.prototype.clone = function (a) {
    void 0 === a && (a = new THREE.Material);
    a.name = this.name;
    a.side = this.side;
    a.opacity = this.opacity;
    a.transparent = this.transparent;
    a.blending = this.blending;
    a.blendSrc = this.blendSrc;
    a.blendDst = this.blendDst;
    a.blendEquation = this.blendEquation;
    a.depthTest = this.depthTest;
    a.depthWrite = this.depthWrite;
    a.polygonOffset = this.polygonOffset;
    a.polygonOffsetFactor = this.polygonOffsetFactor;
    a.polygonOffsetUnits = this.polygonOffsetUnits;
    a.alphaTest = this.alphaTest;
    a.overdraw = this.overdraw;
    a.visible = this.visible;
    return a
};
THREE.Material.prototype.dispose = function () {
    this.dispatchEvent({type: "dispose"})
};
THREE.MaterialIdCount = 0;
THREE.LineBasicMaterial = function (a) {
    THREE.Material.call(this);
    this.color = new THREE.Color(16777215);
    this.linewidth = 1;
    this.linejoin = this.linecap = "round";
    this.vertexColors = !1;
    this.fog = !0;
    this.setValues(a)
};
THREE.LineBasicMaterial.prototype = Object.create(THREE.Material.prototype);
THREE.LineBasicMaterial.prototype.clone = function () {
    var a = new THREE.LineBasicMaterial;
    THREE.Material.prototype.clone.call(this, a);
    a.color.copy(this.color);
    a.linewidth = this.linewidth;
    a.linecap = this.linecap;
    a.linejoin = this.linejoin;
    a.vertexColors = this.vertexColors;
    a.fog = this.fog;
    return a
};
THREE.LineDashedMaterial = function (a) {
    THREE.Material.call(this);
    this.color = new THREE.Color(16777215);
    this.scale = this.linewidth = 1;
    this.dashSize = 3;
    this.gapSize = 1;
    this.vertexColors = !1;
    this.fog = !0;
    this.setValues(a)
};
THREE.LineDashedMaterial.prototype = Object.create(THREE.Material.prototype);
THREE.LineDashedMaterial.prototype.clone = function () {
    var a = new THREE.LineDashedMaterial;
    THREE.Material.prototype.clone.call(this, a);
    a.color.copy(this.color);
    a.linewidth = this.linewidth;
    a.scale = this.scale;
    a.dashSize = this.dashSize;
    a.gapSize = this.gapSize;
    a.vertexColors = this.vertexColors;
    a.fog = this.fog;
    return a
};
THREE.MeshBasicMaterial = function (a) {
    THREE.Material.call(this);
    this.color = new THREE.Color(16777215);
    this.envMap = this.specularMap = this.lightMap = this.map = null;
    this.combine = THREE.MultiplyOperation;
    this.reflectivity = 1;
    this.refractionRatio = 0.98;
    this.fog = !0;
    this.shading = THREE.SmoothShading;
    this.wireframe = !1;
    this.wireframeLinewidth = 1;
    this.wireframeLinejoin = this.wireframeLinecap = "round";
    this.vertexColors = THREE.NoColors;
    this.morphTargets = this.skinning = !1;
    this.setValues(a)
};
THREE.MeshBasicMaterial.prototype = Object.create(THREE.Material.prototype);
THREE.MeshBasicMaterial.prototype.clone = function () {
    var a = new THREE.MeshBasicMaterial;
    THREE.Material.prototype.clone.call(this, a);
    a.color.copy(this.color);
    a.map = this.map;
    a.lightMap = this.lightMap;
    a.specularMap = this.specularMap;
    a.envMap = this.envMap;
    a.combine = this.combine;
    a.reflectivity = this.reflectivity;
    a.refractionRatio = this.refractionRatio;
    a.fog = this.fog;
    a.shading = this.shading;
    a.wireframe = this.wireframe;
    a.wireframeLinewidth = this.wireframeLinewidth;
    a.wireframeLinecap = this.wireframeLinecap;
    a.wireframeLinejoin =
        this.wireframeLinejoin;
    a.vertexColors = this.vertexColors;
    a.skinning = this.skinning;
    a.morphTargets = this.morphTargets;
    return a
};
THREE.MeshLambertMaterial = function (a) {
    THREE.Material.call(this);
    this.color = new THREE.Color(16777215);
    this.ambient = new THREE.Color(16777215);
    this.emissive = new THREE.Color(0);
    this.wrapAround = !1;
    this.wrapRGB = new THREE.Vector3(1, 1, 1);
    this.envMap = this.specularMap = this.lightMap = this.map = null;
    this.combine = THREE.MultiplyOperation;
    this.reflectivity = 1;
    this.refractionRatio = 0.98;
    this.fog = !0;
    this.shading = THREE.SmoothShading;
    this.wireframe = !1;
    this.wireframeLinewidth = 1;
    this.wireframeLinejoin = this.wireframeLinecap =
        "round";
    this.vertexColors = THREE.NoColors;
    this.morphNormals = this.morphTargets = this.skinning = !1;
    this.setValues(a)
};
THREE.MeshLambertMaterial.prototype = Object.create(THREE.Material.prototype);
THREE.MeshLambertMaterial.prototype.clone = function () {
    var a = new THREE.MeshLambertMaterial;
    THREE.Material.prototype.clone.call(this, a);
    a.color.copy(this.color);
    a.ambient.copy(this.ambient);
    a.emissive.copy(this.emissive);
    a.wrapAround = this.wrapAround;
    a.wrapRGB.copy(this.wrapRGB);
    a.map = this.map;
    a.lightMap = this.lightMap;
    a.specularMap = this.specularMap;
    a.envMap = this.envMap;
    a.combine = this.combine;
    a.reflectivity = this.reflectivity;
    a.refractionRatio = this.refractionRatio;
    a.fog = this.fog;
    a.shading = this.shading;
    a.wireframe = this.wireframe;
    a.wireframeLinewidth = this.wireframeLinewidth;
    a.wireframeLinecap = this.wireframeLinecap;
    a.wireframeLinejoin = this.wireframeLinejoin;
    a.vertexColors = this.vertexColors;
    a.skinning = this.skinning;
    a.morphTargets = this.morphTargets;
    a.morphNormals = this.morphNormals;
    return a
};
THREE.MeshPhongMaterial = function (a) {
    THREE.Material.call(this);
    this.color = new THREE.Color(16777215);
    this.ambient = new THREE.Color(16777215);
    this.emissive = new THREE.Color(0);
    this.specular = new THREE.Color(1118481);
    this.shininess = 30;
    this.metal = !1;
    this.perPixel = !0;
    this.wrapAround = !1;
    this.wrapRGB = new THREE.Vector3(1, 1, 1);
    this.bumpMap = this.lightMap = this.map = null;
    this.bumpScale = 1;
    this.normalMap = null;
    this.normalScale = new THREE.Vector2(1, 1);
    this.envMap = this.specularMap = null;
    this.combine = THREE.MultiplyOperation;
    this.reflectivity = 1;
    this.refractionRatio = 0.98;
    this.fog = !0;
    this.shading = THREE.SmoothShading;
    this.wireframe = !1;
    this.wireframeLinewidth = 1;
    this.wireframeLinejoin = this.wireframeLinecap = "round";
    this.vertexColors = THREE.NoColors;
    this.morphNormals = this.morphTargets = this.skinning = !1;
    this.setValues(a)
};
THREE.MeshPhongMaterial.prototype = Object.create(THREE.Material.prototype);
THREE.MeshPhongMaterial.prototype.clone = function () {
    var a = new THREE.MeshPhongMaterial;
    THREE.Material.prototype.clone.call(this, a);
    a.color.copy(this.color);
    a.ambient.copy(this.ambient);
    a.emissive.copy(this.emissive);
    a.specular.copy(this.specular);
    a.shininess = this.shininess;
    a.metal = this.metal;
    a.perPixel = this.perPixel;
    a.wrapAround = this.wrapAround;
    a.wrapRGB.copy(this.wrapRGB);
    a.map = this.map;
    a.lightMap = this.lightMap;
    a.bumpMap = this.bumpMap;
    a.bumpScale = this.bumpScale;
    a.normalMap = this.normalMap;
    a.normalScale.copy(this.normalScale);
    a.specularMap = this.specularMap;
    a.envMap = this.envMap;
    a.combine = this.combine;
    a.reflectivity = this.reflectivity;
    a.refractionRatio = this.refractionRatio;
    a.fog = this.fog;
    a.shading = this.shading;
    a.wireframe = this.wireframe;
    a.wireframeLinewidth = this.wireframeLinewidth;
    a.wireframeLinecap = this.wireframeLinecap;
    a.wireframeLinejoin = this.wireframeLinejoin;
    a.vertexColors = this.vertexColors;
    a.skinning = this.skinning;
    a.morphTargets = this.morphTargets;
    a.morphNormals = this.morphNormals;
    return a
};
THREE.MeshDepthMaterial = function (a) {
    THREE.Material.call(this);
    this.wireframe = !1;
    this.wireframeLinewidth = 1;
    this.setValues(a)
};
THREE.MeshDepthMaterial.prototype = Object.create(THREE.Material.prototype);
THREE.MeshDepthMaterial.prototype.clone = function () {
    var a = new THREE.MeshDepthMaterial;
    THREE.Material.prototype.clone.call(this, a);
    a.wireframe = this.wireframe;
    a.wireframeLinewidth = this.wireframeLinewidth;
    return a
};
THREE.MeshNormalMaterial = function (a) {
    THREE.Material.call(this, a);
    this.shading = THREE.FlatShading;
    this.wireframe = !1;
    this.wireframeLinewidth = 1;
    this.setValues(a)
};
THREE.MeshNormalMaterial.prototype = Object.create(THREE.Material.prototype);
THREE.MeshNormalMaterial.prototype.clone = function () {
    var a = new THREE.MeshNormalMaterial;
    THREE.Material.prototype.clone.call(this, a);
    a.shading = this.shading;
    a.wireframe = this.wireframe;
    a.wireframeLinewidth = this.wireframeLinewidth;
    return a
};
THREE.MeshFaceMaterial = function (a) {
    this.materials = a instanceof Array ? a : []
};
THREE.MeshFaceMaterial.prototype.clone = function () {
    return new THREE.MeshFaceMaterial(this.materials.slice(0))
};
THREE.ParticleBasicMaterial = function (a) {
    THREE.Material.call(this);
    this.color = new THREE.Color(16777215);
    this.map = null;
    this.size = 1;
    this.sizeAttenuation = !0;
    this.vertexColors = !1;
    this.fog = !0;
    this.setValues(a)
};
THREE.ParticleBasicMaterial.prototype = Object.create(THREE.Material.prototype);
THREE.ParticleBasicMaterial.prototype.clone = function () {
    var a = new THREE.ParticleBasicMaterial;
    THREE.Material.prototype.clone.call(this, a);
    a.color.copy(this.color);
    a.map = this.map;
    a.size = this.size;
    a.sizeAttenuation = this.sizeAttenuation;
    a.vertexColors = this.vertexColors;
    a.fog = this.fog;
    return a
};
THREE.ParticleCanvasMaterial = function (a) {
    THREE.Material.call(this);
    this.color = new THREE.Color(16777215);
    this.program = function () {
    };
    this.setValues(a)
};
THREE.ParticleCanvasMaterial.prototype = Object.create(THREE.Material.prototype);
THREE.ParticleCanvasMaterial.prototype.clone = function () {
    var a = new THREE.ParticleCanvasMaterial;
    THREE.Material.prototype.clone.call(this, a);
    a.color.copy(this.color);
    a.program = this.program;
    return a
};
THREE.ShaderMaterial = function (a) {
    THREE.Material.call(this);
    this.vertexShader = this.fragmentShader = "void main() {}";
    this.uniforms = {};
    this.defines = {};
    this.attributes = null;
    this.shading = THREE.SmoothShading;
    this.wireframe = !1;
    this.wireframeLinewidth = 1;
    this.lights = this.fog = !1;
    this.vertexColors = THREE.NoColors;
    this.morphNormals = this.morphTargets = this.skinning = !1;
    this.setValues(a)
};
THREE.ShaderMaterial.prototype = Object.create(THREE.Material.prototype);
THREE.ShaderMaterial.prototype.clone = function () {
    var a = new THREE.ShaderMaterial;
    THREE.Material.prototype.clone.call(this, a);
    a.fragmentShader = this.fragmentShader;
    a.vertexShader = this.vertexShader;
    a.uniforms = THREE.UniformsUtils.clone(this.uniforms);
    a.attributes = this.attributes;
    a.defines = this.defines;
    a.shading = this.shading;
    a.wireframe = this.wireframe;
    a.wireframeLinewidth = this.wireframeLinewidth;
    a.fog = this.fog;
    a.lights = this.lights;
    a.vertexColors = this.vertexColors;
    a.skinning = this.skinning;
    a.morphTargets =
        this.morphTargets;
    a.morphNormals = this.morphNormals;
    return a
};
THREE.SpriteMaterial = function (a) {
    THREE.Material.call(this);
    this.color = new THREE.Color(16777215);
    this.map = new THREE.Texture;
    this.useScreenCoordinates = !0;
    this.depthTest = !this.useScreenCoordinates;
    this.sizeAttenuation = !this.useScreenCoordinates;
    this.scaleByViewport = !this.sizeAttenuation;
    this.alignment = THREE.SpriteAlignment.center.clone();
    this.fog = !1;
    this.uvOffset = new THREE.Vector2(0, 0);
    this.uvScale = new THREE.Vector2(1, 1);
    this.setValues(a);
    a = a || {};
    void 0 === a.depthTest && (this.depthTest = !this.useScreenCoordinates);
    void 0 === a.sizeAttenuation && (this.sizeAttenuation = !this.useScreenCoordinates);
    void 0 === a.scaleByViewport && (this.scaleByViewport = !this.sizeAttenuation)
};
THREE.SpriteMaterial.prototype = Object.create(THREE.Material.prototype);
THREE.SpriteMaterial.prototype.clone = function () {
    var a = new THREE.SpriteMaterial;
    THREE.Material.prototype.clone.call(this, a);
    a.color.copy(this.color);
    a.map = this.map;
    a.useScreenCoordinates = this.useScreenCoordinates;
    a.sizeAttenuation = this.sizeAttenuation;
    a.scaleByViewport = this.scaleByViewport;
    a.alignment.copy(this.alignment);
    a.uvOffset.copy(this.uvOffset);
    a.uvScale.copy(this.uvScale);
    a.fog = this.fog;
    return a
};
THREE.SpriteAlignment = {};
THREE.SpriteAlignment.topLeft = new THREE.Vector2(1, -1);
THREE.SpriteAlignment.topCenter = new THREE.Vector2(0, -1);
THREE.SpriteAlignment.topRight = new THREE.Vector2(-1, -1);
THREE.SpriteAlignment.centerLeft = new THREE.Vector2(1, 0);
THREE.SpriteAlignment.center = new THREE.Vector2(0, 0);
THREE.SpriteAlignment.centerRight = new THREE.Vector2(-1, 0);
THREE.SpriteAlignment.bottomLeft = new THREE.Vector2(1, 1);
THREE.SpriteAlignment.bottomCenter = new THREE.Vector2(0, 1);
THREE.SpriteAlignment.bottomRight = new THREE.Vector2(-1, 1);
THREE.Texture = function (a, b, c, d, e, f, g, h, i) {
    THREE.EventDispatcher.call(this);
    this.id = THREE.TextureIdCount++;
    this.name = "";
    this.image = a;
    this.mipmaps = [];
    this.mapping = void 0 !== b ? b : new THREE.UVMapping;
    this.wrapS = void 0 !== c ? c : THREE.ClampToEdgeWrapping;
    this.wrapT = void 0 !== d ? d : THREE.ClampToEdgeWrapping;
    this.magFilter = void 0 !== e ? e : THREE.LinearFilter;
    this.minFilter = void 0 !== f ? f : THREE.LinearMipMapLinearFilter;
    this.anisotropy = void 0 !== i ? i : 1;
    this.format = void 0 !== g ? g : THREE.RGBAFormat;
    this.type = void 0 !== h ? h : THREE.UnsignedByteType;
    this.offset = new THREE.Vector2(0, 0);
    this.repeat = new THREE.Vector2(1, 1);
    this.generateMipmaps = !0;
    this.premultiplyAlpha = !1;
    this.flipY = !0;
    this.unpackAlignment = 4;
    this.needsUpdate = !1;
    this.onUpdate = null
};
THREE.Texture.prototype = {
    constructor: THREE.Texture, clone: function (a) {
        void 0 === a && (a = new THREE.Texture);
        a.image = this.image;
        a.mipmaps = this.mipmaps.slice(0);
        a.mapping = this.mapping;
        a.wrapS = this.wrapS;
        a.wrapT = this.wrapT;
        a.magFilter = this.magFilter;
        a.minFilter = this.minFilter;
        a.anisotropy = this.anisotropy;
        a.format = this.format;
        a.type = this.type;
        a.offset.copy(this.offset);
        a.repeat.copy(this.repeat);
        a.generateMipmaps = this.generateMipmaps;
        a.premultiplyAlpha = this.premultiplyAlpha;
        a.flipY = this.flipY;
        a.unpackAlignment =
            this.unpackAlignment;
        return a
    }, dispose: function () {
        this.dispatchEvent({type: "dispose"})
    }
};
THREE.TextureIdCount = 0;
THREE.CompressedTexture = function (a, b, c, d, e, f, g, h, i, k, l) {
    THREE.Texture.call(this, null, f, g, h, i, k, d, e, l);
    this.image = {width: b, height: c};
    this.mipmaps = a;
    this.generateMipmaps = !1
};
THREE.CompressedTexture.prototype = Object.create(THREE.Texture.prototype);
THREE.CompressedTexture.prototype.clone = function () {
    var a = new THREE.CompressedTexture;
    THREE.Texture.prototype.clone.call(this, a);
    return a
};
THREE.DataTexture = function (a, b, c, d, e, f, g, h, i, k, l) {
    THREE.Texture.call(this, null, f, g, h, i, k, d, e, l);
    this.image = {data: a, width: b, height: c}
};
THREE.DataTexture.prototype = Object.create(THREE.Texture.prototype);
THREE.DataTexture.prototype.clone = function () {
    var a = new THREE.DataTexture;
    THREE.Texture.prototype.clone.call(this, a);
    return a
};
THREE.Particle = function (a) {
    THREE.Object3D.call(this);
    this.material = a
};
THREE.Particle.prototype = Object.create(THREE.Object3D.prototype);
THREE.Particle.prototype.clone = function (a) {
    void 0 === a && (a = new THREE.Particle(this.material));
    THREE.Object3D.prototype.clone.call(this, a);
    return a
};
THREE.ParticleSystem = function (a, b) {
    THREE.Object3D.call(this);
    this.geometry = a;
    this.material = void 0 !== b ? b : new THREE.ParticleBasicMaterial({color: 16777215 * Math.random()});
    this.sortParticles = !1;
    this.geometry && null === this.geometry.boundingSphere && this.geometry.computeBoundingSphere();
    this.frustumCulled = !1
};
THREE.ParticleSystem.prototype = Object.create(THREE.Object3D.prototype);
THREE.ParticleSystem.prototype.clone = function (a) {
    void 0 === a && (a = new THREE.ParticleSystem(this.geometry, this.material));
    a.sortParticles = this.sortParticles;
    THREE.Object3D.prototype.clone.call(this, a);
    return a
};
THREE.Line = function (a, b, c) {
    THREE.Object3D.call(this);
    this.geometry = a;
    this.material = void 0 !== b ? b : new THREE.LineBasicMaterial({color: 16777215 * Math.random()});
    this.type = void 0 !== c ? c : THREE.LineStrip;
    this.geometry && (this.geometry.boundingSphere || this.geometry.computeBoundingSphere())
};
THREE.LineStrip = 0;
THREE.LinePieces = 1;
THREE.Line.prototype = Object.create(THREE.Object3D.prototype);
THREE.Line.prototype.clone = function (a) {
    void 0 === a && (a = new THREE.Line(this.geometry, this.material, this.type));
    THREE.Object3D.prototype.clone.call(this, a);
    return a
};
THREE.Mesh = function (a, b) {
    THREE.Object3D.call(this);
    this.geometry = a;
    this.material = void 0 !== b ? b : new THREE.MeshBasicMaterial({color: 16777215 * Math.random(), wireframe: !0});
    void 0 !== this.geometry && (null === this.geometry.boundingSphere && this.geometry.computeBoundingSphere(), this.updateMorphTargets())
};
THREE.Mesh.prototype = Object.create(THREE.Object3D.prototype);
THREE.Mesh.prototype.updateMorphTargets = function () {
    if (0 < this.geometry.morphTargets.length) {
        this.morphTargetBase = -1;
        this.morphTargetForcedOrder = [];
        this.morphTargetInfluences = [];
        this.morphTargetDictionary = {};
        for (var a = 0, b = this.geometry.morphTargets.length; a < b; a++) this.morphTargetInfluences.push(0), this.morphTargetDictionary[this.geometry.morphTargets[a].name] = a
    }
};
THREE.Mesh.prototype.getMorphTargetIndexByName = function (a) {
    if (void 0 !== this.morphTargetDictionary[a]) return this.morphTargetDictionary[a];
    console.log("THREE.Mesh.getMorphTargetIndexByName: morph target " + a + " does not exist. Returning 0.");
    return 0
};
THREE.Mesh.prototype.clone = function (a) {
    void 0 === a && (a = new THREE.Mesh(this.geometry, this.material));
    THREE.Object3D.prototype.clone.call(this, a);
    return a
};
THREE.Bone = function (a) {
    THREE.Object3D.call(this);
    this.skin = a;
    this.skinMatrix = new THREE.Matrix4
};
THREE.Bone.prototype = Object.create(THREE.Object3D.prototype);
THREE.Bone.prototype.update = function (a, b) {
    this.matrixAutoUpdate && (b |= this.updateMatrix());
    if (b || this.matrixWorldNeedsUpdate) a ? this.skinMatrix.multiplyMatrices(a, this.matrix) : this.skinMatrix.copy(this.matrix), this.matrixWorldNeedsUpdate = !1, b = !0;
    var c, d = this.children.length;
    for (c = 0; c < d; c++) this.children[c].update(this.skinMatrix, b)
};
THREE.SkinnedMesh = function (a, b, c) {
    THREE.Mesh.call(this, a, b);
    this.useVertexTexture = void 0 !== c ? c : !0;
    this.identityMatrix = new THREE.Matrix4;
    this.bones = [];
    this.boneMatrices = [];
    var d, e, f;
    if (this.geometry && void 0 !== this.geometry.bones) {
        for (a = 0; a < this.geometry.bones.length; a++) c = this.geometry.bones[a], d = c.pos, e = c.rotq, f = c.scl, b = this.addBone(), b.name = c.name, b.position.set(d[0], d[1], d[2]), b.quaternion.set(e[0], e[1], e[2], e[3]), b.useQuaternion = !0, void 0 !== f ? b.scale.set(f[0], f[1], f[2]) : b.scale.set(1, 1, 1);
        for (a =
                 0; a < this.bones.length; a++) c = this.geometry.bones[a], b = this.bones[a], -1 === c.parent ? this.add(b) : this.bones[c.parent].add(b);
        a = this.bones.length;
        this.useVertexTexture ? (this.boneTextureHeight = this.boneTextureWidth = a = 256 < a ? 64 : 64 < a ? 32 : 16 < a ? 16 : 8, this.boneMatrices = new Float32Array(4 * this.boneTextureWidth * this.boneTextureHeight), this.boneTexture = new THREE.DataTexture(this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType), this.boneTexture.minFilter = THREE.NearestFilter,
            this.boneTexture.magFilter = THREE.NearestFilter, this.boneTexture.generateMipmaps = !1, this.boneTexture.flipY = !1) : this.boneMatrices = new Float32Array(16 * a);
        this.pose()
    }
};
THREE.SkinnedMesh.prototype = Object.create(THREE.Mesh.prototype);
THREE.SkinnedMesh.prototype.addBone = function (a) {
    void 0 === a && (a = new THREE.Bone(this));
    this.bones.push(a);
    return a
};
THREE.SkinnedMesh.prototype.updateMatrixWorld = function (a) {
    this.matrixAutoUpdate && this.updateMatrix();
    if (this.matrixWorldNeedsUpdate || a) this.parent ? this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix) : this.matrixWorld.copy(this.matrix), this.matrixWorldNeedsUpdate = !1;
    for (var a = 0, b = this.children.length; a < b; a++) {
        var c = this.children[a];
        c instanceof THREE.Bone ? c.update(this.identityMatrix, !1) : c.updateMatrixWorld(!0)
    }
    if (void 0 == this.boneInverses) {
        this.boneInverses = [];
        a = 0;
        for (b = this.bones.length; a <
        b; a++) c = new THREE.Matrix4, c.getInverse(this.bones[a].skinMatrix), this.boneInverses.push(c)
    }
    a = 0;
    for (b = this.bones.length; a < b; a++) THREE.SkinnedMesh.offsetMatrix.multiplyMatrices(this.bones[a].skinMatrix, this.boneInverses[a]), THREE.SkinnedMesh.offsetMatrix.flattenToArrayOffset(this.boneMatrices, 16 * a);
    this.useVertexTexture && (this.boneTexture.needsUpdate = !0)
};
THREE.SkinnedMesh.prototype.pose = function () {
    this.updateMatrixWorld(!0);
    for (var a = 0; a < this.geometry.skinIndices.length; a++) {
        var b = this.geometry.skinWeights[a], c = 1 / b.lengthManhattan();
        Infinity !== c ? b.multiplyScalar(c) : b.set(1)
    }
};
THREE.SkinnedMesh.prototype.clone = function (a) {
    void 0 === a && (a = new THREE.SkinnedMesh(this.geometry, this.material, this.useVertexTexture));
    THREE.Mesh.prototype.clone.call(this, a);
    return a
};
THREE.SkinnedMesh.offsetMatrix = new THREE.Matrix4;
THREE.MorphAnimMesh = function (a, b) {
    THREE.Mesh.call(this, a, b);
    this.duration = 1E3;
    this.mirroredLoop = !1;
    this.currentKeyframe = this.lastKeyframe = this.time = 0;
    this.direction = 1;
    this.directionBackwards = !1;
    this.setFrameRange(0, this.geometry.morphTargets.length - 1)
};
THREE.MorphAnimMesh.prototype = Object.create(THREE.Mesh.prototype);
THREE.MorphAnimMesh.prototype.setFrameRange = function (a, b) {
    this.startKeyframe = a;
    this.endKeyframe = b;
    this.length = this.endKeyframe - this.startKeyframe + 1
};
THREE.MorphAnimMesh.prototype.setDirectionForward = function () {
    this.direction = 1;
    this.directionBackwards = !1
};
THREE.MorphAnimMesh.prototype.setDirectionBackward = function () {
    this.direction = -1;
    this.directionBackwards = !0
};
THREE.MorphAnimMesh.prototype.parseAnimations = function () {
    var a = this.geometry;
    a.animations || (a.animations = {});
    for (var b, c = a.animations, d = /([a-z]+)(\d+)/, e = 0, f = a.morphTargets.length; e < f; e++) {
        var g = a.morphTargets[e].name.match(d);
        if (g && 1 < g.length) {
            g = g[1];
            c[g] || (c[g] = {start: Infinity, end: -Infinity});
            var h = c[g];
            e < h.start && (h.start = e);
            e > h.end && (h.end = e);
            b || (b = g)
        }
    }
    a.firstAnimation = b
};
THREE.MorphAnimMesh.prototype.setAnimationLabel = function (a, b, c) {
    this.geometry.animations || (this.geometry.animations = {});
    this.geometry.animations[a] = {start: b, end: c}
};
THREE.MorphAnimMesh.prototype.playAnimation = function (a, b) {
    var c = this.geometry.animations[a];
    c ? (this.setFrameRange(c.start, c.end), this.duration = 1E3 * ((c.end - c.start) / b), this.time = 0) : console.warn("animation[" + a + "] undefined")
};
THREE.MorphAnimMesh.prototype.updateAnimation = function (a) {
    var b = this.duration / this.length;
    this.time += this.direction * a;
    if (this.mirroredLoop) {
        if (this.time > this.duration || 0 > this.time) this.direction *= -1, this.time > this.duration && (this.time = this.duration, this.directionBackwards = !0), 0 > this.time && (this.time = 0, this.directionBackwards = !1)
    } else this.time %= this.duration, 0 > this.time && (this.time += this.duration);
    a = this.startKeyframe + THREE.Math.clamp(Math.floor(this.time / b), 0, this.length - 1);
    a !== this.currentKeyframe &&
    (this.morphTargetInfluences[this.lastKeyframe] = 0, this.morphTargetInfluences[this.currentKeyframe] = 1, this.morphTargetInfluences[a] = 0, this.lastKeyframe = this.currentKeyframe, this.currentKeyframe = a);
    b = this.time % b / b;
    this.directionBackwards && (b = 1 - b);
    this.morphTargetInfluences[this.currentKeyframe] = b;
    this.morphTargetInfluences[this.lastKeyframe] = 1 - b
};
THREE.MorphAnimMesh.prototype.clone = function (a) {
    void 0 === a && (a = new THREE.MorphAnimMesh(this.geometry, this.material));
    a.duration = this.duration;
    a.mirroredLoop = this.mirroredLoop;
    a.time = this.time;
    a.lastKeyframe = this.lastKeyframe;
    a.currentKeyframe = this.currentKeyframe;
    a.direction = this.direction;
    a.directionBackwards = this.directionBackwards;
    THREE.Mesh.prototype.clone.call(this, a);
    return a
};
THREE.Ribbon = function (a, b) {
    THREE.Object3D.call(this);
    this.geometry = a;
    this.material = b
};
THREE.Ribbon.prototype = Object.create(THREE.Object3D.prototype);
THREE.Ribbon.prototype.clone = function (a) {
    void 0 === a && (a = new THREE.Ribbon(this.geometry, this.material));
    THREE.Object3D.prototype.clone.call(this, a);
    return a
};
THREE.LOD = function () {
    THREE.Object3D.call(this);
    this.LODs = []
};
THREE.LOD.prototype = Object.create(THREE.Object3D.prototype);
THREE.LOD.prototype.addLevel = function (a, b) {
    void 0 === b && (b = 0);
    for (var b = Math.abs(b), c = 0; c < this.LODs.length && !(b < this.LODs[c].visibleAtDistance); c++) ;
    this.LODs.splice(c, 0, {visibleAtDistance: b, object3D: a});
    this.add(a)
};
THREE.LOD.prototype.update = function (a) {
    if (1 < this.LODs.length) {
        a.matrixWorldInverse.getInverse(a.matrixWorld);
        a = a.matrixWorldInverse;
        a = -(a.elements[2] * this.matrixWorld.elements[12] + a.elements[6] * this.matrixWorld.elements[13] + a.elements[10] * this.matrixWorld.elements[14] + a.elements[14]);
        this.LODs[0].object3D.visible = !0;
        for (var b = 1; b < this.LODs.length; b++) if (a >= this.LODs[b].visibleAtDistance) this.LODs[b - 1].object3D.visible = !1, this.LODs[b].object3D.visible = !0; else break;
        for (; b < this.LODs.length; b++) this.LODs[b].object3D.visible =
            !1
    }
};
THREE.LOD.prototype.clone = function () {
};
THREE.Sprite = function (a) {
    THREE.Object3D.call(this);
    this.material = void 0 !== a ? a : new THREE.SpriteMaterial;
    this.rotation3d = this.rotation;
    this.rotation = 0
};
THREE.Sprite.prototype = Object.create(THREE.Object3D.prototype);
THREE.Sprite.prototype.updateMatrix = function () {
    this.matrix.setPosition(this.position);
    this.rotation3d.set(0, 0, this.rotation);
    this.matrix.setRotationFromEuler(this.rotation3d);
    (1 !== this.scale.x || 1 !== this.scale.y) && this.matrix.scale(this.scale);
    this.matrixWorldNeedsUpdate = !0
};
THREE.Sprite.prototype.clone = function (a) {
    void 0 === a && (a = new THREE.Sprite(this.material));
    THREE.Object3D.prototype.clone.call(this, a);
    return a
};
THREE.Scene = function () {
    THREE.Object3D.call(this);
    this.overrideMaterial = this.fog = null;
    this.matrixAutoUpdate = !1;
    this.__objects = [];
    this.__lights = [];
    this.__objectsAdded = [];
    this.__objectsRemoved = []
};
THREE.Scene.prototype = Object.create(THREE.Object3D.prototype);
THREE.Scene.prototype.__addObject = function (a) {
    if (a instanceof THREE.Light) -1 === this.__lights.indexOf(a) && this.__lights.push(a), a.target && void 0 === a.target.parent && this.add(a.target); else if (!(a instanceof THREE.Camera || a instanceof THREE.Bone) && -1 === this.__objects.indexOf(a)) {
        this.__objects.push(a);
        this.__objectsAdded.push(a);
        var b = this.__objectsRemoved.indexOf(a);
        -1 !== b && this.__objectsRemoved.splice(b, 1)
    }
    for (b = 0; b < a.children.length; b++) this.__addObject(a.children[b])
};
THREE.Scene.prototype.__removeObject = function (a) {
    if (a instanceof THREE.Light) {
        var b = this.__lights.indexOf(a);
        -1 !== b && this.__lights.splice(b, 1)
    } else a instanceof THREE.Camera || (b = this.__objects.indexOf(a), -1 !== b && (this.__objects.splice(b, 1), this.__objectsRemoved.push(a), b = this.__objectsAdded.indexOf(a), -1 !== b && this.__objectsAdded.splice(b, 1)));
    for (b = 0; b < a.children.length; b++) this.__removeObject(a.children[b])
};
THREE.Fog = function (a, b, c) {
    this.name = "";
    this.color = new THREE.Color(a);
    this.near = void 0 !== b ? b : 1;
    this.far = void 0 !== c ? c : 1E3
};
THREE.Fog.prototype.clone = function () {
    return new THREE.Fog(this.color.getHex(), this.near, this.far)
};
THREE.FogExp2 = function (a, b) {
    this.name = "";
    this.color = new THREE.Color(a);
    this.density = void 0 !== b ? b : 2.5E-4
};
THREE.FogExp2.prototype.clone = function () {
    return new THREE.FogExp2(this.color.getHex(), this.density)
};
THREE.CanvasRenderer = function (a) {
    function b(a) {
        D !== a && (D = u.globalAlpha = a)
    }

    function c(a) {
        w !== a && (a === THREE.NormalBlending ? u.globalCompositeOperation = "source-over" : a === THREE.AdditiveBlending ? u.globalCompositeOperation = "lighter" : a === THREE.SubtractiveBlending && (u.globalCompositeOperation = "darker"), w = a)
    }

    function d(a) {
        E !== a && (E = u.lineWidth = a)
    }

    function e(a) {
        Z !== a && (Z = u.lineCap = a)
    }

    function f(a) {
        A !== a && (A = u.lineJoin = a)
    }

    function g(a) {
        I !== a && (I = u.strokeStyle = a)
    }

    function h(a) {
        J !== a && (J = u.fillStyle = a)
    }

    function i(a,
               b) {
        if (S !== a || F !== b) u.setLineDash([a, b]), S = a, F = b
    }

    console.log("THREE.CanvasRenderer", THREE.REVISION);
    var k = THREE.Math.smoothstep, a = a || {}, l = this, m, p, s, r = new THREE.Projector,
        n = void 0 !== a.canvas ? a.canvas : document.createElement("canvas"), q, z, t, x, u = n.getContext("2d"),
        B = new THREE.Color(0), G = 0, D = 1, w = 0, I = null, J = null, E = null, Z = null, A = null, S = null, F = 0,
        H, K, N, fa, ma = new THREE.RenderableVertex, eb = new THREE.RenderableVertex, M, U, ja, L, ca, ta, kb, $a, lb,
        db, La, Oa, ea = new THREE.Color, ha = new THREE.Color, V = new THREE.Color, $ = new THREE.Color,
        Y = new THREE.Color, ia = new THREE.Color, ra = new THREE.Color, na = new THREE.Color, Pa = {}, Ba = {}, fb, jb,
        Sa, ob, Gb, Mb, wb, Nb, Ob, xb, Hb = new THREE.Box2, Qa = new THREE.Box2, Va = new THREE.Box2,
        wa = new THREE.Color, xa = new THREE.Color, yb = new THREE.Color, pb = new THREE.Vector3, j, mb, vb, Wa, Ga, zb,
        Ib = 16;
    j = document.createElement("canvas");
    j.width = j.height = 2;
    mb = j.getContext("2d");
    mb.fillStyle = "rgba(0,0,0,1)";
    mb.fillRect(0, 0, 2, 2);
    vb = mb.getImageData(0, 0, 2, 2);
    Wa = vb.data;
    Ga = document.createElement("canvas");
    Ga.width = Ga.height = Ib;
    zb = Ga.getContext("2d");
    zb.translate(-Ib / 2, -Ib / 2);
    zb.scale(Ib, Ib);
    Ib--;
    void 0 === u.setLineDash && (u.setLineDash = void 0 !== u.mozDash ? function (a) {
        u.mozDash = null !== a[0] ? a : null
    } : function () {
    });
    this.domElement = n;
    this.devicePixelRatio = void 0 !== a.devicePixelRatio ? a.devicePixelRatio : void 0 !== window.devicePixelRatio ? window.devicePixelRatio : 1;
    this.sortElements = this.sortObjects = this.autoClear = !0;
    this.info = {render: {vertices: 0, faces: 0}};
    this.supportsVertexTextures = function () {
    };
    this.setFaceCulling = function () {
    };
    this.setSize = function (a, b) {
        q =
            a * this.devicePixelRatio;
        z = b * this.devicePixelRatio;
        t = Math.floor(q / 2);
        x = Math.floor(z / 2);
        n.width = q;
        n.height = z;
        n.style.width = a + "px";
        n.style.height = b + "px";
        Hb.set(new THREE.Vector2(-t, -x), new THREE.Vector2(t, x));
        Qa.set(new THREE.Vector2(-t, -x), new THREE.Vector2(t, x));
        D = 1;
        w = 0;
        A = Z = E = J = I = null
    };
    this.setClearColor = function (a, b) {
        B.copy(a);
        G = void 0 !== b ? b : 1;
        Qa.set(new THREE.Vector2(-t, -x), new THREE.Vector2(t, x))
    };
    this.setClearColorHex = function (a, b) {
        B.setHex(a);
        G = void 0 !== b ? b : 1;
        Qa.set(new THREE.Vector2(-t, -x), new THREE.Vector2(t,
            x))
    };
    this.getMaxAnisotropy = function () {
        return 0
    };
    this.clear = function () {
        u.setTransform(1, 0, 0, -1, t, x);
        !1 === Qa.empty() && (Qa.intersect(Hb), Qa.expandByScalar(2), 1 > G && u.clearRect(Qa.min.x | 0, Qa.min.y | 0, Qa.max.x - Qa.min.x | 0, Qa.max.y - Qa.min.y | 0), 0 < G && (c(THREE.NormalBlending), b(1), h("rgba(" + Math.floor(255 * B.r) + "," + Math.floor(255 * B.g) + "," + Math.floor(255 * B.b) + "," + G + ")"), u.fillRect(Qa.min.x | 0, Qa.min.y | 0, Qa.max.x - Qa.min.x | 0, Qa.max.y - Qa.min.y | 0)), Qa.makeEmpty())
    };
    this.render = function (a, n) {
        function q(a, b, c) {
            for (var d =
                0, e = s.length; d < e; d++) {
                var f = s[d];
                na.copy(f.color);
                if (f instanceof THREE.DirectionalLight) {
                    var g = pb.getPositionFromMatrix(f.matrixWorld).normalize(), h = b.dot(g);
                    0 >= h || (h *= f.intensity, c.add(na.multiplyScalar(h)))
                } else f instanceof THREE.PointLight && (g = pb.getPositionFromMatrix(f.matrixWorld), h = b.dot(pb.subVectors(g, a).normalize()), 0 >= h || (h *= 0 == f.distance ? 1 : 1 - Math.min(a.distanceTo(g) / f.distance, 1), 0 != h && (h *= f.intensity, c.add(na.multiplyScalar(h)))))
            }
        }

        function w(a, d, e, f, g, h, j, i) {
            l.info.render.vertices +=
                3;
            l.info.render.faces++;
            b(i.opacity);
            c(i.blending);
            M = a.positionScreen.x;
            U = a.positionScreen.y;
            ja = d.positionScreen.x;
            L = d.positionScreen.y;
            ca = e.positionScreen.x;
            ta = e.positionScreen.y;
            z(M, U, ja, L, ca, ta);
            (i instanceof THREE.MeshLambertMaterial || i instanceof THREE.MeshPhongMaterial) && null === i.map ? (ia.copy(i.color), ra.copy(i.emissive), i.vertexColors === THREE.FaceColors && ia.multiply(j.color), !1 === i.wireframe && i.shading == THREE.SmoothShading && 3 == j.vertexNormalsLength ? (ha.copy(wa), V.copy(wa), $.copy(wa), q(j.v1.positionWorld,
                j.vertexNormalsModel[0], ha), q(j.v2.positionWorld, j.vertexNormalsModel[1], V), q(j.v3.positionWorld, j.vertexNormalsModel[2], $), ha.multiply(ia).add(ra), V.multiply(ia).add(ra), $.multiply(ia).add(ra), Y.addColors(V, $).multiplyScalar(0.5), Sa = Dc(ha, V, $, Y), ab(M, U, ja, L, ca, ta, 0, 0, 1, 0, 0, 1, Sa)) : (ea.copy(wa), q(j.centroidModel, j.normalModel, ea), ea.multiply(ia).add(ra), !0 === i.wireframe ? B(ea, i.wireframeLinewidth, i.wireframeLinecap, i.wireframeLinejoin) : D(ea))) : i instanceof THREE.MeshBasicMaterial || i instanceof THREE.MeshLambertMaterial ||
            i instanceof THREE.MeshPhongMaterial ? null !== i.map ? i.map.mapping instanceof THREE.UVMapping && (ob = j.uvs[0], F(M, U, ja, L, ca, ta, ob[f].x, ob[f].y, ob[g].x, ob[g].y, ob[h].x, ob[h].y, i.map)) : null !== i.envMap ? i.envMap.mapping instanceof THREE.SphericalReflectionMapping && (pb.copy(j.vertexNormalsModelView[f]), Gb = 0.5 * pb.x + 0.5, Mb = 0.5 * pb.y + 0.5, pb.copy(j.vertexNormalsModelView[g]), wb = 0.5 * pb.x + 0.5, Nb = 0.5 * pb.y + 0.5, pb.copy(j.vertexNormalsModelView[h]), Ob = 0.5 * pb.x + 0.5, xb = 0.5 * pb.y + 0.5, F(M, U, ja, L, ca, ta, Gb, Mb, wb, Nb, Ob, xb, i.envMap)) :
                (ea.copy(i.color), i.vertexColors === THREE.FaceColors && ea.multiply(j.color), !0 === i.wireframe ? B(ea, i.wireframeLinewidth, i.wireframeLinecap, i.wireframeLinejoin) : D(ea)) : i instanceof THREE.MeshDepthMaterial ? (fb = n.near, jb = n.far, ha.r = ha.g = ha.b = 1 - k(a.positionScreen.z * a.positionScreen.w, fb, jb), V.r = V.g = V.b = 1 - k(d.positionScreen.z * d.positionScreen.w, fb, jb), $.r = $.g = $.b = 1 - k(e.positionScreen.z * e.positionScreen.w, fb, jb), Y.addColors(V, $).multiplyScalar(0.5), Sa = Dc(ha, V, $, Y), ab(M, U, ja, L, ca, ta, 0, 0, 1, 0, 0, 1, Sa)) : i instanceof
                THREE.MeshNormalMaterial && (i.shading == THREE.FlatShading ? (a = j.normalModelView, ea.setRGB(a.x, a.y, a.z).multiplyScalar(0.5).addScalar(0.5), !0 === i.wireframe ? B(ea, i.wireframeLinewidth, i.wireframeLinecap, i.wireframeLinejoin) : D(ea)) : i.shading == THREE.SmoothShading && (a = j.vertexNormalsModelView[f], ha.setRGB(a.x, a.y, a.z).multiplyScalar(0.5).addScalar(0.5), a = j.vertexNormalsModelView[g], V.setRGB(a.x, a.y, a.z).multiplyScalar(0.5).addScalar(0.5), a = j.vertexNormalsModelView[h], $.setRGB(a.x, a.y, a.z).multiplyScalar(0.5).addScalar(0.5),
                    Y.addColors(V, $).multiplyScalar(0.5), Sa = Dc(ha, V, $, Y), ab(M, U, ja, L, ca, ta, 0, 0, 1, 0, 0, 1, Sa)))
        }

        function z(a, b, c, d, e, f) {
            u.beginPath();
            u.moveTo(a, b);
            u.lineTo(c, d);
            u.lineTo(e, f);
            u.closePath()
        }

        function A(a, b, c, d, e, f, g, h) {
            u.beginPath();
            u.moveTo(a, b);
            u.lineTo(c, d);
            u.lineTo(e, f);
            u.lineTo(g, h);
            u.closePath()
        }

        function B(a, b, c, h) {
            d(b);
            e(c);
            f(h);
            g(a.getStyle());
            u.stroke();
            Va.expandByScalar(2 * b)
        }

        function D(a) {
            h(a.getStyle());
            u.fill()
        }

        function F(a, b, c, d, e, f, g, j, i, k, ab, l, m) {
            if (!(m instanceof THREE.DataTexture || void 0 ===
                    m.image || 0 == m.image.width)) {
                if (!0 === m.needsUpdate) {
                    var p = m.wrapS == THREE.RepeatWrapping, n = m.wrapT == THREE.RepeatWrapping;
                    Pa[m.id] = u.createPattern(m.image, !0 === p && !0 === n ? "repeat" : !0 === p && !1 === n ? "repeat-x" : !1 === p && !0 === n ? "repeat-y" : "no-repeat");
                    m.needsUpdate = !1
                }
                void 0 === Pa[m.id] ? h("rgba(0,0,0,1)") : h(Pa[m.id]);
                var p = m.offset.x / m.repeat.x, n = m.offset.y / m.repeat.y, Jb = m.image.width * m.repeat.x,
                    q = m.image.height * m.repeat.y, g = (g + p) * Jb, j = (1 - j + n) * q, c = c - a, d = d - b,
                    e = e - a, f = f - b, i = (i + p) * Jb - g, k = (1 - k + n) * q - j, ab = (ab + p) * Jb -
                    g, l = (1 - l + n) * q - j, p = i * l - ab * k;
                0 === p ? (void 0 === Ba[m.id] && (b = document.createElement("canvas"), b.width = m.image.width, b.height = m.image.height, b = b.getContext("2d"), b.drawImage(m.image, 0, 0), Ba[m.id] = b.getImageData(0, 0, m.image.width, m.image.height).data), b = Ba[m.id], g = 4 * (Math.floor(g) + Math.floor(j) * m.image.width), ea.setRGB(b[g] / 255, b[g + 1] / 255, b[g + 2] / 255), D(ea)) : (p = 1 / p, m = (l * c - k * e) * p, k = (l * d - k * f) * p, c = (i * e - ab * c) * p, d = (i * f - ab * d) * p, a = a - m * g - c * j, g = b - k * g - d * j, u.save(), u.transform(m, k, c, d, a, g), u.fill(), u.restore())
            }
        }

        function ab(a,
                    b, c, d, e, f, g, h, j, i, k, ab, m) {
            var l, p;
            l = m.width - 1;
            p = m.height - 1;
            g *= l;
            h *= p;
            c -= a;
            d -= b;
            e -= a;
            f -= b;
            j = j * l - g;
            i = i * p - h;
            k = k * l - g;
            ab = ab * p - h;
            p = 1 / (j * ab - k * i);
            l = (ab * c - i * e) * p;
            i = (ab * d - i * f) * p;
            c = (j * e - k * c) * p;
            d = (j * f - k * d) * p;
            a = a - l * g - c * h;
            b = b - i * g - d * h;
            u.save();
            u.transform(l, i, c, d, a, b);
            u.clip();
            u.drawImage(m, 0, 0);
            u.restore()
        }

        function Dc(a, b, c, d) {
            Wa[0] = 255 * a.r | 0;
            Wa[1] = 255 * a.g | 0;
            Wa[2] = 255 * a.b | 0;
            Wa[4] = 255 * b.r | 0;
            Wa[5] = 255 * b.g | 0;
            Wa[6] = 255 * b.b | 0;
            Wa[8] = 255 * c.r | 0;
            Wa[9] = 255 * c.g | 0;
            Wa[10] = 255 * c.b | 0;
            Wa[12] = 255 * d.r | 0;
            Wa[13] = 255 * d.g | 0;
            Wa[14] =
                255 * d.b | 0;
            mb.putImageData(vb, 0, 0);
            zb.drawImage(j, 0, 0);
            return Ga
        }

        function Jb(a, b) {
            var c = b.x - a.x, d = b.y - a.y, e = c * c + d * d;
            0 !== e && (e = 1 / Math.sqrt(e), c *= e, d *= e, b.x += c, b.y += d, a.x -= c, a.y -= d)
        }

        if (!1 === n instanceof THREE.Camera) console.error("THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera."); else {
            !0 === this.autoClear && this.clear();
            u.setTransform(1, 0, 0, -1, t, x);
            l.info.render.vertices = 0;
            l.info.render.faces = 0;
            m = r.projectScene(a, n, this.sortObjects, this.sortElements);
            p = m.elements;
            s = m.lights;
            wa.setRGB(0,
                0, 0);
            xa.setRGB(0, 0, 0);
            yb.setRGB(0, 0, 0);
            for (var wc = 0, qd = s.length; wc < qd; wc++) {
                var W = s[wc], ga = W.color;
                W instanceof THREE.AmbientLight ? wa.add(ga) : W instanceof THREE.DirectionalLight ? xa.add(ga) : W instanceof THREE.PointLight && yb.add(ga)
            }
            wc = 0;
            for (qd = p.length; wc < qd; wc++) {
                var oa = p[wc], W = oa.material;
                if (!(void 0 === W || !1 === W.visible)) {
                    Va.makeEmpty();
                    if (oa instanceof THREE.RenderableParticle) {
                        H = oa;
                        H.x *= t;
                        H.y *= x;
                        var ga = H, gb = oa;
                        b(W.opacity);
                        c(W.blending);
                        var Ab = void 0, G = void 0, Bb = void 0, E = void 0, I = oa = void 0, J = void 0;
                        W instanceof THREE.ParticleBasicMaterial ? null === W.map ? (Bb = gb.object.scale.x, E = gb.object.scale.y, Bb *= gb.scale.x * t, E *= gb.scale.y * x, Va.min.set(ga.x - Bb, ga.y - E), Va.max.set(ga.x + Bb, ga.y + E), !1 !== Hb.isIntersectionBox(Va) && (h(W.color.getStyle()), u.save(), u.translate(ga.x, ga.y), u.rotate(-gb.rotation), u.scale(Bb, E), u.fillRect(-1, -1, 2, 2), u.restore())) : (oa = W.map.image, I = oa.width >> 1, J = oa.height >> 1, Bb = gb.scale.x * t, E = gb.scale.y * x, Ab = Bb * I, G = E * J, Va.min.set(ga.x - Ab, ga.y - G), Va.max.set(ga.x + Ab, ga.y + G), !1 !== Hb.isIntersectionBox(Va) &&
                        (u.save(), u.translate(ga.x, ga.y), u.rotate(-gb.rotation), u.scale(Bb, -E), u.translate(-I, -J), u.drawImage(oa, 0, 0), u.restore())) : W instanceof THREE.ParticleCanvasMaterial && (Ab = gb.scale.x * t, G = gb.scale.y * x, Va.min.set(ga.x - Ab, ga.y - G), Va.max.set(ga.x + Ab, ga.y + G), !1 !== Hb.isIntersectionBox(Va) && (g(W.color.getStyle()), h(W.color.getStyle()), u.save(), u.translate(ga.x, ga.y), u.rotate(-gb.rotation), u.scale(Ab, G), W.program(u), u.restore()))
                    } else if (oa instanceof THREE.RenderableLine) H = oa.v1, K = oa.v2, H.positionScreen.x *=
                        t, H.positionScreen.y *= x, K.positionScreen.x *= t, K.positionScreen.y *= x, Va.setFromPoints([H.positionScreen, K.positionScreen]), !0 === Hb.isIntersectionBox(Va) && (ga = H, gb = K, b(W.opacity), c(W.blending), u.beginPath(), u.moveTo(ga.positionScreen.x, ga.positionScreen.y), u.lineTo(gb.positionScreen.x, gb.positionScreen.y), W instanceof THREE.LineBasicMaterial ? (d(W.linewidth), e(W.linecap), f(W.linejoin), g(W.color.getStyle()), u.stroke(), Va.expandByScalar(2 * W.linewidth)) : W instanceof THREE.LineDashedMaterial && (d(W.linewidth),
                        e(W.linecap), f(W.linejoin), g(W.color.getStyle()), i(W.dashSize, W.gapSize), u.stroke(), Va.expandByScalar(2 * W.linewidth), i(null, null))); else if (oa instanceof THREE.RenderableFace3) {
                        H = oa.v1;
                        K = oa.v2;
                        N = oa.v3;
                        if (-1 > H.positionScreen.z || 1 < H.positionScreen.z) continue;
                        if (-1 > K.positionScreen.z || 1 < K.positionScreen.z) continue;
                        if (-1 > N.positionScreen.z || 1 < N.positionScreen.z) continue;
                        H.positionScreen.x *= t;
                        H.positionScreen.y *= x;
                        K.positionScreen.x *= t;
                        K.positionScreen.y *= x;
                        N.positionScreen.x *= t;
                        N.positionScreen.y *= x;
                        !0 === W.overdraw && (Jb(H.positionScreen, K.positionScreen), Jb(K.positionScreen, N.positionScreen), Jb(N.positionScreen, H.positionScreen));
                        Va.setFromPoints([H.positionScreen, K.positionScreen, N.positionScreen]);
                        w(H, K, N, 0, 1, 2, oa, W)
                    } else if (oa instanceof THREE.RenderableFace4) {
                        H = oa.v1;
                        K = oa.v2;
                        N = oa.v3;
                        fa = oa.v4;
                        if (-1 > H.positionScreen.z || 1 < H.positionScreen.z) continue;
                        if (-1 > K.positionScreen.z || 1 < K.positionScreen.z) continue;
                        if (-1 > N.positionScreen.z || 1 < N.positionScreen.z) continue;
                        if (-1 > fa.positionScreen.z || 1 < fa.positionScreen.z) continue;
                        H.positionScreen.x *= t;
                        H.positionScreen.y *= x;
                        K.positionScreen.x *= t;
                        K.positionScreen.y *= x;
                        N.positionScreen.x *= t;
                        N.positionScreen.y *= x;
                        fa.positionScreen.x *= t;
                        fa.positionScreen.y *= x;
                        ma.positionScreen.copy(K.positionScreen);
                        eb.positionScreen.copy(fa.positionScreen);
                        !0 === W.overdraw && (Jb(H.positionScreen, K.positionScreen), Jb(K.positionScreen, fa.positionScreen), Jb(fa.positionScreen, H.positionScreen), Jb(N.positionScreen, ma.positionScreen), Jb(N.positionScreen, eb.positionScreen));
                        Va.setFromPoints([H.positionScreen,
                            K.positionScreen, N.positionScreen, fa.positionScreen]);
                        ga = H;
                        gb = K;
                        Ab = N;
                        G = fa;
                        Bb = ma;
                        E = eb;
                        l.info.render.vertices += 4;
                        l.info.render.faces++;
                        b(W.opacity);
                        c(W.blending);
                        void 0 !== W.map && null !== W.map || void 0 !== W.envMap && null !== W.envMap ? (w(ga, gb, G, 0, 1, 3, oa, W), w(Bb, Ab, E, 1, 2, 3, oa, W)) : (M = ga.positionScreen.x, U = ga.positionScreen.y, ja = gb.positionScreen.x, L = gb.positionScreen.y, ca = Ab.positionScreen.x, ta = Ab.positionScreen.y, kb = G.positionScreen.x, $a = G.positionScreen.y, lb = Bb.positionScreen.x, db = Bb.positionScreen.y, La = E.positionScreen.x,
                            Oa = E.positionScreen.y, W instanceof THREE.MeshLambertMaterial || W instanceof THREE.MeshPhongMaterial ? (ia.copy(W.color), ra.copy(W.emissive), W.vertexColors === THREE.FaceColors && ia.multiply(oa.color), !1 === W.wireframe && W.shading == THREE.SmoothShading && 4 == oa.vertexNormalsLength ? (ha.copy(wa), V.copy(wa), $.copy(wa), Y.copy(wa), q(oa.v1.positionWorld, oa.vertexNormalsModel[0], ha), q(oa.v2.positionWorld, oa.vertexNormalsModel[1], V), q(oa.v4.positionWorld, oa.vertexNormalsModel[3], $), q(oa.v3.positionWorld, oa.vertexNormalsModel[2],
                            Y), ha.multiply(ia).add(ra), V.multiply(ia).add(ra), $.multiply(ia).add(ra), Y.multiply(ia).add(ra), Sa = Dc(ha, V, $, Y), z(M, U, ja, L, kb, $a), ab(M, U, ja, L, kb, $a, 0, 0, 1, 0, 0, 1, Sa), z(lb, db, ca, ta, La, Oa), ab(lb, db, ca, ta, La, Oa, 1, 0, 1, 1, 0, 1, Sa)) : (ea.copy(wa), q(oa.centroidModel, oa.normalModel, ea), ea.multiply(ia).add(ra), A(M, U, ja, L, ca, ta, kb, $a), !0 === W.wireframe ? B(ea, W.wireframeLinewidth, W.wireframeLinecap, W.wireframeLinejoin) : D(ea))) : W instanceof THREE.MeshBasicMaterial ? (ea.copy(W.color), W.vertexColors === THREE.FaceColors &&
                        ea.multiply(oa.color), A(M, U, ja, L, ca, ta, kb, $a), !0 === W.wireframe ? B(ea, W.wireframeLinewidth, W.wireframeLinecap, W.wireframeLinejoin) : D(ea)) : W instanceof THREE.MeshNormalMaterial ? (ga = void 0, W.shading == THREE.FlatShading ? (ga = oa.normalModelView, ea.setRGB(ga.x, ga.y, ga.z).multiplyScalar(0.5).addScalar(0.5), A(M, U, ja, L, ca, ta, kb, $a), !0 === W.wireframe ? B(ea, W.wireframeLinewidth, W.wireframeLinecap, W.wireframeLinejoin) : D(ea)) : W.shading == THREE.SmoothShading && (ga = oa.vertexNormalsModelView[0], ha.setRGB(ga.x, ga.y, ga.z).multiplyScalar(0.5).addScalar(0.5),
                            ga = oa.vertexNormalsModelView[1], V.setRGB(ga.x, ga.y, ga.z).multiplyScalar(0.5).addScalar(0.5), ga = oa.vertexNormalsModelView[3], $.setRGB(ga.x, ga.y, ga.z).multiplyScalar(0.5).addScalar(0.5), ga = oa.vertexNormalsModelView[2], Y.setRGB(ga.x, ga.y, ga.z).multiplyScalar(0.5).addScalar(0.5), Sa = Dc(ha, V, $, Y), z(M, U, ja, L, kb, $a), ab(M, U, ja, L, kb, $a, 0, 0, 1, 0, 0, 1, Sa), z(lb, db, ca, ta, La, Oa), ab(lb, db, ca, ta, La, Oa, 1, 0, 1, 1, 0, 1, Sa))) : W instanceof THREE.MeshDepthMaterial && (fb = n.near, jb = n.far, ha.r = ha.g = ha.b = 1 - k(ga.positionScreen.z *
                            ga.positionScreen.w, fb, jb), V.r = V.g = V.b = 1 - k(gb.positionScreen.z * gb.positionScreen.w, fb, jb), $.r = $.g = $.b = 1 - k(G.positionScreen.z * G.positionScreen.w, fb, jb), Y.r = Y.g = Y.b = 1 - k(Ab.positionScreen.z * Ab.positionScreen.w, fb, jb), Sa = Dc(ha, V, $, Y), z(M, U, ja, L, kb, $a), ab(M, U, ja, L, kb, $a, 0, 0, 1, 0, 0, 1, Sa), z(lb, db, ca, ta, La, Oa), ab(lb, db, ca, ta, La, Oa, 1, 0, 1, 1, 0, 1, Sa)))
                    }
                    Qa.union(Va)
                }
            }
            u.setTransform(1, 0, 0, 1, 0, 0)
        }
    }
};
THREE.ShaderChunk = {
    fog_pars_fragment: "#ifdef USE_FOG\nuniform vec3 fogColor;\n#ifdef FOG_EXP2\nuniform float fogDensity;\n#else\nuniform float fogNear;\nuniform float fogFar;\n#endif\n#endif",
    fog_fragment: "#ifdef USE_FOG\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n#ifdef FOG_EXP2\nconst float LOG2 = 1.442695;\nfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n#else\nfloat fogFactor = smoothstep( fogNear, fogFar, depth );\n#endif\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n#endif",
    envmap_pars_fragment: "#ifdef USE_ENVMAP\nuniform float reflectivity;\nuniform samplerCube envMap;\nuniform float flipEnvMap;\nuniform int combine;\n#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\nuniform bool useRefract;\nuniform float refractionRatio;\n#else\nvarying vec3 vReflect;\n#endif\n#endif",
    envmap_fragment: "#ifdef USE_ENVMAP\nvec3 reflectVec;\n#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\nvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\nif ( useRefract ) {\nreflectVec = refract( cameraToVertex, normal, refractionRatio );\n} else { \nreflectVec = reflect( cameraToVertex, normal );\n}\n#else\nreflectVec = vReflect;\n#endif\n#ifdef DOUBLE_SIDED\nfloat flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\nvec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n#else\nvec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n#endif\n#ifdef GAMMA_INPUT\ncubeColor.xyz *= cubeColor.xyz;\n#endif\nif ( combine == 1 ) {\ngl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );\n} else if ( combine == 2 ) {\ngl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;\n} else {\ngl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );\n}\n#endif",
    envmap_pars_vertex: "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )\nvarying vec3 vReflect;\nuniform float refractionRatio;\nuniform bool useRefract;\n#endif",
    worldpos_vertex: "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n#ifdef USE_SKINNING\nvec4 worldPosition = modelMatrix * skinned;\n#endif\n#if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\nvec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n#endif\n#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\nvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n#endif\n#endif",
    envmap_vertex: "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )\nvec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;\nworldNormal = normalize( worldNormal );\nvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\nif ( useRefract ) {\nvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n} else {\nvReflect = reflect( cameraToVertex, worldNormal );\n}\n#endif",
    map_particle_pars_fragment: "#ifdef USE_MAP\nuniform sampler2D map;\n#endif",
    map_particle_fragment: "#ifdef USE_MAP\ngl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );\n#endif",
    map_pars_vertex: "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )\nvarying vec2 vUv;\nuniform vec4 offsetRepeat;\n#endif",
    map_pars_fragment: "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )\nvarying vec2 vUv;\n#endif\n#ifdef USE_MAP\nuniform sampler2D map;\n#endif",
    map_vertex: "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )\nvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n#endif",
    map_fragment: "#ifdef USE_MAP\nvec4 texelColor = texture2D( map, vUv );\n#ifdef GAMMA_INPUT\ntexelColor.xyz *= texelColor.xyz;\n#endif\ngl_FragColor = gl_FragColor * texelColor;\n#endif",
    lightmap_pars_fragment: "#ifdef USE_LIGHTMAP\nvarying vec2 vUv2;\nuniform sampler2D lightMap;\n#endif",
    lightmap_pars_vertex: "#ifdef USE_LIGHTMAP\nvarying vec2 vUv2;\n#endif",
    lightmap_fragment: "#ifdef USE_LIGHTMAP\ngl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );\n#endif",
    lightmap_vertex: "#ifdef USE_LIGHTMAP\nvUv2 = uv2;\n#endif",
    bumpmap_pars_fragment: "#ifdef USE_BUMPMAP\nuniform sampler2D bumpMap;\nuniform float bumpScale;\nvec2 dHdxy_fwd() {\nvec2 dSTdx = dFdx( vUv );\nvec2 dSTdy = dFdy( vUv );\nfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\nfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\nfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\nreturn vec2( dBx, dBy );\n}\nvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\nvec3 vSigmaX = dFdx( surf_pos );\nvec3 vSigmaY = dFdy( surf_pos );\nvec3 vN = surf_norm;\nvec3 R1 = cross( vSigmaY, vN );\nvec3 R2 = cross( vN, vSigmaX );\nfloat fDet = dot( vSigmaX, R1 );\nvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\nreturn normalize( abs( fDet ) * surf_norm - vGrad );\n}\n#endif",
    normalmap_pars_fragment: "#ifdef USE_NORMALMAP\nuniform sampler2D normalMap;\nuniform vec2 normalScale;\nvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\nvec3 q0 = dFdx( eye_pos.xyz );\nvec3 q1 = dFdy( eye_pos.xyz );\nvec2 st0 = dFdx( vUv.st );\nvec2 st1 = dFdy( vUv.st );\nvec3 S = normalize(  q0 * st1.t - q1 * st0.t );\nvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\nvec3 N = normalize( surf_norm );\nvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\nmapN.xy = normalScale * mapN.xy;\nmat3 tsn = mat3( S, T, N );\nreturn normalize( tsn * mapN );\n}\n#endif",
    specularmap_pars_fragment: "#ifdef USE_SPECULARMAP\nuniform sampler2D specularMap;\n#endif",
    specularmap_fragment: "float specularStrength;\n#ifdef USE_SPECULARMAP\nvec4 texelSpecular = texture2D( specularMap, vUv );\nspecularStrength = texelSpecular.r;\n#else\nspecularStrength = 1.0;\n#endif",
    lights_lambert_pars_vertex: "uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_HEMI_LIGHTS > 0\nuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\nuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\nuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n#endif\n#ifdef WRAP_AROUND\nuniform vec3 wrapRGB;\n#endif",
    lights_lambert_vertex: "vLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\nvLightBack = vec3( 0.0 );\n#endif\ntransformedNormal = normalize( transformedNormal );\n#if MAX_DIR_LIGHTS > 0\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( transformedNormal, dirVector );\nvec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n#ifdef DOUBLE_SIDED\nvec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n#ifdef WRAP_AROUND\nvec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n#endif\n#endif\n#ifdef WRAP_AROUND\nvec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\ndirectionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n#ifdef DOUBLE_SIDED\ndirectionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n#endif\n#endif\nvLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n#ifdef DOUBLE_SIDED\nvLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n#endif\n}\n#endif\n#if MAX_POINT_LIGHTS > 0\nfor( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\nfloat dotProduct = dot( transformedNormal, lVector );\nvec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n#ifdef DOUBLE_SIDED\nvec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n#ifdef WRAP_AROUND\nvec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n#endif\n#endif\n#ifdef WRAP_AROUND\nvec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\npointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n#ifdef DOUBLE_SIDED\npointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n#endif\n#endif\nvLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;\n#ifdef DOUBLE_SIDED\nvLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;\n#endif\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nfor( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\nif ( spotEffect > spotLightAngleCos[ i ] ) {\nspotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );\nfloat lDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\nfloat dotProduct = dot( transformedNormal, lVector );\nvec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n#ifdef DOUBLE_SIDED\nvec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n#ifdef WRAP_AROUND\nvec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n#endif\n#endif\n#ifdef WRAP_AROUND\nvec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\nspotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n#ifdef DOUBLE_SIDED\nspotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n#endif\n#endif\nvLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;\n#ifdef DOUBLE_SIDED\nvLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;\n#endif\n}\n}\n#endif\n#if MAX_HEMI_LIGHTS > 0\nfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\nvec3 lVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( transformedNormal, lVector );\nfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\nfloat hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\nvLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n#ifdef DOUBLE_SIDED\nvLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n#endif\n}\n#endif\nvLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;\n#ifdef DOUBLE_SIDED\nvLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;\n#endif",
    lights_phong_pars_vertex: "#ifndef PHONG_PER_PIXEL\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\nvarying vec4 vPointLight[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\nvarying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )\nvarying vec3 vWorldPosition;\n#endif",
    lights_phong_vertex: "#ifndef PHONG_PER_PIXEL\n#if MAX_POINT_LIGHTS > 0\nfor( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nvPointLight[ i ] = vec4( lVector, lDistance );\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nfor( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\nvSpotLight[ i ] = vec4( lVector, lDistance );\n}\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )\nvWorldPosition = worldPosition.xyz;\n#endif",
    lights_phong_pars_fragment: "uniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_HEMI_LIGHTS > 0\nuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n#ifdef PHONG_PER_PIXEL\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#else\nvarying vec4 vPointLight[ MAX_POINT_LIGHTS ];\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\nuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\nuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n#ifdef PHONG_PER_PIXEL\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n#else\nvarying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )\nvarying vec3 vWorldPosition;\n#endif\n#ifdef WRAP_AROUND\nuniform vec3 wrapRGB;\n#endif\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;",
    lights_phong_fragment: "vec3 normal = normalize( vNormal );\nvec3 viewPosition = normalize( vViewPosition );\n#ifdef DOUBLE_SIDED\nnormal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n#endif\n#ifdef USE_NORMALMAP\nnormal = perturbNormal2Arb( -viewPosition, normal );\n#elif defined( USE_BUMPMAP )\nnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n#if MAX_POINT_LIGHTS > 0\nvec3 pointDiffuse  = vec3( 0.0 );\nvec3 pointSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n#ifdef PHONG_PER_PIXEL\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz + vViewPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\n#else\nvec3 lVector = normalize( vPointLight[ i ].xyz );\nfloat lDistance = vPointLight[ i ].w;\n#endif\nfloat dotProduct = dot( normal, lVector );\n#ifdef WRAP_AROUND\nfloat pointDiffuseWeightFull = max( dotProduct, 0.0 );\nfloat pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\nvec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n#else\nfloat pointDiffuseWeight = max( dotProduct, 0.0 );\n#endif\npointDiffuse  += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;\nvec3 pointHalfVector = normalize( lVector + viewPosition );\nfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\nfloat pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, pointHalfVector ), 5.0 );\npointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;\n#else\npointSpecular += specular * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance;\n#endif\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nvec3 spotDiffuse  = vec3( 0.0 );\nvec3 spotSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n#ifdef PHONG_PER_PIXEL\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz + vViewPosition.xyz;\nfloat lDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\n#else\nvec3 lVector = normalize( vSpotLight[ i ].xyz );\nfloat lDistance = vSpotLight[ i ].w;\n#endif\nfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\nif ( spotEffect > spotLightAngleCos[ i ] ) {\nspotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );\nfloat dotProduct = dot( normal, lVector );\n#ifdef WRAP_AROUND\nfloat spotDiffuseWeightFull = max( dotProduct, 0.0 );\nfloat spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\nvec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n#else\nfloat spotDiffuseWeight = max( dotProduct, 0.0 );\n#endif\nspotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;\nvec3 spotHalfVector = normalize( lVector + viewPosition );\nfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\nfloat spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, spotHalfVector ), 5.0 );\nspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;\n#else\nspotSpecular += specular * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * spotEffect;\n#endif\n}\n}\n#endif\n#if MAX_DIR_LIGHTS > 0\nvec3 dirDiffuse  = vec3( 0.0 );\nvec3 dirSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( normal, dirVector );\n#ifdef WRAP_AROUND\nfloat dirDiffuseWeightFull = max( dotProduct, 0.0 );\nfloat dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\nvec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n#else\nfloat dirDiffuseWeight = max( dotProduct, 0.0 );\n#endif\ndirDiffuse  += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;\nvec3 dirHalfVector = normalize( dirVector + viewPosition );\nfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\nfloat dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );\ndirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n#else\ndirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight;\n#endif\n}\n#endif\n#if MAX_HEMI_LIGHTS > 0\nvec3 hemiDiffuse  = vec3( 0.0 );\nvec3 hemiSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\nvec3 lVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( normal, lVector );\nfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\nvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\nhemiDiffuse += diffuse * hemiColor;\nvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\nfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\nfloat hemiSpecularWeightSky = specularStrength * max( pow( hemiDotNormalHalfSky, shininess ), 0.0 );\nvec3 lVectorGround = -lVector;\nvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\nfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\nfloat hemiSpecularWeightGround = specularStrength * max( pow( hemiDotNormalHalfGround, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat dotProductGround = dot( normal, lVectorGround );\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );\nvec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );\nhemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n#else\nhemiSpecular += specular * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;\n#endif\n}\n#endif\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n#if MAX_DIR_LIGHTS > 0\ntotalDiffuse += dirDiffuse;\ntotalSpecular += dirSpecular;\n#endif\n#if MAX_HEMI_LIGHTS > 0\ntotalDiffuse += hemiDiffuse;\ntotalSpecular += hemiSpecular;\n#endif\n#if MAX_POINT_LIGHTS > 0\ntotalDiffuse += pointDiffuse;\ntotalSpecular += pointSpecular;\n#endif\n#if MAX_SPOT_LIGHTS > 0\ntotalDiffuse += spotDiffuse;\ntotalSpecular += spotSpecular;\n#endif\n#ifdef METAL\ngl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );\n#else\ngl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n#endif",
    color_pars_fragment: "#ifdef USE_COLOR\nvarying vec3 vColor;\n#endif",
    color_fragment: "#ifdef USE_COLOR\ngl_FragColor = gl_FragColor * vec4( vColor, opacity );\n#endif",
    color_pars_vertex: "#ifdef USE_COLOR\nvarying vec3 vColor;\n#endif",
    color_vertex: "#ifdef USE_COLOR\n#ifdef GAMMA_INPUT\nvColor = color * color;\n#else\nvColor = color;\n#endif\n#endif",
    skinning_pars_vertex: "#ifdef USE_SKINNING\n#ifdef BONE_TEXTURE\nuniform sampler2D boneTexture;\nmat4 getBoneMatrix( const in float i ) {\nfloat j = i * 4.0;\nfloat x = mod( j, N_BONE_PIXEL_X );\nfloat y = floor( j / N_BONE_PIXEL_X );\nconst float dx = 1.0 / N_BONE_PIXEL_X;\nconst float dy = 1.0 / N_BONE_PIXEL_Y;\ny = dy * ( y + 0.5 );\nvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\nvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\nvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\nvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\nmat4 bone = mat4( v1, v2, v3, v4 );\nreturn bone;\n}\n#else\nuniform mat4 boneGlobalMatrices[ MAX_BONES ];\nmat4 getBoneMatrix( const in float i ) {\nmat4 bone = boneGlobalMatrices[ int(i) ];\nreturn bone;\n}\n#endif\n#endif",
    skinbase_vertex: "#ifdef USE_SKINNING\nmat4 boneMatX = getBoneMatrix( skinIndex.x );\nmat4 boneMatY = getBoneMatrix( skinIndex.y );\n#endif",
    skinning_vertex: "#ifdef USE_SKINNING\n#ifdef USE_MORPHTARGETS\nvec4 skinVertex = vec4( morphed, 1.0 );\n#else\nvec4 skinVertex = vec4( position, 1.0 );\n#endif\nvec4 skinned  = boneMatX * skinVertex * skinWeight.x;\nskinned \t  += boneMatY * skinVertex * skinWeight.y;\n#endif",
    morphtarget_pars_vertex: "#ifdef USE_MORPHTARGETS\n#ifndef USE_MORPHNORMALS\nuniform float morphTargetInfluences[ 8 ];\n#else\nuniform float morphTargetInfluences[ 4 ];\n#endif\n#endif",
    morphtarget_vertex: "#ifdef USE_MORPHTARGETS\nvec3 morphed = vec3( 0.0 );\nmorphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\nmorphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\nmorphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\nmorphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n#ifndef USE_MORPHNORMALS\nmorphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\nmorphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\nmorphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\nmorphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n#endif\nmorphed += position;\n#endif",
    default_vertex: "vec4 mvPosition;\n#ifdef USE_SKINNING\nmvPosition = modelViewMatrix * skinned;\n#endif\n#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )\nmvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n#endif\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )\nmvPosition = modelViewMatrix * vec4( position, 1.0 );\n#endif\ngl_Position = projectionMatrix * mvPosition;",
    morphnormal_vertex: "#ifdef USE_MORPHNORMALS\nvec3 morphedNormal = vec3( 0.0 );\nmorphedNormal +=  ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\nmorphedNormal +=  ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\nmorphedNormal +=  ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\nmorphedNormal +=  ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\nmorphedNormal += normal;\n#endif",
    skinnormal_vertex: "#ifdef USE_SKINNING\nmat4 skinMatrix = skinWeight.x * boneMatX;\nskinMatrix \t+= skinWeight.y * boneMatY;\n#ifdef USE_MORPHNORMALS\nvec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n#else\nvec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n#endif\n#endif",
    defaultnormal_vertex: "vec3 objectNormal;\n#ifdef USE_SKINNING\nobjectNormal = skinnedNormal.xyz;\n#endif\n#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )\nobjectNormal = morphedNormal;\n#endif\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )\nobjectNormal = normal;\n#endif\n#ifdef FLIP_SIDED\nobjectNormal = -objectNormal;\n#endif\nvec3 transformedNormal = normalMatrix * objectNormal;",
    shadowmap_pars_fragment: "#ifdef USE_SHADOWMAP\nuniform sampler2D shadowMap[ MAX_SHADOWS ];\nuniform vec2 shadowMapSize[ MAX_SHADOWS ];\nuniform float shadowDarkness[ MAX_SHADOWS ];\nuniform float shadowBias[ MAX_SHADOWS ];\nvarying vec4 vShadowCoord[ MAX_SHADOWS ];\nfloat unpackDepth( const in vec4 rgba_depth ) {\nconst vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\nfloat depth = dot( rgba_depth, bit_shift );\nreturn depth;\n}\n#endif",
    shadowmap_fragment: "#ifdef USE_SHADOWMAP\n#ifdef SHADOWMAP_DEBUG\nvec3 frustumColors[3];\nfrustumColors[0] = vec3( 1.0, 0.5, 0.0 );\nfrustumColors[1] = vec3( 0.0, 1.0, 0.8 );\nfrustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n#endif\n#ifdef SHADOWMAP_CASCADE\nint inFrustumCount = 0;\n#endif\nfloat fDepth;\nvec3 shadowColor = vec3( 1.0 );\nfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\nvec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\nbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\nbool inFrustum = all( inFrustumVec );\n#ifdef SHADOWMAP_CASCADE\ninFrustumCount += int( inFrustum );\nbvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n#else\nbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n#endif\nbool frustumTest = all( frustumTestVec );\nif ( frustumTest ) {\nshadowCoord.z += shadowBias[ i ];\n#if defined( SHADOWMAP_TYPE_PCF )\nfloat shadow = 0.0;\nconst float shadowDelta = 1.0 / 9.0;\nfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\nfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\nfloat dx0 = -1.25 * xPixelOffset;\nfloat dy0 = -1.25 * yPixelOffset;\nfloat dx1 = 1.25 * xPixelOffset;\nfloat dy1 = 1.25 * yPixelOffset;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\nfloat shadow = 0.0;\nfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\nfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\nfloat dx0 = -1.0 * xPixelOffset;\nfloat dy0 = -1.0 * yPixelOffset;\nfloat dx1 = 1.0 * xPixelOffset;\nfloat dy1 = 1.0 * yPixelOffset;\nmat3 shadowKernel;\nmat3 depthKernel;\ndepthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\nif ( depthKernel[0][0] < shadowCoord.z ) shadowKernel[0][0] = 0.25;\nelse shadowKernel[0][0] = 0.0;\ndepthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\nif ( depthKernel[0][1] < shadowCoord.z ) shadowKernel[0][1] = 0.25;\nelse shadowKernel[0][1] = 0.0;\ndepthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i], shadowCoord.xy + vec2( dx0, dy1 ) ) );\nif ( depthKernel[0][2] < shadowCoord.z ) shadowKernel[0][2] = 0.25;\nelse shadowKernel[0][2] = 0.0;\ndepthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\nif ( depthKernel[1][0] < shadowCoord.z ) shadowKernel[1][0] = 0.25;\nelse shadowKernel[1][0] = 0.0;\ndepthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\nif ( depthKernel[1][1] < shadowCoord.z ) shadowKernel[1][1] = 0.25;\nelse shadowKernel[1][1] = 0.0;\ndepthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\nif ( depthKernel[1][2] < shadowCoord.z ) shadowKernel[1][2] = 0.25;\nelse shadowKernel[1][2] = 0.0;\ndepthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\nif ( depthKernel[2][0] < shadowCoord.z ) shadowKernel[2][0] = 0.25;\nelse shadowKernel[2][0] = 0.0;\ndepthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\nif ( depthKernel[2][1] < shadowCoord.z ) shadowKernel[2][1] = 0.25;\nelse shadowKernel[2][1] = 0.0;\ndepthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\nif ( depthKernel[2][2] < shadowCoord.z ) shadowKernel[2][2] = 0.25;\nelse shadowKernel[2][2] = 0.0;\nvec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\nshadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\nshadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\nvec4 shadowValues;\nshadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\nshadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\nshadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\nshadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\nshadow = dot( shadowValues, vec4( 1.0 ) );\nshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n#else\nvec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\nfloat fDepth = unpackDepth( rgbaDepth );\nif ( fDepth < shadowCoord.z )\nshadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n#endif\n}\n#ifdef SHADOWMAP_DEBUG\n#ifdef SHADOWMAP_CASCADE\nif ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];\n#else\nif ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];\n#endif\n#endif\n}\n#ifdef GAMMA_OUTPUT\nshadowColor *= shadowColor;\n#endif\ngl_FragColor.xyz = gl_FragColor.xyz * shadowColor;\n#endif",
    shadowmap_pars_vertex: "#ifdef USE_SHADOWMAP\nvarying vec4 vShadowCoord[ MAX_SHADOWS ];\nuniform mat4 shadowMatrix[ MAX_SHADOWS ];\n#endif",
    shadowmap_vertex: "#ifdef USE_SHADOWMAP\nfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\nvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n}\n#endif",
    alphatest_fragment: "#ifdef ALPHATEST\nif ( gl_FragColor.a < ALPHATEST ) discard;\n#endif",
    linear_to_gamma_fragment: "#ifdef GAMMA_OUTPUT\ngl_FragColor.xyz = sqrt( gl_FragColor.xyz );\n#endif"
};
THREE.UniformsUtils = {
    merge: function (a) {
        var b, c, d, e = {};
        for (b = 0; b < a.length; b++) for (c in d = this.clone(a[b]), d) e[c] = d[c];
        return e
    }, clone: function (a) {
        var b, c, d, e = {};
        for (b in a) for (c in e[b] = {}, a[b]) d = a[b][c], e[b][c] = d instanceof THREE.Color || d instanceof THREE.Vector2 || d instanceof THREE.Vector3 || d instanceof THREE.Vector4 || d instanceof THREE.Matrix4 || d instanceof THREE.Texture ? d.clone() : d instanceof Array ? d.slice() : d;
        return e
    }
};
THREE.UniformsLib = {
    common: {
        diffuse: {type: "c", value: new THREE.Color(15658734)},
        opacity: {type: "f", value: 1},
        map: {type: "t", value: null},
        offsetRepeat: {type: "v4", value: new THREE.Vector4(0, 0, 1, 1)},
        lightMap: {type: "t", value: null},
        specularMap: {type: "t", value: null},
        envMap: {type: "t", value: null},
        flipEnvMap: {type: "f", value: -1},
        useRefract: {type: "i", value: 0},
        reflectivity: {type: "f", value: 1},
        refractionRatio: {type: "f", value: 0.98},
        combine: {type: "i", value: 0},
        morphTargetInfluences: {type: "f", value: 0}
    },
    bump: {
        bumpMap: {
            type: "t",
            value: null
        }, bumpScale: {type: "f", value: 1}
    },
    normalmap: {normalMap: {type: "t", value: null}, normalScale: {type: "v2", value: new THREE.Vector2(1, 1)}},
    fog: {
        fogDensity: {type: "f", value: 2.5E-4},
        fogNear: {type: "f", value: 1},
        fogFar: {type: "f", value: 2E3},
        fogColor: {type: "c", value: new THREE.Color(16777215)}
    },
    lights: {
        ambientLightColor: {type: "fv", value: []},
        directionalLightDirection: {type: "fv", value: []},
        directionalLightColor: {type: "fv", value: []},
        hemisphereLightDirection: {type: "fv", value: []},
        hemisphereLightSkyColor: {
            type: "fv",
            value: []
        },
        hemisphereLightGroundColor: {type: "fv", value: []},
        pointLightColor: {type: "fv", value: []},
        pointLightPosition: {type: "fv", value: []},
        pointLightDistance: {type: "fv1", value: []},
        spotLightColor: {type: "fv", value: []},
        spotLightPosition: {type: "fv", value: []},
        spotLightDirection: {type: "fv", value: []},
        spotLightDistance: {type: "fv1", value: []},
        spotLightAngleCos: {type: "fv1", value: []},
        spotLightExponent: {type: "fv1", value: []}
    },
    particle: {
        psColor: {type: "c", value: new THREE.Color(15658734)},
        opacity: {type: "f", value: 1},
        size: {
            type: "f",
            value: 1
        },
        scale: {type: "f", value: 1},
        map: {type: "t", value: null},
        fogDensity: {type: "f", value: 2.5E-4},
        fogNear: {type: "f", value: 1},
        fogFar: {type: "f", value: 2E3},
        fogColor: {type: "c", value: new THREE.Color(16777215)}
    },
    shadowmap: {
        shadowMap: {type: "tv", value: []},
        shadowMapSize: {type: "v2v", value: []},
        shadowBias: {type: "fv1", value: []},
        shadowDarkness: {type: "fv1", value: []},
        shadowMatrix: {type: "m4v", value: []}
    }
};
THREE.ShaderLib = {
    basic: {
        uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.common, THREE.UniformsLib.fog, THREE.UniformsLib.shadowmap]),
        vertexShader: [THREE.ShaderChunk.map_pars_vertex, THREE.ShaderChunk.lightmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, "void main() {", THREE.ShaderChunk.map_vertex, THREE.ShaderChunk.lightmap_vertex, THREE.ShaderChunk.color_vertex,
            THREE.ShaderChunk.skinbase_vertex, "#ifdef USE_ENVMAP", THREE.ShaderChunk.morphnormal_vertex, THREE.ShaderChunk.skinnormal_vertex, THREE.ShaderChunk.defaultnormal_vertex, "#endif", THREE.ShaderChunk.morphtarget_vertex, THREE.ShaderChunk.skinning_vertex, THREE.ShaderChunk.default_vertex, THREE.ShaderChunk.worldpos_vertex, THREE.ShaderChunk.envmap_vertex, THREE.ShaderChunk.shadowmap_vertex, "}"].join("\n"),
        fragmentShader: ["uniform vec3 diffuse;\nuniform float opacity;", THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.map_pars_fragment,
            THREE.ShaderChunk.lightmap_pars_fragment, THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, THREE.ShaderChunk.specularmap_pars_fragment, "void main() {\ngl_FragColor = vec4( diffuse, opacity );", THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.specularmap_fragment, THREE.ShaderChunk.lightmap_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.envmap_fragment, THREE.ShaderChunk.shadowmap_fragment,
            THREE.ShaderChunk.linear_to_gamma_fragment, THREE.ShaderChunk.fog_fragment, "}"].join("\n")
    }, lambert: {
        uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.common, THREE.UniformsLib.fog, THREE.UniformsLib.lights, THREE.UniformsLib.shadowmap, {
            ambient: {
                type: "c",
                value: new THREE.Color(16777215)
            },
            emissive: {type: "c", value: new THREE.Color(0)},
            wrapRGB: {type: "v3", value: new THREE.Vector3(1, 1, 1)}
        }]),
        vertexShader: ["#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\nvarying vec3 vLightBack;\n#endif",
            THREE.ShaderChunk.map_pars_vertex, THREE.ShaderChunk.lightmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.lights_lambert_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, "void main() {", THREE.ShaderChunk.map_vertex, THREE.ShaderChunk.lightmap_vertex, THREE.ShaderChunk.color_vertex, THREE.ShaderChunk.morphnormal_vertex, THREE.ShaderChunk.skinbase_vertex, THREE.ShaderChunk.skinnormal_vertex,
            THREE.ShaderChunk.defaultnormal_vertex, THREE.ShaderChunk.morphtarget_vertex, THREE.ShaderChunk.skinning_vertex, THREE.ShaderChunk.default_vertex, THREE.ShaderChunk.worldpos_vertex, THREE.ShaderChunk.envmap_vertex, THREE.ShaderChunk.lights_lambert_vertex, THREE.ShaderChunk.shadowmap_vertex, "}"].join("\n"),
        fragmentShader: ["uniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\nvarying vec3 vLightBack;\n#endif", THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.map_pars_fragment, THREE.ShaderChunk.lightmap_pars_fragment,
            THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, THREE.ShaderChunk.specularmap_pars_fragment, "void main() {\ngl_FragColor = vec4( vec3 ( 1.0 ), opacity );", THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.specularmap_fragment, "#ifdef DOUBLE_SIDED\nif ( gl_FrontFacing )\ngl_FragColor.xyz *= vLightFront;\nelse\ngl_FragColor.xyz *= vLightBack;\n#else\ngl_FragColor.xyz *= vLightFront;\n#endif", THREE.ShaderChunk.lightmap_fragment,
            THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.envmap_fragment, THREE.ShaderChunk.shadowmap_fragment, THREE.ShaderChunk.linear_to_gamma_fragment, THREE.ShaderChunk.fog_fragment, "}"].join("\n")
    }, phong: {
        uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.common, THREE.UniformsLib.bump, THREE.UniformsLib.normalmap, THREE.UniformsLib.fog, THREE.UniformsLib.lights, THREE.UniformsLib.shadowmap, {
            ambient: {type: "c", value: new THREE.Color(16777215)},
            emissive: {type: "c", value: new THREE.Color(0)},
            specular: {
                type: "c",
                value: new THREE.Color(1118481)
            },
            shininess: {type: "f", value: 30},
            wrapRGB: {type: "v3", value: new THREE.Vector3(1, 1, 1)}
        }]),
        vertexShader: ["#define PHONG\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;", THREE.ShaderChunk.map_pars_vertex, THREE.ShaderChunk.lightmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.lights_phong_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex,
            "void main() {", THREE.ShaderChunk.map_vertex, THREE.ShaderChunk.lightmap_vertex, THREE.ShaderChunk.color_vertex, THREE.ShaderChunk.morphnormal_vertex, THREE.ShaderChunk.skinbase_vertex, THREE.ShaderChunk.skinnormal_vertex, THREE.ShaderChunk.defaultnormal_vertex, "vNormal = normalize( transformedNormal );", THREE.ShaderChunk.morphtarget_vertex, THREE.ShaderChunk.skinning_vertex, THREE.ShaderChunk.default_vertex, "vViewPosition = -mvPosition.xyz;", THREE.ShaderChunk.worldpos_vertex, THREE.ShaderChunk.envmap_vertex,
            THREE.ShaderChunk.lights_phong_vertex, THREE.ShaderChunk.shadowmap_vertex, "}"].join("\n"),
        fragmentShader: ["uniform vec3 diffuse;\nuniform float opacity;\nuniform vec3 ambient;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;", THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.map_pars_fragment, THREE.ShaderChunk.lightmap_pars_fragment, THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.lights_phong_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment,
            THREE.ShaderChunk.bumpmap_pars_fragment, THREE.ShaderChunk.normalmap_pars_fragment, THREE.ShaderChunk.specularmap_pars_fragment, "void main() {\ngl_FragColor = vec4( vec3 ( 1.0 ), opacity );", THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.specularmap_fragment, THREE.ShaderChunk.lights_phong_fragment, THREE.ShaderChunk.lightmap_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.envmap_fragment, THREE.ShaderChunk.shadowmap_fragment, THREE.ShaderChunk.linear_to_gamma_fragment,
            THREE.ShaderChunk.fog_fragment, "}"].join("\n")
    }, particle_basic: {
        uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.particle, THREE.UniformsLib.shadowmap]),
        vertexShader: ["uniform float size;\nuniform float scale;", THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, "void main() {", THREE.ShaderChunk.color_vertex, "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n#ifdef USE_SIZEATTENUATION\ngl_PointSize = size * ( scale / length( mvPosition.xyz ) );\n#else\ngl_PointSize = size;\n#endif\ngl_Position = projectionMatrix * mvPosition;",
            THREE.ShaderChunk.worldpos_vertex, THREE.ShaderChunk.shadowmap_vertex, "}"].join("\n"),
        fragmentShader: ["uniform vec3 psColor;\nuniform float opacity;", THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.map_particle_pars_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, "void main() {\ngl_FragColor = vec4( psColor, opacity );", THREE.ShaderChunk.map_particle_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.shadowmap_fragment,
            THREE.ShaderChunk.fog_fragment, "}"].join("\n")
    }, dashed: {
        uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.common, THREE.UniformsLib.fog, {
            scale: {
                type: "f",
                value: 1
            }, dashSize: {type: "f", value: 1}, totalSize: {type: "f", value: 2}
        }]),
        vertexShader: ["uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;", THREE.ShaderChunk.color_pars_vertex, "void main() {", THREE.ShaderChunk.color_vertex, "vLineDistance = scale * lineDistance;\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\n}"].join("\n"),
        fragmentShader: ["uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;", THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.fog_pars_fragment, "void main() {\nif ( mod( vLineDistance, totalSize ) > dashSize ) {\ndiscard;\n}\ngl_FragColor = vec4( diffuse, opacity );", THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.fog_fragment, "}"].join("\n")
    }, depth: {
        uniforms: {
            mNear: {type: "f", value: 1}, mFar: {type: "f", value: 2E3}, opacity: {
                type: "f",
                value: 1
            }
        },
        vertexShader: "void main() {\ngl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}",
        fragmentShader: "uniform float mNear;\nuniform float mFar;\nuniform float opacity;\nvoid main() {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat color = 1.0 - smoothstep( mNear, mFar, depth );\ngl_FragColor = vec4( vec3( color ), opacity );\n}"
    }, normal: {
        uniforms: {opacity: {type: "f", value: 1}},
        vertexShader: "varying vec3 vNormal;\nvoid main() {\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\nvNormal = normalize( normalMatrix * normal );\ngl_Position = projectionMatrix * mvPosition;\n}",
        fragmentShader: "uniform float opacity;\nvarying vec3 vNormal;\nvoid main() {\ngl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );\n}"
    }, normalmap: {
        uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.fog, THREE.UniformsLib.lights, THREE.UniformsLib.shadowmap, {
            enableAO: {type: "i", value: 0},
            enableDiffuse: {type: "i", value: 0},
            enableSpecular: {type: "i", value: 0},
            enableReflection: {type: "i", value: 0},
            enableDisplacement: {type: "i", value: 0},
            tDisplacement: {type: "t", value: null},
            tDiffuse: {type: "t", value: null},
            tCube: {type: "t", value: null},
            tNormal: {type: "t", value: null},
            tSpecular: {type: "t", value: null},
            tAO: {type: "t", value: null},
            uNormalScale: {type: "v2", value: new THREE.Vector2(1, 1)},
            uDisplacementBias: {type: "f", value: 0},
            uDisplacementScale: {type: "f", value: 1},
            uDiffuseColor: {type: "c", value: new THREE.Color(16777215)},
            uSpecularColor: {type: "c", value: new THREE.Color(1118481)},
            uAmbientColor: {type: "c", value: new THREE.Color(16777215)},
            uShininess: {type: "f", value: 30},
            uOpacity: {type: "f", value: 1},
            useRefract: {type: "i", value: 0},
            uRefractionRatio: {type: "f", value: 0.98},
            uReflectivity: {type: "f", value: 0.5},
            uOffset: {type: "v2", value: new THREE.Vector2(0, 0)},
            uRepeat: {type: "v2", value: new THREE.Vector2(1, 1)},
            wrapRGB: {type: "v3", value: new THREE.Vector3(1, 1, 1)}
        }]),
        fragmentShader: ["uniform vec3 uAmbientColor;\nuniform vec3 uDiffuseColor;\nuniform vec3 uSpecularColor;\nuniform float uShininess;\nuniform float uOpacity;\nuniform bool enableDiffuse;\nuniform bool enableSpecular;\nuniform bool enableAO;\nuniform bool enableReflection;\nuniform sampler2D tDiffuse;\nuniform sampler2D tNormal;\nuniform sampler2D tSpecular;\nuniform sampler2D tAO;\nuniform samplerCube tCube;\nuniform vec2 uNormalScale;\nuniform bool useRefract;\nuniform float uRefractionRatio;\nuniform float uReflectivity;\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nuniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_HEMI_LIGHTS > 0\nuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\nuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\nuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n#endif\n#ifdef WRAP_AROUND\nuniform vec3 wrapRGB;\n#endif\nvarying vec3 vWorldPosition;\nvarying vec3 vViewPosition;",
            THREE.ShaderChunk.shadowmap_pars_fragment, THREE.ShaderChunk.fog_pars_fragment, "void main() {\ngl_FragColor = vec4( vec3( 1.0 ), uOpacity );\nvec3 specularTex = vec3( 1.0 );\nvec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;\nnormalTex.xy *= uNormalScale;\nnormalTex = normalize( normalTex );\nif( enableDiffuse ) {\n#ifdef GAMMA_INPUT\nvec4 texelColor = texture2D( tDiffuse, vUv );\ntexelColor.xyz *= texelColor.xyz;\ngl_FragColor = gl_FragColor * texelColor;\n#else\ngl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );\n#endif\n}\nif( enableAO ) {\n#ifdef GAMMA_INPUT\nvec4 aoColor = texture2D( tAO, vUv );\naoColor.xyz *= aoColor.xyz;\ngl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;\n#else\ngl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;\n#endif\n}\nif( enableSpecular )\nspecularTex = texture2D( tSpecular, vUv ).xyz;\nmat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );\nvec3 finalNormal = tsb * normalTex;\n#ifdef FLIP_SIDED\nfinalNormal = -finalNormal;\n#endif\nvec3 normal = normalize( finalNormal );\nvec3 viewPosition = normalize( vViewPosition );\n#if MAX_POINT_LIGHTS > 0\nvec3 pointDiffuse = vec3( 0.0 );\nvec3 pointSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 pointVector = lPosition.xyz + vViewPosition.xyz;\nfloat pointDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\npointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );\npointVector = normalize( pointVector );\n#ifdef WRAP_AROUND\nfloat pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );\nfloat pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );\nvec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n#else\nfloat pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );\n#endif\npointDiffuse += pointDistance * pointLightColor[ i ] * uDiffuseColor * pointDiffuseWeight;\nvec3 pointHalfVector = normalize( pointVector + viewPosition );\nfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\nfloat pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( pointVector, pointHalfVector ), 5.0 );\npointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;\n#else\npointSpecular += pointDistance * pointLightColor[ i ] * uSpecularColor * pointSpecularWeight * pointDiffuseWeight;\n#endif\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nvec3 spotDiffuse = vec3( 0.0 );\nvec3 spotSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 spotVector = lPosition.xyz + vViewPosition.xyz;\nfloat spotDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nspotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );\nspotVector = normalize( spotVector );\nfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\nif ( spotEffect > spotLightAngleCos[ i ] ) {\nspotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );\n#ifdef WRAP_AROUND\nfloat spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );\nfloat spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );\nvec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n#else\nfloat spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );\n#endif\nspotDiffuse += spotDistance * spotLightColor[ i ] * uDiffuseColor * spotDiffuseWeight * spotEffect;\nvec3 spotHalfVector = normalize( spotVector + viewPosition );\nfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\nfloat spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( spotVector, spotHalfVector ), 5.0 );\nspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;\n#else\nspotSpecular += spotDistance * spotLightColor[ i ] * uSpecularColor * spotSpecularWeight * spotDiffuseWeight * spotEffect;\n#endif\n}\n}\n#endif\n#if MAX_DIR_LIGHTS > 0\nvec3 dirDiffuse = vec3( 0.0 );\nvec3 dirSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\n#ifdef WRAP_AROUND\nfloat directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );\nfloat directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );\nvec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );\n#else\nfloat dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );\n#endif\ndirDiffuse += directionalLightColor[ i ] * uDiffuseColor * dirDiffuseWeight;\nvec3 dirHalfVector = normalize( dirVector + viewPosition );\nfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\nfloat dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );\ndirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n#else\ndirSpecular += directionalLightColor[ i ] * uSpecularColor * dirSpecularWeight * dirDiffuseWeight;\n#endif\n}\n#endif\n#if MAX_HEMI_LIGHTS > 0\nvec3 hemiDiffuse  = vec3( 0.0 );\nvec3 hemiSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\nvec3 lVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( normal, lVector );\nfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\nvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\nhemiDiffuse += uDiffuseColor * hemiColor;\nvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\nfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\nfloat hemiSpecularWeightSky = specularTex.r * max( pow( hemiDotNormalHalfSky, uShininess ), 0.0 );\nvec3 lVectorGround = -lVector;\nvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\nfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\nfloat hemiSpecularWeightGround = specularTex.r * max( pow( hemiDotNormalHalfGround, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat dotProductGround = dot( normal, lVectorGround );\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlickSky = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );\nvec3 schlickGround = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );\nhemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n#else\nhemiSpecular += uSpecularColor * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;\n#endif\n}\n#endif\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n#if MAX_DIR_LIGHTS > 0\ntotalDiffuse += dirDiffuse;\ntotalSpecular += dirSpecular;\n#endif\n#if MAX_HEMI_LIGHTS > 0\ntotalDiffuse += hemiDiffuse;\ntotalSpecular += hemiSpecular;\n#endif\n#if MAX_POINT_LIGHTS > 0\ntotalDiffuse += pointDiffuse;\ntotalSpecular += pointSpecular;\n#endif\n#if MAX_SPOT_LIGHTS > 0\ntotalDiffuse += spotDiffuse;\ntotalSpecular += spotSpecular;\n#endif\n#ifdef METAL\ngl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor + totalSpecular );\n#else\ngl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor ) + totalSpecular;\n#endif\nif ( enableReflection ) {\nvec3 vReflect;\nvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\nif ( useRefract ) {\nvReflect = refract( cameraToVertex, normal, uRefractionRatio );\n} else {\nvReflect = reflect( cameraToVertex, normal );\n}\nvec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );\n#ifdef GAMMA_INPUT\ncubeColor.xyz *= cubeColor.xyz;\n#endif\ngl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * uReflectivity );\n}",
            THREE.ShaderChunk.shadowmap_fragment, THREE.ShaderChunk.linear_to_gamma_fragment, THREE.ShaderChunk.fog_fragment, "}"].join("\n"),
        vertexShader: ["attribute vec4 tangent;\nuniform vec2 uOffset;\nuniform vec2 uRepeat;\nuniform bool enableDisplacement;\n#ifdef VERTEX_TEXTURES\nuniform sampler2D tDisplacement;\nuniform float uDisplacementScale;\nuniform float uDisplacementBias;\n#endif\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nvarying vec3 vWorldPosition;\nvarying vec3 vViewPosition;",
            THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, "void main() {", THREE.ShaderChunk.skinbase_vertex, THREE.ShaderChunk.skinnormal_vertex, "#ifdef USE_SKINNING\nvNormal = normalize( normalMatrix * skinnedNormal.xyz );\nvec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );\nvTangent = normalize( normalMatrix * skinnedTangent.xyz );\n#else\nvNormal = normalize( normalMatrix * normal );\nvTangent = normalize( normalMatrix * tangent.xyz );\n#endif\nvBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );\nvUv = uv * uRepeat + uOffset;\nvec3 displacedPosition;\n#ifdef VERTEX_TEXTURES\nif ( enableDisplacement ) {\nvec3 dv = texture2D( tDisplacement, uv ).xyz;\nfloat df = uDisplacementScale * dv.x + uDisplacementBias;\ndisplacedPosition = position + normalize( normal ) * df;\n} else {\n#ifdef USE_SKINNING\nvec4 skinVertex = vec4( position, 1.0 );\nvec4 skinned  = boneMatX * skinVertex * skinWeight.x;\nskinned \t  += boneMatY * skinVertex * skinWeight.y;\ndisplacedPosition  = skinned.xyz;\n#else\ndisplacedPosition = position;\n#endif\n}\n#else\n#ifdef USE_SKINNING\nvec4 skinVertex = vec4( position, 1.0 );\nvec4 skinned  = boneMatX * skinVertex * skinWeight.x;\nskinned \t  += boneMatY * skinVertex * skinWeight.y;\ndisplacedPosition  = skinned.xyz;\n#else\ndisplacedPosition = position;\n#endif\n#endif\nvec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );\nvec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\nvWorldPosition = worldPosition.xyz;\nvViewPosition = -mvPosition.xyz;\n#ifdef USE_SHADOWMAP\nfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\nvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n}\n#endif\n}"].join("\n")
    },
    cube: {
        uniforms: {tCube: {type: "t", value: null}, tFlip: {type: "f", value: -1}},
        vertexShader: "varying vec3 vWorldPosition;\nvoid main() {\nvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\nvWorldPosition = worldPosition.xyz;\ngl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}",
        fragmentShader: "uniform samplerCube tCube;\nuniform float tFlip;\nvarying vec3 vWorldPosition;\nvoid main() {\ngl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n}"
    },
    depthRGBA: {
        uniforms: {},
        vertexShader: [THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, "void main() {", THREE.ShaderChunk.skinbase_vertex, THREE.ShaderChunk.morphtarget_vertex, THREE.ShaderChunk.skinning_vertex, THREE.ShaderChunk.default_vertex, "}"].join("\n"),
        fragmentShader: "vec4 pack_depth( const in float depth ) {\nconst vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\nconst vec4 bit_mask  = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\nvec4 res = fract( depth * bit_shift );\nres -= res.xxyz * bit_mask;\nreturn res;\n}\nvoid main() {\ngl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );\n}"
    }
};
THREE.WebGLRenderer = function (a) {
    function b(a) {
        if (a.__webglCustomAttributesList) for (var b in a.__webglCustomAttributesList) j.deleteBuffer(a.__webglCustomAttributesList[b].buffer)
    }

    function c(a, b) {
        var c = a.vertices.length, d = b.material;
        if (d.attributes) {
            void 0 === a.__webglCustomAttributesList && (a.__webglCustomAttributesList = []);
            for (var e in d.attributes) {
                var f = d.attributes[e];
                if (!f.__webglInitialized || f.createUniqueBuffers) {
                    f.__webglInitialized = !0;
                    var g = 1;
                    "v2" === f.type ? g = 2 : "v3" === f.type ? g = 3 : "v4" === f.type ?
                        g = 4 : "c" === f.type && (g = 3);
                    f.size = g;
                    f.array = new Float32Array(c * g);
                    f.buffer = j.createBuffer();
                    f.buffer.belongsToAttribute = e;
                    f.needsUpdate = !0
                }
                a.__webglCustomAttributesList.push(f)
            }
        }
    }

    function d(a, b) {
        var c = b.geometry, d = a.faces3, h = a.faces4, i = 3 * d.length + 4 * h.length,
            k = 1 * d.length + 2 * h.length, h = 3 * d.length + 4 * h.length, d = e(b, a), m = g(d), l = f(d),
            p = d.vertexColors ? d.vertexColors : !1;
        a.__vertexArray = new Float32Array(3 * i);
        l && (a.__normalArray = new Float32Array(3 * i));
        c.hasTangents && (a.__tangentArray = new Float32Array(4 * i));
        p &&
        (a.__colorArray = new Float32Array(3 * i));
        if (m) {
            if (0 < c.faceUvs.length || 0 < c.faceVertexUvs.length) a.__uvArray = new Float32Array(2 * i);
            if (1 < c.faceUvs.length || 1 < c.faceVertexUvs.length) a.__uv2Array = new Float32Array(2 * i)
        }
        b.geometry.skinWeights.length && b.geometry.skinIndices.length && (a.__skinIndexArray = new Float32Array(4 * i), a.__skinWeightArray = new Float32Array(4 * i));
        a.__faceArray = new Uint16Array(3 * k);
        a.__lineArray = new Uint16Array(2 * h);
        if (a.numMorphTargets) {
            a.__morphTargetsArrays = [];
            c = 0;
            for (m = a.numMorphTargets; c <
            m; c++) a.__morphTargetsArrays.push(new Float32Array(3 * i))
        }
        if (a.numMorphNormals) {
            a.__morphNormalsArrays = [];
            c = 0;
            for (m = a.numMorphNormals; c < m; c++) a.__morphNormalsArrays.push(new Float32Array(3 * i))
        }
        a.__webglFaceCount = 3 * k;
        a.__webglLineCount = 2 * h;
        if (d.attributes) {
            void 0 === a.__webglCustomAttributesList && (a.__webglCustomAttributesList = []);
            for (var n in d.attributes) {
                var k = d.attributes[n], c = {}, q;
                for (q in k) c[q] = k[q];
                if (!c.__webglInitialized || c.createUniqueBuffers) c.__webglInitialized = !0, h = 1, "v2" === c.type ? h = 2 :
                    "v3" === c.type ? h = 3 : "v4" === c.type ? h = 4 : "c" === c.type && (h = 3), c.size = h, c.array = new Float32Array(i * h), c.buffer = j.createBuffer(), c.buffer.belongsToAttribute = n, k.needsUpdate = !0, c.__original = k;
                a.__webglCustomAttributesList.push(c)
            }
        }
        a.__inittedArrays = !0
    }

    function e(a, b) {
        return a.material instanceof THREE.MeshFaceMaterial ? a.material.materials[b.materialIndex] : a.material
    }

    function f(a) {
        return a instanceof THREE.MeshBasicMaterial && !a.envMap || a instanceof THREE.MeshDepthMaterial ? !1 : a && void 0 !== a.shading && a.shading ===
        THREE.SmoothShading ? THREE.SmoothShading : THREE.FlatShading
    }

    function g(a) {
        return a.map || a.lightMap || a.bumpMap || a.normalMap || a.specularMap || a instanceof THREE.ShaderMaterial ? !0 : !1
    }

    function h(a) {
        xb[a] || (j.enableVertexAttribArray(a), xb[a] = !0)
    }

    function i() {
        for (var a in xb) xb[a] && (j.disableVertexAttribArray(a), xb[a] = !1)
    }

    function k(a, b) {
        return a.z !== b.z ? b.z - a.z : b.id - a.id
    }

    function l(a, b) {
        return b[0] - a[0]
    }

    function m(a, b, c) {
        if (a.length) for (var d = 0, e = a.length; d < e; d++) La = kb = null, lb = db = V = ha = Pa = na = $ = -1, yb = !0, a[d].render(b,
            c, Nb, Ob), La = kb = null, lb = db = V = ha = Pa = na = $ = -1, yb = !0
    }

    function p(a, b, c, d, e, f, g, h) {
        var j, i, k, m;
        b ? (i = a.length - 1, m = b = -1) : (i = 0, b = a.length, m = 1);
        for (var l = i; l !== b; l += m) if (j = a[l], j.render) {
            i = j.object;
            k = j.buffer;
            if (h) j = h; else {
                j = j[c];
                if (!j) continue;
                g && L.setBlending(j.blending, j.blendEquation, j.blendSrc, j.blendDst);
                L.setDepthTest(j.depthTest);
                L.setDepthWrite(j.depthWrite);
                I(j.polygonOffset, j.polygonOffsetFactor, j.polygonOffsetUnits)
            }
            L.setMaterialFaces(j);
            k instanceof THREE.BufferGeometry ? L.renderBufferDirect(d, e,
                f, j, k, i) : L.renderBuffer(d, e, f, j, k, i)
        }
    }

    function s(a, b, c, d, e, f, g) {
        for (var h, j, i = 0, k = a.length; i < k; i++) if (h = a[i], j = h.object, j.visible) {
            if (g) h = g; else {
                h = h[b];
                if (!h) continue;
                f && L.setBlending(h.blending, h.blendEquation, h.blendSrc, h.blendDst);
                L.setDepthTest(h.depthTest);
                L.setDepthWrite(h.depthWrite);
                I(h.polygonOffset, h.polygonOffsetFactor, h.polygonOffsetUnits)
            }
            L.renderImmediateObject(c, d, e, h, j)
        }
    }

    function r(a, b, c) {
        a.push({buffer: b, object: c, opaque: null, transparent: null})
    }

    function n(a) {
        for (var b in a.attributes) if (a.attributes[b].needsUpdate) return !0;
        return !1
    }

    function q(a) {
        for (var b in a.attributes) a.attributes[b].needsUpdate = !1
    }

    function z(a, b) {
        for (var c = a.length - 1; 0 <= c; c--) a[c].object === b && a.splice(c, 1)
    }

    function t(a, b) {
        for (var c = a.length - 1; 0 <= c; c--) a[c] === b && a.splice(c, 1)
    }

    function x(a, b, c, d, e) {
        ea = 0;
        d.needsUpdate && (d.program && Yc(d), L.initMaterial(d, b, c, e), d.needsUpdate = !1);
        d.morphTargets && !e.__webglMorphTargetInfluences && (e.__webglMorphTargetInfluences = new Float32Array(L.maxMorphTargets));
        var f = !1, g = d.program, h = g.uniforms, i = d.uniforms;
        g !== kb &&
        (j.useProgram(g), kb = g, f = !0);
        d.id !== lb && (lb = d.id, f = !0);
        if (f || a !== La) j.uniformMatrix4fv(h.projectionMatrix, !1, a.projectionMatrix.elements), a !== La && (La = a);
        if (d.skinning) if (Cc && e.useVertexTexture) {
            if (null !== h.boneTexture) {
                var k = u();
                j.uniform1i(h.boneTexture, k);
                L.setTexture(e.boneTexture, k)
            }
        } else null !== h.boneGlobalMatrices && j.uniformMatrix4fv(h.boneGlobalMatrices, !1, e.boneMatrices);
        if (f) {
            c && d.fog && (i.fogColor.value = c.color, c instanceof THREE.Fog ? (i.fogNear.value = c.near, i.fogFar.value = c.far) : c instanceof
                THREE.FogExp2 && (i.fogDensity.value = c.density));
            if (d instanceof THREE.MeshPhongMaterial || d instanceof THREE.MeshLambertMaterial || d.lights) {
                if (yb) {
                    for (var m, l = k = 0, p = 0, n, q, s, r = pb, t = r.directional.colors, x = r.directional.positions, w = r.point.colors, z = r.point.positions, A = r.point.distances, B = r.spot.colors, E = r.spot.positions, I = r.spot.distances, J = r.spot.directions, K = r.spot.anglesCos, M = r.spot.exponents, V = r.hemi.skyColors, N = r.hemi.groundColors, S = r.hemi.positions, ta = 0, U = 0, $ = 0, ha = 0, fa = 0, Oa = 0, ia = 0, $a = 0, ca = m = 0, c =
                        s = ca = 0, f = b.length; c < f; c++) m = b[c], m.onlyShadow || (n = m.color, q = m.intensity, s = m.distance, m instanceof THREE.AmbientLight ? m.visible && (L.gammaInput ? (k += n.r * n.r, l += n.g * n.g, p += n.b * n.b) : (k += n.r, l += n.g, p += n.b)) : m instanceof THREE.DirectionalLight ? (fa += 1, m.visible && (xa.getPositionFromMatrix(m.matrixWorld), wa.getPositionFromMatrix(m.target.matrixWorld), xa.sub(wa), xa.normalize(), 0 === xa.x && 0 === xa.y && 0 === xa.z || (m = 3 * ta, x[m] = xa.x, x[m + 1] = xa.y, x[m + 2] = xa.z, L.gammaInput ? G(t, m, n, q * q) : D(t, m, n, q), ta += 1))) : m instanceof THREE.PointLight ?
                        (Oa += 1, m.visible && (ca = 3 * U, L.gammaInput ? G(w, ca, n, q * q) : D(w, ca, n, q), wa.getPositionFromMatrix(m.matrixWorld), z[ca] = wa.x, z[ca + 1] = wa.y, z[ca + 2] = wa.z, A[U] = s, U += 1)) : m instanceof THREE.SpotLight ? (ia += 1, m.visible && (ca = 3 * $, L.gammaInput ? G(B, ca, n, q * q) : D(B, ca, n, q), wa.getPositionFromMatrix(m.matrixWorld), E[ca] = wa.x, E[ca + 1] = wa.y, E[ca + 2] = wa.z, I[$] = s, xa.copy(wa), wa.getPositionFromMatrix(m.target.matrixWorld), xa.sub(wa), xa.normalize(), J[ca] = xa.x, J[ca + 1] = xa.y, J[ca + 2] = xa.z, K[$] = Math.cos(m.angle), M[$] = m.exponent, $ += 1)) :
                            m instanceof THREE.HemisphereLight && ($a += 1, m.visible && (xa.getPositionFromMatrix(m.matrixWorld), xa.normalize(), 0 === xa.x && 0 === xa.y && 0 === xa.z || (s = 3 * ha, S[s] = xa.x, S[s + 1] = xa.y, S[s + 2] = xa.z, n = m.color, m = m.groundColor, L.gammaInput ? (q *= q, G(V, s, n, q), G(N, s, m, q)) : (D(V, s, n, q), D(N, s, m, q)), ha += 1))));
                    c = 3 * ta;
                    for (f = Math.max(t.length, 3 * fa); c < f; c++) t[c] = 0;
                    c = 3 * U;
                    for (f = Math.max(w.length, 3 * Oa); c < f; c++) w[c] = 0;
                    c = 3 * $;
                    for (f = Math.max(B.length, 3 * ia); c < f; c++) B[c] = 0;
                    c = 3 * ha;
                    for (f = Math.max(V.length, 3 * $a); c < f; c++) V[c] = 0;
                    c = 3 * ha;
                    for (f =
                             Math.max(N.length, 3 * $a); c < f; c++) N[c] = 0;
                    r.directional.length = ta;
                    r.point.length = U;
                    r.spot.length = $;
                    r.hemi.length = ha;
                    r.ambient[0] = k;
                    r.ambient[1] = l;
                    r.ambient[2] = p;
                    yb = !1
                }
                c = pb;
                i.ambientLightColor.value = c.ambient;
                i.directionalLightColor.value = c.directional.colors;
                i.directionalLightDirection.value = c.directional.positions;
                i.pointLightColor.value = c.point.colors;
                i.pointLightPosition.value = c.point.positions;
                i.pointLightDistance.value = c.point.distances;
                i.spotLightColor.value = c.spot.colors;
                i.spotLightPosition.value =
                    c.spot.positions;
                i.spotLightDistance.value = c.spot.distances;
                i.spotLightDirection.value = c.spot.directions;
                i.spotLightAngleCos.value = c.spot.anglesCos;
                i.spotLightExponent.value = c.spot.exponents;
                i.hemisphereLightSkyColor.value = c.hemi.skyColors;
                i.hemisphereLightGroundColor.value = c.hemi.groundColors;
                i.hemisphereLightDirection.value = c.hemi.positions
            }
            if (d instanceof THREE.MeshBasicMaterial || d instanceof THREE.MeshLambertMaterial || d instanceof THREE.MeshPhongMaterial) {
                i.opacity.value = d.opacity;
                L.gammaInput ?
                    i.diffuse.value.copyGammaToLinear(d.color) : i.diffuse.value = d.color;
                i.map.value = d.map;
                i.lightMap.value = d.lightMap;
                i.specularMap.value = d.specularMap;
                d.bumpMap && (i.bumpMap.value = d.bumpMap, i.bumpScale.value = d.bumpScale);
                d.normalMap && (i.normalMap.value = d.normalMap, i.normalScale.value.copy(d.normalScale));
                var Y;
                d.map ? Y = d.map : d.specularMap ? Y = d.specularMap : d.normalMap ? Y = d.normalMap : d.bumpMap && (Y = d.bumpMap);
                void 0 !== Y && (c = Y.offset, Y = Y.repeat, i.offsetRepeat.value.set(c.x, c.y, Y.x, Y.y));
                i.envMap.value = d.envMap;
                i.flipEnvMap.value = d.envMap instanceof THREE.WebGLRenderTargetCube ? 1 : -1;
                i.reflectivity.value = d.reflectivity;
                i.refractionRatio.value = d.refractionRatio;
                i.combine.value = d.combine;
                i.useRefract.value = d.envMap && d.envMap.mapping instanceof THREE.CubeRefractionMapping
            }
            d instanceof THREE.LineBasicMaterial ? (i.diffuse.value = d.color, i.opacity.value = d.opacity) : d instanceof THREE.LineDashedMaterial ? (i.diffuse.value = d.color, i.opacity.value = d.opacity, i.dashSize.value = d.dashSize, i.totalSize.value = d.dashSize + d.gapSize,
                i.scale.value = d.scale) : d instanceof THREE.ParticleBasicMaterial ? (i.psColor.value = d.color, i.opacity.value = d.opacity, i.size.value = d.size, i.scale.value = H.height / 2, i.map.value = d.map) : d instanceof THREE.MeshPhongMaterial ? (i.shininess.value = d.shininess, L.gammaInput ? (i.ambient.value.copyGammaToLinear(d.ambient), i.emissive.value.copyGammaToLinear(d.emissive), i.specular.value.copyGammaToLinear(d.specular)) : (i.ambient.value = d.ambient, i.emissive.value = d.emissive, i.specular.value = d.specular), d.wrapAround && i.wrapRGB.value.copy(d.wrapRGB)) :
                d instanceof THREE.MeshLambertMaterial ? (L.gammaInput ? (i.ambient.value.copyGammaToLinear(d.ambient), i.emissive.value.copyGammaToLinear(d.emissive)) : (i.ambient.value = d.ambient, i.emissive.value = d.emissive), d.wrapAround && i.wrapRGB.value.copy(d.wrapRGB)) : d instanceof THREE.MeshDepthMaterial ? (i.mNear.value = a.near, i.mFar.value = a.far, i.opacity.value = d.opacity) : d instanceof THREE.MeshNormalMaterial && (i.opacity.value = d.opacity);
            if (e.receiveShadow && !d._shadowPass && i.shadowMatrix) {
                c = Y = 0;
                for (f = b.length; c < f; c++) if (k =
                        b[c], k.castShadow && (k instanceof THREE.SpotLight || k instanceof THREE.DirectionalLight && !k.shadowCascade)) i.shadowMap.value[Y] = k.shadowMap, i.shadowMapSize.value[Y] = k.shadowMapSize, i.shadowMatrix.value[Y] = k.shadowMatrix, i.shadowDarkness.value[Y] = k.shadowDarkness, i.shadowBias.value[Y] = k.shadowBias, Y++
            }
            b = d.uniformsList;
            i = 0;
            for (Y = b.length; i < Y; i++) if (f = g.uniforms[b[i][1]]) if (c = b[i][0], l = c.type, k = c.value, "i" === l) j.uniform1i(f, k); else if ("f" === l) j.uniform1f(f, k); else if ("v2" === l) j.uniform2f(f, k.x, k.y); else if ("v3" ===
                l) j.uniform3f(f, k.x, k.y, k.z); else if ("v4" === l) j.uniform4f(f, k.x, k.y, k.z, k.w); else if ("c" === l) j.uniform3f(f, k.r, k.g, k.b); else if ("iv1" === l) j.uniform1iv(f, k); else if ("iv" === l) j.uniform3iv(f, k); else if ("fv1" === l) j.uniform1fv(f, k); else if ("fv" === l) j.uniform3fv(f, k); else if ("v2v" === l) {
                void 0 === c._array && (c._array = new Float32Array(2 * k.length));
                l = 0;
                for (p = k.length; l < p; l++) r = 2 * l, c._array[r] = k[l].x, c._array[r + 1] = k[l].y;
                j.uniform2fv(f, c._array)
            } else if ("v3v" === l) {
                void 0 === c._array && (c._array = new Float32Array(3 *
                    k.length));
                l = 0;
                for (p = k.length; l < p; l++) r = 3 * l, c._array[r] = k[l].x, c._array[r + 1] = k[l].y, c._array[r + 2] = k[l].z;
                j.uniform3fv(f, c._array)
            } else if ("v4v" === l) {
                void 0 === c._array && (c._array = new Float32Array(4 * k.length));
                l = 0;
                for (p = k.length; l < p; l++) r = 4 * l, c._array[r] = k[l].x, c._array[r + 1] = k[l].y, c._array[r + 2] = k[l].z, c._array[r + 3] = k[l].w;
                j.uniform4fv(f, c._array)
            } else if ("m4" === l) void 0 === c._array && (c._array = new Float32Array(16)), k.flattenToArray(c._array), j.uniformMatrix4fv(f, !1, c._array); else if ("m4v" === l) {
                void 0 ===
                c._array && (c._array = new Float32Array(16 * k.length));
                l = 0;
                for (p = k.length; l < p; l++) k[l].flattenToArrayOffset(c._array, 16 * l);
                j.uniformMatrix4fv(f, !1, c._array)
            } else if ("t" === l) {
                if (r = k, k = u(), j.uniform1i(f, k), r) if (r.image instanceof Array && 6 === r.image.length) {
                    if (c = r, f = k, 6 === c.image.length) if (c.needsUpdate) {
                        c.image.__webglTextureCube || (c.image.__webglTextureCube = j.createTexture(), L.info.memory.textures++);
                        j.activeTexture(j.TEXTURE0 + f);
                        j.bindTexture(j.TEXTURE_CUBE_MAP, c.image.__webglTextureCube);
                        j.pixelStorei(j.UNPACK_FLIP_Y_WEBGL,
                            c.flipY);
                        f = c instanceof THREE.CompressedTexture;
                        k = [];
                        for (l = 0; 6 > l; l++) L.autoScaleCubemaps && !f ? (p = k, r = l, t = c.image[l], w = pd, t.width <= w && t.height <= w || (z = Math.max(t.width, t.height), x = Math.floor(t.width * w / z), w = Math.floor(t.height * w / z), z = document.createElement("canvas"), z.width = x, z.height = w, z.getContext("2d").drawImage(t, 0, 0, t.width, t.height, 0, 0, x, w), t = z), p[r] = t) : k[l] = c.image[l];
                        l = k[0];
                        p = 0 === (l.width & l.width - 1) && 0 === (l.height & l.height - 1);
                        r = F(c.format);
                        t = F(c.type);
                        Z(j.TEXTURE_CUBE_MAP, c, p);
                        for (l = 0; 6 > l; l++) if (f) {
                            w =
                                k[l].mipmaps;
                            z = 0;
                            for (A = w.length; z < A; z++) x = w[z], j.compressedTexImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X + l, z, r, x.width, x.height, 0, x.data)
                        } else j.texImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X + l, 0, r, r, t, k[l]);
                        c.generateMipmaps && p && j.generateMipmap(j.TEXTURE_CUBE_MAP);
                        c.needsUpdate = !1;
                        if (c.onUpdate) c.onUpdate()
                    } else j.activeTexture(j.TEXTURE0 + f), j.bindTexture(j.TEXTURE_CUBE_MAP, c.image.__webglTextureCube)
                } else r instanceof THREE.WebGLRenderTargetCube ? (c = r, j.activeTexture(j.TEXTURE0 + k), j.bindTexture(j.TEXTURE_CUBE_MAP,
                    c.__webglTexture)) : L.setTexture(r, k)
            } else if ("tv" === l) {
                void 0 === c._array && (c._array = []);
                l = 0;
                for (p = c.value.length; l < p; l++) c._array[l] = u();
                j.uniform1iv(f, c._array);
                l = 0;
                for (p = c.value.length; l < p; l++) r = c.value[l], k = c._array[l], r && L.setTexture(r, k)
            }
            if ((d instanceof THREE.ShaderMaterial || d instanceof THREE.MeshPhongMaterial || d.envMap) && null !== h.cameraPosition) wa.getPositionFromMatrix(a.matrixWorld), j.uniform3f(h.cameraPosition, wa.x, wa.y, wa.z);
            (d instanceof THREE.MeshPhongMaterial || d instanceof THREE.MeshLambertMaterial ||
                d instanceof THREE.ShaderMaterial || d.skinning) && null !== h.viewMatrix && j.uniformMatrix4fv(h.viewMatrix, !1, a.matrixWorldInverse.elements)
        }
        j.uniformMatrix4fv(h.modelViewMatrix, !1, e._modelViewMatrix.elements);
        h.normalMatrix && j.uniformMatrix3fv(h.normalMatrix, !1, e._normalMatrix.elements);
        null !== h.modelMatrix && j.uniformMatrix4fv(h.modelMatrix, !1, e.matrixWorld.elements);
        return g
    }

    function u() {
        var a = ea;
        a >= Ib && console.warn("WebGLRenderer: trying to use " + a + " texture units while this GPU supports only " + Ib);
        ea += 1;
        return a
    }

    function B(a, b) {
        a._modelViewMatrix.multiplyMatrices(b.matrixWorldInverse, a.matrixWorld);
        a._normalMatrix.getInverse(a._modelViewMatrix);
        a._normalMatrix.transpose()
    }

    function G(a, b, c, d) {
        a[b] = c.r * c.r * d;
        a[b + 1] = c.g * c.g * d;
        a[b + 2] = c.b * c.b * d
    }

    function D(a, b, c, d) {
        a[b] = c.r * d;
        a[b + 1] = c.g * d;
        a[b + 2] = c.b * d
    }

    function w(a) {
        a !== Sa && (j.lineWidth(a), Sa = a)
    }

    function I(a, b, c) {
        Ba !== a && (a ? j.enable(j.POLYGON_OFFSET_FILL) : j.disable(j.POLYGON_OFFSET_FILL), Ba = a);
        if (a && (fb !== b || jb !== c)) j.polygonOffset(b, c), fb = b, jb = c
    }

    function J(a) {
        for (var a = a.split("\n"), b = 0, c = a.length; b < c; b++) a[b] = b + 1 + ": " + a[b];
        return a.join("\n")
    }

    function E(a, b) {
        var c;
        "fragment" === a ? c = j.createShader(j.FRAGMENT_SHADER) : "vertex" === a && (c = j.createShader(j.VERTEX_SHADER));
        j.shaderSource(c, b);
        j.compileShader(c);
        return !j.getShaderParameter(c, j.COMPILE_STATUS) ? (console.error(j.getShaderInfoLog(c)), console.error(J(b)), null) : c
    }

    function Z(a, b, c) {
        c ? (j.texParameteri(a, j.TEXTURE_WRAP_S, F(b.wrapS)), j.texParameteri(a, j.TEXTURE_WRAP_T, F(b.wrapT)), j.texParameteri(a,
            j.TEXTURE_MAG_FILTER, F(b.magFilter)), j.texParameteri(a, j.TEXTURE_MIN_FILTER, F(b.minFilter))) : (j.texParameteri(a, j.TEXTURE_WRAP_S, j.CLAMP_TO_EDGE), j.texParameteri(a, j.TEXTURE_WRAP_T, j.CLAMP_TO_EDGE), j.texParameteri(a, j.TEXTURE_MAG_FILTER, S(b.magFilter)), j.texParameteri(a, j.TEXTURE_MIN_FILTER, S(b.minFilter)));
        if (Wa && b.type !== THREE.FloatType && (1 < b.anisotropy || b.__oldAnisotropy)) j.texParameterf(a, Wa.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(b.anisotropy, Wc)), b.__oldAnisotropy = b.anisotropy
    }

    function A(a, b) {
        j.bindRenderbuffer(j.RENDERBUFFER,
            a);
        b.depthBuffer && !b.stencilBuffer ? (j.renderbufferStorage(j.RENDERBUFFER, j.DEPTH_COMPONENT16, b.width, b.height), j.framebufferRenderbuffer(j.FRAMEBUFFER, j.DEPTH_ATTACHMENT, j.RENDERBUFFER, a)) : b.depthBuffer && b.stencilBuffer ? (j.renderbufferStorage(j.RENDERBUFFER, j.DEPTH_STENCIL, b.width, b.height), j.framebufferRenderbuffer(j.FRAMEBUFFER, j.DEPTH_STENCIL_ATTACHMENT, j.RENDERBUFFER, a)) : j.renderbufferStorage(j.RENDERBUFFER, j.RGBA4, b.width, b.height)
    }

    function S(a) {
        return a === THREE.NearestFilter || a === THREE.NearestMipMapNearestFilter ||
        a === THREE.NearestMipMapLinearFilter ? j.NEAREST : j.LINEAR
    }

    function F(a) {
        if (a === THREE.RepeatWrapping) return j.REPEAT;
        if (a === THREE.ClampToEdgeWrapping) return j.CLAMP_TO_EDGE;
        if (a === THREE.MirroredRepeatWrapping) return j.MIRRORED_REPEAT;
        if (a === THREE.NearestFilter) return j.NEAREST;
        if (a === THREE.NearestMipMapNearestFilter) return j.NEAREST_MIPMAP_NEAREST;
        if (a === THREE.NearestMipMapLinearFilter) return j.NEAREST_MIPMAP_LINEAR;
        if (a === THREE.LinearFilter) return j.LINEAR;
        if (a === THREE.LinearMipMapNearestFilter) return j.LINEAR_MIPMAP_NEAREST;
        if (a === THREE.LinearMipMapLinearFilter) return j.LINEAR_MIPMAP_LINEAR;
        if (a === THREE.UnsignedByteType) return j.UNSIGNED_BYTE;
        if (a === THREE.UnsignedShort4444Type) return j.UNSIGNED_SHORT_4_4_4_4;
        if (a === THREE.UnsignedShort5551Type) return j.UNSIGNED_SHORT_5_5_5_1;
        if (a === THREE.UnsignedShort565Type) return j.UNSIGNED_SHORT_5_6_5;
        if (a === THREE.ByteType) return j.BYTE;
        if (a === THREE.ShortType) return j.SHORT;
        if (a === THREE.UnsignedShortType) return j.UNSIGNED_SHORT;
        if (a === THREE.IntType) return j.INT;
        if (a === THREE.UnsignedIntType) return j.UNSIGNED_INT;
        if (a === THREE.FloatType) return j.FLOAT;
        if (a === THREE.AlphaFormat) return j.ALPHA;
        if (a === THREE.RGBFormat) return j.RGB;
        if (a === THREE.RGBAFormat) return j.RGBA;
        if (a === THREE.LuminanceFormat) return j.LUMINANCE;
        if (a === THREE.LuminanceAlphaFormat) return j.LUMINANCE_ALPHA;
        if (a === THREE.AddEquation) return j.FUNC_ADD;
        if (a === THREE.SubtractEquation) return j.FUNC_SUBTRACT;
        if (a === THREE.ReverseSubtractEquation) return j.FUNC_REVERSE_SUBTRACT;
        if (a === THREE.ZeroFactor) return j.ZERO;
        if (a === THREE.OneFactor) return j.ONE;
        if (a ===
            THREE.SrcColorFactor) return j.SRC_COLOR;
        if (a === THREE.OneMinusSrcColorFactor) return j.ONE_MINUS_SRC_COLOR;
        if (a === THREE.SrcAlphaFactor) return j.SRC_ALPHA;
        if (a === THREE.OneMinusSrcAlphaFactor) return j.ONE_MINUS_SRC_ALPHA;
        if (a === THREE.DstAlphaFactor) return j.DST_ALPHA;
        if (a === THREE.OneMinusDstAlphaFactor) return j.ONE_MINUS_DST_ALPHA;
        if (a === THREE.DstColorFactor) return j.DST_COLOR;
        if (a === THREE.OneMinusDstColorFactor) return j.ONE_MINUS_DST_COLOR;
        if (a === THREE.SrcAlphaSaturateFactor) return j.SRC_ALPHA_SATURATE;
        if (void 0 !== Ga) {
            if (a === THREE.RGB_S3TC_DXT1_Format) return Ga.COMPRESSED_RGB_S3TC_DXT1_EXT;
            if (a === THREE.RGBA_S3TC_DXT1_Format) return Ga.COMPRESSED_RGBA_S3TC_DXT1_EXT;
            if (a === THREE.RGBA_S3TC_DXT3_Format) return Ga.COMPRESSED_RGBA_S3TC_DXT3_EXT;
            if (a === THREE.RGBA_S3TC_DXT5_Format) return Ga.COMPRESSED_RGBA_S3TC_DXT5_EXT
        }
        return 0
    }

    console.log("THREE.WebGLRenderer", THREE.REVISION);
    var a = a || {}, H = void 0 !== a.canvas ? a.canvas : document.createElement("canvas"),
        K = void 0 !== a.precision ? a.precision : "highp", N = void 0 !==
        a.alpha ? a.alpha : !0, fa = void 0 !== a.premultipliedAlpha ? a.premultipliedAlpha : !0,
        ma = void 0 !== a.antialias ? a.antialias : !1, eb = void 0 !== a.stencil ? a.stencil : !0,
        M = void 0 !== a.preserveDrawingBuffer ? a.preserveDrawingBuffer : !1,
        U = void 0 !== a.clearColor ? new THREE.Color(a.clearColor) : new THREE.Color(0),
        ja = void 0 !== a.clearAlpha ? a.clearAlpha : 0;
    this.domElement = H;
    this.context = null;
    this.devicePixelRatio = void 0 !== a.devicePixelRatio ? a.devicePixelRatio : void 0 !== window.devicePixelRatio ? window.devicePixelRatio : 1;
    this.autoUpdateScene =
        this.autoUpdateObjects = this.sortObjects = this.autoClearStencil = this.autoClearDepth = this.autoClearColor = this.autoClear = !0;
    this.shadowMapEnabled = this.physicallyBasedShading = this.gammaOutput = this.gammaInput = !1;
    this.shadowMapAutoUpdate = !0;
    this.shadowMapType = THREE.PCFShadowMap;
    this.shadowMapCullFace = THREE.CullFaceFront;
    this.shadowMapCascade = this.shadowMapDebug = !1;
    this.maxMorphTargets = 8;
    this.maxMorphNormals = 4;
    this.autoScaleCubemaps = !0;
    this.renderPluginsPre = [];
    this.renderPluginsPost = [];
    this.info = {
        memory: {
            programs: 0,
            geometries: 0, textures: 0
        }, render: {calls: 0, vertices: 0, faces: 0, points: 0}
    };
    var L = this, ca = [], ta = 0, kb = null, $a = null, lb = -1, db = null, La = null, Oa = 0, ea = 0, ha = -1, V = -1,
        $ = -1, Y = -1, ia = -1, ra = -1, na = -1, Pa = -1, Ba = null, fb = null, jb = null, Sa = null, ob = 0, Gb = 0,
        Mb = 0, wb = 0, Nb = 0, Ob = 0, xb = {}, Hb = new THREE.Frustum, Qa = new THREE.Matrix4, Va = new THREE.Matrix4,
        wa = new THREE.Vector3, xa = new THREE.Vector3, yb = !0, pb = {
            ambient: [0, 0, 0],
            directional: {length: 0, colors: [], positions: []},
            point: {length: 0, colors: [], positions: [], distances: []},
            spot: {
                length: 0, colors: [],
                positions: [], distances: [], directions: [], anglesCos: [], exponents: []
            },
            hemi: {length: 0, skyColors: [], groundColors: [], positions: []}
        }, j, mb, vb, Wa, Ga;
    try {
        if (!(j = H.getContext("experimental-webgl", {
                alpha: N,
                premultipliedAlpha: fa,
                antialias: ma,
                stencil: eb,
                preserveDrawingBuffer: M
            }))) throw"Error creating WebGL context.";
    } catch (zb) {
        console.error(zb)
    }
    mb = j.getExtension("OES_texture_float");
    vb = j.getExtension("OES_standard_derivatives");
    Wa = j.getExtension("EXT_texture_filter_anisotropic") || j.getExtension("MOZ_EXT_texture_filter_anisotropic") ||
        j.getExtension("WEBKIT_EXT_texture_filter_anisotropic");
    Ga = j.getExtension("WEBGL_compressed_texture_s3tc") || j.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || j.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");
    mb || console.log("THREE.WebGLRenderer: Float textures not supported.");
    vb || console.log("THREE.WebGLRenderer: Standard derivatives not supported.");
    Wa || console.log("THREE.WebGLRenderer: Anisotropic texture filtering not supported.");
    Ga || console.log("THREE.WebGLRenderer: S3TC compressed textures not supported.");
    void 0 === j.getShaderPrecisionFormat && (j.getShaderPrecisionFormat = function () {
        return {rangeMin: 1, rangeMax: 1, precision: 1}
    });
    j.clearColor(0, 0, 0, 1);
    j.clearDepth(1);
    j.clearStencil(0);
    j.enable(j.DEPTH_TEST);
    j.depthFunc(j.LEQUAL);
    j.frontFace(j.CCW);
    j.cullFace(j.BACK);
    j.enable(j.CULL_FACE);
    j.enable(j.BLEND);
    j.blendEquation(j.FUNC_ADD);
    j.blendFunc(j.SRC_ALPHA, j.ONE_MINUS_SRC_ALPHA);
    j.clearColor(U.r, U.g, U.b, ja);
    this.context = j;
    var Ib = j.getParameter(j.MAX_TEXTURE_IMAGE_UNITS), a = j.getParameter(j.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
    j.getParameter(j.MAX_TEXTURE_SIZE);
    var pd = j.getParameter(j.MAX_CUBE_MAP_TEXTURE_SIZE),
        Wc = Wa ? j.getParameter(Wa.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0, Bc = 0 < a, Cc = Bc && mb;
    Ga && j.getParameter(j.COMPRESSED_TEXTURE_FORMATS);
    fa = j.getShaderPrecisionFormat(j.VERTEX_SHADER, j.HIGH_FLOAT);
    a = j.getShaderPrecisionFormat(j.VERTEX_SHADER, j.MEDIUM_FLOAT);
    j.getShaderPrecisionFormat(j.VERTEX_SHADER, j.LOW_FLOAT);
    ma = j.getShaderPrecisionFormat(j.FRAGMENT_SHADER, j.HIGH_FLOAT);
    N = j.getShaderPrecisionFormat(j.FRAGMENT_SHADER, j.MEDIUM_FLOAT);
    j.getShaderPrecisionFormat(j.FRAGMENT_SHADER, j.LOW_FLOAT);
    j.getShaderPrecisionFormat(j.VERTEX_SHADER, j.HIGH_INT);
    j.getShaderPrecisionFormat(j.VERTEX_SHADER, j.MEDIUM_INT);
    j.getShaderPrecisionFormat(j.VERTEX_SHADER, j.LOW_INT);
    j.getShaderPrecisionFormat(j.FRAGMENT_SHADER, j.HIGH_INT);
    j.getShaderPrecisionFormat(j.FRAGMENT_SHADER, j.MEDIUM_INT);
    j.getShaderPrecisionFormat(j.FRAGMENT_SHADER, j.LOW_INT);
    fa = 0 < fa.precision && 0 < ma.precision;
    a = 0 < a.precision && 0 < N.precision;
    "highp" === K && !fa && (a ? (K = "mediump", console.warn("WebGLRenderer: highp not supported, using mediump")) :
        (K = "lowp", console.warn("WebGLRenderer: highp and mediump not supported, using lowp")));
    "mediump" === K && !a && (K = "lowp", console.warn("WebGLRenderer: mediump not supported, using lowp"));
    this.getContext = function () {
        return j
    };
    this.supportsVertexTextures = function () {
        return Bc
    };
    this.supportsFloatTextures = function () {
        return mb
    };
    this.supportsStandardDerivatives = function () {
        return vb
    };
    this.supportsCompressedTextureS3TC = function () {
        return Ga
    };
    this.getMaxAnisotropy = function () {
        return Wc
    };
    this.getPrecision = function () {
        return K
    };
    this.setSize = function (a, b) {
        H.width = a * this.devicePixelRatio;
        H.height = b * this.devicePixelRatio;
        H.style.width = a + "px";
        H.style.height = b + "px";
        this.setViewport(0, 0, H.width, H.height)
    };
    this.setViewport = function (a, b, c, d) {
        ob = void 0 !== a ? a : 0;
        Gb = void 0 !== b ? b : 0;
        Mb = void 0 !== c ? c : H.width;
        wb = void 0 !== d ? d : H.height;
        j.viewport(ob, Gb, Mb, wb)
    };
    this.setScissor = function (a, b, c, d) {
        j.scissor(a, b, c, d)
    };
    this.enableScissorTest = function (a) {
        a ? j.enable(j.SCISSOR_TEST) : j.disable(j.SCISSOR_TEST)
    };
    this.setClearColorHex = function (a, b) {
        U.setHex(a);
        ja = b;
        j.clearColor(U.r, U.g, U.b, ja)
    };
    this.setClearColor = function (a, b) {
        U.copy(a);
        ja = b;
        j.clearColor(U.r, U.g, U.b, ja)
    };
    this.getClearColor = function () {
        return U
    };
    this.getClearAlpha = function () {
        return ja
    };
    this.clear = function (a, b, c) {
        var d = 0;
        if (void 0 === a || a) d |= j.COLOR_BUFFER_BIT;
        if (void 0 === b || b) d |= j.DEPTH_BUFFER_BIT;
        if (void 0 === c || c) d |= j.STENCIL_BUFFER_BIT;
        j.clear(d)
    };
    this.clearTarget = function (a, b, c, d) {
        this.setRenderTarget(a);
        this.clear(b, c, d)
    };
    this.addPostPlugin = function (a) {
        a.init(this);
        this.renderPluginsPost.push(a)
    };
    this.addPrePlugin = function (a) {
        a.init(this);
        this.renderPluginsPre.push(a)
    };
    this.updateShadowMap = function (a, b) {
        kb = null;
        lb = db = Pa = na = $ = -1;
        yb = !0;
        V = ha = -1;
        this.shadowMapPlugin.update(a, b)
    };
    var Bd = function (a) {
        a = a.target;
        a.removeEventListener("dispose", Bd);
        a.__webglInit = void 0;
        void 0 !== a.__webglVertexBuffer && j.deleteBuffer(a.__webglVertexBuffer);
        void 0 !== a.__webglNormalBuffer && j.deleteBuffer(a.__webglNormalBuffer);
        void 0 !== a.__webglTangentBuffer && j.deleteBuffer(a.__webglTangentBuffer);
        void 0 !== a.__webglColorBuffer &&
        j.deleteBuffer(a.__webglColorBuffer);
        void 0 !== a.__webglUVBuffer && j.deleteBuffer(a.__webglUVBuffer);
        void 0 !== a.__webglUV2Buffer && j.deleteBuffer(a.__webglUV2Buffer);
        void 0 !== a.__webglSkinIndicesBuffer && j.deleteBuffer(a.__webglSkinIndicesBuffer);
        void 0 !== a.__webglSkinWeightsBuffer && j.deleteBuffer(a.__webglSkinWeightsBuffer);
        void 0 !== a.__webglFaceBuffer && j.deleteBuffer(a.__webglFaceBuffer);
        void 0 !== a.__webglLineBuffer && j.deleteBuffer(a.__webglLineBuffer);
        void 0 !== a.__webglLineDistanceBuffer && j.deleteBuffer(a.__webglLineDistanceBuffer);
        if (void 0 !== a.geometryGroups) for (var c in a.geometryGroups) {
            var d = a.geometryGroups[c];
            if (void 0 !== d.numMorphTargets) for (var e = 0, f = d.numMorphTargets; e < f; e++) j.deleteBuffer(d.__webglMorphTargetsBuffers[e]);
            if (void 0 !== d.numMorphNormals) {
                e = 0;
                for (f = d.numMorphNormals; e < f; e++) j.deleteBuffer(d.__webglMorphNormalsBuffers[e])
            }
            b(d)
        }
        b(a);
        L.info.memory.geometries--
    }, Zc = function (a) {
        a = a.target;
        a.removeEventListener("dispose", Zc);
        a.image && a.image.__webglTextureCube ? j.deleteTexture(a.image.__webglTextureCube) : a.__webglInit &&
            (a.__webglInit = !1, j.deleteTexture(a.__webglTexture));
        L.info.memory.textures--
    }, Jc = function (a) {
        a = a.target;
        a.removeEventListener("dispose", Jc);
        if (a && a.__webglTexture) if (j.deleteTexture(a.__webglTexture), a instanceof THREE.WebGLRenderTargetCube) for (var b = 0; 6 > b; b++) j.deleteFramebuffer(a.__webglFramebuffer[b]), j.deleteRenderbuffer(a.__webglRenderbuffer[b]); else j.deleteFramebuffer(a.__webglFramebuffer), j.deleteRenderbuffer(a.__webglRenderbuffer);
        L.info.memory.textures--
    }, Xc = function (a) {
        a = a.target;
        a.removeEventListener("dispose",
            Xc);
        Yc(a)
    }, Yc = function (a) {
        var b = a.program;
        if (void 0 !== b) {
            a.program = void 0;
            var c, d, e = !1, a = 0;
            for (c = ca.length; a < c; a++) if (d = ca[a], d.program === b) {
                d.usedTimes--;
                0 === d.usedTimes && (e = !0);
                break
            }
            if (!0 === e) {
                e = [];
                a = 0;
                for (c = ca.length; a < c; a++) d = ca[a], d.program !== b && e.push(d);
                ca = e;
                j.deleteProgram(b);
                L.info.memory.programs--
            }
        }
    };
    this.renderBufferImmediate = function (a, b, c) {
        a.hasPositions && !a.__webglVertexBuffer && (a.__webglVertexBuffer = j.createBuffer());
        a.hasNormals && !a.__webglNormalBuffer && (a.__webglNormalBuffer = j.createBuffer());
        a.hasUvs && !a.__webglUvBuffer && (a.__webglUvBuffer = j.createBuffer());
        a.hasColors && !a.__webglColorBuffer && (a.__webglColorBuffer = j.createBuffer());
        a.hasPositions && (j.bindBuffer(j.ARRAY_BUFFER, a.__webglVertexBuffer), j.bufferData(j.ARRAY_BUFFER, a.positionArray, j.DYNAMIC_DRAW), j.enableVertexAttribArray(b.attributes.position), j.vertexAttribPointer(b.attributes.position, 3, j.FLOAT, !1, 0, 0));
        if (a.hasNormals) {
            j.bindBuffer(j.ARRAY_BUFFER, a.__webglNormalBuffer);
            if (c.shading === THREE.FlatShading) {
                var d, e, f, g, h, i,
                    k, l, m, p, n, q = 3 * a.count;
                for (n = 0; n < q; n += 9) p = a.normalArray, d = p[n], e = p[n + 1], f = p[n + 2], g = p[n + 3], i = p[n + 4], l = p[n + 5], h = p[n + 6], k = p[n + 7], m = p[n + 8], d = (d + g + h) / 3, e = (e + i + k) / 3, f = (f + l + m) / 3, p[n] = d, p[n + 1] = e, p[n + 2] = f, p[n + 3] = d, p[n + 4] = e, p[n + 5] = f, p[n + 6] = d, p[n + 7] = e, p[n + 8] = f
            }
            j.bufferData(j.ARRAY_BUFFER, a.normalArray, j.DYNAMIC_DRAW);
            j.enableVertexAttribArray(b.attributes.normal);
            j.vertexAttribPointer(b.attributes.normal, 3, j.FLOAT, !1, 0, 0)
        }
        a.hasUvs && c.map && (j.bindBuffer(j.ARRAY_BUFFER, a.__webglUvBuffer), j.bufferData(j.ARRAY_BUFFER,
            a.uvArray, j.DYNAMIC_DRAW), j.enableVertexAttribArray(b.attributes.uv), j.vertexAttribPointer(b.attributes.uv, 2, j.FLOAT, !1, 0, 0));
        a.hasColors && c.vertexColors !== THREE.NoColors && (j.bindBuffer(j.ARRAY_BUFFER, a.__webglColorBuffer), j.bufferData(j.ARRAY_BUFFER, a.colorArray, j.DYNAMIC_DRAW), j.enableVertexAttribArray(b.attributes.color), j.vertexAttribPointer(b.attributes.color, 3, j.FLOAT, !1, 0, 0));
        j.drawArrays(j.TRIANGLES, 0, a.count);
        a.count = 0
    };
    this.renderBufferDirect = function (a, b, c, d, e, f) {
        if (!1 !== d.visible) {
            var g,
                k, l;
            g = x(a, b, c, d, f);
            a = g.attributes;
            b = e.attributes;
            c = !1;
            g = 16777215 * e.id + 2 * g.id + (d.wireframe ? 1 : 0);
            g !== db && (db = g, c = !0);
            c && i();
            if (f instanceof THREE.Mesh) if (d = b.index) {
                e = e.offsets;
                1 < e.length && (c = !0);
                for (var m = 0, p = e.length; m < p; m++) {
                    var n = e[m].index;
                    if (c) {
                        for (k in b) "index" !== k && (g = a[k], f = b[k], l = f.itemSize, 0 <= g && (j.bindBuffer(j.ARRAY_BUFFER, f.buffer), h(g), j.vertexAttribPointer(g, l, j.FLOAT, !1, 0, 4 * n * l)));
                        j.bindBuffer(j.ELEMENT_ARRAY_BUFFER, d.buffer)
                    }
                    j.drawElements(j.TRIANGLES, e[m].count, j.UNSIGNED_SHORT, 2 *
                        e[m].start);
                    L.info.render.calls++;
                    L.info.render.vertices += e[m].count;
                    L.info.render.faces += e[m].count / 3
                }
            } else {
                if (c) for (k in b) "index" !== k && (g = a[k], f = b[k], l = f.itemSize, 0 <= g && (j.bindBuffer(j.ARRAY_BUFFER, f.buffer), h(g), j.vertexAttribPointer(g, l, j.FLOAT, !1, 0, 0)));
                k = e.attributes.position;
                j.drawArrays(j.TRIANGLES, 0, k.numItems / 3);
                L.info.render.calls++;
                L.info.render.vertices += k.numItems / 3;
                L.info.render.faces += k.numItems / 3 / 3
            } else if (f instanceof THREE.ParticleSystem) {
                if (c) {
                    for (k in b) g = a[k], f = b[k], l = f.itemSize,
                    0 <= g && (j.bindBuffer(j.ARRAY_BUFFER, f.buffer), h(g), j.vertexAttribPointer(g, l, j.FLOAT, !1, 0, 0));
                    k = b.position;
                    j.drawArrays(j.POINTS, 0, k.numItems / 3);
                    L.info.render.calls++;
                    L.info.render.points += k.numItems / 3
                }
            } else if (f instanceof THREE.Line && c) {
                for (k in b) g = a[k], f = b[k], l = f.itemSize, 0 <= g && (j.bindBuffer(j.ARRAY_BUFFER, f.buffer), h(g), j.vertexAttribPointer(g, l, j.FLOAT, !1, 0, 0));
                w(d.linewidth);
                k = b.position;
                j.drawArrays(j.LINE_STRIP, 0, k.numItems / 3);
                L.info.render.calls++;
                L.info.render.points += k.numItems
            }
        }
    };
    this.renderBuffer =
        function (a, b, c, d, e, f) {
            if (!1 !== d.visible) {
                var g, k, c = x(a, b, c, d, f), a = c.attributes, b = !1,
                    c = 16777215 * e.id + 2 * c.id + (d.wireframe ? 1 : 0);
                c !== db && (db = c, b = !0);
                b && i();
                if (!d.morphTargets && 0 <= a.position) b && (j.bindBuffer(j.ARRAY_BUFFER, e.__webglVertexBuffer), h(a.position), j.vertexAttribPointer(a.position, 3, j.FLOAT, !1, 0, 0)); else if (f.morphTargetBase) {
                    c = d.program.attributes;
                    -1 !== f.morphTargetBase && 0 <= c.position ? (j.bindBuffer(j.ARRAY_BUFFER, e.__webglMorphTargetsBuffers[f.morphTargetBase]), h(c.position), j.vertexAttribPointer(c.position,
                        3, j.FLOAT, !1, 0, 0)) : 0 <= c.position && (j.bindBuffer(j.ARRAY_BUFFER, e.__webglVertexBuffer), h(c.position), j.vertexAttribPointer(c.position, 3, j.FLOAT, !1, 0, 0));
                    if (f.morphTargetForcedOrder.length) {
                        var m = 0;
                        k = f.morphTargetForcedOrder;
                        for (g = f.morphTargetInfluences; m < d.numSupportedMorphTargets && m < k.length;) 0 <= c["morphTarget" + m] && (j.bindBuffer(j.ARRAY_BUFFER, e.__webglMorphTargetsBuffers[k[m]]), h(c["morphTarget" + m]), j.vertexAttribPointer(c["morphTarget" + m], 3, j.FLOAT, !1, 0, 0)), 0 <= c["morphNormal" + m] && d.morphNormals &&
                        (j.bindBuffer(j.ARRAY_BUFFER, e.__webglMorphNormalsBuffers[k[m]]), h(c["morphNormal" + m]), j.vertexAttribPointer(c["morphNormal" + m], 3, j.FLOAT, !1, 0, 0)), f.__webglMorphTargetInfluences[m] = g[k[m]], m++
                    } else {
                        k = [];
                        g = f.morphTargetInfluences;
                        var p, n = g.length;
                        for (p = 0; p < n; p++) m = g[p], 0 < m && k.push([m, p]);
                        k.length > d.numSupportedMorphTargets ? (k.sort(l), k.length = d.numSupportedMorphTargets) : k.length > d.numSupportedMorphNormals ? k.sort(l) : 0 === k.length && k.push([0, 0]);
                        for (m = 0; m < d.numSupportedMorphTargets;) k[m] ? (p = k[m][1], 0 <=
                        c["morphTarget" + m] && (j.bindBuffer(j.ARRAY_BUFFER, e.__webglMorphTargetsBuffers[p]), h(c["morphTarget" + m]), j.vertexAttribPointer(c["morphTarget" + m], 3, j.FLOAT, !1, 0, 0)), 0 <= c["morphNormal" + m] && d.morphNormals && (j.bindBuffer(j.ARRAY_BUFFER, e.__webglMorphNormalsBuffers[p]), h(c["morphNormal" + m]), j.vertexAttribPointer(c["morphNormal" + m], 3, j.FLOAT, !1, 0, 0)), f.__webglMorphTargetInfluences[m] = g[p]) : f.__webglMorphTargetInfluences[m] = 0, m++
                    }
                    null !== d.program.uniforms.morphTargetInfluences && j.uniform1fv(d.program.uniforms.morphTargetInfluences,
                        f.__webglMorphTargetInfluences)
                }
                if (b) {
                    if (e.__webglCustomAttributesList) {
                        g = 0;
                        for (k = e.__webglCustomAttributesList.length; g < k; g++) c = e.__webglCustomAttributesList[g], 0 <= a[c.buffer.belongsToAttribute] && (j.bindBuffer(j.ARRAY_BUFFER, c.buffer), h(a[c.buffer.belongsToAttribute]), j.vertexAttribPointer(a[c.buffer.belongsToAttribute], c.size, j.FLOAT, !1, 0, 0))
                    }
                    0 <= a.color && (j.bindBuffer(j.ARRAY_BUFFER, e.__webglColorBuffer), h(a.color), j.vertexAttribPointer(a.color, 3, j.FLOAT, !1, 0, 0));
                    0 <= a.normal && (j.bindBuffer(j.ARRAY_BUFFER,
                        e.__webglNormalBuffer), h(a.normal), j.vertexAttribPointer(a.normal, 3, j.FLOAT, !1, 0, 0));
                    0 <= a.tangent && (j.bindBuffer(j.ARRAY_BUFFER, e.__webglTangentBuffer), h(a.tangent), j.vertexAttribPointer(a.tangent, 4, j.FLOAT, !1, 0, 0));
                    0 <= a.uv && (j.bindBuffer(j.ARRAY_BUFFER, e.__webglUVBuffer), h(a.uv), j.vertexAttribPointer(a.uv, 2, j.FLOAT, !1, 0, 0));
                    0 <= a.uv2 && (j.bindBuffer(j.ARRAY_BUFFER, e.__webglUV2Buffer), h(a.uv2), j.vertexAttribPointer(a.uv2, 2, j.FLOAT, !1, 0, 0));
                    d.skinning && (0 <= a.skinIndex && 0 <= a.skinWeight) && (j.bindBuffer(j.ARRAY_BUFFER,
                        e.__webglSkinIndicesBuffer), h(a.skinIndex), j.vertexAttribPointer(a.skinIndex, 4, j.FLOAT, !1, 0, 0), j.bindBuffer(j.ARRAY_BUFFER, e.__webglSkinWeightsBuffer), h(a.skinWeight), j.vertexAttribPointer(a.skinWeight, 4, j.FLOAT, !1, 0, 0));
                    0 <= a.lineDistance && (j.bindBuffer(j.ARRAY_BUFFER, e.__webglLineDistanceBuffer), h(a.lineDistance), j.vertexAttribPointer(a.lineDistance, 1, j.FLOAT, !1, 0, 0))
                }
                f instanceof THREE.Mesh ? (d.wireframe ? (w(d.wireframeLinewidth), b && j.bindBuffer(j.ELEMENT_ARRAY_BUFFER, e.__webglLineBuffer), j.drawElements(j.LINES,
                    e.__webglLineCount, j.UNSIGNED_SHORT, 0)) : (b && j.bindBuffer(j.ELEMENT_ARRAY_BUFFER, e.__webglFaceBuffer), j.drawElements(j.TRIANGLES, e.__webglFaceCount, j.UNSIGNED_SHORT, 0)), L.info.render.calls++, L.info.render.vertices += e.__webglFaceCount, L.info.render.faces += e.__webglFaceCount / 3) : f instanceof THREE.Line ? (f = f.type === THREE.LineStrip ? j.LINE_STRIP : j.LINES, w(d.linewidth), j.drawArrays(f, 0, e.__webglLineCount), L.info.render.calls++) : f instanceof THREE.ParticleSystem ? (j.drawArrays(j.POINTS, 0, e.__webglParticleCount),
                    L.info.render.calls++, L.info.render.points += e.__webglParticleCount) : f instanceof THREE.Ribbon && (j.drawArrays(j.TRIANGLE_STRIP, 0, e.__webglVertexCount), L.info.render.calls++)
            }
        };
    this.render = function (a, b, c, d) {
        if (!1 === b instanceof THREE.Camera) console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera."); else {
            var e, f, g, h, i = a.__lights, l = a.fog;
            lb = -1;
            yb = !0;
            this.autoUpdateScene && a.updateMatrixWorld();
            void 0 === b.parent && b.updateMatrixWorld();
            b.matrixWorldInverse.getInverse(b.matrixWorld);
            Qa.multiplyMatrices(b.projectionMatrix, b.matrixWorldInverse);
            Hb.setFromMatrix(Qa);
            this.autoUpdateObjects && this.initWebGLObjects(a);
            m(this.renderPluginsPre, a, b);
            L.info.render.calls = 0;
            L.info.render.vertices = 0;
            L.info.render.faces = 0;
            L.info.render.points = 0;
            this.setRenderTarget(c);
            (this.autoClear || d) && this.clear(this.autoClearColor, this.autoClearDepth, this.autoClearStencil);
            h = a.__webglObjects;
            d = 0;
            for (e = h.length; d < e; d++) if (f = h[d], g = f.object, f.render = !1, g.visible && (!(g instanceof THREE.Mesh || g instanceof
                    THREE.ParticleSystem) || !g.frustumCulled || Hb.intersectsObject(g))) {
                B(g, b);
                var n = f, q = n.buffer, r = void 0, t = r = void 0, t = n.object.material;
                if (t instanceof THREE.MeshFaceMaterial) r = q.materialIndex, r = t.materials[r], r.transparent ? (n.transparent = r, n.opaque = null) : (n.opaque = r, n.transparent = null); else if (r = t) r.transparent ? (n.transparent = r, n.opaque = null) : (n.opaque = r, n.transparent = null);
                f.render = !0;
                !0 === this.sortObjects && (null !== g.renderDepth ? f.z = g.renderDepth : (wa.getPositionFromMatrix(g.matrixWorld), wa.applyProjection(Qa),
                    f.z = wa.z), f.id = g.id)
            }
            this.sortObjects && h.sort(k);
            h = a.__webglObjectsImmediate;
            d = 0;
            for (e = h.length; d < e; d++) f = h[d], g = f.object, g.visible && (B(g, b), g = f.object.material, g.transparent ? (f.transparent = g, f.opaque = null) : (f.opaque = g, f.transparent = null));
            a.overrideMaterial ? (d = a.overrideMaterial, this.setBlending(d.blending, d.blendEquation, d.blendSrc, d.blendDst), this.setDepthTest(d.depthTest), this.setDepthWrite(d.depthWrite), I(d.polygonOffset, d.polygonOffsetFactor, d.polygonOffsetUnits), p(a.__webglObjects, !1, "", b,
                i, l, !0, d), s(a.__webglObjectsImmediate, "", b, i, l, !1, d)) : (d = null, this.setBlending(THREE.NoBlending), p(a.__webglObjects, !0, "opaque", b, i, l, !1, d), s(a.__webglObjectsImmediate, "opaque", b, i, l, !1, d), p(a.__webglObjects, !1, "transparent", b, i, l, !0, d), s(a.__webglObjectsImmediate, "transparent", b, i, l, !0, d));
            m(this.renderPluginsPost, a, b);
            c && (c.generateMipmaps && c.minFilter !== THREE.NearestFilter && c.minFilter !== THREE.LinearFilter) && (c instanceof THREE.WebGLRenderTargetCube ? (j.bindTexture(j.TEXTURE_CUBE_MAP, c.__webglTexture),
                j.generateMipmap(j.TEXTURE_CUBE_MAP), j.bindTexture(j.TEXTURE_CUBE_MAP, null)) : (j.bindTexture(j.TEXTURE_2D, c.__webglTexture), j.generateMipmap(j.TEXTURE_2D), j.bindTexture(j.TEXTURE_2D, null)));
            this.setDepthTest(!0);
            this.setDepthWrite(!0)
        }
    };
    this.renderImmediateObject = function (a, b, c, d, e) {
        var f = x(a, b, c, d, e);
        db = -1;
        L.setMaterialFaces(d);
        e.immediateRenderCallback ? e.immediateRenderCallback(f, j, Hb) : e.render(function (a) {
            L.renderBufferImmediate(a, f, d)
        })
    };
    this.initWebGLObjects = function (a) {
        a.__webglObjects || (a.__webglObjects =
            [], a.__webglObjectsImmediate = [], a.__webglSprites = [], a.__webglFlares = []);
        for (; a.__objectsAdded.length;) {
            var b = a.__objectsAdded[0], h = a, i = void 0, k = void 0, m = void 0, p = void 0;
            if (!b.__webglInit && (b.__webglInit = !0, b._modelViewMatrix = new THREE.Matrix4, b._normalMatrix = new THREE.Matrix3, void 0 !== b.geometry && void 0 === b.geometry.__webglInit && (b.geometry.__webglInit = !0, b.geometry.addEventListener("dispose", Bd)), k = b.geometry, void 0 !== k)) if (k instanceof THREE.BufferGeometry) {
                var s = k, u = void 0, x = void 0, w = void 0;
                for (u in s.attributes) w =
                    "index" === u ? j.ELEMENT_ARRAY_BUFFER : j.ARRAY_BUFFER, x = s.attributes[u], x.buffer = j.createBuffer(), j.bindBuffer(w, x.buffer), j.bufferData(w, x.array, j.STATIC_DRAW)
            } else if (b instanceof THREE.Mesh) {
                m = b.material;
                if (void 0 === k.geometryGroups) {
                    var A = k, B = void 0, D = void 0, F = void 0, E = void 0, G = void 0, H = void 0, I = {},
                        J = A.morphTargets.length, K = A.morphNormals.length, M = m instanceof THREE.MeshFaceMaterial;
                    A.geometryGroups = {};
                    B = 0;
                    for (D = A.faces.length; B < D; B++) F = A.faces[B], E = M ? F.materialIndex : 0, void 0 === I[E] && (I[E] = {
                        hash: E,
                        counter: 0
                    }), H = I[E].hash + "_" + I[E].counter, void 0 === A.geometryGroups[H] && (A.geometryGroups[H] = {
                        faces3: [],
                        faces4: [],
                        materialIndex: E,
                        vertices: 0,
                        numMorphTargets: J,
                        numMorphNormals: K
                    }), G = F instanceof THREE.Face3 ? 3 : 4, 65535 < A.geometryGroups[H].vertices + G && (I[E].counter += 1, H = I[E].hash + "_" + I[E].counter, void 0 === A.geometryGroups[H] && (A.geometryGroups[H] = {
                        faces3: [],
                        faces4: [],
                        materialIndex: E,
                        vertices: 0,
                        numMorphTargets: J,
                        numMorphNormals: K
                    })), F instanceof THREE.Face3 ? A.geometryGroups[H].faces3.push(B) : A.geometryGroups[H].faces4.push(B),
                        A.geometryGroups[H].vertices += G;
                    A.geometryGroupsList = [];
                    var V = void 0;
                    for (V in A.geometryGroups) A.geometryGroups[V].id = Oa++, A.geometryGroupsList.push(A.geometryGroups[V])
                }
                for (i in k.geometryGroups) if (p = k.geometryGroups[i], !p.__webglVertexBuffer) {
                    var N = p;
                    N.__webglVertexBuffer = j.createBuffer();
                    N.__webglNormalBuffer = j.createBuffer();
                    N.__webglTangentBuffer = j.createBuffer();
                    N.__webglColorBuffer = j.createBuffer();
                    N.__webglUVBuffer = j.createBuffer();
                    N.__webglUV2Buffer = j.createBuffer();
                    N.__webglSkinIndicesBuffer =
                        j.createBuffer();
                    N.__webglSkinWeightsBuffer = j.createBuffer();
                    N.__webglFaceBuffer = j.createBuffer();
                    N.__webglLineBuffer = j.createBuffer();
                    var S = void 0, ta = void 0;
                    if (N.numMorphTargets) {
                        N.__webglMorphTargetsBuffers = [];
                        S = 0;
                        for (ta = N.numMorphTargets; S < ta; S++) N.__webglMorphTargetsBuffers.push(j.createBuffer())
                    }
                    if (N.numMorphNormals) {
                        N.__webglMorphNormalsBuffers = [];
                        S = 0;
                        for (ta = N.numMorphNormals; S < ta; S++) N.__webglMorphNormalsBuffers.push(j.createBuffer())
                    }
                    L.info.memory.geometries++;
                    d(p, b);
                    k.verticesNeedUpdate =
                        !0;
                    k.morphTargetsNeedUpdate = !0;
                    k.elementsNeedUpdate = !0;
                    k.uvsNeedUpdate = !0;
                    k.normalsNeedUpdate = !0;
                    k.tangentsNeedUpdate = !0;
                    k.colorsNeedUpdate = !0
                }
            } else if (b instanceof THREE.Ribbon) {
                if (!k.__webglVertexBuffer) {
                    var ea = k;
                    ea.__webglVertexBuffer = j.createBuffer();
                    ea.__webglColorBuffer = j.createBuffer();
                    ea.__webglNormalBuffer = j.createBuffer();
                    L.info.memory.geometries++;
                    var U = k, $ = b, ha = U.vertices.length;
                    U.__vertexArray = new Float32Array(3 * ha);
                    U.__colorArray = new Float32Array(3 * ha);
                    U.__normalArray = new Float32Array(3 *
                        ha);
                    U.__webglVertexCount = ha;
                    c(U, $);
                    k.verticesNeedUpdate = !0;
                    k.colorsNeedUpdate = !0;
                    k.normalsNeedUpdate = !0
                }
            } else if (b instanceof THREE.Line) {
                if (!k.__webglVertexBuffer) {
                    var Z = k;
                    Z.__webglVertexBuffer = j.createBuffer();
                    Z.__webglColorBuffer = j.createBuffer();
                    Z.__webglLineDistanceBuffer = j.createBuffer();
                    L.info.memory.geometries++;
                    var Y = k, ca = b, fa = Y.vertices.length;
                    Y.__vertexArray = new Float32Array(3 * fa);
                    Y.__colorArray = new Float32Array(3 * fa);
                    Y.__lineDistanceArray = new Float32Array(1 * fa);
                    Y.__webglLineCount = fa;
                    c(Y,
                        ca);
                    k.verticesNeedUpdate = !0;
                    k.colorsNeedUpdate = !0;
                    k.lineDistancesNeedUpdate = !0
                }
            } else if (b instanceof THREE.ParticleSystem && !k.__webglVertexBuffer) {
                var $a = k;
                $a.__webglVertexBuffer = j.createBuffer();
                $a.__webglColorBuffer = j.createBuffer();
                L.info.memory.geometries++;
                var ia = k, kb = b, ra = ia.vertices.length;
                ia.__vertexArray = new Float32Array(3 * ra);
                ia.__colorArray = new Float32Array(3 * ra);
                ia.__sortArray = [];
                ia.__webglParticleCount = ra;
                c(ia, kb);
                k.verticesNeedUpdate = !0;
                k.colorsNeedUpdate = !0
            }
            if (!b.__webglActive) {
                if (b instanceof
                    THREE.Mesh) if (k = b.geometry, k instanceof THREE.BufferGeometry) r(h.__webglObjects, k, b); else {
                    if (k instanceof THREE.Geometry) for (i in k.geometryGroups) p = k.geometryGroups[i], r(h.__webglObjects, p, b)
                } else b instanceof THREE.Ribbon || b instanceof THREE.Line || b instanceof THREE.ParticleSystem ? (k = b.geometry, r(h.__webglObjects, k, b)) : b instanceof THREE.ImmediateRenderObject || b.immediateRenderCallback ? h.__webglObjectsImmediate.push({
                    object: b,
                    opaque: null,
                    transparent: null
                }) : b instanceof THREE.Sprite ? h.__webglSprites.push(b) :
                    b instanceof THREE.LensFlare && h.__webglFlares.push(b);
                b.__webglActive = !0
            }
            a.__objectsAdded.splice(0, 1)
        }
        for (; a.__objectsRemoved.length;) {
            var ja = a.__objectsRemoved[0], La = a;
            ja instanceof THREE.Mesh || ja instanceof THREE.ParticleSystem || ja instanceof THREE.Ribbon || ja instanceof THREE.Line ? z(La.__webglObjects, ja) : ja instanceof THREE.Sprite ? t(La.__webglSprites, ja) : ja instanceof THREE.LensFlare ? t(La.__webglFlares, ja) : (ja instanceof THREE.ImmediateRenderObject || ja.immediateRenderCallback) && z(La.__webglObjectsImmediate,
                ja);
            ja.__webglActive = !1;
            a.__objectsRemoved.splice(0, 1)
        }
        for (var lb = 0, db = a.__webglObjects.length; lb < db; lb++) {
            var ma = a.__webglObjects[lb].object, la = ma.geometry, Ba = void 0, xa = void 0, na = void 0;
            if (la instanceof THREE.BufferGeometry) {
                var Sa = j.DYNAMIC_DRAW, Wa = !la.dynamic, eb = la.attributes, fb = void 0, Pa = void 0;
                for (fb in eb) Pa = eb[fb], Pa.needsUpdate && ("index" === fb ? (j.bindBuffer(j.ELEMENT_ARRAY_BUFFER, Pa.buffer), j.bufferData(j.ELEMENT_ARRAY_BUFFER, Pa.array, Sa)) : (j.bindBuffer(j.ARRAY_BUFFER, Pa.buffer), j.bufferData(j.ARRAY_BUFFER,
                    Pa.array, Sa)), Pa.needsUpdate = !1), Wa && !Pa.dynamic && delete Pa.array
            } else if (ma instanceof THREE.Mesh) {
                for (var jb = 0, ob = la.geometryGroupsList.length; jb < ob; jb++) if (Ba = la.geometryGroupsList[jb], na = e(ma, Ba), la.buffersNeedUpdate && d(Ba, ma), xa = na.attributes && n(na), la.verticesNeedUpdate || la.morphTargetsNeedUpdate || la.elementsNeedUpdate || la.uvsNeedUpdate || la.normalsNeedUpdate || la.colorsNeedUpdate || la.tangentsNeedUpdate || xa) {
                    var sa = Ba, pb = ma, Ga = j.DYNAMIC_DRAW, Hb = !la.dynamic, mb = na;
                    if (sa.__inittedArrays) {
                        var Gb =
                                f(mb), yb = mb.vertexColors ? mb.vertexColors : !1, Ib = g(mb),
                            xb = Gb === THREE.SmoothShading, C = void 0, T = void 0, pc = void 0, O = void 0,
                            vb = void 0, qc = void 0, Pb = void 0, Mb = void 0, jc = void 0, wb = void 0, zb = void 0,
                            P = void 0, Q = void 0, R = void 0, qa = void 0, Qb = void 0, Rb = void 0, Sb = void 0,
                            Nb = void 0, Tb = void 0, Ub = void 0, Vb = void 0, Ob = void 0, Wb = void 0, Xb = void 0,
                            Yb = void 0, Kc = void 0, Zb = void 0, $b = void 0, ac = void 0, Lc = void 0, bc = void 0,
                            cc = void 0, dc = void 0, Mc = void 0, ya = void 0, Bc = void 0, rc = void 0, Ec = void 0,
                            Fc = void 0, Xa = void 0, Cc = void 0, Ta = void 0,
                            Ua = void 0, sc = void 0, kc = void 0, Ma = 0, Ra = 0, lc = 0, mc = 0, Cb = 0, hb = 0,
                            Ca = 0, nb = 0, Na = 0, aa = 0, ka = 0, y = 0, za = void 0, Ya = sa.__vertexArray,
                            Nc = sa.__uvArray, Oc = sa.__uv2Array, Db = sa.__normalArray, Ha = sa.__tangentArray,
                            Za = sa.__colorArray, Ia = sa.__skinIndexArray, Ja = sa.__skinWeightArray,
                            rd = sa.__morphTargetsArrays, sd = sa.__morphNormalsArrays,
                            td = sa.__webglCustomAttributesList, v = void 0, ec = sa.__faceArray, ub = sa.__lineArray,
                            qb = pb.geometry, Wc = qb.elementsNeedUpdate, Jc = qb.uvsNeedUpdate,
                            Xc = qb.normalsNeedUpdate, Yc = qb.tangentsNeedUpdate,
                            Zc = qb.colorsNeedUpdate, pd = qb.morphTargetsNeedUpdate, xc = qb.vertices, ua = sa.faces3,
                            va = sa.faces4, ib = qb.faces, ud = qb.faceVertexUvs[0], vd = qb.faceVertexUvs[1],
                            yc = qb.skinIndices, tc = qb.skinWeights, uc = qb.morphTargets, $c = qb.morphNormals;
                        if (qb.verticesNeedUpdate) {
                            C = 0;
                            for (T = ua.length; C < T; C++) O = ib[ua[C]], P = xc[O.a], Q = xc[O.b], R = xc[O.c], Ya[Ra] = P.x, Ya[Ra + 1] = P.y, Ya[Ra + 2] = P.z, Ya[Ra + 3] = Q.x, Ya[Ra + 4] = Q.y, Ya[Ra + 5] = Q.z, Ya[Ra + 6] = R.x, Ya[Ra + 7] = R.y, Ya[Ra + 8] = R.z, Ra += 9;
                            C = 0;
                            for (T = va.length; C < T; C++) O = ib[va[C]], P = xc[O.a], Q = xc[O.b],
                                R = xc[O.c], qa = xc[O.d], Ya[Ra] = P.x, Ya[Ra + 1] = P.y, Ya[Ra + 2] = P.z, Ya[Ra + 3] = Q.x, Ya[Ra + 4] = Q.y, Ya[Ra + 5] = Q.z, Ya[Ra + 6] = R.x, Ya[Ra + 7] = R.y, Ya[Ra + 8] = R.z, Ya[Ra + 9] = qa.x, Ya[Ra + 10] = qa.y, Ya[Ra + 11] = qa.z, Ra += 12;
                            j.bindBuffer(j.ARRAY_BUFFER, sa.__webglVertexBuffer);
                            j.bufferData(j.ARRAY_BUFFER, Ya, Ga)
                        }
                        if (pd) {
                            Xa = 0;
                            for (Cc = uc.length; Xa < Cc; Xa++) {
                                C = ka = 0;
                                for (T = ua.length; C < T; C++) sc = ua[C], O = ib[sc], P = uc[Xa].vertices[O.a], Q = uc[Xa].vertices[O.b], R = uc[Xa].vertices[O.c], Ta = rd[Xa], Ta[ka] = P.x, Ta[ka + 1] = P.y, Ta[ka + 2] = P.z, Ta[ka + 3] = Q.x, Ta[ka + 4] =
                                    Q.y, Ta[ka + 5] = Q.z, Ta[ka + 6] = R.x, Ta[ka + 7] = R.y, Ta[ka + 8] = R.z, mb.morphNormals && (xb ? (kc = $c[Xa].vertexNormals[sc], Tb = kc.a, Ub = kc.b, Vb = kc.c) : Vb = Ub = Tb = $c[Xa].faceNormals[sc], Ua = sd[Xa], Ua[ka] = Tb.x, Ua[ka + 1] = Tb.y, Ua[ka + 2] = Tb.z, Ua[ka + 3] = Ub.x, Ua[ka + 4] = Ub.y, Ua[ka + 5] = Ub.z, Ua[ka + 6] = Vb.x, Ua[ka + 7] = Vb.y, Ua[ka + 8] = Vb.z), ka += 9;
                                C = 0;
                                for (T = va.length; C < T; C++) sc = va[C], O = ib[sc], P = uc[Xa].vertices[O.a], Q = uc[Xa].vertices[O.b], R = uc[Xa].vertices[O.c], qa = uc[Xa].vertices[O.d], Ta = rd[Xa], Ta[ka] = P.x, Ta[ka + 1] = P.y, Ta[ka + 2] = P.z, Ta[ka + 3] = Q.x,
                                    Ta[ka + 4] = Q.y, Ta[ka + 5] = Q.z, Ta[ka + 6] = R.x, Ta[ka + 7] = R.y, Ta[ka + 8] = R.z, Ta[ka + 9] = qa.x, Ta[ka + 10] = qa.y, Ta[ka + 11] = qa.z, mb.morphNormals && (xb ? (kc = $c[Xa].vertexNormals[sc], Tb = kc.a, Ub = kc.b, Vb = kc.c, Ob = kc.d) : Ob = Vb = Ub = Tb = $c[Xa].faceNormals[sc], Ua = sd[Xa], Ua[ka] = Tb.x, Ua[ka + 1] = Tb.y, Ua[ka + 2] = Tb.z, Ua[ka + 3] = Ub.x, Ua[ka + 4] = Ub.y, Ua[ka + 5] = Ub.z, Ua[ka + 6] = Vb.x, Ua[ka + 7] = Vb.y, Ua[ka + 8] = Vb.z, Ua[ka + 9] = Ob.x, Ua[ka + 10] = Ob.y, Ua[ka + 11] = Ob.z), ka += 12;
                                j.bindBuffer(j.ARRAY_BUFFER, sa.__webglMorphTargetsBuffers[Xa]);
                                j.bufferData(j.ARRAY_BUFFER,
                                    rd[Xa], Ga);
                                mb.morphNormals && (j.bindBuffer(j.ARRAY_BUFFER, sa.__webglMorphNormalsBuffers[Xa]), j.bufferData(j.ARRAY_BUFFER, sd[Xa], Ga))
                            }
                        }
                        if (tc.length) {
                            C = 0;
                            for (T = ua.length; C < T; C++) O = ib[ua[C]], Zb = tc[O.a], $b = tc[O.b], ac = tc[O.c], Ja[aa] = Zb.x, Ja[aa + 1] = Zb.y, Ja[aa + 2] = Zb.z, Ja[aa + 3] = Zb.w, Ja[aa + 4] = $b.x, Ja[aa + 5] = $b.y, Ja[aa + 6] = $b.z, Ja[aa + 7] = $b.w, Ja[aa + 8] = ac.x, Ja[aa + 9] = ac.y, Ja[aa + 10] = ac.z, Ja[aa + 11] = ac.w, bc = yc[O.a], cc = yc[O.b], dc = yc[O.c], Ia[aa] = bc.x, Ia[aa + 1] = bc.y, Ia[aa + 2] = bc.z, Ia[aa + 3] = bc.w, Ia[aa + 4] = cc.x, Ia[aa + 5] = cc.y,
                                Ia[aa + 6] = cc.z, Ia[aa + 7] = cc.w, Ia[aa + 8] = dc.x, Ia[aa + 9] = dc.y, Ia[aa + 10] = dc.z, Ia[aa + 11] = dc.w, aa += 12;
                            C = 0;
                            for (T = va.length; C < T; C++) O = ib[va[C]], Zb = tc[O.a], $b = tc[O.b], ac = tc[O.c], Lc = tc[O.d], Ja[aa] = Zb.x, Ja[aa + 1] = Zb.y, Ja[aa + 2] = Zb.z, Ja[aa + 3] = Zb.w, Ja[aa + 4] = $b.x, Ja[aa + 5] = $b.y, Ja[aa + 6] = $b.z, Ja[aa + 7] = $b.w, Ja[aa + 8] = ac.x, Ja[aa + 9] = ac.y, Ja[aa + 10] = ac.z, Ja[aa + 11] = ac.w, Ja[aa + 12] = Lc.x, Ja[aa + 13] = Lc.y, Ja[aa + 14] = Lc.z, Ja[aa + 15] = Lc.w, bc = yc[O.a], cc = yc[O.b], dc = yc[O.c], Mc = yc[O.d], Ia[aa] = bc.x, Ia[aa + 1] = bc.y, Ia[aa + 2] = bc.z, Ia[aa + 3] = bc.w,
                                Ia[aa + 4] = cc.x, Ia[aa + 5] = cc.y, Ia[aa + 6] = cc.z, Ia[aa + 7] = cc.w, Ia[aa + 8] = dc.x, Ia[aa + 9] = dc.y, Ia[aa + 10] = dc.z, Ia[aa + 11] = dc.w, Ia[aa + 12] = Mc.x, Ia[aa + 13] = Mc.y, Ia[aa + 14] = Mc.z, Ia[aa + 15] = Mc.w, aa += 16;
                            0 < aa && (j.bindBuffer(j.ARRAY_BUFFER, sa.__webglSkinIndicesBuffer), j.bufferData(j.ARRAY_BUFFER, Ia, Ga), j.bindBuffer(j.ARRAY_BUFFER, sa.__webglSkinWeightsBuffer), j.bufferData(j.ARRAY_BUFFER, Ja, Ga))
                        }
                        if (Zc && yb) {
                            C = 0;
                            for (T = ua.length; C < T; C++) O = ib[ua[C]], Pb = O.vertexColors, Mb = O.color, 3 === Pb.length && yb === THREE.VertexColors ? (Wb = Pb[0],
                                Xb = Pb[1], Yb = Pb[2]) : Yb = Xb = Wb = Mb, Za[Na] = Wb.r, Za[Na + 1] = Wb.g, Za[Na + 2] = Wb.b, Za[Na + 3] = Xb.r, Za[Na + 4] = Xb.g, Za[Na + 5] = Xb.b, Za[Na + 6] = Yb.r, Za[Na + 7] = Yb.g, Za[Na + 8] = Yb.b, Na += 9;
                            C = 0;
                            for (T = va.length; C < T; C++) O = ib[va[C]], Pb = O.vertexColors, Mb = O.color, 4 === Pb.length && yb === THREE.VertexColors ? (Wb = Pb[0], Xb = Pb[1], Yb = Pb[2], Kc = Pb[3]) : Kc = Yb = Xb = Wb = Mb, Za[Na] = Wb.r, Za[Na + 1] = Wb.g, Za[Na + 2] = Wb.b, Za[Na + 3] = Xb.r, Za[Na + 4] = Xb.g, Za[Na + 5] = Xb.b, Za[Na + 6] = Yb.r, Za[Na + 7] = Yb.g, Za[Na + 8] = Yb.b, Za[Na + 9] = Kc.r, Za[Na + 10] = Kc.g, Za[Na + 11] = Kc.b, Na += 12;
                            0 < Na &&
                            (j.bindBuffer(j.ARRAY_BUFFER, sa.__webglColorBuffer), j.bufferData(j.ARRAY_BUFFER, Za, Ga))
                        }
                        if (Yc && qb.hasTangents) {
                            C = 0;
                            for (T = ua.length; C < T; C++) O = ib[ua[C]], jc = O.vertexTangents, Qb = jc[0], Rb = jc[1], Sb = jc[2], Ha[Ca] = Qb.x, Ha[Ca + 1] = Qb.y, Ha[Ca + 2] = Qb.z, Ha[Ca + 3] = Qb.w, Ha[Ca + 4] = Rb.x, Ha[Ca + 5] = Rb.y, Ha[Ca + 6] = Rb.z, Ha[Ca + 7] = Rb.w, Ha[Ca + 8] = Sb.x, Ha[Ca + 9] = Sb.y, Ha[Ca + 10] = Sb.z, Ha[Ca + 11] = Sb.w, Ca += 12;
                            C = 0;
                            for (T = va.length; C < T; C++) O = ib[va[C]], jc = O.vertexTangents, Qb = jc[0], Rb = jc[1], Sb = jc[2], Nb = jc[3], Ha[Ca] = Qb.x, Ha[Ca + 1] = Qb.y, Ha[Ca +
                            2] = Qb.z, Ha[Ca + 3] = Qb.w, Ha[Ca + 4] = Rb.x, Ha[Ca + 5] = Rb.y, Ha[Ca + 6] = Rb.z, Ha[Ca + 7] = Rb.w, Ha[Ca + 8] = Sb.x, Ha[Ca + 9] = Sb.y, Ha[Ca + 10] = Sb.z, Ha[Ca + 11] = Sb.w, Ha[Ca + 12] = Nb.x, Ha[Ca + 13] = Nb.y, Ha[Ca + 14] = Nb.z, Ha[Ca + 15] = Nb.w, Ca += 16;
                            j.bindBuffer(j.ARRAY_BUFFER, sa.__webglTangentBuffer);
                            j.bufferData(j.ARRAY_BUFFER, Ha, Ga)
                        }
                        if (Xc && Gb) {
                            C = 0;
                            for (T = ua.length; C < T; C++) if (O = ib[ua[C]], vb = O.vertexNormals, qc = O.normal, 3 === vb.length && xb) for (ya = 0; 3 > ya; ya++) rc = vb[ya], Db[hb] = rc.x, Db[hb + 1] = rc.y, Db[hb + 2] = rc.z, hb += 3; else for (ya = 0; 3 > ya; ya++) Db[hb] = qc.x,
                                Db[hb + 1] = qc.y, Db[hb + 2] = qc.z, hb += 3;
                            C = 0;
                            for (T = va.length; C < T; C++) if (O = ib[va[C]], vb = O.vertexNormals, qc = O.normal, 4 === vb.length && xb) for (ya = 0; 4 > ya; ya++) rc = vb[ya], Db[hb] = rc.x, Db[hb + 1] = rc.y, Db[hb + 2] = rc.z, hb += 3; else for (ya = 0; 4 > ya; ya++) Db[hb] = qc.x, Db[hb + 1] = qc.y, Db[hb + 2] = qc.z, hb += 3;
                            j.bindBuffer(j.ARRAY_BUFFER, sa.__webglNormalBuffer);
                            j.bufferData(j.ARRAY_BUFFER, Db, Ga)
                        }
                        if (Jc && ud && Ib) {
                            C = 0;
                            for (T = ua.length; C < T; C++) if (pc = ua[C], wb = ud[pc], void 0 !== wb) for (ya = 0; 3 > ya; ya++) Ec = wb[ya], Nc[lc] = Ec.x, Nc[lc + 1] = Ec.y, lc += 2;
                            C = 0;
                            for (T =
                                     va.length; C < T; C++) if (pc = va[C], wb = ud[pc], void 0 !== wb) for (ya = 0; 4 > ya; ya++) Ec = wb[ya], Nc[lc] = Ec.x, Nc[lc + 1] = Ec.y, lc += 2;
                            0 < lc && (j.bindBuffer(j.ARRAY_BUFFER, sa.__webglUVBuffer), j.bufferData(j.ARRAY_BUFFER, Nc, Ga))
                        }
                        if (Jc && vd && Ib) {
                            C = 0;
                            for (T = ua.length; C < T; C++) if (pc = ua[C], zb = vd[pc], void 0 !== zb) for (ya = 0; 3 > ya; ya++) Fc = zb[ya], Oc[mc] = Fc.x, Oc[mc + 1] = Fc.y, mc += 2;
                            C = 0;
                            for (T = va.length; C < T; C++) if (pc = va[C], zb = vd[pc], void 0 !== zb) for (ya = 0; 4 > ya; ya++) Fc = zb[ya], Oc[mc] = Fc.x, Oc[mc + 1] = Fc.y, mc += 2;
                            0 < mc && (j.bindBuffer(j.ARRAY_BUFFER, sa.__webglUV2Buffer),
                                j.bufferData(j.ARRAY_BUFFER, Oc, Ga))
                        }
                        if (Wc) {
                            C = 0;
                            for (T = ua.length; C < T; C++) ec[Cb] = Ma, ec[Cb + 1] = Ma + 1, ec[Cb + 2] = Ma + 2, Cb += 3, ub[nb] = Ma, ub[nb + 1] = Ma + 1, ub[nb + 2] = Ma, ub[nb + 3] = Ma + 2, ub[nb + 4] = Ma + 1, ub[nb + 5] = Ma + 2, nb += 6, Ma += 3;
                            C = 0;
                            for (T = va.length; C < T; C++) ec[Cb] = Ma, ec[Cb + 1] = Ma + 1, ec[Cb + 2] = Ma + 3, ec[Cb + 3] = Ma + 1, ec[Cb + 4] = Ma + 2, ec[Cb + 5] = Ma + 3, Cb += 6, ub[nb] = Ma, ub[nb + 1] = Ma + 1, ub[nb + 2] = Ma, ub[nb + 3] = Ma + 3, ub[nb + 4] = Ma + 1, ub[nb + 5] = Ma + 2, ub[nb + 6] = Ma + 2, ub[nb + 7] = Ma + 3, nb += 8, Ma += 4;
                            j.bindBuffer(j.ELEMENT_ARRAY_BUFFER, sa.__webglFaceBuffer);
                            j.bufferData(j.ELEMENT_ARRAY_BUFFER,
                                ec, Ga);
                            j.bindBuffer(j.ELEMENT_ARRAY_BUFFER, sa.__webglLineBuffer);
                            j.bufferData(j.ELEMENT_ARRAY_BUFFER, ub, Ga)
                        }
                        if (td) {
                            ya = 0;
                            for (Bc = td.length; ya < Bc; ya++) if (v = td[ya], v.__original.needsUpdate) {
                                y = 0;
                                if (1 === v.size) if (void 0 === v.boundTo || "vertices" === v.boundTo) {
                                    C = 0;
                                    for (T = ua.length; C < T; C++) O = ib[ua[C]], v.array[y] = v.value[O.a], v.array[y + 1] = v.value[O.b], v.array[y + 2] = v.value[O.c], y += 3;
                                    C = 0;
                                    for (T = va.length; C < T; C++) O = ib[va[C]], v.array[y] = v.value[O.a], v.array[y + 1] = v.value[O.b], v.array[y + 2] = v.value[O.c], v.array[y + 3] =
                                        v.value[O.d], y += 4
                                } else {
                                    if ("faces" === v.boundTo) {
                                        C = 0;
                                        for (T = ua.length; C < T; C++) za = v.value[ua[C]], v.array[y] = za, v.array[y + 1] = za, v.array[y + 2] = za, y += 3;
                                        C = 0;
                                        for (T = va.length; C < T; C++) za = v.value[va[C]], v.array[y] = za, v.array[y + 1] = za, v.array[y + 2] = za, v.array[y + 3] = za, y += 4
                                    }
                                } else if (2 === v.size) if (void 0 === v.boundTo || "vertices" === v.boundTo) {
                                    C = 0;
                                    for (T = ua.length; C < T; C++) O = ib[ua[C]], P = v.value[O.a], Q = v.value[O.b], R = v.value[O.c], v.array[y] = P.x, v.array[y + 1] = P.y, v.array[y + 2] = Q.x, v.array[y + 3] = Q.y, v.array[y + 4] = R.x, v.array[y +
                                    5] = R.y, y += 6;
                                    C = 0;
                                    for (T = va.length; C < T; C++) O = ib[va[C]], P = v.value[O.a], Q = v.value[O.b], R = v.value[O.c], qa = v.value[O.d], v.array[y] = P.x, v.array[y + 1] = P.y, v.array[y + 2] = Q.x, v.array[y + 3] = Q.y, v.array[y + 4] = R.x, v.array[y + 5] = R.y, v.array[y + 6] = qa.x, v.array[y + 7] = qa.y, y += 8
                                } else {
                                    if ("faces" === v.boundTo) {
                                        C = 0;
                                        for (T = ua.length; C < T; C++) R = Q = P = za = v.value[ua[C]], v.array[y] = P.x, v.array[y + 1] = P.y, v.array[y + 2] = Q.x, v.array[y + 3] = Q.y, v.array[y + 4] = R.x, v.array[y + 5] = R.y, y += 6;
                                        C = 0;
                                        for (T = va.length; C < T; C++) qa = R = Q = P = za = v.value[va[C]], v.array[y] =
                                            P.x, v.array[y + 1] = P.y, v.array[y + 2] = Q.x, v.array[y + 3] = Q.y, v.array[y + 4] = R.x, v.array[y + 5] = R.y, v.array[y + 6] = qa.x, v.array[y + 7] = qa.y, y += 8
                                    }
                                } else if (3 === v.size) {
                                    var X;
                                    X = "c" === v.type ? ["r", "g", "b"] : ["x", "y", "z"];
                                    if (void 0 === v.boundTo || "vertices" === v.boundTo) {
                                        C = 0;
                                        for (T = ua.length; C < T; C++) O = ib[ua[C]], P = v.value[O.a], Q = v.value[O.b], R = v.value[O.c], v.array[y] = P[X[0]], v.array[y + 1] = P[X[1]], v.array[y + 2] = P[X[2]], v.array[y + 3] = Q[X[0]], v.array[y + 4] = Q[X[1]], v.array[y + 5] = Q[X[2]], v.array[y + 6] = R[X[0]], v.array[y + 7] = R[X[1]], v.array[y +
                                        8] = R[X[2]], y += 9;
                                        C = 0;
                                        for (T = va.length; C < T; C++) O = ib[va[C]], P = v.value[O.a], Q = v.value[O.b], R = v.value[O.c], qa = v.value[O.d], v.array[y] = P[X[0]], v.array[y + 1] = P[X[1]], v.array[y + 2] = P[X[2]], v.array[y + 3] = Q[X[0]], v.array[y + 4] = Q[X[1]], v.array[y + 5] = Q[X[2]], v.array[y + 6] = R[X[0]], v.array[y + 7] = R[X[1]], v.array[y + 8] = R[X[2]], v.array[y + 9] = qa[X[0]], v.array[y + 10] = qa[X[1]], v.array[y + 11] = qa[X[2]], y += 12
                                    } else if ("faces" === v.boundTo) {
                                        C = 0;
                                        for (T = ua.length; C < T; C++) R = Q = P = za = v.value[ua[C]], v.array[y] = P[X[0]], v.array[y + 1] = P[X[1]], v.array[y +
                                        2] = P[X[2]], v.array[y + 3] = Q[X[0]], v.array[y + 4] = Q[X[1]], v.array[y + 5] = Q[X[2]], v.array[y + 6] = R[X[0]], v.array[y + 7] = R[X[1]], v.array[y + 8] = R[X[2]], y += 9;
                                        C = 0;
                                        for (T = va.length; C < T; C++) qa = R = Q = P = za = v.value[va[C]], v.array[y] = P[X[0]], v.array[y + 1] = P[X[1]], v.array[y + 2] = P[X[2]], v.array[y + 3] = Q[X[0]], v.array[y + 4] = Q[X[1]], v.array[y + 5] = Q[X[2]], v.array[y + 6] = R[X[0]], v.array[y + 7] = R[X[1]], v.array[y + 8] = R[X[2]], v.array[y + 9] = qa[X[0]], v.array[y + 10] = qa[X[1]], v.array[y + 11] = qa[X[2]], y += 12
                                    } else if ("faceVertices" === v.boundTo) {
                                        C = 0;
                                        for (T =
                                                 ua.length; C < T; C++) za = v.value[ua[C]], P = za[0], Q = za[1], R = za[2], v.array[y] = P[X[0]], v.array[y + 1] = P[X[1]], v.array[y + 2] = P[X[2]], v.array[y + 3] = Q[X[0]], v.array[y + 4] = Q[X[1]], v.array[y + 5] = Q[X[2]], v.array[y + 6] = R[X[0]], v.array[y + 7] = R[X[1]], v.array[y + 8] = R[X[2]], y += 9;
                                        C = 0;
                                        for (T = va.length; C < T; C++) za = v.value[va[C]], P = za[0], Q = za[1], R = za[2], qa = za[3], v.array[y] = P[X[0]], v.array[y + 1] = P[X[1]], v.array[y + 2] = P[X[2]], v.array[y + 3] = Q[X[0]], v.array[y + 4] = Q[X[1]], v.array[y + 5] = Q[X[2]], v.array[y + 6] = R[X[0]], v.array[y + 7] = R[X[1]], v.array[y +
                                        8] = R[X[2]], v.array[y + 9] = qa[X[0]], v.array[y + 10] = qa[X[1]], v.array[y + 11] = qa[X[2]], y += 12
                                    }
                                } else if (4 === v.size) if (void 0 === v.boundTo || "vertices" === v.boundTo) {
                                    C = 0;
                                    for (T = ua.length; C < T; C++) O = ib[ua[C]], P = v.value[O.a], Q = v.value[O.b], R = v.value[O.c], v.array[y] = P.x, v.array[y + 1] = P.y, v.array[y + 2] = P.z, v.array[y + 3] = P.w, v.array[y + 4] = Q.x, v.array[y + 5] = Q.y, v.array[y + 6] = Q.z, v.array[y + 7] = Q.w, v.array[y + 8] = R.x, v.array[y + 9] = R.y, v.array[y + 10] = R.z, v.array[y + 11] = R.w, y += 12;
                                    C = 0;
                                    for (T = va.length; C < T; C++) O = ib[va[C]], P = v.value[O.a],
                                        Q = v.value[O.b], R = v.value[O.c], qa = v.value[O.d], v.array[y] = P.x, v.array[y + 1] = P.y, v.array[y + 2] = P.z, v.array[y + 3] = P.w, v.array[y + 4] = Q.x, v.array[y + 5] = Q.y, v.array[y + 6] = Q.z, v.array[y + 7] = Q.w, v.array[y + 8] = R.x, v.array[y + 9] = R.y, v.array[y + 10] = R.z, v.array[y + 11] = R.w, v.array[y + 12] = qa.x, v.array[y + 13] = qa.y, v.array[y + 14] = qa.z, v.array[y + 15] = qa.w, y += 16
                                } else if ("faces" === v.boundTo) {
                                    C = 0;
                                    for (T = ua.length; C < T; C++) R = Q = P = za = v.value[ua[C]], v.array[y] = P.x, v.array[y + 1] = P.y, v.array[y + 2] = P.z, v.array[y + 3] = P.w, v.array[y + 4] = Q.x, v.array[y +
                                    5] = Q.y, v.array[y + 6] = Q.z, v.array[y + 7] = Q.w, v.array[y + 8] = R.x, v.array[y + 9] = R.y, v.array[y + 10] = R.z, v.array[y + 11] = R.w, y += 12;
                                    C = 0;
                                    for (T = va.length; C < T; C++) qa = R = Q = P = za = v.value[va[C]], v.array[y] = P.x, v.array[y + 1] = P.y, v.array[y + 2] = P.z, v.array[y + 3] = P.w, v.array[y + 4] = Q.x, v.array[y + 5] = Q.y, v.array[y + 6] = Q.z, v.array[y + 7] = Q.w, v.array[y + 8] = R.x, v.array[y + 9] = R.y, v.array[y + 10] = R.z, v.array[y + 11] = R.w, v.array[y + 12] = qa.x, v.array[y + 13] = qa.y, v.array[y + 14] = qa.z, v.array[y + 15] = qa.w, y += 16
                                } else if ("faceVertices" === v.boundTo) {
                                    C = 0;
                                    for (T =
                                             ua.length; C < T; C++) za = v.value[ua[C]], P = za[0], Q = za[1], R = za[2], v.array[y] = P.x, v.array[y + 1] = P.y, v.array[y + 2] = P.z, v.array[y + 3] = P.w, v.array[y + 4] = Q.x, v.array[y + 5] = Q.y, v.array[y + 6] = Q.z, v.array[y + 7] = Q.w, v.array[y + 8] = R.x, v.array[y + 9] = R.y, v.array[y + 10] = R.z, v.array[y + 11] = R.w, y += 12;
                                    C = 0;
                                    for (T = va.length; C < T; C++) za = v.value[va[C]], P = za[0], Q = za[1], R = za[2], qa = za[3], v.array[y] = P.x, v.array[y + 1] = P.y, v.array[y + 2] = P.z, v.array[y + 3] = P.w, v.array[y + 4] = Q.x, v.array[y + 5] = Q.y, v.array[y + 6] = Q.z, v.array[y + 7] = Q.w, v.array[y + 8] = R.x, v.array[y +
                                    9] = R.y, v.array[y + 10] = R.z, v.array[y + 11] = R.w, v.array[y + 12] = qa.x, v.array[y + 13] = qa.y, v.array[y + 14] = qa.z, v.array[y + 15] = qa.w, y += 16
                                }
                                j.bindBuffer(j.ARRAY_BUFFER, v.buffer);
                                j.bufferData(j.ARRAY_BUFFER, v.array, Ga)
                            }
                        }
                        Hb && (delete sa.__inittedArrays, delete sa.__colorArray, delete sa.__normalArray, delete sa.__tangentArray, delete sa.__uvArray, delete sa.__uv2Array, delete sa.__faceArray, delete sa.__vertexArray, delete sa.__lineArray, delete sa.__skinIndexArray, delete sa.__skinWeightArray)
                    }
                }
                la.verticesNeedUpdate = !1;
                la.morphTargetsNeedUpdate =
                    !1;
                la.elementsNeedUpdate = !1;
                la.uvsNeedUpdate = !1;
                la.normalsNeedUpdate = !1;
                la.colorsNeedUpdate = !1;
                la.tangentsNeedUpdate = !1;
                la.buffersNeedUpdate = !1;
                na.attributes && q(na)
            } else if (ma instanceof THREE.Ribbon) {
                na = e(ma, la);
                xa = na.attributes && n(na);
                if (la.verticesNeedUpdate || la.colorsNeedUpdate || la.normalsNeedUpdate || xa) {
                    var Eb = la, ad = j.DYNAMIC_DRAW, Pc = void 0, Qc = void 0, Rc = void 0, bd = void 0, Aa = void 0,
                        cd = void 0, dd = void 0, ed = void 0, Cd = void 0, bb = void 0, Gc = void 0, Ea = void 0,
                        rb = void 0, Dd = Eb.vertices, Ed = Eb.colors, Fd = Eb.normals,
                        Od = Dd.length, Pd = Ed.length, Qd = Fd.length, fd = Eb.__vertexArray, gd = Eb.__colorArray,
                        hd = Eb.__normalArray, Rd = Eb.colorsNeedUpdate, Sd = Eb.normalsNeedUpdate,
                        wd = Eb.__webglCustomAttributesList;
                    if (Eb.verticesNeedUpdate) {
                        for (Pc = 0; Pc < Od; Pc++) bd = Dd[Pc], Aa = 3 * Pc, fd[Aa] = bd.x, fd[Aa + 1] = bd.y, fd[Aa + 2] = bd.z;
                        j.bindBuffer(j.ARRAY_BUFFER, Eb.__webglVertexBuffer);
                        j.bufferData(j.ARRAY_BUFFER, fd, ad)
                    }
                    if (Rd) {
                        for (Qc = 0; Qc < Pd; Qc++) cd = Ed[Qc], Aa = 3 * Qc, gd[Aa] = cd.r, gd[Aa + 1] = cd.g, gd[Aa + 2] = cd.b;
                        j.bindBuffer(j.ARRAY_BUFFER, Eb.__webglColorBuffer);
                        j.bufferData(j.ARRAY_BUFFER, gd, ad)
                    }
                    if (Sd) {
                        for (Rc = 0; Rc < Qd; Rc++) dd = Fd[Rc], Aa = 3 * Rc, hd[Aa] = dd.x, hd[Aa + 1] = dd.y, hd[Aa + 2] = dd.z;
                        j.bindBuffer(j.ARRAY_BUFFER, Eb.__webglNormalBuffer);
                        j.bufferData(j.ARRAY_BUFFER, hd, ad)
                    }
                    if (wd) {
                        ed = 0;
                        for (Cd = wd.length; ed < Cd; ed++) if (Ea = wd[ed], Ea.needsUpdate && (void 0 === Ea.boundTo || "vertices" === Ea.boundTo)) {
                            Aa = 0;
                            Gc = Ea.value.length;
                            if (1 === Ea.size) for (bb = 0; bb < Gc; bb++) Ea.array[bb] = Ea.value[bb]; else if (2 === Ea.size) for (bb = 0; bb < Gc; bb++) rb = Ea.value[bb], Ea.array[Aa] = rb.x, Ea.array[Aa + 1] = rb.y,
                                Aa += 2; else if (3 === Ea.size) if ("c" === Ea.type) for (bb = 0; bb < Gc; bb++) rb = Ea.value[bb], Ea.array[Aa] = rb.r, Ea.array[Aa + 1] = rb.g, Ea.array[Aa + 2] = rb.b, Aa += 3; else for (bb = 0; bb < Gc; bb++) rb = Ea.value[bb], Ea.array[Aa] = rb.x, Ea.array[Aa + 1] = rb.y, Ea.array[Aa + 2] = rb.z, Aa += 3; else if (4 === Ea.size) for (bb = 0; bb < Gc; bb++) rb = Ea.value[bb], Ea.array[Aa] = rb.x, Ea.array[Aa + 1] = rb.y, Ea.array[Aa + 2] = rb.z, Ea.array[Aa + 3] = rb.w, Aa += 4;
                            j.bindBuffer(j.ARRAY_BUFFER, Ea.buffer);
                            j.bufferData(j.ARRAY_BUFFER, Ea.array, ad)
                        }
                    }
                }
                la.verticesNeedUpdate = !1;
                la.colorsNeedUpdate =
                    !1;
                la.normalsNeedUpdate = !1;
                na.attributes && q(na)
            } else if (ma instanceof THREE.Line) {
                na = e(ma, la);
                xa = na.attributes && n(na);
                if (la.verticesNeedUpdate || la.colorsNeedUpdate || la.lineDistancesNeedUpdate || xa) {
                    var Fb = la, id = j.DYNAMIC_DRAW, Sc = void 0, Tc = void 0, Uc = void 0, jd = void 0, Ka = void 0,
                        kd = void 0, Gd = Fb.vertices, Hd = Fb.colors, Id = Fb.lineDistances, Td = Gd.length,
                        Ud = Hd.length, Vd = Id.length, ld = Fb.__vertexArray, md = Fb.__colorArray,
                        Jd = Fb.__lineDistanceArray, Wd = Fb.colorsNeedUpdate, Xd = Fb.lineDistancesNeedUpdate,
                        xd = Fb.__webglCustomAttributesList,
                        nd = void 0, Kd = void 0, cb = void 0, Hc = void 0, sb = void 0, Fa = void 0;
                    if (Fb.verticesNeedUpdate) {
                        for (Sc = 0; Sc < Td; Sc++) jd = Gd[Sc], Ka = 3 * Sc, ld[Ka] = jd.x, ld[Ka + 1] = jd.y, ld[Ka + 2] = jd.z;
                        j.bindBuffer(j.ARRAY_BUFFER, Fb.__webglVertexBuffer);
                        j.bufferData(j.ARRAY_BUFFER, ld, id)
                    }
                    if (Wd) {
                        for (Tc = 0; Tc < Ud; Tc++) kd = Hd[Tc], Ka = 3 * Tc, md[Ka] = kd.r, md[Ka + 1] = kd.g, md[Ka + 2] = kd.b;
                        j.bindBuffer(j.ARRAY_BUFFER, Fb.__webglColorBuffer);
                        j.bufferData(j.ARRAY_BUFFER, md, id)
                    }
                    if (Xd) {
                        for (Uc = 0; Uc < Vd; Uc++) Jd[Uc] = Id[Uc];
                        j.bindBuffer(j.ARRAY_BUFFER, Fb.__webglLineDistanceBuffer);
                        j.bufferData(j.ARRAY_BUFFER, Jd, id)
                    }
                    if (xd) {
                        nd = 0;
                        for (Kd = xd.length; nd < Kd; nd++) if (Fa = xd[nd], Fa.needsUpdate && (void 0 === Fa.boundTo || "vertices" === Fa.boundTo)) {
                            Ka = 0;
                            Hc = Fa.value.length;
                            if (1 === Fa.size) for (cb = 0; cb < Hc; cb++) Fa.array[cb] = Fa.value[cb]; else if (2 === Fa.size) for (cb = 0; cb < Hc; cb++) sb = Fa.value[cb], Fa.array[Ka] = sb.x, Fa.array[Ka + 1] = sb.y, Ka += 2; else if (3 === Fa.size) if ("c" === Fa.type) for (cb = 0; cb < Hc; cb++) sb = Fa.value[cb], Fa.array[Ka] = sb.r, Fa.array[Ka + 1] = sb.g, Fa.array[Ka + 2] = sb.b, Ka += 3; else for (cb = 0; cb < Hc; cb++) sb =
                                Fa.value[cb], Fa.array[Ka] = sb.x, Fa.array[Ka + 1] = sb.y, Fa.array[Ka + 2] = sb.z, Ka += 3; else if (4 === Fa.size) for (cb = 0; cb < Hc; cb++) sb = Fa.value[cb], Fa.array[Ka] = sb.x, Fa.array[Ka + 1] = sb.y, Fa.array[Ka + 2] = sb.z, Fa.array[Ka + 3] = sb.w, Ka += 4;
                            j.bindBuffer(j.ARRAY_BUFFER, Fa.buffer);
                            j.bufferData(j.ARRAY_BUFFER, Fa.array, id)
                        }
                    }
                }
                la.verticesNeedUpdate = !1;
                la.colorsNeedUpdate = !1;
                la.lineDistancesNeedUpdate = !1;
                na.attributes && q(na)
            } else if (ma instanceof THREE.ParticleSystem) {
                na = e(ma, la);
                xa = na.attributes && n(na);
                if (la.verticesNeedUpdate ||
                    la.colorsNeedUpdate || ma.sortParticles || xa) {
                    var fc = la, yd = j.DYNAMIC_DRAW, Vc = ma, tb = void 0, gc = void 0, hc = void 0, da = void 0,
                        ic = void 0, vc = void 0, od = fc.vertices, zd = od.length, Ad = fc.colors, Ld = Ad.length,
                        zc = fc.__vertexArray, Ac = fc.__colorArray, nc = fc.__sortArray, Md = fc.verticesNeedUpdate,
                        Nd = fc.colorsNeedUpdate, oc = fc.__webglCustomAttributesList, Kb = void 0, Ic = void 0,
                        pa = void 0, Lb = void 0, Da = void 0, ba = void 0;
                    if (Vc.sortParticles) {
                        Va.copy(Qa);
                        Va.multiply(Vc.matrixWorld);
                        for (tb = 0; tb < zd; tb++) hc = od[tb], wa.copy(hc), wa.applyProjection(Va),
                            nc[tb] = [wa.z, tb];
                        nc.sort(l);
                        for (tb = 0; tb < zd; tb++) hc = od[nc[tb][1]], da = 3 * tb, zc[da] = hc.x, zc[da + 1] = hc.y, zc[da + 2] = hc.z;
                        for (gc = 0; gc < Ld; gc++) da = 3 * gc, vc = Ad[nc[gc][1]], Ac[da] = vc.r, Ac[da + 1] = vc.g, Ac[da + 2] = vc.b;
                        if (oc) {
                            Kb = 0;
                            for (Ic = oc.length; Kb < Ic; Kb++) if (ba = oc[Kb], void 0 === ba.boundTo || "vertices" === ba.boundTo) if (da = 0, Lb = ba.value.length, 1 === ba.size) for (pa = 0; pa < Lb; pa++) ic = nc[pa][1], ba.array[pa] = ba.value[ic]; else if (2 === ba.size) for (pa = 0; pa < Lb; pa++) ic = nc[pa][1], Da = ba.value[ic], ba.array[da] = Da.x, ba.array[da + 1] = Da.y,
                                da += 2; else if (3 === ba.size) if ("c" === ba.type) for (pa = 0; pa < Lb; pa++) ic = nc[pa][1], Da = ba.value[ic], ba.array[da] = Da.r, ba.array[da + 1] = Da.g, ba.array[da + 2] = Da.b, da += 3; else for (pa = 0; pa < Lb; pa++) ic = nc[pa][1], Da = ba.value[ic], ba.array[da] = Da.x, ba.array[da + 1] = Da.y, ba.array[da + 2] = Da.z, da += 3; else if (4 === ba.size) for (pa = 0; pa < Lb; pa++) ic = nc[pa][1], Da = ba.value[ic], ba.array[da] = Da.x, ba.array[da + 1] = Da.y, ba.array[da + 2] = Da.z, ba.array[da + 3] = Da.w, da += 4
                        }
                    } else {
                        if (Md) for (tb = 0; tb < zd; tb++) hc = od[tb], da = 3 * tb, zc[da] = hc.x, zc[da + 1] = hc.y,
                            zc[da + 2] = hc.z;
                        if (Nd) for (gc = 0; gc < Ld; gc++) vc = Ad[gc], da = 3 * gc, Ac[da] = vc.r, Ac[da + 1] = vc.g, Ac[da + 2] = vc.b;
                        if (oc) {
                            Kb = 0;
                            for (Ic = oc.length; Kb < Ic; Kb++) if (ba = oc[Kb], ba.needsUpdate && (void 0 === ba.boundTo || "vertices" === ba.boundTo)) if (Lb = ba.value.length, da = 0, 1 === ba.size) for (pa = 0; pa < Lb; pa++) ba.array[pa] = ba.value[pa]; else if (2 === ba.size) for (pa = 0; pa < Lb; pa++) Da = ba.value[pa], ba.array[da] = Da.x, ba.array[da + 1] = Da.y, da += 2; else if (3 === ba.size) if ("c" === ba.type) for (pa = 0; pa < Lb; pa++) Da = ba.value[pa], ba.array[da] = Da.r, ba.array[da +
                            1] = Da.g, ba.array[da + 2] = Da.b, da += 3; else for (pa = 0; pa < Lb; pa++) Da = ba.value[pa], ba.array[da] = Da.x, ba.array[da + 1] = Da.y, ba.array[da + 2] = Da.z, da += 3; else if (4 === ba.size) for (pa = 0; pa < Lb; pa++) Da = ba.value[pa], ba.array[da] = Da.x, ba.array[da + 1] = Da.y, ba.array[da + 2] = Da.z, ba.array[da + 3] = Da.w, da += 4
                        }
                    }
                    if (Md || Vc.sortParticles) j.bindBuffer(j.ARRAY_BUFFER, fc.__webglVertexBuffer), j.bufferData(j.ARRAY_BUFFER, zc, yd);
                    if (Nd || Vc.sortParticles) j.bindBuffer(j.ARRAY_BUFFER, fc.__webglColorBuffer), j.bufferData(j.ARRAY_BUFFER, Ac, yd);
                    if (oc) {
                        Kb = 0;
                        for (Ic = oc.length; Kb < Ic; Kb++) if (ba = oc[Kb], ba.needsUpdate || Vc.sortParticles) j.bindBuffer(j.ARRAY_BUFFER, ba.buffer), j.bufferData(j.ARRAY_BUFFER, ba.array, yd)
                    }
                }
                la.verticesNeedUpdate = !1;
                la.colorsNeedUpdate = !1;
                na.attributes && q(na)
            }
        }
    };
    this.initMaterial = function (a, b, c, d) {
        var e, f, g, h;
        a.addEventListener("dispose", Xc);
        var i, k, l, m, p;
        a instanceof THREE.MeshDepthMaterial ? p = "depth" : a instanceof THREE.MeshNormalMaterial ? p = "normal" : a instanceof THREE.MeshBasicMaterial ? p = "basic" : a instanceof THREE.MeshLambertMaterial ?
            p = "lambert" : a instanceof THREE.MeshPhongMaterial ? p = "phong" : a instanceof THREE.LineBasicMaterial ? p = "basic" : a instanceof THREE.LineDashedMaterial ? p = "dashed" : a instanceof THREE.ParticleBasicMaterial && (p = "particle_basic");
        if (p) {
            var n = THREE.ShaderLib[p];
            a.uniforms = THREE.UniformsUtils.clone(n.uniforms);
            a.vertexShader = n.vertexShader;
            a.fragmentShader = n.fragmentShader
        }
        var q, s, r;
        e = g = s = r = n = 0;
        for (f = b.length; e < f; e++) q = b[e], q.onlyShadow || (q instanceof THREE.DirectionalLight && g++, q instanceof THREE.PointLight &&
        s++, q instanceof THREE.SpotLight && r++, q instanceof THREE.HemisphereLight && n++);
        e = g;
        f = s;
        g = r;
        h = n;
        n = q = 0;
        for (r = b.length; n < r; n++) s = b[n], s.castShadow && (s instanceof THREE.SpotLight && q++, s instanceof THREE.DirectionalLight && !s.shadowCascade && q++);
        m = q;
        Cc && d && d.useVertexTexture ? l = 1024 : (b = j.getParameter(j.MAX_VERTEX_UNIFORM_VECTORS), b = Math.floor((b - 20) / 4), void 0 !== d && d instanceof THREE.SkinnedMesh && (b = Math.min(d.bones.length, b), b < d.bones.length && console.warn("WebGLRenderer: too many bones - " + d.bones.length +
            ", this GPU supports just " + b + " (try OpenGL instead of ANGLE)")), l = b);
        a:{
            s = a.fragmentShader;
            r = a.vertexShader;
            n = a.uniforms;
            b = a.attributes;
            q = a.defines;
            var c = {
                map: !!a.map,
                envMap: !!a.envMap,
                lightMap: !!a.lightMap,
                bumpMap: !!a.bumpMap,
                normalMap: !!a.normalMap,
                specularMap: !!a.specularMap,
                vertexColors: a.vertexColors,
                fog: c,
                useFog: a.fog,
                fogExp: c instanceof THREE.FogExp2,
                sizeAttenuation: a.sizeAttenuation,
                skinning: a.skinning,
                maxBones: l,
                useVertexTexture: Cc && d && d.useVertexTexture,
                boneTextureWidth: d && d.boneTextureWidth,
                boneTextureHeight: d && d.boneTextureHeight,
                morphTargets: a.morphTargets,
                morphNormals: a.morphNormals,
                maxMorphTargets: this.maxMorphTargets,
                maxMorphNormals: this.maxMorphNormals,
                maxDirLights: e,
                maxPointLights: f,
                maxSpotLights: g,
                maxHemiLights: h,
                maxShadows: m,
                shadowMapEnabled: this.shadowMapEnabled && d.receiveShadow,
                shadowMapType: this.shadowMapType,
                shadowMapDebug: this.shadowMapDebug,
                shadowMapCascade: this.shadowMapCascade,
                alphaTest: a.alphaTest,
                metal: a.metal,
                perPixel: a.perPixel,
                wrapAround: a.wrapAround,
                doubleSided: a.side ===
                THREE.DoubleSide,
                flipSided: a.side === THREE.BackSide
            }, t, u, x, d = [];
            p ? d.push(p) : (d.push(s), d.push(r));
            for (u in q) d.push(u), d.push(q[u]);
            for (t in c) d.push(t), d.push(c[t]);
            p = d.join();
            t = 0;
            for (u = ca.length; t < u; t++) if (d = ca[t], d.code === p) {
                d.usedTimes++;
                k = d.program;
                break a
            }
            t = "SHADOWMAP_TYPE_BASIC";
            c.shadowMapType === THREE.PCFShadowMap ? t = "SHADOWMAP_TYPE_PCF" : c.shadowMapType === THREE.PCFSoftShadowMap && (t = "SHADOWMAP_TYPE_PCF_SOFT");
            u = [];
            for (x in q) d = q[x], !1 !== d && (d = "#define " + x + " " + d, u.push(d));
            d = u.join("\n");
            x = j.createProgram();
            u = ["precision " + K + " float;", d, Bc ? "#define VERTEX_TEXTURES" : "", L.gammaInput ? "#define GAMMA_INPUT" : "", L.gammaOutput ? "#define GAMMA_OUTPUT" : "", L.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", "#define MAX_DIR_LIGHTS " + c.maxDirLights, "#define MAX_POINT_LIGHTS " + c.maxPointLights, "#define MAX_SPOT_LIGHTS " + c.maxSpotLights, "#define MAX_HEMI_LIGHTS " + c.maxHemiLights, "#define MAX_SHADOWS " + c.maxShadows, "#define MAX_BONES " + c.maxBones, c.map ? "#define USE_MAP" : "", c.envMap ? "#define USE_ENVMAP" : "",
                c.lightMap ? "#define USE_LIGHTMAP" : "", c.bumpMap ? "#define USE_BUMPMAP" : "", c.normalMap ? "#define USE_NORMALMAP" : "", c.specularMap ? "#define USE_SPECULARMAP" : "", c.vertexColors ? "#define USE_COLOR" : "", c.skinning ? "#define USE_SKINNING" : "", c.useVertexTexture ? "#define BONE_TEXTURE" : "", c.boneTextureWidth ? "#define N_BONE_PIXEL_X " + c.boneTextureWidth.toFixed(1) : "", c.boneTextureHeight ? "#define N_BONE_PIXEL_Y " + c.boneTextureHeight.toFixed(1) : "", c.morphTargets ? "#define USE_MORPHTARGETS" : "", c.morphNormals ? "#define USE_MORPHNORMALS" :
                    "", c.perPixel ? "#define PHONG_PER_PIXEL" : "", c.wrapAround ? "#define WRAP_AROUND" : "", c.doubleSided ? "#define DOUBLE_SIDED" : "", c.flipSided ? "#define FLIP_SIDED" : "", c.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", c.shadowMapEnabled ? "#define " + t : "", c.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", c.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", c.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", "uniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec2 uv;\nattribute vec2 uv2;\n#ifdef USE_COLOR\nattribute vec3 color;\n#endif\n#ifdef USE_MORPHTARGETS\nattribute vec3 morphTarget0;\nattribute vec3 morphTarget1;\nattribute vec3 morphTarget2;\nattribute vec3 morphTarget3;\n#ifdef USE_MORPHNORMALS\nattribute vec3 morphNormal0;\nattribute vec3 morphNormal1;\nattribute vec3 morphNormal2;\nattribute vec3 morphNormal3;\n#else\nattribute vec3 morphTarget4;\nattribute vec3 morphTarget5;\nattribute vec3 morphTarget6;\nattribute vec3 morphTarget7;\n#endif\n#endif\n#ifdef USE_SKINNING\nattribute vec4 skinIndex;\nattribute vec4 skinWeight;\n#endif\n"].join("\n");
            t = ["precision " + K + " float;", c.bumpMap || c.normalMap ? "#extension GL_OES_standard_derivatives : enable" : "", d, "#define MAX_DIR_LIGHTS " + c.maxDirLights, "#define MAX_POINT_LIGHTS " + c.maxPointLights, "#define MAX_SPOT_LIGHTS " + c.maxSpotLights, "#define MAX_HEMI_LIGHTS " + c.maxHemiLights, "#define MAX_SHADOWS " + c.maxShadows, c.alphaTest ? "#define ALPHATEST " + c.alphaTest : "", L.gammaInput ? "#define GAMMA_INPUT" : "", L.gammaOutput ? "#define GAMMA_OUTPUT" : "", L.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
                c.useFog && c.fog ? "#define USE_FOG" : "", c.useFog && c.fogExp ? "#define FOG_EXP2" : "", c.map ? "#define USE_MAP" : "", c.envMap ? "#define USE_ENVMAP" : "", c.lightMap ? "#define USE_LIGHTMAP" : "", c.bumpMap ? "#define USE_BUMPMAP" : "", c.normalMap ? "#define USE_NORMALMAP" : "", c.specularMap ? "#define USE_SPECULARMAP" : "", c.vertexColors ? "#define USE_COLOR" : "", c.metal ? "#define METAL" : "", c.perPixel ? "#define PHONG_PER_PIXEL" : "", c.wrapAround ? "#define WRAP_AROUND" : "", c.doubleSided ? "#define DOUBLE_SIDED" : "", c.flipSided ? "#define FLIP_SIDED" :
                    "", c.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", c.shadowMapEnabled ? "#define " + t : "", c.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", c.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", "uniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n"].join("\n");
            t = E("fragment", t + s);
            u = E("vertex", u + r);
            j.attachShader(x, u);
            j.attachShader(x, t);
            j.linkProgram(x);
            j.getProgramParameter(x, j.LINK_STATUS) || console.error("Could not initialise shader\nVALIDATE_STATUS: " + j.getProgramParameter(x, j.VALIDATE_STATUS) + ", gl error [" +
                j.getError() + "]");
            j.deleteShader(t);
            j.deleteShader(u);
            x.uniforms = {};
            x.attributes = {};
            var w;
            t = "viewMatrix modelViewMatrix projectionMatrix normalMatrix modelMatrix cameraPosition morphTargetInfluences".split(" ");
            c.useVertexTexture ? t.push("boneTexture") : t.push("boneGlobalMatrices");
            for (w in n) t.push(w);
            w = t;
            t = 0;
            for (u = w.length; t < u; t++) n = w[t], x.uniforms[n] = j.getUniformLocation(x, n);
            t = "position normal uv uv2 tangent color skinIndex skinWeight lineDistance".split(" ");
            for (w = 0; w < c.maxMorphTargets; w++) t.push("morphTarget" +
                w);
            for (w = 0; w < c.maxMorphNormals; w++) t.push("morphNormal" + w);
            for (k in b) t.push(k);
            k = t;
            w = 0;
            for (b = k.length; w < b; w++) t = k[w], x.attributes[t] = j.getAttribLocation(x, t);
            x.id = ta++;
            ca.push({program: x, code: p, usedTimes: 1});
            L.info.memory.programs = ca.length;
            k = x
        }
        a.program = k;
        w = a.program.attributes;
        if (a.morphTargets) {
            a.numSupportedMorphTargets = 0;
            b = "morphTarget";
            for (k = 0; k < this.maxMorphTargets; k++) x = b + k, 0 <= w[x] && a.numSupportedMorphTargets++
        }
        if (a.morphNormals) {
            a.numSupportedMorphNormals = 0;
            b = "morphNormal";
            for (k = 0; k < this.maxMorphNormals; k++) x =
                b + k, 0 <= w[x] && a.numSupportedMorphNormals++
        }
        a.uniformsList = [];
        for (i in a.uniforms) a.uniformsList.push([a.uniforms[i], i])
    };
    this.setFaceCulling = function (a, b) {
        a === THREE.CullFaceNone ? j.disable(j.CULL_FACE) : (b === THREE.FrontFaceDirectionCW ? j.frontFace(j.CW) : j.frontFace(j.CCW), a === THREE.CullFaceBack ? j.cullFace(j.BACK) : a === THREE.CullFaceFront ? j.cullFace(j.FRONT) : j.cullFace(j.FRONT_AND_BACK), j.enable(j.CULL_FACE))
    };
    this.setMaterialFaces = function (a) {
        var b = a.side === THREE.DoubleSide, a = a.side === THREE.BackSide;
        ha !==
        b && (b ? j.disable(j.CULL_FACE) : j.enable(j.CULL_FACE), ha = b);
        V !== a && (a ? j.frontFace(j.CW) : j.frontFace(j.CCW), V = a)
    };
    this.setDepthTest = function (a) {
        na !== a && (a ? j.enable(j.DEPTH_TEST) : j.disable(j.DEPTH_TEST), na = a)
    };
    this.setDepthWrite = function (a) {
        Pa !== a && (j.depthMask(a), Pa = a)
    };
    this.setBlending = function (a, b, c, d) {
        a !== $ && (a === THREE.NoBlending ? j.disable(j.BLEND) : a === THREE.AdditiveBlending ? (j.enable(j.BLEND), j.blendEquation(j.FUNC_ADD), j.blendFunc(j.SRC_ALPHA, j.ONE)) : a === THREE.SubtractiveBlending ? (j.enable(j.BLEND),
            j.blendEquation(j.FUNC_ADD), j.blendFunc(j.ZERO, j.ONE_MINUS_SRC_COLOR)) : a === THREE.MultiplyBlending ? (j.enable(j.BLEND), j.blendEquation(j.FUNC_ADD), j.blendFunc(j.ZERO, j.SRC_COLOR)) : a === THREE.CustomBlending ? j.enable(j.BLEND) : (j.enable(j.BLEND), j.blendEquationSeparate(j.FUNC_ADD, j.FUNC_ADD), j.blendFuncSeparate(j.SRC_ALPHA, j.ONE_MINUS_SRC_ALPHA, j.ONE, j.ONE_MINUS_SRC_ALPHA)), $ = a);
        if (a === THREE.CustomBlending) {
            if (b !== Y && (j.blendEquation(F(b)), Y = b), c !== ia || d !== ra) j.blendFunc(F(c), F(d)), ia = c, ra = d
        } else ra = ia =
            Y = null
    };
    this.setTexture = function (a, b) {
        if (a.needsUpdate) {
            a.__webglInit || (a.__webglInit = !0, a.addEventListener("dispose", Zc), a.__webglTexture = j.createTexture(), L.info.memory.textures++);
            j.activeTexture(j.TEXTURE0 + b);
            j.bindTexture(j.TEXTURE_2D, a.__webglTexture);
            j.pixelStorei(j.UNPACK_FLIP_Y_WEBGL, a.flipY);
            j.pixelStorei(j.UNPACK_PREMULTIPLY_ALPHA_WEBGL, a.premultiplyAlpha);
            j.pixelStorei(j.UNPACK_ALIGNMENT, a.unpackAlignment);
            var c = a.image, d = 0 === (c.width & c.width - 1) && 0 === (c.height & c.height - 1), e = F(a.format),
                f = F(a.type);
            Z(j.TEXTURE_2D, a, d);
            var g = a.mipmaps;
            if (a instanceof THREE.DataTexture) if (0 < g.length && d) {
                for (var h = 0, i = g.length; h < i; h++) c = g[h], j.texImage2D(j.TEXTURE_2D, h, e, c.width, c.height, 0, e, f, c.data);
                a.generateMipmaps = !1
            } else j.texImage2D(j.TEXTURE_2D, 0, e, c.width, c.height, 0, e, f, c.data); else if (a instanceof THREE.CompressedTexture) {
                h = 0;
                for (i = g.length; h < i; h++) c = g[h], j.compressedTexImage2D(j.TEXTURE_2D, h, e, c.width, c.height, 0, c.data)
            } else if (0 < g.length && d) {
                h = 0;
                for (i = g.length; h < i; h++) c = g[h], j.texImage2D(j.TEXTURE_2D,
                    h, e, e, f, c);
                a.generateMipmaps = !1
            } else j.texImage2D(j.TEXTURE_2D, 0, e, e, f, a.image);
            a.generateMipmaps && d && j.generateMipmap(j.TEXTURE_2D);
            a.needsUpdate = !1;
            if (a.onUpdate) a.onUpdate()
        } else j.activeTexture(j.TEXTURE0 + b), j.bindTexture(j.TEXTURE_2D, a.__webglTexture)
    };
    this.setRenderTarget = function (a) {
        var b = a instanceof THREE.WebGLRenderTargetCube;
        if (a && !a.__webglFramebuffer) {
            void 0 === a.depthBuffer && (a.depthBuffer = !0);
            void 0 === a.stencilBuffer && (a.stencilBuffer = !0);
            a.addEventListener("dispose", Jc);
            a.__webglTexture =
                j.createTexture();
            L.info.memory.textures++;
            var c = 0 === (a.width & a.width - 1) && 0 === (a.height & a.height - 1), d = F(a.format), e = F(a.type);
            if (b) {
                a.__webglFramebuffer = [];
                a.__webglRenderbuffer = [];
                j.bindTexture(j.TEXTURE_CUBE_MAP, a.__webglTexture);
                Z(j.TEXTURE_CUBE_MAP, a, c);
                for (var f = 0; 6 > f; f++) {
                    a.__webglFramebuffer[f] = j.createFramebuffer();
                    a.__webglRenderbuffer[f] = j.createRenderbuffer();
                    j.texImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X + f, 0, d, a.width, a.height, 0, d, e, null);
                    var g = a, h = j.TEXTURE_CUBE_MAP_POSITIVE_X + f;
                    j.bindFramebuffer(j.FRAMEBUFFER,
                        a.__webglFramebuffer[f]);
                    j.framebufferTexture2D(j.FRAMEBUFFER, j.COLOR_ATTACHMENT0, h, g.__webglTexture, 0);
                    A(a.__webglRenderbuffer[f], a)
                }
                c && j.generateMipmap(j.TEXTURE_CUBE_MAP)
            } else a.__webglFramebuffer = j.createFramebuffer(), a.__webglRenderbuffer = a.shareDepthFrom ? a.shareDepthFrom.__webglRenderbuffer : j.createRenderbuffer(), j.bindTexture(j.TEXTURE_2D, a.__webglTexture), Z(j.TEXTURE_2D, a, c), j.texImage2D(j.TEXTURE_2D, 0, d, a.width, a.height, 0, d, e, null), d = j.TEXTURE_2D, j.bindFramebuffer(j.FRAMEBUFFER, a.__webglFramebuffer),
                j.framebufferTexture2D(j.FRAMEBUFFER, j.COLOR_ATTACHMENT0, d, a.__webglTexture, 0), a.shareDepthFrom ? a.depthBuffer && !a.stencilBuffer ? j.framebufferRenderbuffer(j.FRAMEBUFFER, j.DEPTH_ATTACHMENT, j.RENDERBUFFER, a.__webglRenderbuffer) : a.depthBuffer && a.stencilBuffer && j.framebufferRenderbuffer(j.FRAMEBUFFER, j.DEPTH_STENCIL_ATTACHMENT, j.RENDERBUFFER, a.__webglRenderbuffer) : A(a.__webglRenderbuffer, a), c && j.generateMipmap(j.TEXTURE_2D);
            b ? j.bindTexture(j.TEXTURE_CUBE_MAP, null) : j.bindTexture(j.TEXTURE_2D, null);
            j.bindRenderbuffer(j.RENDERBUFFER,
                null);
            j.bindFramebuffer(j.FRAMEBUFFER, null)
        }
        a ? (b = b ? a.__webglFramebuffer[a.activeCubeFace] : a.__webglFramebuffer, c = a.width, a = a.height, e = d = 0) : (b = null, c = Mb, a = wb, d = ob, e = Gb);
        b !== $a && (j.bindFramebuffer(j.FRAMEBUFFER, b), j.viewport(d, e, c, a), $a = b);
        Nb = c;
        Ob = a
    };
    this.shadowMapPlugin = new THREE.ShadowMapPlugin;
    this.addPrePlugin(this.shadowMapPlugin);
    this.addPostPlugin(new THREE.SpritePlugin);
    this.addPostPlugin(new THREE.LensFlarePlugin)
};
THREE.WebGLRenderTarget = function (a, b, c) {
    THREE.EventDispatcher.call(this);
    this.width = a;
    this.height = b;
    c = c || {};
    this.wrapS = void 0 !== c.wrapS ? c.wrapS : THREE.ClampToEdgeWrapping;
    this.wrapT = void 0 !== c.wrapT ? c.wrapT : THREE.ClampToEdgeWrapping;
    this.magFilter = void 0 !== c.magFilter ? c.magFilter : THREE.LinearFilter;
    this.minFilter = void 0 !== c.minFilter ? c.minFilter : THREE.LinearMipMapLinearFilter;
    this.anisotropy = void 0 !== c.anisotropy ? c.anisotropy : 1;
    this.offset = new THREE.Vector2(0, 0);
    this.repeat = new THREE.Vector2(1, 1);
    this.format = void 0 !== c.format ? c.format : THREE.RGBAFormat;
    this.type = void 0 !== c.type ? c.type : THREE.UnsignedByteType;
    this.depthBuffer = void 0 !== c.depthBuffer ? c.depthBuffer : !0;
    this.stencilBuffer = void 0 !== c.stencilBuffer ? c.stencilBuffer : !0;
    this.generateMipmaps = !0;
    this.shareDepthFrom = null
};
THREE.WebGLRenderTarget.prototype.clone = function () {
    var a = new THREE.WebGLRenderTarget(this.width, this.height);
    a.wrapS = this.wrapS;
    a.wrapT = this.wrapT;
    a.magFilter = this.magFilter;
    a.minFilter = this.minFilter;
    a.anisotropy = this.anisotropy;
    a.offset.copy(this.offset);
    a.repeat.copy(this.repeat);
    a.format = this.format;
    a.type = this.type;
    a.depthBuffer = this.depthBuffer;
    a.stencilBuffer = this.stencilBuffer;
    a.generateMipmaps = this.generateMipmaps;
    a.shareDepthFrom = this.shareDepthFrom;
    return a
};
THREE.WebGLRenderTarget.prototype.dispose = function () {
    this.dispatchEvent({type: "dispose"})
};
THREE.WebGLRenderTargetCube = function (a, b, c) {
    THREE.WebGLRenderTarget.call(this, a, b, c);
    this.activeCubeFace = 0
};
THREE.WebGLRenderTargetCube.prototype = Object.create(THREE.WebGLRenderTarget.prototype);
THREE.RenderableVertex = function () {
    this.positionWorld = new THREE.Vector3;
    this.positionScreen = new THREE.Vector4;
    this.visible = !0
};
THREE.RenderableVertex.prototype.copy = function (a) {
    this.positionWorld.copy(a.positionWorld);
    this.positionScreen.copy(a.positionScreen)
};
THREE.RenderableFace3 = function () {
    this.v1 = new THREE.RenderableVertex;
    this.v2 = new THREE.RenderableVertex;
    this.v3 = new THREE.RenderableVertex;
    this.centroidModel = new THREE.Vector3;
    this.normalModel = new THREE.Vector3;
    this.normalModelView = new THREE.Vector3;
    this.vertexNormalsLength = 0;
    this.vertexNormalsModel = [new THREE.Vector3, new THREE.Vector3, new THREE.Vector3];
    this.vertexNormalsModelView = [new THREE.Vector3, new THREE.Vector3, new THREE.Vector3];
    this.material = this.color = null;
    this.uvs = [[]];
    this.z = null
};
THREE.RenderableFace4 = function () {
    this.v1 = new THREE.RenderableVertex;
    this.v2 = new THREE.RenderableVertex;
    this.v3 = new THREE.RenderableVertex;
    this.v4 = new THREE.RenderableVertex;
    this.centroidModel = new THREE.Vector3;
    this.normalModel = new THREE.Vector3;
    this.normalModelView = new THREE.Vector3;
    this.vertexNormalsLength = 0;
    this.vertexNormalsModel = [new THREE.Vector3, new THREE.Vector3, new THREE.Vector3, new THREE.Vector3];
    this.vertexNormalsModelView = [new THREE.Vector3, new THREE.Vector3, new THREE.Vector3, new THREE.Vector3];
    this.material = this.color = null;
    this.uvs = [[]];
    this.z = null
};
THREE.RenderableObject = function () {
    this.z = this.object = null
};
THREE.RenderableParticle = function () {
    this.rotation = this.z = this.y = this.x = this.object = null;
    this.scale = new THREE.Vector2;
    this.material = null
};
THREE.RenderableLine = function () {
    this.z = null;
    this.v1 = new THREE.RenderableVertex;
    this.v2 = new THREE.RenderableVertex;
    this.material = null
};
THREE.GeometryUtils = {
    merge: function (a, b) {
        var c, d, e = a.vertices.length, f = b instanceof THREE.Mesh ? b.geometry : b, g = a.vertices, h = f.vertices,
            i = a.faces, k = f.faces, l = a.faceVertexUvs[0], f = f.faceVertexUvs[0];
        b instanceof THREE.Mesh && (b.matrixAutoUpdate && b.updateMatrix(), c = b.matrix, d = new THREE.Matrix3, d.getInverse(c), d.transpose());
        for (var m = 0, p = h.length; m < p; m++) {
            var s = h[m].clone();
            c && s.applyMatrix4(c);
            g.push(s)
        }
        m = 0;
        for (p = k.length; m < p; m++) {
            var s = k[m], r, n, q = s.vertexNormals, z = s.vertexColors;
            s instanceof THREE.Face3 ?
                r = new THREE.Face3(s.a + e, s.b + e, s.c + e) : s instanceof THREE.Face4 && (r = new THREE.Face4(s.a + e, s.b + e, s.c + e, s.d + e));
            r.normal.copy(s.normal);
            d && r.normal.applyMatrix3(d).normalize();
            g = 0;
            for (h = q.length; g < h; g++) n = q[g].clone(), d && n.applyMatrix3(d).normalize(), r.vertexNormals.push(n);
            r.color.copy(s.color);
            g = 0;
            for (h = z.length; g < h; g++) n = z[g], r.vertexColors.push(n.clone());
            r.materialIndex = s.materialIndex;
            r.centroid.copy(s.centroid);
            c && r.centroid.applyMatrix4(c);
            i.push(r)
        }
        m = 0;
        for (p = f.length; m < p; m++) {
            c = f[m];
            d = [];
            g = 0;
            for (h = c.length; g < h; g++) d.push(new THREE.Vector2(c[g].x, c[g].y));
            l.push(d)
        }
    }, removeMaterials: function (a, b) {
        for (var c = {}, d = 0, e = b.length; d < e; d++) c[b[d]] = !0;
        for (var f, g = [], d = 0, e = a.faces.length; d < e; d++) f = a.faces[d], f.materialIndex in c || g.push(f);
        a.faces = g
    }, randomPointInTriangle: function (a, b, c) {
        var d, e, f, g = new THREE.Vector3, h = THREE.GeometryUtils.__v1;
        d = THREE.GeometryUtils.random();
        e = THREE.GeometryUtils.random();
        1 < d + e && (d = 1 - d, e = 1 - e);
        f = 1 - d - e;
        g.copy(a);
        g.multiplyScalar(d);
        h.copy(b);
        h.multiplyScalar(e);
        g.add(h);
        h.copy(c);
        h.multiplyScalar(f);
        g.add(h);
        return g
    }, randomPointInFace: function (a, b, c) {
        var d, e, f;
        if (a instanceof THREE.Face3) return d = b.vertices[a.a], e = b.vertices[a.b], f = b.vertices[a.c], THREE.GeometryUtils.randomPointInTriangle(d, e, f);
        if (a instanceof THREE.Face4) {
            d = b.vertices[a.a];
            e = b.vertices[a.b];
            f = b.vertices[a.c];
            var b = b.vertices[a.d], g;
            c ? a._area1 && a._area2 ? (c = a._area1, g = a._area2) : (c = THREE.GeometryUtils.triangleArea(d, e, b), g = THREE.GeometryUtils.triangleArea(e, f, b), a._area1 = c, a._area2 = g) : (c = THREE.GeometryUtils.triangleArea(d,
                e, b), g = THREE.GeometryUtils.triangleArea(e, f, b));
            return THREE.GeometryUtils.random() * (c + g) < c ? THREE.GeometryUtils.randomPointInTriangle(d, e, b) : THREE.GeometryUtils.randomPointInTriangle(e, f, b)
        }
    }, randomPointsInGeometry: function (a, b) {
        function c(a) {
            function b(c, d) {
                if (d < c) return c;
                var e = c + Math.floor((d - c) / 2);
                return k[e] > a ? b(c, e - 1) : k[e] < a ? b(e + 1, d) : e
            }

            return b(0, k.length - 1)
        }

        var d, e, f = a.faces, g = a.vertices, h = f.length, i = 0, k = [], l, m, p, s;
        for (e = 0; e < h; e++) d = f[e], d instanceof THREE.Face3 ? (l = g[d.a], m = g[d.b], p = g[d.c],
            d._area = THREE.GeometryUtils.triangleArea(l, m, p)) : d instanceof THREE.Face4 && (l = g[d.a], m = g[d.b], p = g[d.c], s = g[d.d], d._area1 = THREE.GeometryUtils.triangleArea(l, m, s), d._area2 = THREE.GeometryUtils.triangleArea(m, p, s), d._area = d._area1 + d._area2), i += d._area, k[e] = i;
        d = [];
        for (e = 0; e < b; e++) g = THREE.GeometryUtils.random() * i, g = c(g), d[e] = THREE.GeometryUtils.randomPointInFace(f[g], a, !0);
        return d
    }, triangleArea: function (a, b, c) {
        var d = THREE.GeometryUtils.__v1, e = THREE.GeometryUtils.__v2;
        d.subVectors(b, a);
        e.subVectors(c, a);
        d.cross(e);
        return 0.5 * d.length()
    }, center: function (a) {
        a.computeBoundingBox();
        var b = a.boundingBox, c = new THREE.Vector3;
        c.addVectors(b.min, b.max);
        c.multiplyScalar(-0.5);
        a.applyMatrix((new THREE.Matrix4).makeTranslation(c.x, c.y, c.z));
        a.computeBoundingBox();
        return c
    }, normalizeUVs: function (a) {
        for (var a = a.faceVertexUvs[0], b = 0, c = a.length; b < c; b++) for (var d = a[b], e = 0, f = d.length; e < f; e++) 1 !== d[e].x && (d[e].x -= Math.floor(d[e].x)), 1 !== d[e].y && (d[e].y -= Math.floor(d[e].y))
    }, triangulateQuads: function (a) {
        var b, c, d, e,
            f = [], g = [], h = [];
        b = 0;
        for (c = a.faceUvs.length; b < c; b++) g[b] = [];
        b = 0;
        for (c = a.faceVertexUvs.length; b < c; b++) h[b] = [];
        b = 0;
        for (c = a.faces.length; b < c; b++) if (d = a.faces[b], d instanceof THREE.Face4) {
            e = d.a;
            var i = d.b, k = d.c, l = d.d, m = new THREE.Face3, p = new THREE.Face3;
            m.color.copy(d.color);
            p.color.copy(d.color);
            m.materialIndex = d.materialIndex;
            p.materialIndex = d.materialIndex;
            m.a = e;
            m.b = i;
            m.c = l;
            p.a = i;
            p.b = k;
            p.c = l;
            4 === d.vertexColors.length && (m.vertexColors[0] = d.vertexColors[0].clone(), m.vertexColors[1] = d.vertexColors[1].clone(),
                m.vertexColors[2] = d.vertexColors[3].clone(), p.vertexColors[0] = d.vertexColors[1].clone(), p.vertexColors[1] = d.vertexColors[2].clone(), p.vertexColors[2] = d.vertexColors[3].clone());
            f.push(m, p);
            d = 0;
            for (e = a.faceVertexUvs.length; d < e; d++) a.faceVertexUvs[d].length && (m = a.faceVertexUvs[d][b], i = m[1], k = m[2], l = m[3], m = [m[0].clone(), i.clone(), l.clone()], i = [i.clone(), k.clone(), l.clone()], h[d].push(m, i));
            d = 0;
            for (e = a.faceUvs.length; d < e; d++) a.faceUvs[d].length && (i = a.faceUvs[d][b], g[d].push(i, i))
        } else {
            f.push(d);
            d = 0;
            for (e =
                     a.faceUvs.length; d < e; d++) g[d].push(a.faceUvs[d][b]);
            d = 0;
            for (e = a.faceVertexUvs.length; d < e; d++) h[d].push(a.faceVertexUvs[d][b])
        }
        a.faces = f;
        a.faceUvs = g;
        a.faceVertexUvs = h;
        a.computeCentroids();
        a.computeFaceNormals();
        a.computeVertexNormals();
        a.hasTangents && a.computeTangents()
    }, setMaterialIndex: function (a, b, c, d) {
        a = a.faces;
        d = d || a.length - 1;
        for (c = c || 0; c <= d; c++) a[c].materialIndex = b
    }
};
THREE.GeometryUtils.random = THREE.Math.random16;
THREE.GeometryUtils.__v1 = new THREE.Vector3;
THREE.GeometryUtils.__v2 = new THREE.Vector3;
THREE.ImageUtils = {
    crossOrigin: "anonymous", loadTexture: function (a, b, c, d) {
        var e = new Image, f = new THREE.Texture(e, b), b = new THREE.ImageLoader;
        b.addEventListener("load", function (a) {
            f.image = a.content;
            f.needsUpdate = !0;
            c && c(f)
        });
        b.addEventListener("error", function (a) {
            d && d(a.message)
        });
        b.crossOrigin = this.crossOrigin;
        b.load(a, e);
        f.sourceFile = a;
        return f
    }, loadCompressedTexture: function (a, b, c, d) {
        var e = new THREE.CompressedTexture;
        e.mapping = b;
        var f = new XMLHttpRequest;
        f.onload = function () {
            var a = THREE.ImageUtils.parseDDS(f.response,
                !0);
            e.format = a.format;
            e.mipmaps = a.mipmaps;
            e.image.width = a.width;
            e.image.height = a.height;
            e.generateMipmaps = !1;
            e.needsUpdate = !0;
            c && c(e)
        };
        f.onerror = d;
        f.open("GET", a, !0);
        f.responseType = "arraybuffer";
        f.send(null);
        return e
    }, loadTextureCube: function (a, b, c, d) {
        var e = [];
        e.loadCount = 0;
        var f = new THREE.Texture;
        f.image = e;
        void 0 !== b && (f.mapping = b);
        f.flipY = !1;
        for (var b = 0, g = a.length; b < g; ++b) {
            var h = new Image;
            e[b] = h;
            h.onload = function () {
                e.loadCount += 1;
                6 === e.loadCount && (f.needsUpdate = !0, c && c(f))
            };
            h.onerror = d;
            h.crossOrigin =
                this.crossOrigin;
            h.src = a[b]
        }
        return f
    }, loadCompressedTextureCube: function (a, b, c, d) {
        var e = [];
        e.loadCount = 0;
        var f = new THREE.CompressedTexture;
        f.image = e;
        void 0 !== b && (f.mapping = b);
        f.flipY = !1;
        f.generateMipmaps = !1;
        b = function (a, b) {
            return function () {
                var d = THREE.ImageUtils.parseDDS(a.response, !0);
                b.format = d.format;
                b.mipmaps = d.mipmaps;
                b.width = d.width;
                b.height = d.height;
                e.loadCount += 1;
                6 === e.loadCount && (f.format = d.format, f.needsUpdate = !0, c && c(f))
            }
        };
        if (a instanceof Array) for (var g = 0, h = a.length; g < h; ++g) {
            var i = {};
            e[g] = i;
            var k = new XMLHttpRequest;
            k.onload = b(k, i);
            k.onerror = d;
            i = a[g];
            k.open("GET", i, !0);
            k.responseType = "arraybuffer";
            k.send(null)
        } else k = new XMLHttpRequest, k.onload = function () {
            var a = THREE.ImageUtils.parseDDS(k.response, !0);
            if (a.isCubemap) {
                for (var b = a.mipmaps.length / a.mipmapCount, d = 0; d < b; d++) {
                    e[d] = {mipmaps: []};
                    for (var g = 0; g < a.mipmapCount; g++) e[d].mipmaps.push(a.mipmaps[d * a.mipmapCount + g]), e[d].format = a.format, e[d].width = a.width, e[d].height = a.height
                }
                f.format = a.format;
                f.needsUpdate = !0;
                c && c(f)
            }
        }, k.onerror =
            d, k.open("GET", a, !0), k.responseType = "arraybuffer", k.send(null);
        return f
    }, parseDDS: function (a, b) {
        function c(a) {
            return a.charCodeAt(0) + (a.charCodeAt(1) << 8) + (a.charCodeAt(2) << 16) + (a.charCodeAt(3) << 24)
        }

        var d = {mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1}, e = c("DXT1"), f = c("DXT3"),
            g = c("DXT5"), h = new Int32Array(a, 0, 31);
        if (542327876 !== h[0]) return console.error("ImageUtils.parseDDS(): Invalid magic number in DDS header"), d;
        if (!h[20] & 4) return console.error("ImageUtils.parseDDS(): Unsupported format, must contain a FourCC code"),
            d;
        var i = h[21];
        switch (i) {
            case e:
                e = 8;
                d.format = THREE.RGB_S3TC_DXT1_Format;
                break;
            case f:
                e = 16;
                d.format = THREE.RGBA_S3TC_DXT3_Format;
                break;
            case g:
                e = 16;
                d.format = THREE.RGBA_S3TC_DXT5_Format;
                break;
            default:
                return console.error("ImageUtils.parseDDS(): Unsupported FourCC code: ", String.fromCharCode(i & 255, i >> 8 & 255, i >> 16 & 255, i >> 24 & 255)), d
        }
        d.mipmapCount = 1;
        h[2] & 131072 && !1 !== b && (d.mipmapCount = Math.max(1, h[7]));
        d.isCubemap = h[28] & 512 ? !0 : !1;
        d.width = h[4];
        d.height = h[3];
        for (var h = h[1] + 4, f = d.width, g = d.height, i = d.isCubemap ?
            6 : 1, k = 0; k < i; k++) {
            for (var l = 0; l < d.mipmapCount; l++) {
                var m = Math.max(4, f) / 4 * Math.max(4, g) / 4 * e,
                    p = {data: new Uint8Array(a, h, m), width: f, height: g};
                d.mipmaps.push(p);
                h += m;
                f = Math.max(0.5 * f, 1);
                g = Math.max(0.5 * g, 1)
            }
            f = d.width;
            g = d.height
        }
        return d
    }, getNormalMap: function (a, b) {
        var c = function (a) {
            var b = Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
            return [a[0] / b, a[1] / b, a[2] / b]
        }, b = b | 1, d = a.width, e = a.height, f = document.createElement("canvas");
        f.width = d;
        f.height = e;
        var g = f.getContext("2d");
        g.drawImage(a, 0, 0);
        for (var h = g.getImageData(0,
            0, d, e).data, i = g.createImageData(d, e), k = i.data, l = 0; l < d; l++) for (var m = 0; m < e; m++) {
            var p = 0 > m - 1 ? 0 : m - 1, s = m + 1 > e - 1 ? e - 1 : m + 1, r = 0 > l - 1 ? 0 : l - 1,
                n = l + 1 > d - 1 ? d - 1 : l + 1, q = [], z = [0, 0, h[4 * (m * d + l)] / 255 * b];
            q.push([-1, 0, h[4 * (m * d + r)] / 255 * b]);
            q.push([-1, -1, h[4 * (p * d + r)] / 255 * b]);
            q.push([0, -1, h[4 * (p * d + l)] / 255 * b]);
            q.push([1, -1, h[4 * (p * d + n)] / 255 * b]);
            q.push([1, 0, h[4 * (m * d + n)] / 255 * b]);
            q.push([1, 1, h[4 * (s * d + n)] / 255 * b]);
            q.push([0, 1, h[4 * (s * d + l)] / 255 * b]);
            q.push([-1, 1, h[4 * (s * d + r)] / 255 * b]);
            p = [];
            r = q.length;
            for (s = 0; s < r; s++) {
                var n = q[s], t = q[(s + 1) %
                r], n = [n[0] - z[0], n[1] - z[1], n[2] - z[2]], t = [t[0] - z[0], t[1] - z[1], t[2] - z[2]];
                p.push(c([n[1] * t[2] - n[2] * t[1], n[2] * t[0] - n[0] * t[2], n[0] * t[1] - n[1] * t[0]]))
            }
            q = [0, 0, 0];
            for (s = 0; s < p.length; s++) q[0] += p[s][0], q[1] += p[s][1], q[2] += p[s][2];
            q[0] /= p.length;
            q[1] /= p.length;
            q[2] /= p.length;
            z = 4 * (m * d + l);
            k[z] = 255 * ((q[0] + 1) / 2) | 0;
            k[z + 1] = 255 * ((q[1] + 1) / 2) | 0;
            k[z + 2] = 255 * q[2] | 0;
            k[z + 3] = 255
        }
        g.putImageData(i, 0, 0);
        return f
    }, generateDataTexture: function (a, b, c) {
        for (var d = a * b, e = new Uint8Array(3 * d), f = Math.floor(255 * c.r), g = Math.floor(255 * c.g),
                 c = Math.floor(255 * c.b), h = 0; h < d; h++) e[3 * h] = f, e[3 * h + 1] = g, e[3 * h + 2] = c;
        a = new THREE.DataTexture(e, a, b, THREE.RGBFormat);
        a.needsUpdate = !0;
        return a
    }
};
THREE.SceneUtils = {
    createMultiMaterialObject: function (a, b) {
        for (var c = new THREE.Object3D, d = 0, e = b.length; d < e; d++) c.add(new THREE.Mesh(a, b[d]));
        return c
    }, detach: function (a, b, c) {
        a.applyMatrix(b.matrixWorld);
        b.remove(a);
        c.add(a)
    }, attach: function (a, b, c) {
        var d = new THREE.Matrix4;
        d.getInverse(c.matrixWorld);
        a.applyMatrix(d);
        b.remove(a);
        c.add(a)
    }
};
THREE.FontUtils = {
    faces: {}, face: "helvetiker", weight: "normal", style: "normal", size: 150, divisions: 10, getFace: function () {
        return this.faces[this.face][this.weight][this.style]
    }, loadFace: function (a) {
        var b = a.familyName.toLowerCase();
        this.faces[b] = this.faces[b] || {};
        this.faces[b][a.cssFontWeight] = this.faces[b][a.cssFontWeight] || {};
        this.faces[b][a.cssFontWeight][a.cssFontStyle] = a;
        return this.faces[b][a.cssFontWeight][a.cssFontStyle] = a
    }, drawText: function (a) {
        for (var b = this.getFace(), c = this.size / b.resolution, d =
            0, e = String(a).split(""), f = e.length, g = [], a = 0; a < f; a++) {
            var h = new THREE.Path, h = this.extractGlyphPoints(e[a], b, c, d, h), d = d + h.offset;
            g.push(h.path)
        }
        return {paths: g, offset: d / 2}
    }, extractGlyphPoints: function (a, b, c, d, e) {
        var f = [], g, h, i, k, l, m, p, s, r, n, q, z = b.glyphs[a] || b.glyphs["?"];
        if (z) {
            if (z.o) {
                b = z._cachedOutline || (z._cachedOutline = z.o.split(" "));
                k = b.length;
                for (a = 0; a < k;) switch (i = b[a++], i) {
                    case "m":
                        i = b[a++] * c + d;
                        l = b[a++] * c;
                        e.moveTo(i, l);
                        break;
                    case "l":
                        i = b[a++] * c + d;
                        l = b[a++] * c;
                        e.lineTo(i, l);
                        break;
                    case "q":
                        i = b[a++] *
                            c + d;
                        l = b[a++] * c;
                        s = b[a++] * c + d;
                        r = b[a++] * c;
                        e.quadraticCurveTo(s, r, i, l);
                        if (g = f[f.length - 1]) {
                            m = g.x;
                            p = g.y;
                            g = 1;
                            for (h = this.divisions; g <= h; g++) {
                                var t = g / h;
                                THREE.Shape.Utils.b2(t, m, s, i);
                                THREE.Shape.Utils.b2(t, p, r, l)
                            }
                        }
                        break;
                    case "b":
                        if (i = b[a++] * c + d, l = b[a++] * c, s = b[a++] * c + d, r = b[a++] * -c, n = b[a++] * c + d, q = b[a++] * -c, e.bezierCurveTo(i, l, s, r, n, q), g = f[f.length - 1]) {
                            m = g.x;
                            p = g.y;
                            g = 1;
                            for (h = this.divisions; g <= h; g++) t = g / h, THREE.Shape.Utils.b3(t, m, s, n, i), THREE.Shape.Utils.b3(t, p, r, q, l)
                        }
                }
            }
            return {offset: z.ha * c, path: e}
        }
    }
};
THREE.FontUtils.generateShapes = function (a, b) {
    var b = b || {}, c = void 0 !== b.curveSegments ? b.curveSegments : 4,
        d = void 0 !== b.font ? b.font : "helvetiker", e = void 0 !== b.weight ? b.weight : "normal",
        f = void 0 !== b.style ? b.style : "normal";
    THREE.FontUtils.size = void 0 !== b.size ? b.size : 100;
    THREE.FontUtils.divisions = c;
    THREE.FontUtils.face = d;
    THREE.FontUtils.weight = e;
    THREE.FontUtils.style = f;
    c = THREE.FontUtils.drawText(a).paths;
    d = [];
    e = 0;
    for (f = c.length; e < f; e++) Array.prototype.push.apply(d, c[e].toShapes());
    return d
};
(function (a) {
    var b = function (a) {
        for (var b = a.length, e = 0, f = b - 1, g = 0; g < b; f = g++) e += a[f].x * a[g].y - a[g].x * a[f].y;
        return 0.5 * e
    };
    a.Triangulate = function (a, d) {
        var e = a.length;
        if (3 > e) return null;
        var f = [], g = [], h = [], i, k, l;
        if (0 < b(a)) for (k = 0; k < e; k++) g[k] = k; else for (k = 0; k < e; k++) g[k] = e - 1 - k;
        var m = 2 * e;
        for (k = e - 1; 2 < e;) {
            if (0 >= m--) {
                console.log("Warning, unable to triangulate polygon!");
                break
            }
            i = k;
            e <= i && (i = 0);
            k = i + 1;
            e <= k && (k = 0);
            l = k + 1;
            e <= l && (l = 0);
            var p;
            a:{
                var s = p = void 0, r = void 0, n = void 0, q = void 0, z = void 0, t = void 0, x = void 0, u =
                    void 0, s = a[g[i]].x, r = a[g[i]].y, n = a[g[k]].x, q = a[g[k]].y, z = a[g[l]].x, t = a[g[l]].y;
                if (1E-10 > (n - s) * (t - r) - (q - r) * (z - s)) p = !1; else {
                    var B = void 0, G = void 0, D = void 0, w = void 0, I = void 0, J = void 0, E = void 0, Z = void 0,
                        A = void 0, S = void 0, A = Z = E = u = x = void 0, B = z - n, G = t - q, D = s - z, w = r - t,
                        I = n - s, J = q - r;
                    for (p = 0; p < e; p++) if (!(p === i || p === k || p === l)) if (x = a[g[p]].x, u = a[g[p]].y, E = x - s, Z = u - r, A = x - n, S = u - q, x -= z, u -= t, A = B * S - G * A, E = I * Z - J * E, Z = D * u - w * x, 0 <= A && 0 <= Z && 0 <= E) {
                        p = !1;
                        break a
                    }
                    p = !0
                }
            }
            if (p) {
                f.push([a[g[i]], a[g[k]], a[g[l]]]);
                h.push([g[i], g[k], g[l]]);
                i = k;
                for (l = k + 1; l < e; i++, l++) g[i] = g[l];
                e--;
                m = 2 * e
            }
        }
        return d ? h : f
    };
    a.Triangulate.area = b;
    return a
})(THREE.FontUtils);
self._typeface_js = {faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace};
THREE.Curve = function () {
};
THREE.Curve.prototype.getPoint = function () {
    console.log("Warning, getPoint() not implemented!");
    return null
};
THREE.Curve.prototype.getPointAt = function (a) {
    a = this.getUtoTmapping(a);
    return this.getPoint(a)
};
THREE.Curve.prototype.getPoints = function (a) {
    a || (a = 5);
    var b, c = [];
    for (b = 0; b <= a; b++) c.push(this.getPoint(b / a));
    return c
};
THREE.Curve.prototype.getSpacedPoints = function (a) {
    a || (a = 5);
    var b, c = [];
    for (b = 0; b <= a; b++) c.push(this.getPointAt(b / a));
    return c
};
THREE.Curve.prototype.getLength = function () {
    var a = this.getLengths();
    return a[a.length - 1]
};
THREE.Curve.prototype.getLengths = function (a) {
    a || (a = this.__arcLengthDivisions ? this.__arcLengthDivisions : 200);
    if (this.cacheArcLengths && this.cacheArcLengths.length == a + 1 && !this.needsUpdate) return this.cacheArcLengths;
    this.needsUpdate = !1;
    var b = [], c, d = this.getPoint(0), e, f = 0;
    b.push(0);
    for (e = 1; e <= a; e++) c = this.getPoint(e / a), f += c.distanceTo(d), b.push(f), d = c;
    return this.cacheArcLengths = b
};
THREE.Curve.prototype.updateArcLengths = function () {
    this.needsUpdate = !0;
    this.getLengths()
};
THREE.Curve.prototype.getUtoTmapping = function (a, b) {
    var c = this.getLengths(), d = 0, e = c.length, f;
    f = b ? b : a * c[e - 1];
    for (var g = 0, h = e - 1, i; g <= h;) if (d = Math.floor(g + (h - g) / 2), i = c[d] - f, 0 > i) g = d + 1; else if (0 < i) h = d - 1; else {
        h = d;
        break
    }
    d = h;
    if (c[d] == f) return d / (e - 1);
    g = c[d];
    return c = (d + (f - g) / (c[d + 1] - g)) / (e - 1)
};
THREE.Curve.prototype.getTangent = function (a) {
    var b = a - 1E-4, a = a + 1E-4;
    0 > b && (b = 0);
    1 < a && (a = 1);
    b = this.getPoint(b);
    return this.getPoint(a).clone().sub(b).normalize()
};
THREE.Curve.prototype.getTangentAt = function (a) {
    a = this.getUtoTmapping(a);
    return this.getTangent(a)
};
THREE.LineCurve = function (a, b) {
    this.v1 = a;
    this.v2 = b
};
THREE.LineCurve.prototype = Object.create(THREE.Curve.prototype);
THREE.LineCurve.prototype.getPoint = function (a) {
    var b = this.v2.clone().sub(this.v1);
    b.multiplyScalar(a).add(this.v1);
    return b
};
THREE.LineCurve.prototype.getPointAt = function (a) {
    return this.getPoint(a)
};
THREE.LineCurve.prototype.getTangent = function () {
    return this.v2.clone().sub(this.v1).normalize()
};
THREE.QuadraticBezierCurve = function (a, b, c) {
    this.v0 = a;
    this.v1 = b;
    this.v2 = c
};
THREE.QuadraticBezierCurve.prototype = Object.create(THREE.Curve.prototype);
THREE.QuadraticBezierCurve.prototype.getPoint = function (a) {
    var b;
    b = THREE.Shape.Utils.b2(a, this.v0.x, this.v1.x, this.v2.x);
    a = THREE.Shape.Utils.b2(a, this.v0.y, this.v1.y, this.v2.y);
    return new THREE.Vector2(b, a)
};
THREE.QuadraticBezierCurve.prototype.getTangent = function (a) {
    var b;
    b = THREE.Curve.Utils.tangentQuadraticBezier(a, this.v0.x, this.v1.x, this.v2.x);
    a = THREE.Curve.Utils.tangentQuadraticBezier(a, this.v0.y, this.v1.y, this.v2.y);
    b = new THREE.Vector2(b, a);
    b.normalize();
    return b
};
THREE.CubicBezierCurve = function (a, b, c, d) {
    this.v0 = a;
    this.v1 = b;
    this.v2 = c;
    this.v3 = d
};
THREE.CubicBezierCurve.prototype = Object.create(THREE.Curve.prototype);
THREE.CubicBezierCurve.prototype.getPoint = function (a) {
    var b;
    b = THREE.Shape.Utils.b3(a, this.v0.x, this.v1.x, this.v2.x, this.v3.x);
    a = THREE.Shape.Utils.b3(a, this.v0.y, this.v1.y, this.v2.y, this.v3.y);
    return new THREE.Vector2(b, a)
};
THREE.CubicBezierCurve.prototype.getTangent = function (a) {
    var b;
    b = THREE.Curve.Utils.tangentCubicBezier(a, this.v0.x, this.v1.x, this.v2.x, this.v3.x);
    a = THREE.Curve.Utils.tangentCubicBezier(a, this.v0.y, this.v1.y, this.v2.y, this.v3.y);
    b = new THREE.Vector2(b, a);
    b.normalize();
    return b
};
THREE.SplineCurve = function (a) {
    this.points = void 0 == a ? [] : a
};
THREE.SplineCurve.prototype = Object.create(THREE.Curve.prototype);
THREE.SplineCurve.prototype.getPoint = function (a) {
    var b = new THREE.Vector2, c = [], d = this.points, e;
    e = (d.length - 1) * a;
    a = Math.floor(e);
    e -= a;
    c[0] = 0 == a ? a : a - 1;
    c[1] = a;
    c[2] = a > d.length - 2 ? d.length - 1 : a + 1;
    c[3] = a > d.length - 3 ? d.length - 1 : a + 2;
    b.x = THREE.Curve.Utils.interpolate(d[c[0]].x, d[c[1]].x, d[c[2]].x, d[c[3]].x, e);
    b.y = THREE.Curve.Utils.interpolate(d[c[0]].y, d[c[1]].y, d[c[2]].y, d[c[3]].y, e);
    return b
};
THREE.EllipseCurve = function (a, b, c, d, e, f, g) {
    this.aX = a;
    this.aY = b;
    this.xRadius = c;
    this.yRadius = d;
    this.aStartAngle = e;
    this.aEndAngle = f;
    this.aClockwise = g
};
THREE.EllipseCurve.prototype = Object.create(THREE.Curve.prototype);
THREE.EllipseCurve.prototype.getPoint = function (a) {
    var b = this.aEndAngle - this.aStartAngle;
    this.aClockwise || (a = 1 - a);
    b = this.aStartAngle + a * b;
    a = this.aX + this.xRadius * Math.cos(b);
    b = this.aY + this.yRadius * Math.sin(b);
    return new THREE.Vector2(a, b)
};
THREE.ArcCurve = function (a, b, c, d, e, f) {
    THREE.EllipseCurve.call(this, a, b, c, c, d, e, f)
};
THREE.ArcCurve.prototype = Object.create(THREE.EllipseCurve.prototype);
THREE.Curve.Utils = {
    tangentQuadraticBezier: function (a, b, c, d) {
        return 2 * (1 - a) * (c - b) + 2 * a * (d - c)
    }, tangentCubicBezier: function (a, b, c, d, e) {
        return -3 * b * (1 - a) * (1 - a) + 3 * c * (1 - a) * (1 - a) - 6 * a * c * (1 - a) + 6 * a * d * (1 - a) - 3 * a * a * d + 3 * a * a * e
    }, tangentSpline: function (a) {
        return 6 * a * a - 6 * a + (3 * a * a - 4 * a + 1) + (-6 * a * a + 6 * a) + (3 * a * a - 2 * a)
    }, interpolate: function (a, b, c, d, e) {
        var a = 0.5 * (c - a), d = 0.5 * (d - b), f = e * e;
        return (2 * b - 2 * c + a + d) * e * f + (-3 * b + 3 * c - 2 * a - d) * f + a * e + b
    }
};
THREE.Curve.create = function (a, b) {
    a.prototype = Object.create(THREE.Curve.prototype);
    a.prototype.getPoint = b;
    return a
};
THREE.LineCurve3 = THREE.Curve.create(function (a, b) {
    this.v1 = a;
    this.v2 = b
}, function (a) {
    var b = new THREE.Vector3;
    b.subVectors(this.v2, this.v1);
    b.multiplyScalar(a);
    b.add(this.v1);
    return b
});
THREE.QuadraticBezierCurve3 = THREE.Curve.create(function (a, b, c) {
    this.v0 = a;
    this.v1 = b;
    this.v2 = c
}, function (a) {
    var b, c;
    b = THREE.Shape.Utils.b2(a, this.v0.x, this.v1.x, this.v2.x);
    c = THREE.Shape.Utils.b2(a, this.v0.y, this.v1.y, this.v2.y);
    a = THREE.Shape.Utils.b2(a, this.v0.z, this.v1.z, this.v2.z);
    return new THREE.Vector3(b, c, a)
});
THREE.CubicBezierCurve3 = THREE.Curve.create(function (a, b, c, d) {
    this.v0 = a;
    this.v1 = b;
    this.v2 = c;
    this.v3 = d
}, function (a) {
    var b, c;
    b = THREE.Shape.Utils.b3(a, this.v0.x, this.v1.x, this.v2.x, this.v3.x);
    c = THREE.Shape.Utils.b3(a, this.v0.y, this.v1.y, this.v2.y, this.v3.y);
    a = THREE.Shape.Utils.b3(a, this.v0.z, this.v1.z, this.v2.z, this.v3.z);
    return new THREE.Vector3(b, c, a)
});
THREE.SplineCurve3 = THREE.Curve.create(function (a) {
    this.points = void 0 == a ? [] : a
}, function (a) {
    var b = new THREE.Vector3, c = [], d = this.points, e, a = (d.length - 1) * a;
    e = Math.floor(a);
    a -= e;
    c[0] = 0 == e ? e : e - 1;
    c[1] = e;
    c[2] = e > d.length - 2 ? d.length - 1 : e + 1;
    c[3] = e > d.length - 3 ? d.length - 1 : e + 2;
    e = d[c[0]];
    var f = d[c[1]], g = d[c[2]], c = d[c[3]];
    b.x = THREE.Curve.Utils.interpolate(e.x, f.x, g.x, c.x, a);
    b.y = THREE.Curve.Utils.interpolate(e.y, f.y, g.y, c.y, a);
    b.z = THREE.Curve.Utils.interpolate(e.z, f.z, g.z, c.z, a);
    return b
});
THREE.ClosedSplineCurve3 = THREE.Curve.create(function (a) {
    this.points = void 0 == a ? [] : a
}, function (a) {
    var b = new THREE.Vector3, c = [], d = this.points, e;
    e = (d.length - 0) * a;
    a = Math.floor(e);
    e -= a;
    a += 0 < a ? 0 : (Math.floor(Math.abs(a) / d.length) + 1) * d.length;
    c[0] = (a - 1) % d.length;
    c[1] = a % d.length;
    c[2] = (a + 1) % d.length;
    c[3] = (a + 2) % d.length;
    b.x = THREE.Curve.Utils.interpolate(d[c[0]].x, d[c[1]].x, d[c[2]].x, d[c[3]].x, e);
    b.y = THREE.Curve.Utils.interpolate(d[c[0]].y, d[c[1]].y, d[c[2]].y, d[c[3]].y, e);
    b.z = THREE.Curve.Utils.interpolate(d[c[0]].z,
        d[c[1]].z, d[c[2]].z, d[c[3]].z, e);
    return b
});
THREE.CurvePath = function () {
    this.curves = [];
    this.bends = [];
    this.autoClose = !1
};
THREE.CurvePath.prototype = Object.create(THREE.Curve.prototype);
THREE.CurvePath.prototype.add = function (a) {
    this.curves.push(a)
};
THREE.CurvePath.prototype.checkConnection = function () {
};
THREE.CurvePath.prototype.closePath = function () {
    var a = this.curves[0].getPoint(0), b = this.curves[this.curves.length - 1].getPoint(1);
    a.equals(b) || this.curves.push(new THREE.LineCurve(b, a))
};
THREE.CurvePath.prototype.getPoint = function (a) {
    for (var b = a * this.getLength(), c = this.getCurveLengths(), a = 0; a < c.length;) {
        if (c[a] >= b) return b = c[a] - b, a = this.curves[a], b = 1 - b / a.getLength(), a.getPointAt(b);
        a++
    }
    return null
};
THREE.CurvePath.prototype.getLength = function () {
    var a = this.getCurveLengths();
    return a[a.length - 1]
};
THREE.CurvePath.prototype.getCurveLengths = function () {
    if (this.cacheLengths && this.cacheLengths.length == this.curves.length) return this.cacheLengths;
    var a = [], b = 0, c, d = this.curves.length;
    for (c = 0; c < d; c++) b += this.curves[c].getLength(), a.push(b);
    return this.cacheLengths = a
};
THREE.CurvePath.prototype.getBoundingBox = function () {
    var a = this.getPoints(), b, c, d, e, f, g;
    b = c = Number.NEGATIVE_INFINITY;
    e = f = Number.POSITIVE_INFINITY;
    var h, i, k, l, m = a[0] instanceof THREE.Vector3;
    l = m ? new THREE.Vector3 : new THREE.Vector2;
    i = 0;
    for (k = a.length; i < k; i++) h = a[i], h.x > b ? b = h.x : h.x < e && (e = h.x), h.y > c ? c = h.y : h.y < f && (f = h.y), m && (h.z > d ? d = h.z : h.z < g && (g = h.z)), l.add(h);
    a = {minX: e, minY: f, maxX: b, maxY: c, centroid: l.divideScalar(k)};
    m && (a.maxZ = d, a.minZ = g);
    return a
};
THREE.CurvePath.prototype.createPointsGeometry = function (a) {
    a = this.getPoints(a, !0);
    return this.createGeometry(a)
};
THREE.CurvePath.prototype.createSpacedPointsGeometry = function (a) {
    a = this.getSpacedPoints(a, !0);
    return this.createGeometry(a)
};
THREE.CurvePath.prototype.createGeometry = function (a) {
    for (var b = new THREE.Geometry, c = 0; c < a.length; c++) b.vertices.push(new THREE.Vector3(a[c].x, a[c].y, a[c].z || 0));
    return b
};
THREE.CurvePath.prototype.addWrapPath = function (a) {
    this.bends.push(a)
};
THREE.CurvePath.prototype.getTransformedPoints = function (a, b) {
    var c = this.getPoints(a), d, e;
    b || (b = this.bends);
    d = 0;
    for (e = b.length; d < e; d++) c = this.getWrapPoints(c, b[d]);
    return c
};
THREE.CurvePath.prototype.getTransformedSpacedPoints = function (a, b) {
    var c = this.getSpacedPoints(a), d, e;
    b || (b = this.bends);
    d = 0;
    for (e = b.length; d < e; d++) c = this.getWrapPoints(c, b[d]);
    return c
};
THREE.CurvePath.prototype.getWrapPoints = function (a, b) {
    var c = this.getBoundingBox(), d, e, f, g, h, i;
    d = 0;
    for (e = a.length; d < e; d++) f = a[d], g = f.x, h = f.y, i = g / c.maxX, i = b.getUtoTmapping(i, g), g = b.getPoint(i), h = b.getNormalVector(i).multiplyScalar(h), f.x = g.x + h.x, f.y = g.y + h.y;
    return a
};
THREE.Gyroscope = function () {
    THREE.Object3D.call(this)
};
THREE.Gyroscope.prototype = Object.create(THREE.Object3D.prototype);
THREE.Gyroscope.prototype.updateMatrixWorld = function (a) {
    this.matrixAutoUpdate && this.updateMatrix();
    if (this.matrixWorldNeedsUpdate || a) this.parent ? (this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix), this.matrixWorld.decompose(this.translationWorld, this.rotationWorld, this.scaleWorld), this.matrix.decompose(this.translationObject, this.rotationObject, this.scaleObject), this.matrixWorld.compose(this.translationWorld, this.rotationObject, this.scaleWorld)) : this.matrixWorld.copy(this.matrix),
        this.matrixWorldNeedsUpdate = !1, a = !0;
    for (var b = 0, c = this.children.length; b < c; b++) this.children[b].updateMatrixWorld(a)
};
THREE.Gyroscope.prototype.translationWorld = new THREE.Vector3;
THREE.Gyroscope.prototype.translationObject = new THREE.Vector3;
THREE.Gyroscope.prototype.rotationWorld = new THREE.Quaternion;
THREE.Gyroscope.prototype.rotationObject = new THREE.Quaternion;
THREE.Gyroscope.prototype.scaleWorld = new THREE.Vector3;
THREE.Gyroscope.prototype.scaleObject = new THREE.Vector3;
THREE.Path = function (a) {
    THREE.CurvePath.call(this);
    this.actions = [];
    a && this.fromPoints(a)
};
THREE.Path.prototype = Object.create(THREE.CurvePath.prototype);
THREE.PathActions = {
    MOVE_TO: "moveTo",
    LINE_TO: "lineTo",
    QUADRATIC_CURVE_TO: "quadraticCurveTo",
    BEZIER_CURVE_TO: "bezierCurveTo",
    CSPLINE_THRU: "splineThru",
    ARC: "arc",
    ELLIPSE: "ellipse"
};
THREE.Path.prototype.fromPoints = function (a) {
    this.moveTo(a[0].x, a[0].y);
    for (var b = 1, c = a.length; b < c; b++) this.lineTo(a[b].x, a[b].y)
};
THREE.Path.prototype.moveTo = function (a, b) {
    var c = Array.prototype.slice.call(arguments);
    this.actions.push({action: THREE.PathActions.MOVE_TO, args: c})
};
THREE.Path.prototype.lineTo = function (a, b) {
    var c = Array.prototype.slice.call(arguments), d = this.actions[this.actions.length - 1].args,
        d = new THREE.LineCurve(new THREE.Vector2(d[d.length - 2], d[d.length - 1]), new THREE.Vector2(a, b));
    this.curves.push(d);
    this.actions.push({action: THREE.PathActions.LINE_TO, args: c})
};
THREE.Path.prototype.quadraticCurveTo = function (a, b, c, d) {
    var e = Array.prototype.slice.call(arguments), f = this.actions[this.actions.length - 1].args,
        f = new THREE.QuadraticBezierCurve(new THREE.Vector2(f[f.length - 2], f[f.length - 1]), new THREE.Vector2(a, b), new THREE.Vector2(c, d));
    this.curves.push(f);
    this.actions.push({action: THREE.PathActions.QUADRATIC_CURVE_TO, args: e})
};
THREE.Path.prototype.bezierCurveTo = function (a, b, c, d, e, f) {
    var g = Array.prototype.slice.call(arguments), h = this.actions[this.actions.length - 1].args,
        h = new THREE.CubicBezierCurve(new THREE.Vector2(h[h.length - 2], h[h.length - 1]), new THREE.Vector2(a, b), new THREE.Vector2(c, d), new THREE.Vector2(e, f));
    this.curves.push(h);
    this.actions.push({action: THREE.PathActions.BEZIER_CURVE_TO, args: g})
};
THREE.Path.prototype.splineThru = function (a) {
    var b = Array.prototype.slice.call(arguments), c = this.actions[this.actions.length - 1].args,
        c = [new THREE.Vector2(c[c.length - 2], c[c.length - 1])];
    Array.prototype.push.apply(c, a);
    c = new THREE.SplineCurve(c);
    this.curves.push(c);
    this.actions.push({action: THREE.PathActions.CSPLINE_THRU, args: b})
};
THREE.Path.prototype.arc = function (a, b, c, d, e, f) {
    var g = this.actions[this.actions.length - 1].args;
    this.absarc(a + g[g.length - 2], b + g[g.length - 1], c, d, e, f)
};
THREE.Path.prototype.absarc = function (a, b, c, d, e, f) {
    this.absellipse(a, b, c, c, d, e, f)
};
THREE.Path.prototype.ellipse = function (a, b, c, d, e, f, g) {
    var h = this.actions[this.actions.length - 1].args;
    this.absellipse(a + h[h.length - 2], b + h[h.length - 1], c, d, e, f, g)
};
THREE.Path.prototype.absellipse = function (a, b, c, d, e, f, g) {
    var h = Array.prototype.slice.call(arguments), i = new THREE.EllipseCurve(a, b, c, d, e, f, g);
    this.curves.push(i);
    i = i.getPoint(g ? 1 : 0);
    h.push(i.x);
    h.push(i.y);
    this.actions.push({action: THREE.PathActions.ELLIPSE, args: h})
};
THREE.Path.prototype.getSpacedPoints = function (a) {
    a || (a = 40);
    for (var b = [], c = 0; c < a; c++) b.push(this.getPoint(c / a));
    return b
};
THREE.Path.prototype.getPoints = function (a, b) {
    if (this.useSpacedPoints) return console.log("tata"), this.getSpacedPoints(a, b);
    var a = a || 12, c = [], d, e, f, g, h, i, k, l, m, p, s, r, n;
    d = 0;
    for (e = this.actions.length; d < e; d++) switch (f = this.actions[d], g = f.action, f = f.args, g) {
        case THREE.PathActions.MOVE_TO:
            c.push(new THREE.Vector2(f[0], f[1]));
            break;
        case THREE.PathActions.LINE_TO:
            c.push(new THREE.Vector2(f[0], f[1]));
            break;
        case THREE.PathActions.QUADRATIC_CURVE_TO:
            h = f[2];
            i = f[3];
            m = f[0];
            p = f[1];
            0 < c.length ? (g = c[c.length - 1], s = g.x,
                r = g.y) : (g = this.actions[d - 1].args, s = g[g.length - 2], r = g[g.length - 1]);
            for (f = 1; f <= a; f++) n = f / a, g = THREE.Shape.Utils.b2(n, s, m, h), n = THREE.Shape.Utils.b2(n, r, p, i), c.push(new THREE.Vector2(g, n));
            break;
        case THREE.PathActions.BEZIER_CURVE_TO:
            h = f[4];
            i = f[5];
            m = f[0];
            p = f[1];
            k = f[2];
            l = f[3];
            0 < c.length ? (g = c[c.length - 1], s = g.x, r = g.y) : (g = this.actions[d - 1].args, s = g[g.length - 2], r = g[g.length - 1]);
            for (f = 1; f <= a; f++) n = f / a, g = THREE.Shape.Utils.b3(n, s, m, k, h), n = THREE.Shape.Utils.b3(n, r, p, l, i), c.push(new THREE.Vector2(g, n));
            break;
        case THREE.PathActions.CSPLINE_THRU:
            g =
                this.actions[d - 1].args;
            n = [new THREE.Vector2(g[g.length - 2], g[g.length - 1])];
            g = a * f[0].length;
            n = n.concat(f[0]);
            n = new THREE.SplineCurve(n);
            for (f = 1; f <= g; f++) c.push(n.getPointAt(f / g));
            break;
        case THREE.PathActions.ARC:
            h = f[0];
            i = f[1];
            p = f[2];
            k = f[3];
            g = f[4];
            m = !!f[5];
            s = g - k;
            r = 2 * a;
            for (f = 1; f <= r; f++) n = f / r, m || (n = 1 - n), n = k + n * s, g = h + p * Math.cos(n), n = i + p * Math.sin(n), c.push(new THREE.Vector2(g, n));
            break;
        case THREE.PathActions.ELLIPSE:
            h = f[0];
            i = f[1];
            p = f[2];
            l = f[3];
            k = f[4];
            g = f[5];
            m = !!f[6];
            s = g - k;
            r = 2 * a;
            for (f = 1; f <= r; f++) n = f / r, m ||
            (n = 1 - n), n = k + n * s, g = h + p * Math.cos(n), n = i + l * Math.sin(n), c.push(new THREE.Vector2(g, n))
    }
    d = c[c.length - 1];
    1E-10 > Math.abs(d.x - c[0].x) && 1E-10 > Math.abs(d.y - c[0].y) && c.splice(c.length - 1, 1);
    b && c.push(c[0]);
    return c
};
THREE.Path.prototype.toShapes = function () {
    var a, b, c, d, e = [], f = new THREE.Path;
    a = 0;
    for (b = this.actions.length; a < b; a++) c = this.actions[a], d = c.args, c = c.action, c == THREE.PathActions.MOVE_TO && 0 != f.actions.length && (e.push(f), f = new THREE.Path), f[c].apply(f, d);
    0 != f.actions.length && e.push(f);
    if (0 == e.length) return [];
    var g;
    d = [];
    a = !THREE.Shape.Utils.isClockWise(e[0].getPoints());
    if (1 == e.length) return f = e[0], g = new THREE.Shape, g.actions = f.actions, g.curves = f.curves, d.push(g), d;
    if (a) {
        g = new THREE.Shape;
        a = 0;
        for (b = e.length; a <
        b; a++) f = e[a], THREE.Shape.Utils.isClockWise(f.getPoints()) ? (g.actions = f.actions, g.curves = f.curves, d.push(g), g = new THREE.Shape) : g.holes.push(f)
    } else {
        a = 0;
        for (b = e.length; a < b; a++) f = e[a], THREE.Shape.Utils.isClockWise(f.getPoints()) ? (g && d.push(g), g = new THREE.Shape, g.actions = f.actions, g.curves = f.curves) : g.holes.push(f);
        d.push(g)
    }
    return d
};
THREE.Shape = function () {
    THREE.Path.apply(this, arguments);
    this.holes = []
};
THREE.Shape.prototype = Object.create(THREE.Path.prototype);
THREE.Shape.prototype.extrude = function (a) {
    return new THREE.ExtrudeGeometry(this, a)
};
THREE.Shape.prototype.makeGeometry = function (a) {
    return new THREE.ShapeGeometry(this, a)
};
THREE.Shape.prototype.getPointsHoles = function (a) {
    var b, c = this.holes.length, d = [];
    for (b = 0; b < c; b++) d[b] = this.holes[b].getTransformedPoints(a, this.bends);
    return d
};
THREE.Shape.prototype.getSpacedPointsHoles = function (a) {
    var b, c = this.holes.length, d = [];
    for (b = 0; b < c; b++) d[b] = this.holes[b].getTransformedSpacedPoints(a, this.bends);
    return d
};
THREE.Shape.prototype.extractAllPoints = function (a) {
    return {shape: this.getTransformedPoints(a), holes: this.getPointsHoles(a)}
};
THREE.Shape.prototype.extractPoints = function (a) {
    return this.useSpacedPoints ? this.extractAllSpacedPoints(a) : this.extractAllPoints(a)
};
THREE.Shape.prototype.extractAllSpacedPoints = function (a) {
    return {shape: this.getTransformedSpacedPoints(a), holes: this.getSpacedPointsHoles(a)}
};
THREE.Shape.Utils = {
    removeHoles: function (a, b) {
        var c = a.concat(), d = c.concat(), e, f, g, h, i, k, l, m, p, s, r = [];
        for (i = 0; i < b.length; i++) {
            k = b[i];
            Array.prototype.push.apply(d, k);
            f = Number.POSITIVE_INFINITY;
            for (e = 0; e < k.length; e++) {
                p = k[e];
                s = [];
                for (m = 0; m < c.length; m++) l = c[m], l = p.distanceToSquared(l), s.push(l), l < f && (f = l, g = e, h = m)
            }
            e = 0 <= h - 1 ? h - 1 : c.length - 1;
            f = 0 <= g - 1 ? g - 1 : k.length - 1;
            var n = [k[g], c[h], c[e]];
            m = THREE.FontUtils.Triangulate.area(n);
            var q = [k[g], k[f], c[h]];
            p = THREE.FontUtils.Triangulate.area(q);
            s = h;
            l = g;
            h += 1;
            g += -1;
            0 >
            h && (h += c.length);
            h %= c.length;
            0 > g && (g += k.length);
            g %= k.length;
            e = 0 <= h - 1 ? h - 1 : c.length - 1;
            f = 0 <= g - 1 ? g - 1 : k.length - 1;
            n = [k[g], c[h], c[e]];
            n = THREE.FontUtils.Triangulate.area(n);
            q = [k[g], k[f], c[h]];
            q = THREE.FontUtils.Triangulate.area(q);
            m + p > n + q && (h = s, g = l, 0 > h && (h += c.length), h %= c.length, 0 > g && (g += k.length), g %= k.length, e = 0 <= h - 1 ? h - 1 : c.length - 1, f = 0 <= g - 1 ? g - 1 : k.length - 1);
            m = c.slice(0, h);
            p = c.slice(h);
            s = k.slice(g);
            l = k.slice(0, g);
            f = [k[g], k[f], c[h]];
            r.push([k[g], c[h], c[e]]);
            r.push(f);
            c = m.concat(s).concat(l).concat(p)
        }
        return {
            shape: c,
            isolatedPts: r, allpoints: d
        }
    }, triangulateShape: function (a, b) {
        var c = THREE.Shape.Utils.removeHoles(a, b), d = c.allpoints, e = c.isolatedPts,
            c = THREE.FontUtils.Triangulate(c.shape, !1), f, g, h, i, k = {};
        f = 0;
        for (g = d.length; f < g; f++) i = d[f].x + ":" + d[f].y, void 0 !== k[i] && console.log("Duplicate point", i), k[i] = f;
        f = 0;
        for (g = c.length; f < g; f++) {
            h = c[f];
            for (d = 0; 3 > d; d++) i = h[d].x + ":" + h[d].y, i = k[i], void 0 !== i && (h[d] = i)
        }
        f = 0;
        for (g = e.length; f < g; f++) {
            h = e[f];
            for (d = 0; 3 > d; d++) i = h[d].x + ":" + h[d].y, i = k[i], void 0 !== i && (h[d] = i)
        }
        return c.concat(e)
    },
    isClockWise: function (a) {
        return 0 > THREE.FontUtils.Triangulate.area(a)
    }, b2p0: function (a, b) {
        var c = 1 - a;
        return c * c * b
    }, b2p1: function (a, b) {
        return 2 * (1 - a) * a * b
    }, b2p2: function (a, b) {
        return a * a * b
    }, b2: function (a, b, c, d) {
        return this.b2p0(a, b) + this.b2p1(a, c) + this.b2p2(a, d)
    }, b3p0: function (a, b) {
        var c = 1 - a;
        return c * c * c * b
    }, b3p1: function (a, b) {
        var c = 1 - a;
        return 3 * c * c * a * b
    }, b3p2: function (a, b) {
        return 3 * (1 - a) * a * a * b
    }, b3p3: function (a, b) {
        return a * a * a * b
    }, b3: function (a, b, c, d, e) {
        return this.b3p0(a, b) + this.b3p1(a, c) + this.b3p2(a, d) +
            this.b3p3(a, e)
    }
};
THREE.AnimationHandler = function () {
    var a = [], b = {}, c = {
        update: function (b) {
            for (var c = 0; c < a.length; c++) a[c].update(b)
        }, addToUpdate: function (b) {
            -1 === a.indexOf(b) && a.push(b)
        }, removeFromUpdate: function (b) {
            b = a.indexOf(b);
            -1 !== b && a.splice(b, 1)
        }, add: function (a) {
            void 0 !== b[a.name] && console.log("THREE.AnimationHandler.add: Warning! " + a.name + " already exists in library. Overwriting.");
            b[a.name] = a;
            if (!0 !== a.initialized) {
                for (var c = 0; c < a.hierarchy.length; c++) {
                    for (var d = 0; d < a.hierarchy[c].keys.length; d++) if (0 > a.hierarchy[c].keys[d].time &&
                        (a.hierarchy[c].keys[d].time = 0), void 0 !== a.hierarchy[c].keys[d].rot && !(a.hierarchy[c].keys[d].rot instanceof THREE.Quaternion)) {
                        var h = a.hierarchy[c].keys[d].rot;
                        a.hierarchy[c].keys[d].rot = new THREE.Quaternion(h[0], h[1], h[2], h[3])
                    }
                    if (a.hierarchy[c].keys.length && void 0 !== a.hierarchy[c].keys[0].morphTargets) {
                        h = {};
                        for (d = 0; d < a.hierarchy[c].keys.length; d++) for (var i = 0; i < a.hierarchy[c].keys[d].morphTargets.length; i++) {
                            var k = a.hierarchy[c].keys[d].morphTargets[i];
                            h[k] = -1
                        }
                        a.hierarchy[c].usedMorphTargets = h;
                        for (d = 0; d < a.hierarchy[c].keys.length; d++) {
                            var l = {};
                            for (k in h) {
                                for (i = 0; i < a.hierarchy[c].keys[d].morphTargets.length; i++) if (a.hierarchy[c].keys[d].morphTargets[i] === k) {
                                    l[k] = a.hierarchy[c].keys[d].morphTargetsInfluences[i];
                                    break
                                }
                                i === a.hierarchy[c].keys[d].morphTargets.length && (l[k] = 0)
                            }
                            a.hierarchy[c].keys[d].morphTargetsInfluences = l
                        }
                    }
                    for (d = 1; d < a.hierarchy[c].keys.length; d++) a.hierarchy[c].keys[d].time === a.hierarchy[c].keys[d - 1].time && (a.hierarchy[c].keys.splice(d, 1), d--);
                    for (d = 0; d < a.hierarchy[c].keys.length; d++) a.hierarchy[c].keys[d].index =
                        d
                }
                d = parseInt(a.length * a.fps, 10);
                a.JIT = {};
                a.JIT.hierarchy = [];
                for (c = 0; c < a.hierarchy.length; c++) a.JIT.hierarchy.push(Array(d));
                a.initialized = !0
            }
        }, get: function (a) {
            if ("string" === typeof a) {
                if (b[a]) return b[a];
                console.log("THREE.AnimationHandler.get: Couldn't find animation " + a);
                return null
            }
        }, parse: function (a) {
            var b = [];
            if (a instanceof THREE.SkinnedMesh) for (var c = 0; c < a.bones.length; c++) b.push(a.bones[c]); else d(a, b);
            return b
        }
    }, d = function (a, b) {
        b.push(a);
        for (var c = 0; c < a.children.length; c++) d(a.children[c],
            b)
    };
    c.LINEAR = 0;
    c.CATMULLROM = 1;
    c.CATMULLROM_FORWARD = 2;
    return c
}();
THREE.Animation = function (a, b, c) {
    this.root = a;
    this.data = THREE.AnimationHandler.get(b);
    this.hierarchy = THREE.AnimationHandler.parse(a);
    this.currentTime = 0;
    this.timeScale = 1;
    this.isPlaying = !1;
    this.loop = this.isPaused = !0;
    this.interpolationType = void 0 !== c ? c : THREE.AnimationHandler.LINEAR;
    this.points = [];
    this.target = new THREE.Vector3
};
THREE.Animation.prototype.play = function (a, b) {
    if (!1 === this.isPlaying) {
        this.isPlaying = !0;
        this.loop = void 0 !== a ? a : !0;
        this.currentTime = void 0 !== b ? b : 0;
        var c, d = this.hierarchy.length, e;
        for (c = 0; c < d; c++) {
            e = this.hierarchy[c];
            this.interpolationType !== THREE.AnimationHandler.CATMULLROM_FORWARD && (e.useQuaternion = !0);
            e.matrixAutoUpdate = !0;
            void 0 === e.animationCache && (e.animationCache = {}, e.animationCache.prevKey = {
                pos: 0,
                rot: 0,
                scl: 0
            }, e.animationCache.nextKey = {pos: 0, rot: 0, scl: 0}, e.animationCache.originalMatrix = e instanceof
            THREE.Bone ? e.skinMatrix : e.matrix);
            var f = e.animationCache.prevKey;
            e = e.animationCache.nextKey;
            f.pos = this.data.hierarchy[c].keys[0];
            f.rot = this.data.hierarchy[c].keys[0];
            f.scl = this.data.hierarchy[c].keys[0];
            e.pos = this.getNextKeyWith("pos", c, 1);
            e.rot = this.getNextKeyWith("rot", c, 1);
            e.scl = this.getNextKeyWith("scl", c, 1)
        }
        this.update(0)
    }
    this.isPaused = !1;
    THREE.AnimationHandler.addToUpdate(this)
};
THREE.Animation.prototype.pause = function () {
    !0 === this.isPaused ? THREE.AnimationHandler.addToUpdate(this) : THREE.AnimationHandler.removeFromUpdate(this);
    this.isPaused = !this.isPaused
};
THREE.Animation.prototype.stop = function () {
    this.isPaused = this.isPlaying = !1;
    THREE.AnimationHandler.removeFromUpdate(this)
};
THREE.Animation.prototype.update = function (a) {
    if (!1 !== this.isPlaying) {
        var b = ["pos", "rot", "scl"], c, d, e, f, g, h, i, k, l;
        l = this.currentTime += a * this.timeScale;
        k = this.currentTime %= this.data.length;
        parseInt(Math.min(k * this.data.fps, this.data.length * this.data.fps), 10);
        for (var m = 0, p = this.hierarchy.length; m < p; m++) {
            a = this.hierarchy[m];
            i = a.animationCache;
            for (var s = 0; 3 > s; s++) {
                c = b[s];
                g = i.prevKey[c];
                h = i.nextKey[c];
                if (h.time <= l) {
                    if (k < l) if (this.loop) {
                        g = this.data.hierarchy[m].keys[0];
                        for (h = this.getNextKeyWith(c, m, 1); h.time <
                        k;) g = h, h = this.getNextKeyWith(c, m, h.index + 1)
                    } else {
                        this.stop();
                        return
                    } else {
                        do g = h, h = this.getNextKeyWith(c, m, h.index + 1); while (h.time < k)
                    }
                    i.prevKey[c] = g;
                    i.nextKey[c] = h
                }
                a.matrixAutoUpdate = !0;
                a.matrixWorldNeedsUpdate = !0;
                d = (k - g.time) / (h.time - g.time);
                e = g[c];
                f = h[c];
                if (0 > d || 1 < d) console.log("THREE.Animation.update: Warning! Scale out of bounds:" + d + " on bone " + m), d = 0 > d ? 0 : 1;
                if ("pos" === c) if (c = a.position, this.interpolationType === THREE.AnimationHandler.LINEAR) c.x = e[0] + (f[0] - e[0]) * d, c.y = e[1] + (f[1] - e[1]) * d, c.z = e[2] +
                    (f[2] - e[2]) * d; else {
                    if (this.interpolationType === THREE.AnimationHandler.CATMULLROM || this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD) this.points[0] = this.getPrevKeyWith("pos", m, g.index - 1).pos, this.points[1] = e, this.points[2] = f, this.points[3] = this.getNextKeyWith("pos", m, h.index + 1).pos, d = 0.33 * d + 0.33, e = this.interpolateCatmullRom(this.points, d), c.x = e[0], c.y = e[1], c.z = e[2], this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD && (d = this.interpolateCatmullRom(this.points, 1.01 * d),
                        this.target.set(d[0], d[1], d[2]), this.target.sub(c), this.target.y = 0, this.target.normalize(), d = Math.atan2(this.target.x, this.target.z), a.rotation.set(0, d, 0))
                } else "rot" === c ? THREE.Quaternion.slerp(e, f, a.quaternion, d) : "scl" === c && (c = a.scale, c.x = e[0] + (f[0] - e[0]) * d, c.y = e[1] + (f[1] - e[1]) * d, c.z = e[2] + (f[2] - e[2]) * d)
            }
        }
    }
};
THREE.Animation.prototype.interpolateCatmullRom = function (a, b) {
    var c = [], d = [], e, f, g, h, i, k;
    e = (a.length - 1) * b;
    f = Math.floor(e);
    e -= f;
    c[0] = 0 === f ? f : f - 1;
    c[1] = f;
    c[2] = f > a.length - 2 ? f : f + 1;
    c[3] = f > a.length - 3 ? f : f + 2;
    f = a[c[0]];
    h = a[c[1]];
    i = a[c[2]];
    k = a[c[3]];
    c = e * e;
    g = e * c;
    d[0] = this.interpolate(f[0], h[0], i[0], k[0], e, c, g);
    d[1] = this.interpolate(f[1], h[1], i[1], k[1], e, c, g);
    d[2] = this.interpolate(f[2], h[2], i[2], k[2], e, c, g);
    return d
};
THREE.Animation.prototype.interpolate = function (a, b, c, d, e, f, g) {
    a = 0.5 * (c - a);
    d = 0.5 * (d - b);
    return (2 * (b - c) + a + d) * g + (-3 * (b - c) - 2 * a - d) * f + a * e + b
};
THREE.Animation.prototype.getNextKeyWith = function (a, b, c) {
    for (var d = this.data.hierarchy[b].keys, c = this.interpolationType === THREE.AnimationHandler.CATMULLROM || this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ? c < d.length - 1 ? c : d.length - 1 : c % d.length; c < d.length; c++) if (void 0 !== d[c][a]) return d[c];
    return this.data.hierarchy[b].keys[0]
};
THREE.Animation.prototype.getPrevKeyWith = function (a, b, c) {
    for (var d = this.data.hierarchy[b].keys, c = this.interpolationType === THREE.AnimationHandler.CATMULLROM || this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ? 0 < c ? c : 0 : 0 <= c ? c : c + d.length; 0 <= c; c--) if (void 0 !== d[c][a]) return d[c];
    return this.data.hierarchy[b].keys[d.length - 1]
};
THREE.KeyFrameAnimation = function (a, b, c) {
    this.root = a;
    this.data = THREE.AnimationHandler.get(b);
    this.hierarchy = THREE.AnimationHandler.parse(a);
    this.currentTime = 0;
    this.timeScale = 0.001;
    this.isPlaying = !1;
    this.loop = this.isPaused = !0;
    this.JITCompile = void 0 !== c ? c : !0;
    a = 0;
    for (b = this.hierarchy.length; a < b; a++) {
        var c = this.data.hierarchy[a].sids, d = this.hierarchy[a];
        if (this.data.hierarchy[a].keys.length && c) {
            for (var e = 0; e < c.length; e++) {
                var f = c[e], g = this.getNextKeyWith(f, a, 0);
                g && g.apply(f)
            }
            d.matrixAutoUpdate = !1;
            this.data.hierarchy[a].node.updateMatrix();
            d.matrixWorldNeedsUpdate = !0
        }
    }
};
THREE.KeyFrameAnimation.prototype.play = function (a, b) {
    if (!this.isPlaying) {
        this.isPlaying = !0;
        this.loop = void 0 !== a ? a : !0;
        this.currentTime = void 0 !== b ? b : 0;
        this.startTimeMs = b;
        this.startTime = 1E7;
        this.endTime = -this.startTime;
        var c, d = this.hierarchy.length, e, f;
        for (c = 0; c < d; c++) e = this.hierarchy[c], f = this.data.hierarchy[c], e.useQuaternion = !0, void 0 === f.animationCache && (f.animationCache = {}, f.animationCache.prevKey = null, f.animationCache.nextKey = null, f.animationCache.originalMatrix = e instanceof THREE.Bone ? e.skinMatrix :
            e.matrix), e = this.data.hierarchy[c].keys, e.length && (f.animationCache.prevKey = e[0], f.animationCache.nextKey = e[1], this.startTime = Math.min(e[0].time, this.startTime), this.endTime = Math.max(e[e.length - 1].time, this.endTime));
        this.update(0)
    }
    this.isPaused = !1;
    THREE.AnimationHandler.addToUpdate(this)
};
THREE.KeyFrameAnimation.prototype.pause = function () {
    this.isPaused ? THREE.AnimationHandler.addToUpdate(this) : THREE.AnimationHandler.removeFromUpdate(this);
    this.isPaused = !this.isPaused
};
THREE.KeyFrameAnimation.prototype.stop = function () {
    this.isPaused = this.isPlaying = !1;
    THREE.AnimationHandler.removeFromUpdate(this);
    for (var a = 0; a < this.data.hierarchy.length; a++) {
        var b = this.hierarchy[a], c = this.data.hierarchy[a];
        if (void 0 !== c.animationCache) {
            var d = c.animationCache.originalMatrix;
            b instanceof THREE.Bone ? (d.copy(b.skinMatrix), b.skinMatrix = d) : (d.copy(b.matrix), b.matrix = d);
            delete c.animationCache
        }
    }
};
THREE.KeyFrameAnimation.prototype.update = function (a) {
    if (this.isPlaying) {
        var b, c, d, e, f = this.data.JIT.hierarchy, g, h, i;
        h = this.currentTime += a * this.timeScale;
        g = this.currentTime %= this.data.length;
        g < this.startTimeMs && (g = this.currentTime = this.startTimeMs + g);
        e = parseInt(Math.min(g * this.data.fps, this.data.length * this.data.fps), 10);
        if ((i = g < h) && !this.loop) {
            for (var a = 0, k = this.hierarchy.length; a < k; a++) {
                var l = this.data.hierarchy[a].keys, f = this.data.hierarchy[a].sids;
                d = l.length - 1;
                e = this.hierarchy[a];
                if (l.length) {
                    for (l =
                             0; l < f.length; l++) g = f[l], (h = this.getPrevKeyWith(g, a, d)) && h.apply(g);
                    this.data.hierarchy[a].node.updateMatrix();
                    e.matrixWorldNeedsUpdate = !0
                }
            }
            this.stop()
        } else if (!(g < this.startTime)) {
            a = 0;
            for (k = this.hierarchy.length; a < k; a++) {
                d = this.hierarchy[a];
                b = this.data.hierarchy[a];
                var l = b.keys, m = b.animationCache;
                if (this.JITCompile && void 0 !== f[a][e]) d instanceof THREE.Bone ? (d.skinMatrix = f[a][e], d.matrixWorldNeedsUpdate = !1) : (d.matrix = f[a][e], d.matrixWorldNeedsUpdate = !0); else if (l.length) {
                    this.JITCompile && m && (d instanceof
                    THREE.Bone ? d.skinMatrix = m.originalMatrix : d.matrix = m.originalMatrix);
                    b = m.prevKey;
                    c = m.nextKey;
                    if (b && c) {
                        if (c.time <= h) {
                            if (i && this.loop) {
                                b = l[0];
                                for (c = l[1]; c.time < g;) b = c, c = l[b.index + 1]
                            } else if (!i) for (var p = l.length - 1; c.time < g && c.index !== p;) b = c, c = l[b.index + 1];
                            m.prevKey = b;
                            m.nextKey = c
                        }
                        c.time >= g ? b.interpolate(c, g) : b.interpolate(c, c.time)
                    }
                    this.data.hierarchy[a].node.updateMatrix();
                    d.matrixWorldNeedsUpdate = !0
                }
            }
            if (this.JITCompile && void 0 === f[0][e]) {
                this.hierarchy[0].updateMatrixWorld(!0);
                for (a = 0; a < this.hierarchy.length; a++) f[a][e] =
                    this.hierarchy[a] instanceof THREE.Bone ? this.hierarchy[a].skinMatrix.clone() : this.hierarchy[a].matrix.clone()
            }
        }
    }
};
THREE.KeyFrameAnimation.prototype.getNextKeyWith = function (a, b, c) {
    b = this.data.hierarchy[b].keys;
    for (c %= b.length; c < b.length; c++) if (b[c].hasTarget(a)) return b[c];
    return b[0]
};
THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function (a, b, c) {
    b = this.data.hierarchy[b].keys;
    for (c = 0 <= c ? c : c + b.length; 0 <= c; c--) if (b[c].hasTarget(a)) return b[c];
    return b[b.length - 1]
};
THREE.CubeCamera = function (a, b, c) {
    THREE.Object3D.call(this);
    var d = new THREE.PerspectiveCamera(90, 1, a, b);
    d.up.set(0, -1, 0);
    d.lookAt(new THREE.Vector3(1, 0, 0));
    this.add(d);
    var e = new THREE.PerspectiveCamera(90, 1, a, b);
    e.up.set(0, -1, 0);
    e.lookAt(new THREE.Vector3(-1, 0, 0));
    this.add(e);
    var f = new THREE.PerspectiveCamera(90, 1, a, b);
    f.up.set(0, 0, 1);
    f.lookAt(new THREE.Vector3(0, 1, 0));
    this.add(f);
    var g = new THREE.PerspectiveCamera(90, 1, a, b);
    g.up.set(0, 0, -1);
    g.lookAt(new THREE.Vector3(0, -1, 0));
    this.add(g);
    var h = new THREE.PerspectiveCamera(90,
        1, a, b);
    h.up.set(0, -1, 0);
    h.lookAt(new THREE.Vector3(0, 0, 1));
    this.add(h);
    var i = new THREE.PerspectiveCamera(90, 1, a, b);
    i.up.set(0, -1, 0);
    i.lookAt(new THREE.Vector3(0, 0, -1));
    this.add(i);
    this.renderTarget = new THREE.WebGLRenderTargetCube(c, c, {
        format: THREE.RGBFormat,
        magFilter: THREE.LinearFilter,
        minFilter: THREE.LinearFilter
    });
    this.updateCubeMap = function (a, b) {
        var c = this.renderTarget, p = c.generateMipmaps;
        c.generateMipmaps = !1;
        c.activeCubeFace = 0;
        a.render(b, d, c);
        c.activeCubeFace = 1;
        a.render(b, e, c);
        c.activeCubeFace =
            2;
        a.render(b, f, c);
        c.activeCubeFace = 3;
        a.render(b, g, c);
        c.activeCubeFace = 4;
        a.render(b, h, c);
        c.generateMipmaps = p;
        c.activeCubeFace = 5;
        a.render(b, i, c)
    }
};
THREE.CubeCamera.prototype = Object.create(THREE.Object3D.prototype);
THREE.CombinedCamera = function (a, b, c, d, e, f, g) {
    THREE.Camera.call(this);
    this.fov = c;
    this.left = -a / 2;
    this.right = a / 2;
    this.top = b / 2;
    this.bottom = -b / 2;
    this.cameraO = new THREE.OrthographicCamera(a / -2, a / 2, b / 2, b / -2, f, g);
    this.cameraP = new THREE.PerspectiveCamera(c, a / b, d, e);
    this.zoom = 1;
    this.toPerspective()
};
THREE.CombinedCamera.prototype = Object.create(THREE.Camera.prototype);
THREE.CombinedCamera.prototype.toPerspective = function () {
    this.near = this.cameraP.near;
    this.far = this.cameraP.far;
    this.cameraP.fov = this.fov / this.zoom;
    this.cameraP.updateProjectionMatrix();
    this.projectionMatrix = this.cameraP.projectionMatrix;
    this.inPerspectiveMode = !0;
    this.inOrthographicMode = !1
};
THREE.CombinedCamera.prototype.toOrthographic = function () {
    var a = this.cameraP.aspect, b = (this.cameraP.near + this.cameraP.far) / 2, b = Math.tan(this.fov / 2) * b,
        a = 2 * b * a / 2, b = b / this.zoom, a = a / this.zoom;
    this.cameraO.left = -a;
    this.cameraO.right = a;
    this.cameraO.top = b;
    this.cameraO.bottom = -b;
    this.cameraO.updateProjectionMatrix();
    this.near = this.cameraO.near;
    this.far = this.cameraO.far;
    this.projectionMatrix = this.cameraO.projectionMatrix;
    this.inPerspectiveMode = !1;
    this.inOrthographicMode = !0
};
THREE.CombinedCamera.prototype.setSize = function (a, b) {
    this.cameraP.aspect = a / b;
    this.left = -a / 2;
    this.right = a / 2;
    this.top = b / 2;
    this.bottom = -b / 2
};
THREE.CombinedCamera.prototype.setFov = function (a) {
    this.fov = a;
    this.inPerspectiveMode ? this.toPerspective() : this.toOrthographic()
};
THREE.CombinedCamera.prototype.updateProjectionMatrix = function () {
    this.inPerspectiveMode ? this.toPerspective() : (this.toPerspective(), this.toOrthographic())
};
THREE.CombinedCamera.prototype.setLens = function (a, b) {
    void 0 === b && (b = 24);
    var c = 2 * THREE.Math.radToDeg(Math.atan(b / (2 * a)));
    this.setFov(c);
    return c
};
THREE.CombinedCamera.prototype.setZoom = function (a) {
    this.zoom = a;
    this.inPerspectiveMode ? this.toPerspective() : this.toOrthographic()
};
THREE.CombinedCamera.prototype.toFrontView = function () {
    this.rotation.x = 0;
    this.rotation.y = 0;
    this.rotation.z = 0;
    this.rotationAutoUpdate = !1
};
THREE.CombinedCamera.prototype.toBackView = function () {
    this.rotation.x = 0;
    this.rotation.y = Math.PI;
    this.rotation.z = 0;
    this.rotationAutoUpdate = !1
};
THREE.CombinedCamera.prototype.toLeftView = function () {
    this.rotation.x = 0;
    this.rotation.y = -Math.PI / 2;
    this.rotation.z = 0;
    this.rotationAutoUpdate = !1
};
THREE.CombinedCamera.prototype.toRightView = function () {
    this.rotation.x = 0;
    this.rotation.y = Math.PI / 2;
    this.rotation.z = 0;
    this.rotationAutoUpdate = !1
};
THREE.CombinedCamera.prototype.toTopView = function () {
    this.rotation.x = -Math.PI / 2;
    this.rotation.y = 0;
    this.rotation.z = 0;
    this.rotationAutoUpdate = !1
};
THREE.CombinedCamera.prototype.toBottomView = function () {
    this.rotation.x = Math.PI / 2;
    this.rotation.y = 0;
    this.rotation.z = 0;
    this.rotationAutoUpdate = !1
};
THREE.CircleGeometry = function (a, b, c, d) {
    THREE.Geometry.call(this);
    var a = a || 50, c = void 0 !== c ? c : 0, d = void 0 !== d ? d : 2 * Math.PI,
        b = void 0 !== b ? Math.max(3, b) : 8, e, f = [];
    e = new THREE.Vector3;
    var g = new THREE.Vector2(0.5, 0.5);
    this.vertices.push(e);
    f.push(g);
    for (e = 0; e <= b; e++) {
        var h = new THREE.Vector3, i = c + e / b * d;
        h.x = a * Math.cos(i);
        h.y = a * Math.sin(i);
        this.vertices.push(h);
        f.push(new THREE.Vector2((h.x / a + 1) / 2, -(h.y / a + 1) / 2 + 1))
    }
    c = new THREE.Vector3(0, 0, -1);
    for (e = 1; e <= b; e++) this.faces.push(new THREE.Face3(e, e + 1, 0, [c, c, c])), this.faceVertexUvs[0].push([f[e],
        f[e + 1], g]);
    this.computeCentroids();
    this.computeFaceNormals();
    this.boundingSphere = new THREE.Sphere(new THREE.Vector3, a)
};
THREE.CircleGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.CubeGeometry = function (a, b, c, d, e, f) {
    function g(a, b, c, d, e, f, g, n) {
        var q, z = h.widthSegments, t = h.heightSegments, x = e / 2, u = f / 2, B = h.vertices.length;
        if ("x" === a && "y" === b || "y" === a && "x" === b) q = "z"; else if ("x" === a && "z" === b || "z" === a && "x" === b) q = "y", t = h.depthSegments; else if ("z" === a && "y" === b || "y" === a && "z" === b) q = "x", z = h.depthSegments;
        var G = z + 1, D = t + 1, w = e / z, I = f / t, J = new THREE.Vector3;
        J[q] = 0 < g ? 1 : -1;
        for (e = 0; e < D; e++) for (f = 0; f < G; f++) {
            var E = new THREE.Vector3;
            E[a] = (f * w - x) * c;
            E[b] = (e * I - u) * d;
            E[q] = g;
            h.vertices.push(E)
        }
        for (e =
                 0; e < t; e++) for (f = 0; f < z; f++) a = new THREE.Face4(f + G * e + B, f + G * (e + 1) + B, f + 1 + G * (e + 1) + B, f + 1 + G * e + B), a.normal.copy(J), a.vertexNormals.push(J.clone(), J.clone(), J.clone(), J.clone()), a.materialIndex = n, h.faces.push(a), h.faceVertexUvs[0].push([new THREE.Vector2(f / z, 1 - e / t), new THREE.Vector2(f / z, 1 - (e + 1) / t), new THREE.Vector2((f + 1) / z, 1 - (e + 1) / t), new THREE.Vector2((f + 1) / z, 1 - e / t)])
    }

    THREE.Geometry.call(this);
    var h = this;
    this.width = a;
    this.height = b;
    this.depth = c;
    this.widthSegments = d || 1;
    this.heightSegments = e || 1;
    this.depthSegments =
        f || 1;
    a = this.width / 2;
    b = this.height / 2;
    c = this.depth / 2;
    g("z", "y", -1, -1, this.depth, this.height, a, 0);
    g("z", "y", 1, -1, this.depth, this.height, -a, 1);
    g("x", "z", 1, 1, this.width, this.depth, b, 2);
    g("x", "z", 1, -1, this.width, this.depth, -b, 3);
    g("x", "y", 1, -1, this.width, this.height, c, 4);
    g("x", "y", -1, -1, this.width, this.height, -c, 5);
    this.computeCentroids();
    this.mergeVertices()
};
THREE.CubeGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.CylinderGeometry = function (a, b, c, d, e, f) {
    THREE.Geometry.call(this);
    var a = void 0 !== a ? a : 20, b = void 0 !== b ? b : 20, c = void 0 !== c ? c : 100, g = c / 2, d = d || 8,
        e = e || 1, h, i, k = [], l = [];
    for (i = 0; i <= e; i++) {
        var m = [], p = [], s = i / e, r = s * (b - a) + a;
        for (h = 0; h <= d; h++) {
            var n = h / d, q = new THREE.Vector3;
            q.x = r * Math.sin(2 * n * Math.PI);
            q.y = -s * c + g;
            q.z = r * Math.cos(2 * n * Math.PI);
            this.vertices.push(q);
            m.push(this.vertices.length - 1);
            p.push(new THREE.Vector2(n, 1 - s))
        }
        k.push(m);
        l.push(p)
    }
    c = (b - a) / c;
    for (h = 0; h < d; h++) {
        0 !== a ? (m = this.vertices[k[0][h]].clone(),
            p = this.vertices[k[0][h + 1]].clone()) : (m = this.vertices[k[1][h]].clone(), p = this.vertices[k[1][h + 1]].clone());
        m.setY(Math.sqrt(m.x * m.x + m.z * m.z) * c).normalize();
        p.setY(Math.sqrt(p.x * p.x + p.z * p.z) * c).normalize();
        for (i = 0; i < e; i++) {
            var s = k[i][h], r = k[i + 1][h], n = k[i + 1][h + 1], q = k[i][h + 1], z = m.clone(), t = m.clone(),
                x = p.clone(), u = p.clone(), B = l[i][h].clone(), G = l[i + 1][h].clone(), D = l[i + 1][h + 1].clone(),
                w = l[i][h + 1].clone();
            this.faces.push(new THREE.Face4(s, r, n, q, [z, t, x, u]));
            this.faceVertexUvs[0].push([B, G, D, w])
        }
    }
    if (!f && 0 <
        a) {
        this.vertices.push(new THREE.Vector3(0, g, 0));
        for (h = 0; h < d; h++) s = k[0][h], r = k[0][h + 1], n = this.vertices.length - 1, z = new THREE.Vector3(0, 1, 0), t = new THREE.Vector3(0, 1, 0), x = new THREE.Vector3(0, 1, 0), B = l[0][h].clone(), G = l[0][h + 1].clone(), D = new THREE.Vector2(G.u, 0), this.faces.push(new THREE.Face3(s, r, n, [z, t, x])), this.faceVertexUvs[0].push([B, G, D])
    }
    if (!f && 0 < b) {
        this.vertices.push(new THREE.Vector3(0, -g, 0));
        for (h = 0; h < d; h++) s = k[i][h + 1], r = k[i][h], n = this.vertices.length - 1, z = new THREE.Vector3(0, -1, 0), t = new THREE.Vector3(0,
            -1, 0), x = new THREE.Vector3(0, -1, 0), B = l[i][h + 1].clone(), G = l[i][h].clone(), D = new THREE.Vector2(G.u, 1), this.faces.push(new THREE.Face3(s, r, n, [z, t, x])), this.faceVertexUvs[0].push([B, G, D])
    }
    this.computeCentroids();
    this.computeFaceNormals()
};
THREE.CylinderGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.ExtrudeGeometry = function (a, b) {
    "undefined" !== typeof a && (THREE.Geometry.call(this), a = a instanceof Array ? a : [a], this.shapebb = a[a.length - 1].getBoundingBox(), this.addShapeList(a, b), this.computeCentroids(), this.computeFaceNormals())
};
THREE.ExtrudeGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.ExtrudeGeometry.prototype.addShapeList = function (a, b) {
    for (var c = a.length, d = 0; d < c; d++) this.addShape(a[d], b)
};
THREE.ExtrudeGeometry.prototype.addShape = function (a, b) {
    function c(a, b, c) {
        b || console.log("die");
        return b.clone().multiplyScalar(c).add(a)
    }

    function d(a, b, c) {
        var d = THREE.ExtrudeGeometry.__v1, e = THREE.ExtrudeGeometry.__v2, f = THREE.ExtrudeGeometry.__v3,
            g = THREE.ExtrudeGeometry.__v4, h = THREE.ExtrudeGeometry.__v5, i = THREE.ExtrudeGeometry.__v6;
        d.set(a.x - b.x, a.y - b.y);
        e.set(a.x - c.x, a.y - c.y);
        d = d.normalize();
        e = e.normalize();
        f.set(-d.y, d.x);
        g.set(e.y, -e.x);
        h.copy(a).add(f);
        i.copy(a).add(g);
        if (h.equals(i)) return g.clone();
        h.copy(b).add(f);
        i.copy(c).add(g);
        f = d.dot(g);
        g = i.sub(h).dot(g);
        0 === f && (console.log("Either infinite or no solutions!"), 0 === g ? console.log("Its finite solutions.") : console.log("Too bad, no solutions."));
        g /= f;
        return 0 > g ? (b = Math.atan2(b.y - a.y, b.x - a.x), a = Math.atan2(c.y - a.y, c.x - a.x), b > a && (a += 2 * Math.PI), c = (b + a) / 2, a = -Math.cos(c), c = -Math.sin(c), new THREE.Vector2(a, c)) : d.multiplyScalar(g).add(h).sub(a).clone()
    }

    function e(c, d) {
        var e, f;
        for (M = c.length; 0 <= --M;) {
            e = M;
            f = M - 1;
            0 > f && (f = c.length - 1);
            for (var g = 0, h = s + 2 * l,
                     g = 0; g < h; g++) {
                var i = fa * g, k = fa * (g + 1), m = d + e + i, i = d + f + i, p = d + f + k, k = d + e + k, n = c,
                    q = g, r = h, u = e, w = f, m = m + Z, i = i + Z, p = p + Z, k = k + Z;
                E.faces.push(new THREE.Face4(m, i, p, k, null, null, t));
                m = x.generateSideWallUV(E, a, n, b, m, i, p, k, q, r, u, w);
                E.faceVertexUvs[0].push(m)
            }
        }
    }

    function f(a, b, c) {
        E.vertices.push(new THREE.Vector3(a, b, c))
    }

    function g(c, d, e, f) {
        c += Z;
        d += Z;
        e += Z;
        E.faces.push(new THREE.Face3(c, d, e, null, null, z));
        c = f ? x.generateBottomUV(E, a, b, c, d, e) : x.generateTopUV(E, a, b, c, d, e);
        E.faceVertexUvs[0].push(c)
    }

    var h = void 0 !== b.amount ? b.amount :
        100, i = void 0 !== b.bevelThickness ? b.bevelThickness : 6, k = void 0 !== b.bevelSize ? b.bevelSize : i - 2,
        l = void 0 !== b.bevelSegments ? b.bevelSegments : 3, m = void 0 !== b.bevelEnabled ? b.bevelEnabled : !0,
        p = void 0 !== b.curveSegments ? b.curveSegments : 12, s = void 0 !== b.steps ? b.steps : 1, r = b.extrudePath,
        n, q = !1, z = b.material, t = b.extrudeMaterial,
        x = void 0 !== b.UVGenerator ? b.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator, u, B, G, D;
    r && (n = r.getSpacedPoints(s), q = !0, m = !1, u = void 0 !== b.frames ? b.frames : new THREE.TubeGeometry.FrenetFrames(r, s,
        !1), B = new THREE.Vector3, G = new THREE.Vector3, D = new THREE.Vector3);
    m || (k = i = l = 0);
    var w, I, J, E = this, Z = this.vertices.length, p = a.extractPoints(p), A = p.shape, p = p.holes;
    if (r = !THREE.Shape.Utils.isClockWise(A)) {
        A = A.reverse();
        I = 0;
        for (J = p.length; I < J; I++) w = p[I], THREE.Shape.Utils.isClockWise(w) && (p[I] = w.reverse());
        r = !1
    }
    var S = THREE.Shape.Utils.triangulateShape(A, p), r = A;
    I = 0;
    for (J = p.length; I < J; I++) w = p[I], A = A.concat(w);
    var F, H, K, N, fa = A.length, ma = S.length, eb = [], M = 0, U = r.length;
    F = U - 1;
    for (H = M + 1; M < U; M++, F++, H++) F === U && (F =
        0), H === U && (H = 0), eb[M] = d(r[M], r[F], r[H]);
    var ja = [], L, ca = eb.concat();
    I = 0;
    for (J = p.length; I < J; I++) {
        w = p[I];
        L = [];
        M = 0;
        U = w.length;
        F = U - 1;
        for (H = M + 1; M < U; M++, F++, H++) F === U && (F = 0), H === U && (H = 0), L[M] = d(w[M], w[F], w[H]);
        ja.push(L);
        ca = ca.concat(L)
    }
    for (F = 0; F < l; F++) {
        w = F / l;
        K = i * (1 - w);
        H = k * Math.sin(w * Math.PI / 2);
        M = 0;
        for (U = r.length; M < U; M++) N = c(r[M], eb[M], H), f(N.x, N.y, -K);
        I = 0;
        for (J = p.length; I < J; I++) {
            w = p[I];
            L = ja[I];
            M = 0;
            for (U = w.length; M < U; M++) N = c(w[M], L[M], H), f(N.x, N.y, -K)
        }
    }
    H = k;
    for (M = 0; M < fa; M++) N = m ? c(A[M], ca[M], H) : A[M], q ? (G.copy(u.normals[0]).multiplyScalar(N.x),
        B.copy(u.binormals[0]).multiplyScalar(N.y), D.copy(n[0]).add(G).add(B), f(D.x, D.y, D.z)) : f(N.x, N.y, 0);
    for (w = 1; w <= s; w++) for (M = 0; M < fa; M++) N = m ? c(A[M], ca[M], H) : A[M], q ? (G.copy(u.normals[w]).multiplyScalar(N.x), B.copy(u.binormals[w]).multiplyScalar(N.y), D.copy(n[w]).add(G).add(B), f(D.x, D.y, D.z)) : f(N.x, N.y, h / s * w);
    for (F = l - 1; 0 <= F; F--) {
        w = F / l;
        K = i * (1 - w);
        H = k * Math.sin(w * Math.PI / 2);
        M = 0;
        for (U = r.length; M < U; M++) N = c(r[M], eb[M], H), f(N.x, N.y, h + K);
        I = 0;
        for (J = p.length; I < J; I++) {
            w = p[I];
            L = ja[I];
            M = 0;
            for (U = w.length; M < U; M++) N = c(w[M],
                L[M], H), q ? f(N.x, N.y + n[s - 1].y, n[s - 1].x + K) : f(N.x, N.y, h + K)
        }
    }
    if (m) {
        i = 0 * fa;
        for (M = 0; M < ma; M++) h = S[M], g(h[2] + i, h[1] + i, h[0] + i, !0);
        i = fa * (s + 2 * l);
        for (M = 0; M < ma; M++) h = S[M], g(h[0] + i, h[1] + i, h[2] + i, !1)
    } else {
        for (M = 0; M < ma; M++) h = S[M], g(h[2], h[1], h[0], !0);
        for (M = 0; M < ma; M++) h = S[M], g(h[0] + fa * s, h[1] + fa * s, h[2] + fa * s, !1)
    }
    h = 0;
    e(r, h);
    h += r.length;
    I = 0;
    for (J = p.length; I < J; I++) w = p[I], e(w, h), h += w.length
};
THREE.ExtrudeGeometry.WorldUVGenerator = {
    generateTopUV: function (a, b, c, d, e, f) {
        b = a.vertices[e].x;
        e = a.vertices[e].y;
        c = a.vertices[f].x;
        f = a.vertices[f].y;
        return [new THREE.Vector2(a.vertices[d].x, a.vertices[d].y), new THREE.Vector2(b, e), new THREE.Vector2(c, f)]
    }, generateBottomUV: function (a, b, c, d, e, f) {
        return this.generateTopUV(a, b, c, d, e, f)
    }, generateSideWallUV: function (a, b, c, d, e, f, g, h) {
        var b = a.vertices[e].x, c = a.vertices[e].y, e = a.vertices[e].z, d = a.vertices[f].x, i = a.vertices[f].y,
            f = a.vertices[f].z, k = a.vertices[g].x,
            l = a.vertices[g].y, g = a.vertices[g].z, m = a.vertices[h].x, p = a.vertices[h].y, a = a.vertices[h].z;
        return 0.01 > Math.abs(c - i) ? [new THREE.Vector2(b, 1 - e), new THREE.Vector2(d, 1 - f), new THREE.Vector2(k, 1 - g), new THREE.Vector2(m, 1 - a)] : [new THREE.Vector2(c, 1 - e), new THREE.Vector2(i, 1 - f), new THREE.Vector2(l, 1 - g), new THREE.Vector2(p, 1 - a)]
    }
};
THREE.ExtrudeGeometry.__v1 = new THREE.Vector2;
THREE.ExtrudeGeometry.__v2 = new THREE.Vector2;
THREE.ExtrudeGeometry.__v3 = new THREE.Vector2;
THREE.ExtrudeGeometry.__v4 = new THREE.Vector2;
THREE.ExtrudeGeometry.__v5 = new THREE.Vector2;
THREE.ExtrudeGeometry.__v6 = new THREE.Vector2;
THREE.ShapeGeometry = function (a, b) {
    THREE.Geometry.call(this);
    !1 === a instanceof Array && (a = [a]);
    this.shapebb = a[a.length - 1].getBoundingBox();
    this.addShapeList(a, b);
    this.computeCentroids();
    this.computeFaceNormals()
};
THREE.ShapeGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.ShapeGeometry.prototype.addShapeList = function (a, b) {
    for (var c = 0, d = a.length; c < d; c++) this.addShape(a[c], b);
    return this
};
THREE.ShapeGeometry.prototype.addShape = function (a, b) {
    void 0 === b && (b = {});
    var c = b.material, d = void 0 === b.UVGenerator ? THREE.ExtrudeGeometry.WorldUVGenerator : b.UVGenerator, e, f, g,
        h = this.vertices.length;
    e = a.extractPoints(void 0 !== b.curveSegments ? b.curveSegments : 12);
    var i = e.shape, k = e.holes;
    if (!THREE.Shape.Utils.isClockWise(i)) {
        i = i.reverse();
        e = 0;
        for (f = k.length; e < f; e++) g = k[e], THREE.Shape.Utils.isClockWise(g) && (k[e] = g.reverse())
    }
    var l = THREE.Shape.Utils.triangulateShape(i, k);
    e = 0;
    for (f = k.length; e < f; e++) g = k[e],
        i = i.concat(g);
    k = i.length;
    f = l.length;
    for (e = 0; e < k; e++) g = i[e], this.vertices.push(new THREE.Vector3(g.x, g.y, 0));
    for (e = 0; e < f; e++) k = l[e], i = k[0] + h, g = k[1] + h, k = k[2] + h, this.faces.push(new THREE.Face3(i, g, k, null, null, c)), this.faceVertexUvs[0].push(d.generateBottomUV(this, a, b, i, g, k))
};
THREE.LatheGeometry = function (a, b, c, d) {
    THREE.Geometry.call(this);
    for (var b = b || 12, c = c || 0, d = d || 2 * Math.PI, e = 1 / (a.length - 1), f = 1 / b, g = 0, h = b; g <= h; g++) for (var i = c + g * f * d, k = Math.cos(i), l = Math.sin(i), i = 0, m = a.length; i < m; i++) {
        var p = a[i], s = new THREE.Vector3;
        s.x = k * p.x - l * p.y;
        s.y = l * p.x + k * p.y;
        s.z = p.z;
        this.vertices.push(s)
    }
    c = a.length;
    g = 0;
    for (h = b; g < h; g++) {
        i = 0;
        for (m = a.length - 1; i < m; i++) d = b = i + c * g, l = b + c, k = b + 1 + c, this.faces.push(new THREE.Face4(d, l, k, b + 1)), k = g * f, b = i * e, d = k + f, l = b + e, this.faceVertexUvs[0].push([new THREE.Vector2(k,
            b), new THREE.Vector2(d, b), new THREE.Vector2(d, l), new THREE.Vector2(k, l)])
    }
    this.mergeVertices();
    this.computeCentroids();
    this.computeFaceNormals();
    this.computeVertexNormals()
};
THREE.LatheGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.PlaneGeometry = function (a, b, c, d) {
    THREE.Geometry.call(this);
    this.width = a;
    this.height = b;
    this.widthSegments = c || 1;
    this.heightSegments = d || 1;
    for (var c = a / 2, e = b / 2, d = this.widthSegments, f = this.heightSegments, g = d + 1, h = f + 1, i = this.width / d, k = this.height / f, l = new THREE.Vector3(0, 0, 1), a = 0; a < h; a++) for (b = 0; b < g; b++) this.vertices.push(new THREE.Vector3(b * i - c, -(a * k - e), 0));
    for (a = 0; a < f; a++) for (b = 0; b < d; b++) c = new THREE.Face4(b + g * a, b + g * (a + 1), b + 1 + g * (a + 1), b + 1 + g * a), c.normal.copy(l), c.vertexNormals.push(l.clone(), l.clone(),
        l.clone(), l.clone()), this.faces.push(c), this.faceVertexUvs[0].push([new THREE.Vector2(b / d, 1 - a / f), new THREE.Vector2(b / d, 1 - (a + 1) / f), new THREE.Vector2((b + 1) / d, 1 - (a + 1) / f), new THREE.Vector2((b + 1) / d, 1 - a / f)]);
    this.computeCentroids()
};
THREE.PlaneGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.RingGeometry = function (a, b, c, d, e, f) {
    THREE.Geometry.call(this);
    for (var a = a || 0, b = b || 50, e = void 0 !== e ? e : 0, f = void 0 !== f ? f : 2 * Math.PI, c = void 0 !== c ? Math.max(3, c) : 8, d = void 0 !== d ? Math.max(3, d) : 8, g = [], h = a, i = (b - a) / d, a = 0; a <= d; a++) {
        for (b = 0; b <= c; b++) {
            var k = new THREE.Vector3, l = e + b / c * f;
            k.x = h * Math.cos(l);
            k.y = h * Math.sin(l);
            this.vertices.push(k);
            g.push(new THREE.Vector2((k.x / h + 1) / 2, -(k.y / h + 1) / 2 + 1))
        }
        h += i
    }
    e = new THREE.Vector3(0, 0, 1);
    for (a = 0; a < d; a++) {
        f = a * c;
        for (b = 0; b <= c; b++) {
            var l = b + f, i = l + a, k = l + c + a, m = l + c + 1 + a;
            this.faces.push(new THREE.Face3(i,
                k, m, [e, e, e]));
            this.faceVertexUvs[0].push([g[i], g[k], g[m]]);
            i = l + a;
            k = l + c + 1 + a;
            m = l + 1 + a;
            this.faces.push(new THREE.Face3(i, k, m, [e, e, e]));
            this.faceVertexUvs[0].push([g[i], g[k], g[m]])
        }
    }
    this.computeCentroids();
    this.computeFaceNormals();
    this.boundingSphere = new THREE.Sphere(new THREE.Vector3, h)
};
THREE.RingGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.SphereGeometry = function (a, b, c, d, e, f, g) {
    THREE.Geometry.call(this);
    this.radius = a || 50;
    this.widthSegments = Math.max(3, Math.floor(b) || 8);
    this.heightSegments = Math.max(2, Math.floor(c) || 6);
    for (var d = void 0 !== d ? d : 0, e = void 0 !== e ? e : 2 * Math.PI, f = void 0 !== f ? f : 0, g = void 0 !== g ? g : Math.PI, h = [], i = [], c = 0; c <= this.heightSegments; c++) {
        for (var k = [], l = [], b = 0; b <= this.widthSegments; b++) {
            var m = b / this.widthSegments, p = c / this.heightSegments, s = new THREE.Vector3;
            s.x = -this.radius * Math.cos(d + m * e) * Math.sin(f + p * g);
            s.y = this.radius *
                Math.cos(f + p * g);
            s.z = this.radius * Math.sin(d + m * e) * Math.sin(f + p * g);
            this.vertices.push(s);
            k.push(this.vertices.length - 1);
            l.push(new THREE.Vector2(m, 1 - p))
        }
        h.push(k);
        i.push(l)
    }
    for (c = 0; c < this.heightSegments; c++) for (b = 0; b < this.widthSegments; b++) {
        var d = h[c][b + 1], e = h[c][b], f = h[c + 1][b], g = h[c + 1][b + 1],
            k = this.vertices[d].clone().normalize(), l = this.vertices[e].clone().normalize(),
            m = this.vertices[f].clone().normalize(), p = this.vertices[g].clone().normalize(), s = i[c][b + 1].clone(),
            r = i[c][b].clone(), n = i[c + 1][b].clone(),
            q = i[c + 1][b + 1].clone();
        Math.abs(this.vertices[d].y) === this.radius ? (this.faces.push(new THREE.Face3(d, f, g, [k, m, p])), this.faceVertexUvs[0].push([s, n, q])) : Math.abs(this.vertices[f].y) === this.radius ? (this.faces.push(new THREE.Face3(d, e, f, [k, l, m])), this.faceVertexUvs[0].push([s, r, n])) : (this.faces.push(new THREE.Face4(d, e, f, g, [k, l, m, p])), this.faceVertexUvs[0].push([s, r, n, q]))
    }
    this.computeCentroids();
    this.computeFaceNormals();
    this.boundingSphere = new THREE.Sphere(new THREE.Vector3, a)
};
THREE.SphereGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.TextGeometry = function (a, b) {
    var c = THREE.FontUtils.generateShapes(a, b);
    b.amount = void 0 !== b.height ? b.height : 50;
    void 0 === b.bevelThickness && (b.bevelThickness = 10);
    void 0 === b.bevelSize && (b.bevelSize = 8);
    void 0 === b.bevelEnabled && (b.bevelEnabled = !1);
    THREE.ExtrudeGeometry.call(this, c, b)
};
THREE.TextGeometry.prototype = Object.create(THREE.ExtrudeGeometry.prototype);
THREE.TorusGeometry = function (a, b, c, d, e) {
    THREE.Geometry.call(this);
    this.radius = a || 100;
    this.tube = b || 40;
    this.radialSegments = c || 8;
    this.tubularSegments = d || 6;
    this.arc = e || 2 * Math.PI;
    e = new THREE.Vector3;
    a = [];
    b = [];
    for (c = 0; c <= this.radialSegments; c++) for (d = 0; d <= this.tubularSegments; d++) {
        var f = d / this.tubularSegments * this.arc, g = 2 * c / this.radialSegments * Math.PI;
        e.x = this.radius * Math.cos(f);
        e.y = this.radius * Math.sin(f);
        var h = new THREE.Vector3;
        h.x = (this.radius + this.tube * Math.cos(g)) * Math.cos(f);
        h.y = (this.radius + this.tube *
            Math.cos(g)) * Math.sin(f);
        h.z = this.tube * Math.sin(g);
        this.vertices.push(h);
        a.push(new THREE.Vector2(d / this.tubularSegments, c / this.radialSegments));
        b.push(h.clone().sub(e).normalize())
    }
    for (c = 1; c <= this.radialSegments; c++) for (d = 1; d <= this.tubularSegments; d++) {
        var e = (this.tubularSegments + 1) * c + d - 1, f = (this.tubularSegments + 1) * (c - 1) + d - 1,
            g = (this.tubularSegments + 1) * (c - 1) + d, h = (this.tubularSegments + 1) * c + d,
            i = new THREE.Face4(e, f, g, h, [b[e], b[f], b[g], b[h]]);
        i.normal.add(b[e]);
        i.normal.add(b[f]);
        i.normal.add(b[g]);
        i.normal.add(b[h]);
        i.normal.normalize();
        this.faces.push(i);
        this.faceVertexUvs[0].push([a[e].clone(), a[f].clone(), a[g].clone(), a[h].clone()])
    }
    this.computeCentroids()
};
THREE.TorusGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.TorusKnotGeometry = function (a, b, c, d, e, f, g) {
    function h(a, b, c, d, e, f) {
        var g = Math.cos(a);
        Math.cos(b);
        b = Math.sin(a);
        a *= c / d;
        c = Math.cos(a);
        g *= 0.5 * e * (2 + c);
        b = 0.5 * e * (2 + c) * b;
        e = 0.5 * f * e * Math.sin(a);
        return new THREE.Vector3(g, b, e)
    }

    THREE.Geometry.call(this);
    this.radius = a || 100;
    this.tube = b || 40;
    this.radialSegments = c || 64;
    this.tubularSegments = d || 8;
    this.p = e || 2;
    this.q = f || 3;
    this.heightScale = g || 1;
    this.grid = Array(this.radialSegments);
    c = new THREE.Vector3;
    d = new THREE.Vector3;
    e = new THREE.Vector3;
    for (a = 0; a < this.radialSegments; ++a) {
        this.grid[a] =
            Array(this.tubularSegments);
        for (b = 0; b < this.tubularSegments; ++b) {
            var i = 2 * (a / this.radialSegments) * this.p * Math.PI, g = 2 * (b / this.tubularSegments) * Math.PI,
                f = h(i, g, this.q, this.p, this.radius, this.heightScale),
                i = h(i + 0.01, g, this.q, this.p, this.radius, this.heightScale);
            c.subVectors(i, f);
            d.addVectors(i, f);
            e.crossVectors(c, d);
            d.crossVectors(e, c);
            e.normalize();
            d.normalize();
            i = -this.tube * Math.cos(g);
            g = this.tube * Math.sin(g);
            f.x += i * d.x + g * e.x;
            f.y += i * d.y + g * e.y;
            f.z += i * d.z + g * e.z;
            this.grid[a][b] = this.vertices.push(new THREE.Vector3(f.x,
                f.y, f.z)) - 1
        }
    }
    for (a = 0; a < this.radialSegments; ++a) for (b = 0; b < this.tubularSegments; ++b) {
        var e = (a + 1) % this.radialSegments, f = (b + 1) % this.tubularSegments, c = this.grid[a][b],
            d = this.grid[e][b], e = this.grid[e][f], f = this.grid[a][f],
            g = new THREE.Vector2(a / this.radialSegments, b / this.tubularSegments),
            i = new THREE.Vector2((a + 1) / this.radialSegments, b / this.tubularSegments),
            k = new THREE.Vector2((a + 1) / this.radialSegments, (b + 1) / this.tubularSegments),
            l = new THREE.Vector2(a / this.radialSegments, (b + 1) / this.tubularSegments);
        this.faces.push(new THREE.Face4(c,
            d, e, f));
        this.faceVertexUvs[0].push([g, i, k, l])
    }
    this.computeCentroids();
    this.computeFaceNormals();
    this.computeVertexNormals()
};
THREE.TorusKnotGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.TubeGeometry = function (a, b, c, d, e, f) {
    THREE.Geometry.call(this);
    this.path = a;
    this.segments = b || 64;
    this.radius = c || 1;
    this.radiusSegments = d || 8;
    this.closed = e || !1;
    f && (this.debug = new THREE.Object3D);
    this.grid = [];
    var g, h, e = this.segments + 1, i, k, l, f = new THREE.Vector3, m, p, s,
        b = new THREE.TubeGeometry.FrenetFrames(this.path, this.segments, this.closed);
    m = b.tangents;
    p = b.normals;
    s = b.binormals;
    this.tangents = m;
    this.normals = p;
    this.binormals = s;
    for (b = 0; b < e; b++) {
        this.grid[b] = [];
        d = b / (e - 1);
        l = a.getPointAt(d);
        d = m[b];
        g = p[b];
        h = s[b];
        this.debug && (this.debug.add(new THREE.ArrowHelper(d, l, c, 255)), this.debug.add(new THREE.ArrowHelper(g, l, c, 16711680)), this.debug.add(new THREE.ArrowHelper(h, l, c, 65280)));
        for (d = 0; d < this.radiusSegments; d++) i = 2 * (d / this.radiusSegments) * Math.PI, k = -this.radius * Math.cos(i), i = this.radius * Math.sin(i), f.copy(l), f.x += k * g.x + i * h.x, f.y += k * g.y + i * h.y, f.z += k * g.z + i * h.z, this.grid[b][d] = this.vertices.push(new THREE.Vector3(f.x, f.y, f.z)) - 1
    }
    for (b = 0; b < this.segments; b++) for (d = 0; d < this.radiusSegments; d++) e = this.closed ?
        (b + 1) % this.segments : b + 1, f = (d + 1) % this.radiusSegments, a = this.grid[b][d], c = this.grid[e][d], e = this.grid[e][f], f = this.grid[b][f], m = new THREE.Vector2(b / this.segments, d / this.radiusSegments), p = new THREE.Vector2((b + 1) / this.segments, d / this.radiusSegments), s = new THREE.Vector2((b + 1) / this.segments, (d + 1) / this.radiusSegments), g = new THREE.Vector2(b / this.segments, (d + 1) / this.radiusSegments), this.faces.push(new THREE.Face4(a, c, e, f)), this.faceVertexUvs[0].push([m, p, s, g]);
    this.computeCentroids();
    this.computeFaceNormals();
    this.computeVertexNormals()
};
THREE.TubeGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.TubeGeometry.FrenetFrames = function (a, b, c) {
    new THREE.Vector3;
    var d = new THREE.Vector3;
    new THREE.Vector3;
    var e = [], f = [], g = [], h = new THREE.Vector3, i = new THREE.Matrix4, b = b + 1, k, l, m;
    this.tangents = e;
    this.normals = f;
    this.binormals = g;
    for (k = 0; k < b; k++) l = k / (b - 1), e[k] = a.getTangentAt(l), e[k].normalize();
    f[0] = new THREE.Vector3;
    g[0] = new THREE.Vector3;
    a = Number.MAX_VALUE;
    k = Math.abs(e[0].x);
    l = Math.abs(e[0].y);
    m = Math.abs(e[0].z);
    k <= a && (a = k, d.set(1, 0, 0));
    l <= a && (a = l, d.set(0, 1, 0));
    m <= a && d.set(0, 0, 1);
    h.crossVectors(e[0],
        d).normalize();
    f[0].crossVectors(e[0], h);
    g[0].crossVectors(e[0], f[0]);
    for (k = 1; k < b; k++) f[k] = f[k - 1].clone(), g[k] = g[k - 1].clone(), h.crossVectors(e[k - 1], e[k]), 1E-4 < h.length() && (h.normalize(), d = Math.acos(e[k - 1].dot(e[k])), f[k].applyMatrix4(i.makeRotationAxis(h, d))), g[k].crossVectors(e[k], f[k]);
    if (c) {
        d = Math.acos(f[0].dot(f[b - 1]));
        d /= b - 1;
        0 < e[0].dot(h.crossVectors(f[0], f[b - 1])) && (d = -d);
        for (k = 1; k < b; k++) f[k].applyMatrix4(i.makeRotationAxis(e[k], d * k)), g[k].crossVectors(e[k], f[k])
    }
};
THREE.PolyhedronGeometry = function (a, b, c, d) {
    function e(a) {
        var b = a.normalize().clone();
        b.index = i.vertices.push(b) - 1;
        var c = Math.atan2(a.z, -a.x) / 2 / Math.PI + 0.5,
            a = Math.atan2(-a.y, Math.sqrt(a.x * a.x + a.z * a.z)) / Math.PI + 0.5;
        b.uv = new THREE.Vector2(c, 1 - a);
        return b
    }

    function f(a, b, c, d) {
        1 > d ? (d = new THREE.Face3(a.index, b.index, c.index, [a.clone(), b.clone(), c.clone()]), d.centroid.add(a).add(b).add(c).divideScalar(3), d.normal = d.centroid.clone().normalize(), i.faces.push(d), d = Math.atan2(d.centroid.z, -d.centroid.x), i.faceVertexUvs[0].push([h(a.uv,
            a, d), h(b.uv, b, d), h(c.uv, c, d)])) : (d -= 1, f(a, g(a, b), g(a, c), d), f(g(a, b), b, g(b, c), d), f(g(a, c), g(b, c), c, d), f(g(a, b), g(b, c), g(a, c), d))
    }

    function g(a, b) {
        m[a.index] || (m[a.index] = []);
        m[b.index] || (m[b.index] = []);
        var c = m[a.index][b.index];
        void 0 === c && (m[a.index][b.index] = m[b.index][a.index] = c = e((new THREE.Vector3).addVectors(a, b).divideScalar(2)));
        return c
    }

    function h(a, b, c) {
        0 > c && 1 === a.x && (a = new THREE.Vector2(a.x - 1, a.y));
        0 === b.x && 0 === b.z && (a = new THREE.Vector2(c / 2 / Math.PI + 0.5, a.y));
        return a
    }

    THREE.Geometry.call(this);
    for (var c = c || 1, d = d || 0, i = this, k = 0, l = a.length; k < l; k++) e(new THREE.Vector3(a[k][0], a[k][1], a[k][2]));
    for (var m = [], a = this.vertices, k = 0, l = b.length; k < l; k++) f(a[b[k][0]], a[b[k][1]], a[b[k][2]], d);
    this.mergeVertices();
    k = 0;
    for (l = this.vertices.length; k < l; k++) this.vertices[k].multiplyScalar(c);
    this.computeCentroids();
    this.boundingSphere = new THREE.Sphere(new THREE.Vector3, c)
};
THREE.PolyhedronGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.IcosahedronGeometry = function (a, b) {
    var c = (1 + Math.sqrt(5)) / 2;
    THREE.PolyhedronGeometry.call(this, [[-1, c, 0], [1, c, 0], [-1, -c, 0], [1, -c, 0], [0, -1, c], [0, 1, c], [0, -1, -c], [0, 1, -c], [c, 0, -1], [c, 0, 1], [-c, 0, -1], [-c, 0, 1]], [[0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11], [1, 5, 9], [5, 11, 4], [11, 10, 2], [10, 7, 6], [7, 1, 8], [3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9], [4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1]], a, b)
};
THREE.IcosahedronGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.OctahedronGeometry = function (a, b) {
    THREE.PolyhedronGeometry.call(this, [[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]], [[0, 2, 4], [0, 4, 3], [0, 3, 5], [0, 5, 2], [1, 2, 5], [1, 5, 3], [1, 3, 4], [1, 4, 2]], a, b)
};
THREE.OctahedronGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.TetrahedronGeometry = function (a, b) {
    THREE.PolyhedronGeometry.call(this, [[1, 1, 1], [-1, -1, 1], [-1, 1, -1], [1, -1, -1]], [[2, 1, 0], [0, 3, 2], [1, 3, 0], [2, 3, 1]], a, b)
};
THREE.TetrahedronGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.ParametricGeometry = function (a, b, c, d) {
    THREE.Geometry.call(this);
    var e = this.vertices, f = this.faces, g = this.faceVertexUvs[0], d = void 0 === d ? !1 : d, h, i, k, l, m = b + 1;
    for (h = 0; h <= c; h++) {
        l = h / c;
        for (i = 0; i <= b; i++) k = i / b, k = a(k, l), e.push(k)
    }
    var p, s, r, n;
    for (h = 0; h < c; h++) for (i = 0; i < b; i++) a = h * m + i, e = h * m + i + 1, l = (h + 1) * m + i, k = (h + 1) * m + i + 1, p = new THREE.Vector2(i / b, h / c), s = new THREE.Vector2((i + 1) / b, h / c), r = new THREE.Vector2(i / b, (h + 1) / c), n = new THREE.Vector2((i + 1) / b, (h + 1) / c), d ? (f.push(new THREE.Face3(a, e, l)), f.push(new THREE.Face3(e,
        k, l)), g.push([p, s, r]), g.push([s, n, r])) : (f.push(new THREE.Face4(a, e, k, l)), g.push([p, s, n, r]));
    this.computeCentroids();
    this.computeFaceNormals();
    this.computeVertexNormals()
};
THREE.ParametricGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.ConvexGeometry = function (a) {
    function b(a) {
        var b = a.length();
        return new THREE.Vector2(a.x / b, a.y / b)
    }

    THREE.Geometry.call(this);
    for (var c = [[0, 1, 2], [0, 2, 1]], d = 3; d < a.length; d++) {
        var e = d, f = a[e].clone(), g = f.length();
        f.x += g * 2E-6 * (Math.random() - 0.5);
        f.y += g * 2E-6 * (Math.random() - 0.5);
        f.z += g * 2E-6 * (Math.random() - 0.5);
        for (var g = [], h = 0; h < c.length;) {
            var i = c[h], k = f, l = a[i[0]], m;
            m = l;
            var p = a[i[1]], s = a[i[2]], r = new THREE.Vector3, n = new THREE.Vector3;
            r.subVectors(s, p);
            n.subVectors(m, p);
            r.cross(n);
            r.normalize();
            m = r;
            l = m.dot(l);
            if (m.dot(k) >= l) {
                for (k = 0; 3 > k; k++) {
                    l = [i[k], i[(k + 1) % 3]];
                    m = !0;
                    for (p = 0; p < g.length; p++) if (g[p][0] === l[1] && g[p][1] === l[0]) {
                        g[p] = g[g.length - 1];
                        g.pop();
                        m = !1;
                        break
                    }
                    m && g.push(l)
                }
                c[h] = c[c.length - 1];
                c.pop()
            } else h++
        }
        for (p = 0; p < g.length; p++) c.push([g[p][0], g[p][1], e])
    }
    e = 0;
    f = Array(a.length);
    for (d = 0; d < c.length; d++) {
        g = c[d];
        for (h = 0; 3 > h; h++) void 0 === f[g[h]] && (f[g[h]] = e++, this.vertices.push(a[g[h]])), g[h] = f[g[h]]
    }
    for (d = 0; d < c.length; d++) this.faces.push(new THREE.Face3(c[d][0], c[d][1], c[d][2]));
    for (d = 0; d < this.faces.length; d++) g =
        this.faces[d], this.faceVertexUvs[0].push([b(this.vertices[g.a]), b(this.vertices[g.b]), b(this.vertices[g.c])]);
    this.computeCentroids();
    this.computeFaceNormals();
    this.computeVertexNormals()
};
THREE.ConvexGeometry.prototype = Object.create(THREE.Geometry.prototype);
THREE.AxisHelper = function (a) {
    var b = new THREE.Geometry;
    b.vertices.push(new THREE.Vector3, new THREE.Vector3(a || 1, 0, 0), new THREE.Vector3, new THREE.Vector3(0, a || 1, 0), new THREE.Vector3, new THREE.Vector3(0, 0, a || 1));
    b.colors.push(new THREE.Color(16711680), new THREE.Color(16755200), new THREE.Color(65280), new THREE.Color(11206400), new THREE.Color(255), new THREE.Color(43775));
    a = new THREE.LineBasicMaterial({vertexColors: THREE.VertexColors});
    THREE.Line.call(this, b, a, THREE.LinePieces)
};
THREE.AxisHelper.prototype = Object.create(THREE.Line.prototype);
THREE.ArrowHelper = function (a, b, c, d) {
    THREE.Object3D.call(this);
    void 0 === c && (c = 20);
    void 0 === d && (d = 16776960);
    var e = new THREE.Geometry;
    e.vertices.push(new THREE.Vector3(0, 0, 0));
    e.vertices.push(new THREE.Vector3(0, 1, 0));
    this.line = new THREE.Line(e, new THREE.LineBasicMaterial({color: d}));
    this.add(this.line);
    e = new THREE.CylinderGeometry(0, 0.05, 0.25, 5, 1);
    this.cone = new THREE.Mesh(e, new THREE.MeshBasicMaterial({color: d}));
    this.cone.position.set(0, 1, 0);
    this.add(this.cone);
    b instanceof THREE.Vector3 && (this.position =
        b);
    this.setDirection(a);
    this.setLength(c)
};
THREE.ArrowHelper.prototype = Object.create(THREE.Object3D.prototype);
THREE.ArrowHelper.prototype.setDirection = function (a) {
    var b = THREE.ArrowHelper.__v1.copy(a).normalize();
    0.999 < b.y ? this.rotation.set(0, 0, 0) : -0.999 > b.y ? this.rotation.set(Math.PI, 0, 0) : (a = THREE.ArrowHelper.__v2.set(b.z, 0, -b.x).normalize(), b = Math.acos(b.y), a = THREE.ArrowHelper.__q1.setFromAxisAngle(a, b), this.rotation.setEulerFromQuaternion(a, this.eulerOrder))
};
THREE.ArrowHelper.prototype.setLength = function (a) {
    this.scale.set(a, a, a)
};
THREE.ArrowHelper.prototype.setColor = function (a) {
    this.line.material.color.setHex(a);
    this.cone.material.color.setHex(a)
};
THREE.ArrowHelper.__v1 = new THREE.Vector3;
THREE.ArrowHelper.__v2 = new THREE.Vector3;
THREE.ArrowHelper.__q1 = new THREE.Quaternion;
THREE.CameraHelper = function (a) {
    function b(a, b, d) {
        c(a, d);
        c(b, d)
    }

    function c(a, b) {
        d.geometry.vertices.push(new THREE.Vector3);
        d.geometry.colors.push(new THREE.Color(b));
        void 0 === d.pointMap[a] && (d.pointMap[a] = []);
        d.pointMap[a].push(d.geometry.vertices.length - 1)
    }

    THREE.Line.call(this);
    var d = this;
    this.geometry = new THREE.Geometry;
    this.material = new THREE.LineBasicMaterial({color: 16777215, vertexColors: THREE.FaceColors});
    this.type = THREE.LinePieces;
    this.matrixWorld = a.matrixWorld;
    this.matrixAutoUpdate = !1;
    this.pointMap =
        {};
    b("n1", "n2", 16755200);
    b("n2", "n4", 16755200);
    b("n4", "n3", 16755200);
    b("n3", "n1", 16755200);
    b("f1", "f2", 16755200);
    b("f2", "f4", 16755200);
    b("f4", "f3", 16755200);
    b("f3", "f1", 16755200);
    b("n1", "f1", 16755200);
    b("n2", "f2", 16755200);
    b("n3", "f3", 16755200);
    b("n4", "f4", 16755200);
    b("p", "n1", 16711680);
    b("p", "n2", 16711680);
    b("p", "n3", 16711680);
    b("p", "n4", 16711680);
    b("u1", "u2", 43775);
    b("u2", "u3", 43775);
    b("u3", "u1", 43775);
    b("c", "t", 16777215);
    b("p", "c", 3355443);
    b("cn1", "cn2", 3355443);
    b("cn3", "cn4", 3355443);
    b("cf1",
        "cf2", 3355443);
    b("cf3", "cf4", 3355443);
    this.camera = a;
    this.update(a)
};
THREE.CameraHelper.prototype = Object.create(THREE.Line.prototype);
THREE.CameraHelper.prototype.update = function () {
    function a(a, d, e, f) {
        THREE.CameraHelper.__v.set(d, e, f);
        THREE.CameraHelper.__projector.unprojectVector(THREE.CameraHelper.__v, THREE.CameraHelper.__c);
        a = b.pointMap[a];
        if (void 0 !== a) {
            d = 0;
            for (e = a.length; d < e; d++) b.geometry.vertices[a[d]].copy(THREE.CameraHelper.__v)
        }
    }

    var b = this;
    THREE.CameraHelper.__c.projectionMatrix.copy(this.camera.projectionMatrix);
    a("c", 0, 0, -1);
    a("t", 0, 0, 1);
    a("n1", -1, -1, -1);
    a("n2", 1, -1, -1);
    a("n3", -1, 1, -1);
    a("n4", 1, 1, -1);
    a("f1", -1, -1, 1);
    a("f2", 1, -1, 1);
    a("f3", -1, 1, 1);
    a("f4", 1, 1, 1);
    a("u1", 0.7, 1.1, -1);
    a("u2", -0.7, 1.1, -1);
    a("u3", 0, 2, -1);
    a("cf1", -1, 0, 1);
    a("cf2", 1, 0, 1);
    a("cf3", 0, -1, 1);
    a("cf4", 0, 1, 1);
    a("cn1", -1, 0, -1);
    a("cn2", 1, 0, -1);
    a("cn3", 0, -1, -1);
    a("cn4", 0, 1, -1);
    this.geometry.verticesNeedUpdate = !0
};
THREE.CameraHelper.__projector = new THREE.Projector;
THREE.CameraHelper.__v = new THREE.Vector3;
THREE.CameraHelper.__c = new THREE.Camera;
THREE.DirectionalLightHelper = function (a, b) {
    THREE.Object3D.call(this);
    this.light = a;
    var c = new THREE.SphereGeometry(b, 4, 2), d = new THREE.MeshBasicMaterial({fog: !1, wireframe: !0});
    d.color.copy(this.light.color).multiplyScalar(this.light.intensity);
    this.lightSphere = new THREE.Mesh(c, d);
    this.lightSphere.position = this.light.position;
    this.add(this.lightSphere);
    c = new THREE.Geometry;
    c.vertices.push(this.light.position);
    c.vertices.push(this.light.target.position);
    c.computeLineDistances();
    d = new THREE.LineDashedMaterial({
        dashSize: 4,
        gapSize: 4, opacity: 0.75, transparent: !0, fog: !1
    });
    d.color.copy(this.light.color).multiplyScalar(this.light.intensity);
    this.targetLine = new THREE.Line(c, d);
    this.add(this.targetLine)
};
THREE.DirectionalLightHelper.prototype = Object.create(THREE.Object3D.prototype);
THREE.DirectionalLightHelper.prototype.update = function () {
    this.lightSphere.material.color.copy(this.light.color).multiplyScalar(this.light.intensity);
    this.targetLine.geometry.computeLineDistances();
    this.targetLine.geometry.verticesNeedUpdate = !0;
    this.targetLine.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)
};
THREE.GridHelper = function (a, b) {
    for (var c = new THREE.Geometry, d = new THREE.LineBasicMaterial({vertexColors: THREE.VertexColors}), e = new THREE.Color(4473924), f = new THREE.Color(8947848), g = -a; g <= a; g += b) {
        c.vertices.push(new THREE.Vector3(-a, 0, g));
        c.vertices.push(new THREE.Vector3(a, 0, g));
        c.vertices.push(new THREE.Vector3(g, 0, -a));
        c.vertices.push(new THREE.Vector3(g, 0, a));
        var h = 0 === g ? e : f;
        c.colors.push(h, h, h, h)
    }
    THREE.Line.call(this, c, d, THREE.LinePieces)
};
THREE.GridHelper.prototype = Object.create(THREE.Line.prototype);
THREE.HemisphereLightHelper = function (a, b) {
    THREE.Object3D.call(this);
    this.light = a;
    var c = new THREE.SphereGeometry(b, 4, 2);
    c.applyMatrix((new THREE.Matrix4).makeRotationX(-Math.PI / 2));
    for (var d = 0; 8 > d; d++) c.faces[d].materialIndex = 4 > d ? 0 : 1;
    d = new THREE.MeshBasicMaterial({fog: !1, wireframe: !0});
    d.color.copy(a.color).multiplyScalar(a.intensity);
    var e = new THREE.MeshBasicMaterial({fog: !1, wireframe: !0});
    e.color.copy(a.groundColor).multiplyScalar(a.intensity);
    this.lightSphere = new THREE.Mesh(c, new THREE.MeshFaceMaterial([d,
        e]));
    this.lightSphere.position = a.position;
    this.lightSphere.lookAt(new THREE.Vector3);
    this.add(this.lightSphere)
};
THREE.HemisphereLightHelper.prototype = Object.create(THREE.Object3D.prototype);
THREE.HemisphereLightHelper.prototype.update = function () {
    this.lightSphere.lookAt(new THREE.Vector3);
    this.lightSphere.material.materials[0].color.copy(this.light.color).multiplyScalar(this.light.intensity);
    this.lightSphere.material.materials[1].color.copy(this.light.groundColor).multiplyScalar(this.light.intensity)
};
THREE.PointLightHelper = function (a, b) {
    THREE.Object3D.call(this);
    this.light = a;
    var c = new THREE.SphereGeometry(b, 4, 2), d = new THREE.MeshBasicMaterial({fog: !1, wireframe: !0});
    d.color.copy(this.light.color).multiplyScalar(this.light.intensity);
    this.lightSphere = new THREE.Mesh(c, d);
    this.lightSphere.position = this.light.position;
    this.add(this.lightSphere)
};
THREE.PointLightHelper.prototype = Object.create(THREE.Object3D.prototype);
THREE.PointLightHelper.prototype.update = function () {
    this.lightSphere.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)
};
THREE.SpotLightHelper = function (a, b) {
    THREE.Object3D.call(this);
    this.light = a;
    var c = new THREE.SphereGeometry(b, 4, 2), d = new THREE.MeshBasicMaterial({fog: !1, wireframe: !0});
    d.color.copy(this.light.color).multiplyScalar(this.light.intensity);
    this.lightSphere = new THREE.Mesh(c, d);
    this.lightSphere.position = this.light.position;
    this.add(this.lightSphere);
    c = new THREE.CylinderGeometry(1E-4, 1, 1, 8, 1, !0);
    c.applyMatrix((new THREE.Matrix4).makeRotationX(-Math.PI / 2).translate(new THREE.Vector3(0, -0.5, 0)));
    d = new THREE.MeshBasicMaterial({
        fog: !1,
        wireframe: !0, opacity: 0.3, transparent: !0
    });
    d.color.copy(this.light.color).multiplyScalar(this.light.intensity);
    this.lightCone = new THREE.Mesh(c, d);
    this.lightCone.position = this.light.position;
    c = a.distance ? a.distance : 1E4;
    d = 2 * c * Math.tan(0.5 * a.angle);
    this.lightCone.scale.set(d, d, c);
    this.lightCone.lookAt(this.light.target.position);
    this.add(this.lightCone)
};
THREE.SpotLightHelper.prototype = Object.create(THREE.Object3D.prototype);
THREE.SpotLightHelper.prototype.update = function () {
    var a = this.light.distance ? this.light.distance : 1E4, b = 2 * a * Math.tan(0.5 * this.light.angle);
    this.lightCone.scale.set(b, b, a);
    this.lightCone.lookAt(this.light.target.position);
    this.lightSphere.material.color.copy(this.light.color).multiplyScalar(this.light.intensity);
    this.lightCone.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)
};
THREE.ImmediateRenderObject = function () {
    THREE.Object3D.call(this);
    this.render = function () {
    }
};
THREE.ImmediateRenderObject.prototype = Object.create(THREE.Object3D.prototype);
THREE.LensFlare = function (a, b, c, d, e) {
    THREE.Object3D.call(this);
    this.lensFlares = [];
    this.positionScreen = new THREE.Vector3;
    this.customUpdateCallback = void 0;
    void 0 !== a && this.add(a, b, c, d, e)
};
THREE.LensFlare.prototype = Object.create(THREE.Object3D.prototype);
THREE.LensFlare.prototype.add = function (a, b, c, d, e, f) {
    void 0 === b && (b = -1);
    void 0 === c && (c = 0);
    void 0 === f && (f = 1);
    void 0 === e && (e = new THREE.Color(16777215));
    void 0 === d && (d = THREE.NormalBlending);
    c = Math.min(c, Math.max(0, c));
    this.lensFlares.push({
        texture: a,
        size: b,
        distance: c,
        x: 0,
        y: 0,
        z: 0,
        scale: 1,
        rotation: 1,
        opacity: f,
        color: e,
        blending: d
    })
};
THREE.LensFlare.prototype.updateLensFlares = function () {
    var a, b = this.lensFlares.length, c, d = 2 * -this.positionScreen.x, e = 2 * -this.positionScreen.y;
    for (a = 0; a < b; a++) c = this.lensFlares[a], c.x = this.positionScreen.x + d * c.distance, c.y = this.positionScreen.y + e * c.distance, c.wantedRotation = 0.25 * c.x * Math.PI, c.rotation += 0.25 * (c.wantedRotation - c.rotation)
};
THREE.MorphBlendMesh = function (a, b) {
    THREE.Mesh.call(this, a, b);
    this.animationsMap = {};
    this.animationsList = [];
    var c = this.geometry.morphTargets.length;
    this.createAnimation("__default", 0, c - 1, c / 1);
    this.setAnimationWeight("__default", 1)
};
THREE.MorphBlendMesh.prototype = Object.create(THREE.Mesh.prototype);
THREE.MorphBlendMesh.prototype.createAnimation = function (a, b, c, d) {
    b = {
        startFrame: b,
        endFrame: c,
        length: c - b + 1,
        fps: d,
        duration: (c - b) / d,
        lastFrame: 0,
        currentFrame: 0,
        active: !1,
        time: 0,
        direction: 1,
        weight: 1,
        directionBackwards: !1,
        mirroredLoop: !1
    };
    this.animationsMap[a] = b;
    this.animationsList.push(b)
};
THREE.MorphBlendMesh.prototype.autoCreateAnimations = function (a) {
    for (var b = /([a-z]+)(\d+)/, c, d = {}, e = this.geometry, f = 0, g = e.morphTargets.length; f < g; f++) {
        var h = e.morphTargets[f].name.match(b);
        if (h && 1 < h.length) {
            var i = h[1];
            d[i] || (d[i] = {start: Infinity, end: -Infinity});
            h = d[i];
            f < h.start && (h.start = f);
            f > h.end && (h.end = f);
            c || (c = i)
        }
    }
    for (i in d) h = d[i], this.createAnimation(i, h.start, h.end, a);
    this.firstAnimation = c
};
THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function (a) {
    if (a = this.animationsMap[a]) a.direction = 1, a.directionBackwards = !1
};
THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function (a) {
    if (a = this.animationsMap[a]) a.direction = -1, a.directionBackwards = !0
};
THREE.MorphBlendMesh.prototype.setAnimationFPS = function (a, b) {
    var c = this.animationsMap[a];
    c && (c.fps = b, c.duration = (c.end - c.start) / c.fps)
};
THREE.MorphBlendMesh.prototype.setAnimationDuration = function (a, b) {
    var c = this.animationsMap[a];
    c && (c.duration = b, c.fps = (c.end - c.start) / c.duration)
};
THREE.MorphBlendMesh.prototype.setAnimationWeight = function (a, b) {
    var c = this.animationsMap[a];
    c && (c.weight = b)
};
THREE.MorphBlendMesh.prototype.setAnimationTime = function (a, b) {
    var c = this.animationsMap[a];
    c && (c.time = b)
};
THREE.MorphBlendMesh.prototype.getAnimationTime = function (a) {
    var b = 0;
    if (a = this.animationsMap[a]) b = a.time;
    return b
};
THREE.MorphBlendMesh.prototype.getAnimationDuration = function (a) {
    var b = -1;
    if (a = this.animationsMap[a]) b = a.duration;
    return b
};
THREE.MorphBlendMesh.prototype.playAnimation = function (a) {
    var b = this.animationsMap[a];
    b ? (b.time = 0, b.active = !0) : console.warn("animation[" + a + "] undefined")
};
THREE.MorphBlendMesh.prototype.stopAnimation = function (a) {
    if (a = this.animationsMap[a]) a.active = !1
};
THREE.MorphBlendMesh.prototype.update = function (a) {
    for (var b = 0, c = this.animationsList.length; b < c; b++) {
        var d = this.animationsList[b];
        if (d.active) {
            var e = d.duration / d.length;
            d.time += d.direction * a;
            if (d.mirroredLoop) {
                if (d.time > d.duration || 0 > d.time) d.direction *= -1, d.time > d.duration && (d.time = d.duration, d.directionBackwards = !0), 0 > d.time && (d.time = 0, d.directionBackwards = !1)
            } else d.time %= d.duration, 0 > d.time && (d.time += d.duration);
            var f = d.startFrame + THREE.Math.clamp(Math.floor(d.time / e), 0, d.length - 1), g = d.weight;
            f !== d.currentFrame && (this.morphTargetInfluences[d.lastFrame] = 0, this.morphTargetInfluences[d.currentFrame] = 1 * g, this.morphTargetInfluences[f] = 0, d.lastFrame = d.currentFrame, d.currentFrame = f);
            e = d.time % e / e;
            d.directionBackwards && (e = 1 - e);
            this.morphTargetInfluences[d.currentFrame] = e * g;
            this.morphTargetInfluences[d.lastFrame] = (1 - e) * g
        }
    }
};
THREE.LensFlarePlugin = function () {
    function a(a, c) {
        var d = b.createProgram(), e = b.createShader(b.FRAGMENT_SHADER), f = b.createShader(b.VERTEX_SHADER),
            g = "precision " + c + " float;\n";
        b.shaderSource(e, g + a.fragmentShader);
        b.shaderSource(f, g + a.vertexShader);
        b.compileShader(e);
        b.compileShader(f);
        b.attachShader(d, e);
        b.attachShader(d, f);
        b.linkProgram(d);
        return d
    }

    var b, c, d, e, f, g, h, i, k, l, m, p, s;
    this.init = function (r) {
        b = r.context;
        c = r;
        d = r.getPrecision();
        e = new Float32Array(16);
        f = new Uint16Array(6);
        r = 0;
        e[r++] = -1;
        e[r++] = -1;
        e[r++] = 0;
        e[r++] = 0;
        e[r++] = 1;
        e[r++] = -1;
        e[r++] = 1;
        e[r++] = 0;
        e[r++] = 1;
        e[r++] = 1;
        e[r++] = 1;
        e[r++] = 1;
        e[r++] = -1;
        e[r++] = 1;
        e[r++] = 0;
        e[r++] = 1;
        r = 0;
        f[r++] = 0;
        f[r++] = 1;
        f[r++] = 2;
        f[r++] = 0;
        f[r++] = 2;
        f[r++] = 3;
        g = b.createBuffer();
        h = b.createBuffer();
        b.bindBuffer(b.ARRAY_BUFFER, g);
        b.bufferData(b.ARRAY_BUFFER, e, b.STATIC_DRAW);
        b.bindBuffer(b.ELEMENT_ARRAY_BUFFER, h);
        b.bufferData(b.ELEMENT_ARRAY_BUFFER, f, b.STATIC_DRAW);
        i = b.createTexture();
        k = b.createTexture();
        b.bindTexture(b.TEXTURE_2D, i);
        b.texImage2D(b.TEXTURE_2D, 0, b.RGB, 16, 16,
            0, b.RGB, b.UNSIGNED_BYTE, null);
        b.texParameteri(b.TEXTURE_2D, b.TEXTURE_WRAP_S, b.CLAMP_TO_EDGE);
        b.texParameteri(b.TEXTURE_2D, b.TEXTURE_WRAP_T, b.CLAMP_TO_EDGE);
        b.texParameteri(b.TEXTURE_2D, b.TEXTURE_MAG_FILTER, b.NEAREST);
        b.texParameteri(b.TEXTURE_2D, b.TEXTURE_MIN_FILTER, b.NEAREST);
        b.bindTexture(b.TEXTURE_2D, k);
        b.texImage2D(b.TEXTURE_2D, 0, b.RGBA, 16, 16, 0, b.RGBA, b.UNSIGNED_BYTE, null);
        b.texParameteri(b.TEXTURE_2D, b.TEXTURE_WRAP_S, b.CLAMP_TO_EDGE);
        b.texParameteri(b.TEXTURE_2D, b.TEXTURE_WRAP_T, b.CLAMP_TO_EDGE);
        b.texParameteri(b.TEXTURE_2D, b.TEXTURE_MAG_FILTER, b.NEAREST);
        b.texParameteri(b.TEXTURE_2D, b.TEXTURE_MIN_FILTER, b.NEAREST);
        0 >= b.getParameter(b.MAX_VERTEX_TEXTURE_IMAGE_UNITS) ? (l = !1, m = a(THREE.ShaderFlares.lensFlare, d)) : (l = !0, m = a(THREE.ShaderFlares.lensFlareVertexTexture, d));
        p = {};
        s = {};
        p.vertex = b.getAttribLocation(m, "position");
        p.uv = b.getAttribLocation(m, "uv");
        s.renderType = b.getUniformLocation(m, "renderType");
        s.map = b.getUniformLocation(m, "map");
        s.occlusionMap = b.getUniformLocation(m, "occlusionMap");
        s.opacity =
            b.getUniformLocation(m, "opacity");
        s.color = b.getUniformLocation(m, "color");
        s.scale = b.getUniformLocation(m, "scale");
        s.rotation = b.getUniformLocation(m, "rotation");
        s.screenPosition = b.getUniformLocation(m, "screenPosition")
    };
    this.render = function (a, d, e, f) {
        var a = a.__webglFlares, t = a.length;
        if (t) {
            var x = new THREE.Vector3, u = f / e, B = 0.5 * e, G = 0.5 * f, D = 16 / f, w = new THREE.Vector2(D * u, D),
                I = new THREE.Vector3(1, 1, 0), J = new THREE.Vector2(1, 1), E = s, D = p;
            b.useProgram(m);
            b.enableVertexAttribArray(p.vertex);
            b.enableVertexAttribArray(p.uv);
            b.uniform1i(E.occlusionMap, 0);
            b.uniform1i(E.map, 1);
            b.bindBuffer(b.ARRAY_BUFFER, g);
            b.vertexAttribPointer(D.vertex, 2, b.FLOAT, !1, 16, 0);
            b.vertexAttribPointer(D.uv, 2, b.FLOAT, !1, 16, 8);
            b.bindBuffer(b.ELEMENT_ARRAY_BUFFER, h);
            b.disable(b.CULL_FACE);
            b.depthMask(!1);
            var Z, A, S, F, H;
            for (Z = 0; Z < t; Z++) if (D = 16 / f, w.set(D * u, D), F = a[Z], x.set(F.matrixWorld.elements[12], F.matrixWorld.elements[13], F.matrixWorld.elements[14]), x.applyMatrix4(d.matrixWorldInverse), x.applyProjection(d.projectionMatrix), I.copy(x), J.x = I.x * B + B,
                    J.y = I.y * G + G, l || 0 < J.x && J.x < e && 0 < J.y && J.y < f) {
                b.activeTexture(b.TEXTURE1);
                b.bindTexture(b.TEXTURE_2D, i);
                b.copyTexImage2D(b.TEXTURE_2D, 0, b.RGB, J.x - 8, J.y - 8, 16, 16, 0);
                b.uniform1i(E.renderType, 0);
                b.uniform2f(E.scale, w.x, w.y);
                b.uniform3f(E.screenPosition, I.x, I.y, I.z);
                b.disable(b.BLEND);
                b.enable(b.DEPTH_TEST);
                b.drawElements(b.TRIANGLES, 6, b.UNSIGNED_SHORT, 0);
                b.activeTexture(b.TEXTURE0);
                b.bindTexture(b.TEXTURE_2D, k);
                b.copyTexImage2D(b.TEXTURE_2D, 0, b.RGBA, J.x - 8, J.y - 8, 16, 16, 0);
                b.uniform1i(E.renderType, 1);
                b.disable(b.DEPTH_TEST);
                b.activeTexture(b.TEXTURE1);
                b.bindTexture(b.TEXTURE_2D, i);
                b.drawElements(b.TRIANGLES, 6, b.UNSIGNED_SHORT, 0);
                F.positionScreen.copy(I);
                F.customUpdateCallback ? F.customUpdateCallback(F) : F.updateLensFlares();
                b.uniform1i(E.renderType, 2);
                b.enable(b.BLEND);
                A = 0;
                for (S = F.lensFlares.length; A < S; A++) H = F.lensFlares[A], 0.001 < H.opacity && 0.001 < H.scale && (I.x = H.x, I.y = H.y, I.z = H.z, D = H.size * H.scale / f, w.x = D * u, w.y = D, b.uniform3f(E.screenPosition, I.x, I.y, I.z), b.uniform2f(E.scale, w.x, w.y), b.uniform1f(E.rotation, H.rotation), b.uniform1f(E.opacity,
                    H.opacity), b.uniform3f(E.color, H.color.r, H.color.g, H.color.b), c.setBlending(H.blending, H.blendEquation, H.blendSrc, H.blendDst), c.setTexture(H.texture, 1), b.drawElements(b.TRIANGLES, 6, b.UNSIGNED_SHORT, 0))
            }
            b.enable(b.CULL_FACE);
            b.enable(b.DEPTH_TEST);
            b.depthMask(!0)
        }
    }
};
THREE.ShadowMapPlugin = function () {
    var a, b, c, d, e, f, g = new THREE.Frustum, h = new THREE.Matrix4, i = new THREE.Vector3, k = new THREE.Vector3,
        l = new THREE.Vector3;
    this.init = function (g) {
        a = g.context;
        b = g;
        var g = THREE.ShaderLib.depthRGBA, h = THREE.UniformsUtils.clone(g.uniforms);
        c = new THREE.ShaderMaterial({fragmentShader: g.fragmentShader, vertexShader: g.vertexShader, uniforms: h});
        d = new THREE.ShaderMaterial({
            fragmentShader: g.fragmentShader,
            vertexShader: g.vertexShader,
            uniforms: h,
            morphTargets: !0
        });
        e = new THREE.ShaderMaterial({
            fragmentShader: g.fragmentShader,
            vertexShader: g.vertexShader, uniforms: h, skinning: !0
        });
        f = new THREE.ShaderMaterial({
            fragmentShader: g.fragmentShader,
            vertexShader: g.vertexShader,
            uniforms: h,
            morphTargets: !0,
            skinning: !0
        });
        c._shadowPass = !0;
        d._shadowPass = !0;
        e._shadowPass = !0;
        f._shadowPass = !0
    };
    this.render = function (a, c) {
        b.shadowMapEnabled && b.shadowMapAutoUpdate && this.update(a, c)
    };
    this.update = function (m, p) {
        var s, r, n, q, z, t, x, u, B, G = [];
        q = 0;
        a.clearColor(1, 1, 1, 1);
        a.disable(a.BLEND);
        a.enable(a.CULL_FACE);
        a.frontFace(a.CCW);
        b.shadowMapCullFace === THREE.CullFaceFront ?
            a.cullFace(a.FRONT) : a.cullFace(a.BACK);
        b.setDepthTest(!0);
        s = 0;
        for (r = m.__lights.length; s < r; s++) if (n = m.__lights[s], n.castShadow) if (n instanceof THREE.DirectionalLight && n.shadowCascade) for (z = 0; z < n.shadowCascadeCount; z++) {
            var D;
            if (n.shadowCascadeArray[z]) D = n.shadowCascadeArray[z]; else {
                B = n;
                x = z;
                D = new THREE.DirectionalLight;
                D.isVirtual = !0;
                D.onlyShadow = !0;
                D.castShadow = !0;
                D.shadowCameraNear = B.shadowCameraNear;
                D.shadowCameraFar = B.shadowCameraFar;
                D.shadowCameraLeft = B.shadowCameraLeft;
                D.shadowCameraRight = B.shadowCameraRight;
                D.shadowCameraBottom = B.shadowCameraBottom;
                D.shadowCameraTop = B.shadowCameraTop;
                D.shadowCameraVisible = B.shadowCameraVisible;
                D.shadowDarkness = B.shadowDarkness;
                D.shadowBias = B.shadowCascadeBias[x];
                D.shadowMapWidth = B.shadowCascadeWidth[x];
                D.shadowMapHeight = B.shadowCascadeHeight[x];
                D.pointsWorld = [];
                D.pointsFrustum = [];
                u = D.pointsWorld;
                t = D.pointsFrustum;
                for (var w = 0; 8 > w; w++) u[w] = new THREE.Vector3, t[w] = new THREE.Vector3;
                u = B.shadowCascadeNearZ[x];
                B = B.shadowCascadeFarZ[x];
                t[0].set(-1, -1, u);
                t[1].set(1, -1, u);
                t[2].set(-1,
                    1, u);
                t[3].set(1, 1, u);
                t[4].set(-1, -1, B);
                t[5].set(1, -1, B);
                t[6].set(-1, 1, B);
                t[7].set(1, 1, B);
                D.originalCamera = p;
                t = new THREE.Gyroscope;
                t.position = n.shadowCascadeOffset;
                t.add(D);
                t.add(D.target);
                p.add(t);
                n.shadowCascadeArray[z] = D;
                console.log("Created virtualLight", D)
            }
            x = n;
            u = z;
            B = x.shadowCascadeArray[u];
            B.position.copy(x.position);
            B.target.position.copy(x.target.position);
            B.lookAt(B.target);
            B.shadowCameraVisible = x.shadowCameraVisible;
            B.shadowDarkness = x.shadowDarkness;
            B.shadowBias = x.shadowCascadeBias[u];
            t = x.shadowCascadeNearZ[u];
            x = x.shadowCascadeFarZ[u];
            B = B.pointsFrustum;
            B[0].z = t;
            B[1].z = t;
            B[2].z = t;
            B[3].z = t;
            B[4].z = x;
            B[5].z = x;
            B[6].z = x;
            B[7].z = x;
            G[q] = D;
            q++
        } else G[q] = n, q++;
        s = 0;
        for (r = G.length; s < r; s++) {
            n = G[s];
            n.shadowMap || (z = THREE.LinearFilter, b.shadowMapType === THREE.PCFSoftShadowMap && (z = THREE.NearestFilter), n.shadowMap = new THREE.WebGLRenderTarget(n.shadowMapWidth, n.shadowMapHeight, {
                minFilter: z,
                magFilter: z,
                format: THREE.RGBAFormat
            }), n.shadowMapSize = new THREE.Vector2(n.shadowMapWidth, n.shadowMapHeight), n.shadowMatrix = new THREE.Matrix4);
            if (!n.shadowCamera) {
                if (n instanceof THREE.SpotLight) n.shadowCamera = new THREE.PerspectiveCamera(n.shadowCameraFov, n.shadowMapWidth / n.shadowMapHeight, n.shadowCameraNear, n.shadowCameraFar); else if (n instanceof THREE.DirectionalLight) n.shadowCamera = new THREE.OrthographicCamera(n.shadowCameraLeft, n.shadowCameraRight, n.shadowCameraTop, n.shadowCameraBottom, n.shadowCameraNear, n.shadowCameraFar); else {
                    console.error("Unsupported light type for shadow");
                    continue
                }
                m.add(n.shadowCamera);
                b.autoUpdateScene && m.updateMatrixWorld()
            }
            n.shadowCameraVisible &&
            !n.cameraHelper && (n.cameraHelper = new THREE.CameraHelper(n.shadowCamera), n.shadowCamera.add(n.cameraHelper));
            if (n.isVirtual && D.originalCamera == p) {
                z = p;
                q = n.shadowCamera;
                t = n.pointsFrustum;
                B = n.pointsWorld;
                i.set(Infinity, Infinity, Infinity);
                k.set(-Infinity, -Infinity, -Infinity);
                for (x = 0; 8 > x; x++) u = B[x], u.copy(t[x]), THREE.ShadowMapPlugin.__projector.unprojectVector(u, z), u.applyMatrix4(q.matrixWorldInverse), u.x < i.x && (i.x = u.x), u.x > k.x && (k.x = u.x), u.y < i.y && (i.y = u.y), u.y > k.y && (k.y = u.y), u.z < i.z && (i.z = u.z), u.z > k.z &&
                (k.z = u.z);
                q.left = i.x;
                q.right = k.x;
                q.top = k.y;
                q.bottom = i.y;
                q.updateProjectionMatrix()
            }
            q = n.shadowMap;
            t = n.shadowMatrix;
            z = n.shadowCamera;
            z.position.getPositionFromMatrix(n.matrixWorld);
            l.getPositionFromMatrix(n.target.matrixWorld);
            z.lookAt(l);
            z.updateMatrixWorld();
            z.matrixWorldInverse.getInverse(z.matrixWorld);
            n.cameraHelper && (n.cameraHelper.visible = n.shadowCameraVisible);
            n.shadowCameraVisible && n.cameraHelper.update();
            t.set(0.5, 0, 0, 0.5, 0, 0.5, 0, 0.5, 0, 0, 0.5, 0.5, 0, 0, 0, 1);
            t.multiply(z.projectionMatrix);
            t.multiply(z.matrixWorldInverse);
            h.multiplyMatrices(z.projectionMatrix, z.matrixWorldInverse);
            g.setFromMatrix(h);
            b.setRenderTarget(q);
            b.clear();
            B = m.__webglObjects;
            n = 0;
            for (q = B.length; n < q; n++) if (x = B[n], t = x.object, x.render = !1, t.visible && t.castShadow && (!(t instanceof THREE.Mesh || t instanceof THREE.ParticleSystem) || !t.frustumCulled || g.intersectsObject(t))) t._modelViewMatrix.multiplyMatrices(z.matrixWorldInverse, t.matrixWorld), x.render = !0;
            n = 0;
            for (q = B.length; n < q; n++) x = B[n], x.render && (t = x.object, x = x.buffer, w = t.material instanceof THREE.MeshFaceMaterial ?
                t.material.materials[0] : t.material, u = 0 < t.geometry.morphTargets.length && w.morphTargets, w = t instanceof THREE.SkinnedMesh && w.skinning, u = t.customDepthMaterial ? t.customDepthMaterial : w ? u ? f : e : u ? d : c, x instanceof THREE.BufferGeometry ? b.renderBufferDirect(z, m.__lights, null, u, x, t) : b.renderBuffer(z, m.__lights, null, u, x, t));
            B = m.__webglObjectsImmediate;
            n = 0;
            for (q = B.length; n < q; n++) x = B[n], t = x.object, t.visible && t.castShadow && (t._modelViewMatrix.multiplyMatrices(z.matrixWorldInverse, t.matrixWorld), b.renderImmediateObject(z,
                m.__lights, null, c, t))
        }
        s = b.getClearColor();
        r = b.getClearAlpha();
        a.clearColor(s.r, s.g, s.b, r);
        a.enable(a.BLEND);
        b.shadowMapCullFace === THREE.CullFaceFront && a.cullFace(a.BACK)
    }
};
THREE.ShadowMapPlugin.__projector = new THREE.Projector;
THREE.SpritePlugin = function () {
    function a(a, b) {
        return a.z !== b.z ? b.z - a.z : b.id - a.id
    }

    var b, c, d, e, f, g, h, i, k, l;
    this.init = function (a) {
        b = a.context;
        c = a;
        d = a.getPrecision();
        e = new Float32Array(16);
        f = new Uint16Array(6);
        a = 0;
        e[a++] = -1;
        e[a++] = -1;
        e[a++] = 0;
        e[a++] = 0;
        e[a++] = 1;
        e[a++] = -1;
        e[a++] = 1;
        e[a++] = 0;
        e[a++] = 1;
        e[a++] = 1;
        e[a++] = 1;
        e[a++] = 1;
        e[a++] = -1;
        e[a++] = 1;
        e[a++] = 0;
        e[a++] = 1;
        a = 0;
        f[a++] = 0;
        f[a++] = 1;
        f[a++] = 2;
        f[a++] = 0;
        f[a++] = 2;
        f[a++] = 3;
        g = b.createBuffer();
        h = b.createBuffer();
        b.bindBuffer(b.ARRAY_BUFFER, g);
        b.bufferData(b.ARRAY_BUFFER,
            e, b.STATIC_DRAW);
        b.bindBuffer(b.ELEMENT_ARRAY_BUFFER, h);
        b.bufferData(b.ELEMENT_ARRAY_BUFFER, f, b.STATIC_DRAW);
        var a = THREE.ShaderSprite.sprite, p = b.createProgram(), s = b.createShader(b.FRAGMENT_SHADER),
            r = b.createShader(b.VERTEX_SHADER), n = "precision " + d + " float;\n";
        b.shaderSource(s, n + a.fragmentShader);
        b.shaderSource(r, n + a.vertexShader);
        b.compileShader(s);
        b.compileShader(r);
        b.attachShader(p, s);
        b.attachShader(p, r);
        b.linkProgram(p);
        i = p;
        k = {};
        l = {};
        k.position = b.getAttribLocation(i, "position");
        k.uv = b.getAttribLocation(i,
            "uv");
        l.uvOffset = b.getUniformLocation(i, "uvOffset");
        l.uvScale = b.getUniformLocation(i, "uvScale");
        l.rotation = b.getUniformLocation(i, "rotation");
        l.scale = b.getUniformLocation(i, "scale");
        l.alignment = b.getUniformLocation(i, "alignment");
        l.color = b.getUniformLocation(i, "color");
        l.map = b.getUniformLocation(i, "map");
        l.opacity = b.getUniformLocation(i, "opacity");
        l.useScreenCoordinates = b.getUniformLocation(i, "useScreenCoordinates");
        l.sizeAttenuation = b.getUniformLocation(i, "sizeAttenuation");
        l.screenPosition = b.getUniformLocation(i,
            "screenPosition");
        l.modelViewMatrix = b.getUniformLocation(i, "modelViewMatrix");
        l.projectionMatrix = b.getUniformLocation(i, "projectionMatrix");
        l.fogType = b.getUniformLocation(i, "fogType");
        l.fogDensity = b.getUniformLocation(i, "fogDensity");
        l.fogNear = b.getUniformLocation(i, "fogNear");
        l.fogFar = b.getUniformLocation(i, "fogFar");
        l.fogColor = b.getUniformLocation(i, "fogColor");
        l.alphaTest = b.getUniformLocation(i, "alphaTest")
    };
    this.render = function (d, e, f, r) {
        var n = d.__webglSprites, q = n.length;
        if (q) {
            var z = k, t = l, x = r /
                f, f = 0.5 * f, u = 0.5 * r;
            b.useProgram(i);
            b.enableVertexAttribArray(z.position);
            b.enableVertexAttribArray(z.uv);
            b.disable(b.CULL_FACE);
            b.enable(b.BLEND);
            b.bindBuffer(b.ARRAY_BUFFER, g);
            b.vertexAttribPointer(z.position, 2, b.FLOAT, !1, 16, 0);
            b.vertexAttribPointer(z.uv, 2, b.FLOAT, !1, 16, 8);
            b.bindBuffer(b.ELEMENT_ARRAY_BUFFER, h);
            b.uniformMatrix4fv(t.projectionMatrix, !1, e.projectionMatrix.elements);
            b.activeTexture(b.TEXTURE0);
            b.uniform1i(t.map, 0);
            var B = z = 0, G = d.fog;
            G ? (b.uniform3f(t.fogColor, G.color.r, G.color.g, G.color.b),
                G instanceof THREE.Fog ? (b.uniform1f(t.fogNear, G.near), b.uniform1f(t.fogFar, G.far), b.uniform1i(t.fogType, 1), B = z = 1) : G instanceof THREE.FogExp2 && (b.uniform1f(t.fogDensity, G.density), b.uniform1i(t.fogType, 2), B = z = 2)) : (b.uniform1i(t.fogType, 0), B = z = 0);
            for (var D, w, I = [], G = 0; G < q; G++) D = n[G], w = D.material, D.visible && 0 !== w.opacity && (w.useScreenCoordinates ? D.z = -D.position.z : (D._modelViewMatrix.multiplyMatrices(e.matrixWorldInverse, D.matrixWorld), D.z = -D._modelViewMatrix.elements[14]));
            n.sort(a);
            for (G = 0; G < q; G++) D =
                n[G], w = D.material, D.visible && 0 !== w.opacity && (w.map && w.map.image && w.map.image.width) && (b.uniform1f(t.alphaTest, w.alphaTest), !0 === w.useScreenCoordinates ? (b.uniform1i(t.useScreenCoordinates, 1), b.uniform3f(t.screenPosition, (D.position.x * c.devicePixelRatio - f) / f, (u - D.position.y * c.devicePixelRatio) / u, Math.max(0, Math.min(1, D.position.z))), I[0] = c.devicePixelRatio, I[1] = c.devicePixelRatio) : (b.uniform1i(t.useScreenCoordinates, 0), b.uniform1i(t.sizeAttenuation, w.sizeAttenuation ? 1 : 0), b.uniformMatrix4fv(t.modelViewMatrix,
                !1, D._modelViewMatrix.elements), I[0] = 1, I[1] = 1), e = d.fog && w.fog ? B : 0, z !== e && (b.uniform1i(t.fogType, e), z = e), e = 1 / (w.scaleByViewport ? r : 1), I[0] *= e * x * D.scale.x, I[1] *= e * D.scale.y, b.uniform2f(t.uvScale, w.uvScale.x, w.uvScale.y), b.uniform2f(t.uvOffset, w.uvOffset.x, w.uvOffset.y), b.uniform2f(t.alignment, w.alignment.x, w.alignment.y), b.uniform1f(t.opacity, w.opacity), b.uniform3f(t.color, w.color.r, w.color.g, w.color.b), b.uniform1f(t.rotation, D.rotation), b.uniform2fv(t.scale, I), c.setBlending(w.blending, w.blendEquation,
                w.blendSrc, w.blendDst), c.setDepthTest(w.depthTest), c.setDepthWrite(w.depthWrite), c.setTexture(w.map, 0), b.drawElements(b.TRIANGLES, 6, b.UNSIGNED_SHORT, 0));
            b.enable(b.CULL_FACE)
        }
    }
};
THREE.DepthPassPlugin = function () {
    this.enabled = !1;
    this.renderTarget = null;
    var a, b, c, d, e, f, g = new THREE.Frustum, h = new THREE.Matrix4;
    this.init = function (g) {
        a = g.context;
        b = g;
        var g = THREE.ShaderLib.depthRGBA, h = THREE.UniformsUtils.clone(g.uniforms);
        c = new THREE.ShaderMaterial({fragmentShader: g.fragmentShader, vertexShader: g.vertexShader, uniforms: h});
        d = new THREE.ShaderMaterial({
            fragmentShader: g.fragmentShader,
            vertexShader: g.vertexShader,
            uniforms: h,
            morphTargets: !0
        });
        e = new THREE.ShaderMaterial({
            fragmentShader: g.fragmentShader,
            vertexShader: g.vertexShader, uniforms: h, skinning: !0
        });
        f = new THREE.ShaderMaterial({
            fragmentShader: g.fragmentShader,
            vertexShader: g.vertexShader,
            uniforms: h,
            morphTargets: !0,
            skinning: !0
        });
        c._shadowPass = !0;
        d._shadowPass = !0;
        e._shadowPass = !0;
        f._shadowPass = !0
    };
    this.render = function (a, b) {
        this.enabled && this.update(a, b)
    };
    this.update = function (i, k) {
        var l, m, p, s, r, n;
        a.clearColor(1, 1, 1, 1);
        a.disable(a.BLEND);
        b.setDepthTest(!0);
        b.autoUpdateScene && i.updateMatrixWorld();
        k.matrixWorldInverse.getInverse(k.matrixWorld);
        h.multiplyMatrices(k.projectionMatrix,
            k.matrixWorldInverse);
        g.setFromMatrix(h);
        b.setRenderTarget(this.renderTarget);
        b.clear();
        n = i.__webglObjects;
        l = 0;
        for (m = n.length; l < m; l++) if (p = n[l], r = p.object, p.render = !1, r.visible && (!(r instanceof THREE.Mesh || r instanceof THREE.ParticleSystem) || !r.frustumCulled || g.intersectsObject(r))) r._modelViewMatrix.multiplyMatrices(k.matrixWorldInverse, r.matrixWorld), p.render = !0;
        var q;
        l = 0;
        for (m = n.length; l < m; l++) if (p = n[l], p.render && (r = p.object, p = p.buffer, !(r instanceof THREE.ParticleSystem) || r.customDepthMaterial)) (q =
            r.material instanceof THREE.MeshFaceMaterial ? r.material.materials[0] : r.material) && b.setMaterialFaces(r.material), s = 0 < r.geometry.morphTargets.length && q.morphTargets, q = r instanceof THREE.SkinnedMesh && q.skinning, s = r.customDepthMaterial ? r.customDepthMaterial : q ? s ? f : e : s ? d : c, p instanceof THREE.BufferGeometry ? b.renderBufferDirect(k, i.__lights, null, s, p, r) : b.renderBuffer(k, i.__lights, null, s, p, r);
        n = i.__webglObjectsImmediate;
        l = 0;
        for (m = n.length; l < m; l++) p = n[l], r = p.object, r.visible && (r._modelViewMatrix.multiplyMatrices(k.matrixWorldInverse,
            r.matrixWorld), b.renderImmediateObject(k, i.__lights, null, c, r));
        l = b.getClearColor();
        m = b.getClearAlpha();
        a.clearColor(l.r, l.g, l.b, m);
        a.enable(a.BLEND)
    }
};
THREE.ShaderFlares = {
    lensFlareVertexTexture: {
        vertexShader: "uniform lowp int renderType;\nuniform vec3 screenPosition;\nuniform vec2 scale;\nuniform float rotation;\nuniform sampler2D occlusionMap;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvarying float vVisibility;\nvoid main() {\nvUV = uv;\nvec2 pos = position;\nif( renderType == 2 ) {\nvec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) ) +\ntexture2D( occlusionMap, vec2( 0.5, 0.1 ) ) +\ntexture2D( occlusionMap, vec2( 0.9, 0.1 ) ) +\ntexture2D( occlusionMap, vec2( 0.9, 0.5 ) ) +\ntexture2D( occlusionMap, vec2( 0.9, 0.9 ) ) +\ntexture2D( occlusionMap, vec2( 0.5, 0.9 ) ) +\ntexture2D( occlusionMap, vec2( 0.1, 0.9 ) ) +\ntexture2D( occlusionMap, vec2( 0.1, 0.5 ) ) +\ntexture2D( occlusionMap, vec2( 0.5, 0.5 ) );\nvVisibility = (       visibility.r / 9.0 ) *\n( 1.0 - visibility.g / 9.0 ) *\n(       visibility.b / 9.0 ) *\n( 1.0 - visibility.a / 9.0 );\npos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\npos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\n}\ngl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\n}",
        fragmentShader: "uniform lowp int renderType;\nuniform sampler2D map;\nuniform float opacity;\nuniform vec3 color;\nvarying vec2 vUV;\nvarying float vVisibility;\nvoid main() {\nif( renderType == 0 ) {\ngl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );\n} else if( renderType == 1 ) {\ngl_FragColor = texture2D( map, vUV );\n} else {\nvec4 texture = texture2D( map, vUV );\ntexture.a *= opacity * vVisibility;\ngl_FragColor = texture;\ngl_FragColor.rgb *= color;\n}\n}"
    }, lensFlare: {
        vertexShader: "uniform lowp int renderType;\nuniform vec3 screenPosition;\nuniform vec2 scale;\nuniform float rotation;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uv;\nvec2 pos = position;\nif( renderType == 2 ) {\npos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\npos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\n}\ngl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\n}",
        fragmentShader: "precision mediump float;\nuniform lowp int renderType;\nuniform sampler2D map;\nuniform sampler2D occlusionMap;\nuniform float opacity;\nuniform vec3 color;\nvarying vec2 vUV;\nvoid main() {\nif( renderType == 0 ) {\ngl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );\n} else if( renderType == 1 ) {\ngl_FragColor = texture2D( map, vUV );\n} else {\nfloat visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a +\ntexture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a +\ntexture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a +\ntexture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;\nvisibility = ( 1.0 - visibility / 4.0 );\nvec4 texture = texture2D( map, vUV );\ntexture.a *= opacity * visibility;\ngl_FragColor = texture;\ngl_FragColor.rgb *= color;\n}\n}"
    }
};
THREE.ShaderSprite = {
    sprite: {
        vertexShader: "uniform int useScreenCoordinates;\nuniform int sizeAttenuation;\nuniform vec3 screenPosition;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float rotation;\nuniform vec2 scale;\nuniform vec2 alignment;\nuniform vec2 uvOffset;\nuniform vec2 uvScale;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uvOffset + uv * uvScale;\nvec2 alignedPosition = position + alignment;\nvec2 rotatedPosition;\nrotatedPosition.x = ( cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y ) * scale.x;\nrotatedPosition.y = ( sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y ) * scale.y;\nvec4 finalPosition;\nif( useScreenCoordinates != 0 ) {\nfinalPosition = vec4( screenPosition.xy + rotatedPosition, screenPosition.z, 1.0 );\n} else {\nfinalPosition = projectionMatrix * modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\nfinalPosition.xy += rotatedPosition * ( sizeAttenuation == 1 ? 1.0 : finalPosition.z );\n}\ngl_Position = finalPosition;\n}",
        fragmentShader: "uniform vec3 color;\nuniform sampler2D map;\nuniform float opacity;\nuniform int fogType;\nuniform vec3 fogColor;\nuniform float fogDensity;\nuniform float fogNear;\nuniform float fogFar;\nuniform float alphaTest;\nvarying vec2 vUV;\nvoid main() {\nvec4 texture = texture2D( map, vUV );\nif ( texture.a < alphaTest ) discard;\ngl_FragColor = vec4( color * texture.xyz, texture.a * opacity );\nif ( fogType > 0 ) {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat fogFactor = 0.0;\nif ( fogType == 1 ) {\nfogFactor = smoothstep( fogNear, fogFar, depth );\n} else {\nconst float LOG2 = 1.442695;\nfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n}\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}\n}"
    }
};
</script>

<!-- pack me -->

<!-- jQuery extenders -->
<script type="text/javascript">/* Copyright (c) 2006 Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
 * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
 *
 * $LastChangedDate: 2007-06-20 16:24:37 -0500 (Wed, 20 Jun 2007) $
 * $Rev: 2124 $
 *
 * Version: 2.2
 */

(function ($) {

    $.fn.extend({

        /**
         * Apply the mousewheel event to the elements in the jQuery object.
         * The handler function should be prepared to take the event object
         * and a param called 'delta'. The 'delta' param is a number
         * either > 0 or < 0. > 0 = up and < 0 = down.
         *
         * The pageX, pageY, clientX and clientY event properties
         * are fixed in Firefox.
         *
         * @example $("p").mousewheel(function(event, delta){
	 *   if (delta > 0)
	 *     // do something on mousewheel scroll up
	 *   else if (delta < 0)
	 *     //do something on mousewheel scroll down
	 * });
         *
         * @name mousewheel
         * @type jQuery
         * @param Function handler A function to call when onmousewheel fires. Should take two params: event and delta.
         * @cat Plugins/Mousewheel
         * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
         */
        mousewheel: function (f) {
            if (!f.guid) f.guid = $.event.guid++;
            if (!$.event._mwCache) $.event._mwCache = [];

            return this.each(function () {
                if (this._mwHandlers) return this._mwHandlers.push(f);
                else this._mwHandlers = [];

                this._mwHandlers.push(f);

                var s = this;

                this._mwHandler = function (e) {
                    e = $.event.fix(e || window.event);
                    $.extend(e, this._mwCursorPos || {});
                    var delta = 0, returnValue = true;

                    if (e.wheelDelta) delta = e.wheelDelta / 120;
                    if (e.detail) delta = -e.detail / 3;
                    if (window.opera) delta = -e.wheelDelta;

                    for (var i = 0; i < s._mwHandlers.length; i++)
                        if (s._mwHandlers[i])
                            if (s._mwHandlers[i].call(s, e, delta) === false) {
                                returnValue = false;
                                e.preventDefault();
                                e.stopPropagation();
                            }

                    return returnValue;
                };

                if ($.browser.mozilla && !this._mwFixCursorPos) {
                    // fix pageX, pageY, clientX and clientY for mozilla
                    this._mwFixCursorPos = function (e) {
                        this._mwCursorPos = {
                            pageX: e.pageX,
                            pageY: e.pageY,
                            clientX: e.clientX,
                            clientY: e.clientY
                        };
                    };
                    $(this).bind('mousemove', this._mwFixCursorPos);
                }

                if (this.addEventListener)
                    if ($.browser.mozilla) this.addEventListener('DOMMouseScroll', this._mwHandler, false);
                    else this.addEventListener('mousewheel', this._mwHandler, false);
                else
                    this.onmousewheel = this._mwHandler;

                $.event._mwCache.push($(this));
            });
        },

        /**
         * This method removes one or all applied mousewheel events from the elements.
         * You can remove a single handler function by passing it as the first param.
         * If you do not pass anything, it will remove all handlers.
         *
         * @name unmousewheel
         * @param Function handler The handler function to remove from the mousewheel event.
         * @type jQuery
         * @cat Plugins/Mousewheel
         * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
         */
        unmousewheel: function (f) {
            return this.each(function () {
                if (f && this._mwHandlers) {
                    for (var i = 0; i < this._mwHandlers.length; i++)
                        if (this._mwHandlers[i] && this._mwHandlers[i].guid == f.guid)
                            delete this._mwHandlers[i];
                } else {
                    if ($.browser.mozilla && !this._mwFixCursorPos)
                        $(this).unbind('mousemove', this._mwFixCursorPos);

                    if (this.addEventListener)
                        if ($.browser.mozilla) this.removeEventListener('DOMMouseScroll', this._mwHandler, false);
                        else this.removeEventListener('mousewheel', this._mwHandler, false);
                    else
                        this.onmousewheel = null;

                    this._mwHandlers = this._mwHandler = this._mwFixCursorPos = this._mwCursorPos = null;
                }
            });
        }
    });

// clean-up
    $(window)
        .one('unload', function () {
            var els = $.event._mwCache || [];
            for (var i = 0; i < els.length; i++)
                els[i].unmousewheel();
        });

})(jQuery);</script>

<!-- tracker -->
<script type="text/javascript">Tracker = function () {
    // detect local session
    var isLocal = (location.protocol === 'file:');

    // save logs on unload calc window
    $(window).on('unload', function () {
        trackerData["endAt"] = +new Date;

        $.ajax({
            type: 'POST',
            url: 'http://acidome.ru/lab/calc/logs/track.php',
            data: trackerData,
            async: false
        });
    });

    // tracker data
    // key with value equals [empty array] will not be transfered
    var trackerData = {
            // identify
            client:
                isLocal ? [] : // undefined client (we can't to recognize local client)
                    cookie('client', rand, 'expires=Fri, 09 Apr 2038 15:04:40 GMT;'),
            session:
                cookie('session', isLocal ? 'local' : rand),
            page:
                rand(),

            // geo info
            geo: '',

            // usage history
            initial: {},
            flow: [],

            iframe: IS_IFRAME,

            // relative timing started here
            startAt: +new Date,

            // start url
            url: isLocal ?
                document.location.href.replace(/^[^#]*[\/\\](.*)$/, '$1') : // get no local path to file (security issue)
                document.location.href,

            // referrer to this domain, including one redirect
            referrer: function () {
                var ref = decodeURIComponent(cookie('strong-redirect-referrer'));
                if (ref) {
                    // remove temporary cookie
                    document.cookie = 'strong-redirect-referrer=; expires=' + (new Date).toUTCString();
                    return ref;
                }
                // simple referrer
                return document.referrer || []
            }()
        },
        timings = {};

    return {
        push: push,
        extend: function (data) {
            _.extend(trackerData, data);
        },
        time: function (key) {
            timings[key] = +new Date;
        },
        timeEnd: function (key, started) {
            trackerData[key] = (+new Date) - (started || timings[key]);
            console.log(key + ' at: ' + trackerData[key] + 'ms');
        },
        updateGeo: function (providerName, data) {
            var geo = trackerData["geo"] = trackerData["geo"] || {provided: []};

            var hasReceived = _.reduce(data, function (hasReceived, value, key) {
                if (value && !geo[key]) {
                    geo[key] = (value + '')
                    // remove text artefacts
                        .replace(/\\(['"\\])/g, '$1');
                    return true;
                }
                return hasReceived;
            }, false);

            if (hasReceived) {
                trackerData["geo"].provided.push(providerName);
            }

            return geo;
        }
    };


    // push log
    function push(type, data, extra) {
        if (typeof data == 'object') {
            // map boolean values to string (else will be wrong mapped to string in POST body)
            data = _.reduce(data, function (memo, value, key) {
                memo[key] = (typeof value == 'boolean') ? value ? '1' : '' : value;
                return memo;
            }, {});
        }

        // detect first push for this key
        if (!trackerData.initial.hasOwnProperty(type)) {
            trackerData.initial[type] = _.extend({
                time: time(),
                data: data
            }, extra);
        } else {
            trackerData.flow.push(
                _.extend({
                    type: type,
                    time: time(),
                    data: data
                }, extra)
            );
        }
    }

    // relative time from started
    function time() {
        return +new Date - trackerData.startAt;
    }

    // helpers
    function cookie(key, defaults, extra) {
        // get
        var value = (document.cookie.split(key)[1] || '=;').replace(/^=([^;]*);.*$/, '$1');

        if (!value && defaults) {
            // set & save
            value = extracted(defaults);
            document.cookie = key + "=" + value + ';' + extracted(extra || '');
        }

        return value;

        function extracted(defaults) {
            return (typeof defaults == 'function') ? defaults() : defaults;
        }
    }

    function rand() {
        return random(1e+9, 1e+12).toString(36).toUpperCase();

        function random(min, max) {
            if (max == null) {
                max = min;
                min = 0;
            }
            return min + Math.floor(Math.random() * (max - min + 1));
        }
    }
}();


// geo info request
$(function () {
    var providers = [/* offline from 14.01.2015 {
			name: 'freegeoip.net',
			request: 'http://freegeoip.net/json/?callback=__geoDataReceiver',
			access: function(data) {
				if (typeof data !== 'object')
					return {};

				// ignore ip, in order to privacy
				return {
					countryCode: data.country_code,
					country: data.country_name,
					regionCode: data.region_code,
					region: data.region_name,
					city: data.city,
					lat: data.latitude,
					long: data.longitude
				};
			}
		},*/
            /* payment required from 14.01.2015 {
			name: 'maxmind.com',
			request: 'http://j.maxmind.com/app/geoip.js',
			access: function() {
				if (typeof geoip_country_code != 'function')
					return {};

				return {
					countryCode: geoip_country_code(),
					country: geoip_country_name(),
					regionCode: geoip_region(),
					region: geoip_region_name(),
					city: geoip_city(),
					lat: geoip_latitude(),
					long: geoip_longitude()
				};
			}
		},*/ {
                name: 'telize.com',
                request: 'http://www.telize.com/geoip?callback=__geoDataReceiver',
                access: function (data) {
                    if (typeof data !== 'object')
                        return {};

                    // ignore ip, in order to privacy
                    return {
                        continentCode: data.continent_code,
                        countryCode: data.country_code,
                        country: data.country_name,
                        regionCode: data.region_code,
                        region: data.region,
                        city: data.city,
                        lat: data.latitude,
                        long: data.longitude
                    };
                }
            }],
        requiredKeys = ['countryCode', 'city'];

    if (!IS_OFFLINE) {
        nextProvider();
    }

    function nextProvider() {
        var provider = providers.pop(); // right-to-left

        if (!provider) return;

        window.__geoDataReceiver = function (data) {
            var geo = Tracker.updateGeo(provider.name, provider.access(data));

            var allFilled = _.any(requiredKeys, function (key) {
                return !!geo[key];
            });

            delete window.__geoDataReceiver;

            if (allFilled) {
                // geo provided
                $(document).trigger('geo-complete', geo);
            } else {
                // try to run more, after script's onload called
                _.defer(nextProvider);
            }
        };

        if (provider.request) {
            var script = $('<script>')[0];
            script.type = 'text/javascript';
            script.async = true;
            script.src = provider.request;
            script.onload = function () {
                if (window.__geoDataReceiver) {
                    // error happens OR receiver-function was not called in JSONP response code
                    // handle provider.access() this
                    window.__geoDataReceiver();
                }
            };
            script.onerror = nextProvider;
            document.body.appendChild(script);
        }
        else {
            // without-request method used O_O
            window.__geoDataReceiver();
        }
    }
});</script>

<!-- calc basic -->
<script type="text/javascript">// Универсальная функция наследования
Function.prototype.inherits = function (superClass) {
    var Inheritance = function () {
    };
    Inheritance.prototype = superClass.prototype;
    this.prototype = new Inheritance();
    this.prototype.constructor = this;
    this.superClass = superClass;
    // доступ к родительскому классу из инстанса
    this.prototype.superClass = superClass.prototype;
    return this;
}

// переопределение свойств/методов
Function.prototype.override = function (proto) {
    for (var k in proto)
        this.prototype[k] = proto[k];
    return this;
}

// статические свойства/методы
Function.prototype.statics = function (proto) {
    for (var k in proto)
        this[k] = proto[k];
    return this;
}
</script>
<script type="text/javascript">/*
 * Palette is the js class.
 *
 * Legalize Cannabis Licence
 */

Palette = function (hex) {
    this.rgb = Palette.hex2rgb(hex);
}
    .override({
        hl: function (inc) {
            this.rgb ||
            console.log('!this.rgb');
            this.rgb[0] += inc;
            this.rgb[1] += inc;
            this.rgb[2] += inc;
            return this;
        },
        color: function () {
            return Palette.rgb(this.rgb[0], this.rgb[1], this.rgb[2]);
        }
    });

Palette.HEX = '0123456789ABCDEF';

Palette.hex2 = function (i) {
    i = i < 0 ? 0 : i;
    i = i > 255 ? 255 : i;
    var l = i % 16, h = (i - l) / 16;
    return '' + Palette.HEX[h] + Palette.HEX[l];
};

Palette.hex2int = function (hex) {
    if (hex.length > 1)
        return Palette.hex2int(hex.substr(0, hex.length - 1)) * 16 + Palette.hex2int(hex.substr(hex.length - 1))
    for (var i = Palette.HEX.indexOf(hex.toUpperCase()); i >= 0;)
        return i;
    return console.log('Palette.hex2int(): unknown', hex);
};

Palette.hex2rgb = function (hex) {
    var result = /^#?(\w\w?)(\w\w?)(\w\w?)$/.exec(hex);
    if (result)
        return [Palette.hex2int(result[1]), Palette.hex2int(result[2]), Palette.hex2int(result[3])];
    return console.log('Palette.hex2rgb(): unknown', hex);
};

Palette.rgb = function (r, g, b) {
    return '#' + Palette.hex2(r) + Palette.hex2(g) + Palette.hex2(b);
};


Palette.collections = {
    vertex: [],
    line: ['#CC2222', '#CCCC22', '#22CC22'],
    face: []
};

(function () {
    var cube = function (dim, fn) {
        var shifting = [], pos,
            collection = [];
        for (var steps = [], i = 0; i < dim; i++) {
            steps.push(255 - Math.floor((255 / dim) * i));
            i % 2 && shifting.push(i);
        }
        for (i = 0; i < dim; i++)
            i % 2 || shifting.push(i);
        for (var k = 0; k < steps.length; k++) {
            var b = steps[shifting[k]];
            for (var j = 0; j < steps.length; j++) {
                var g = steps[shifting[j]];
                for (i = 0; i < steps.length; i++) {
                    var r = steps[shifting[i]];
                    fn(r, g, b, shifting[i], shifting[j], shifting[k], pos++);
                }
            }
        }
    }

    Palette._colors = [];
    var steps = 10;
    cube(steps, function (r, g, b, i, j, k, pos) {
        if ((i + j + k) % 2 == 0 && (i + j + k) > 2 && (i + j + k) < steps * 3 - 2) {
            var rgb = Palette.rgb(r, g, b);
            Palette._colors.push(rgb);
            // collections
            var mod = (i + j + k) / 2;
            //mod =
            if (mod % 3 == 2)
                Palette.collections.vertex.push(rgb);
            else if (mod % 2)
                Palette.collections.face.push(rgb);
            else
                Palette.collections.line.push(rgb);

        }
    });
})();

0 && $(function () {
    $.each(Palette.collections, function (key, colors) {
        $(colors.length).prependTo('body');
        $(colors).each(function () {
            $('<span style="background-color:' + this + '">&nbsp;&nbsp;' + key + '&nbsp;&nbsp;</span> &nbsp; ').prependTo('body');
        });
        $('<br>').prependTo('body');
    });
});
</script>
<script type="text/javascript">//##############################################################################
// File:
//		metrics.js
// Dependencies:
//		utils.js
// Description:
//		Defines a Vector object for position, distance and dimensions in 3D, a
//		Rectangle object for neatly packaging position and dimensions together and
//		several utility functions for working with dimensions and/or positions of
//		DOM objects, the document, the window and the viewport.
//##############################################################################
// (c)2005-2006 Jeff Lau
//##############################################################################

function Metrics() {
};

//##############################################################################

Vector =
    Metrics.Vector = function (x, y, z) {
        if (y === undefined && z === undefined && x === 0) {
            return new Vector();
        }
        this.x = isNaN(x) ? 0 : x.valueOf();
        this.y = isNaN(y) ? 0 : y.valueOf();
        this.z = isNaN(z) ? 0 : z.valueOf();
    };

//using("Metrics.Vector");

Metrics.Vector.prototype.clone = function () {
    return new Metrics.Vector(this.x, this.y, this.z);
};

Metrics.Vector.prototype.equals = function (to) {
    return
    Math.abs(this.x - to.x) +
    Math.abs(this.y - to.y) +
    Math.abs(this.z - to.z) < .000001;
}


Metrics.Vector.prototype.add = function (vector) {
    this.x += vector.x;
    this.y += vector.y;
    this.z += vector.z;

    return this;
};

Metrics.Vector.add = function (vector1, vector2) {
    return new Metrics.Vector(vector1.x + vector2.x, vector1.y + vector2.y, vector1.z + vector2.z);
};

Metrics.Vector.prototype.subtract = function (vector) {
    this.x -= vector.x;
    this.y -= vector.y;
    this.z -= vector.z;

    return this;
};

Metrics.Vector.subtract = function (vector1, vector2) {
    return new Metrics.Vector(vector1.x - vector2.x, vector1.y - vector2.y, vector1.z - vector2.z);
};

Metrics.Vector.prototype.scale = function (scalar) {
    this.x *= scalar;
    this.y *= scalar;
    this.z *= scalar;

    return this;
};

Metrics.Vector.scale = function (vector, scalar) {
    return new Metrics.Vector(vector.x * scalar, vector.y * scalar, vector.z * scalar);
};

Metrics.Vector.prototype.length = function () {
    return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
};

Metrics.Vector.prototype.isZero = function () {
    return Vector.dotProduct(this, this) < 1e-20;
};

Metrics.Vector.prototype.normalize = function () {
    var length = this.x * this.x + this.y * this.y + this.z * this.z;

    //if (length && Math.abs(length - 1) > 0.01) {
    length = Math.sqrt(length);
    this.x /= length;
    this.y /= length;
    this.z /= length;
    //}
    return this;
};

Metrics.Vector.normalize = function (vector) {
    return (new Metrics.Vector(vector.x, vector.y, vector.z)).normalize();
};

Metrics.Vector.dotProduct = function (vector1, vector2) {
    return vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z;
};

Metrics.Vector.crossProduct = function (vector1, vector2) {
    return new Metrics.Vector(
        vector1.y * vector2.z - vector1.z * vector2.y,
        vector1.z * vector2.x - vector1.x * vector2.z,
        vector1.x * vector2.y - vector1.y * vector2.x
    );
};

// проекция вектора1 на вектор2, вдоль вектора2
Metrics.Vector.project = function (vector1, vector2) {
    var square2 = Metrics.Vector.dotProduct(vector2, vector2);

    if (square2) {
        return Metrics.Vector.scale(vector2, Metrics.Vector.dotProduct(vector1, vector2) / square2);
    }

    return new Metrics.Vector();
};

Metrics.Vector.component = function (vector1, vector2) {
    return Metrics.Vector.dotProduct(vector1, Metrics.Vector.normalize(vector2));
};

Metrics.Vector.perpendicular = function (vector1, vector2) {
    return Metrics.Vector.subtract(vector1, Metrics.Vector.project(vector1, vector2));
};

Metrics.Vector.rotate = function (vector, angle, axis) {
    var cosAngle = Math.cos(angle);
    var sinAngle = Math.sin(angle);

    switch (axis) {
        case "x":
            return new Metrics.Vector(
                vector.x,
                vector.y * cosAngle + vector.z * sinAngle,
                vector.z * cosAngle - vector.y * sinAngle
            );
        case "y":
            return new Metrics.Vector(
                vector.x * cosAngle + vector.z * sinAngle,
                vector.y,
                vector.z * cosAngle - vector.x * sinAngle
            );
        default:
            return new Metrics.Vector(
                vector.x * cosAngle + vector.y * sinAngle,
                vector.y * cosAngle - vector.x * sinAngle,
                vector.z
            );
    }
};

Metrics.Vector.prototype.toString = function () {
    return "(" + (this._enum ? 'enum:' + this._enum + ', ' : '') + this.x + ", " + this.y + ", " + this.z + ")";
};

Metrics.Vector.prototype.cosWith = function (B) {
    var a2 = this.x * this.x + this.y * this.y + this.z * this.z;
    var b2 = B.x * B.x + B.y * B.y + B.z * B.z;
    var c = this.clone().subtract(B);
    var c2 = c.x * c.x + c.y * c.y + c.z * c.z;
    var cos = (a2 + b2 - c2) / Math.sqrt(4 * a2 * b2);
    return Math.abs(cos + 1) > 1e-9 ? Math.abs(cos - 1) > 1e-9 ? cos : 1 : -1;
}

Metrics.Vector.prototype.angleWith = function (B, precision) {
    var angle = Math.acos(this.cosWith(B));
    return precision ? Math.round(angle * 180 / Math.PI / precision) * precision : angle;
}

Metrics.Vector.angle = function (A, B, C, precision) {
    return C ? A.clone().subtract(B).angleWith(C.clone().subtract(B), precision) :
        A.angleWith(B, precision);
}

Metrics.Vector.distance = function (A, B, precision) {
    var d = A.clone().subtract(B).length();
    return precision ? Math.round(d / precision) * precision : d;
}

Metrics.Vector.prototype.distance = function (B, precision) {
    return Metrics.Vector.distance(this, B, precision);
}

Vector.prototype.carousel = function () {
    var t = this.x;
    this.x = this.y;
    this.y = this.z;
    this.z = t;
    return this;
}

//############################################################################## plane

Plane =
    Metrics.Plane = function (A, B, C, D) {
        if (arguments.length == 2) {
            Plane.apply(this, [A.x, A.y, A.z, B || 0]);
            return;
        }
        if (typeof D != 'number') {
            Plane.apply(this, [A, B, C, 0]);
            this.D = -this.result(D);
            return;
        }
        this.A = A;
        this.B = B;
        this.C = C;
        this.D = D;
    }
Plane.prototype = {
    normal: function () {
        return new Vector(this.A, this.B, this.C);
    },

    normalize: function () {
        var length = Math.sqrt(this.A * this.A + this.B * this.B + this.C * this.C);
        this.A /= length;
        this.B /= length;
        this.C /= length;
        this.D /= length;
        return this;
    },

    result: function (p) {
        return this.A * p.x + this.B * p.y + this.C * p.z + this.D;
    },

    clone: function () {
        return new Plane(this.A, this.B, this.C, this.D);
    },

    revert: function () {
        this.A = -this.A;
        this.B = -this.B;
        this.C = -this.C;
        this.D = -this.D;
        return this;
    },

    crossWithRay: function (point, vector) {
        return point.clone().add(
            vector.clone().scale(
                -(Vector.dotProduct(this.normal(), point) + this.D) / Vector.dotProduct(this.normal(), vector)
            )
        );
    },

    _add: function (p) {
        this.A += p.A;
        this.B += p.B;
        this.C += p.C;
        this.D += p.D;
        return this;
    }
};

Plane.average = function (p1, p2,
                          acute, // true/false = разбивать острый/тупой угол
                          impossibleResult  // вернется если результат не определен
) {
    if (arguments.length < 3) {
        return [
            Plane.average(p1, p2, false),
            Plane.average(p1, p2, true)
        ];
    }
    p1 = p1.clone().normalize();
    p2 = p2.clone().normalize();
    var cos = p1.normal().cosWith(p2.normal());
    // cos > 0 => сумма нормалей внутри тупого угла => сумма нормальных плоскостей внутри острого
    var revert = cos > 0 ^ acute;
    var ret = p1._add(revert ? p2.revert() : p2).normalize();
    if (isNaN(ret.A) || isNaN(ret.B) || isNaN(ret.C) || isNaN(ret.D))
        ret = (arguments.length == 4) ? typeof(impossibleResult) == 'function' ? impossibleResult() : impossibleResult :
            console.warn('Plane.average(): impossible');
    return ret;
}

//############################################################################## not linear solutions

Solutions = {
    planesCross: function (p0, p1, p2) {
        var A = [p0.A, p1.A, p2.A];
        var B = [p0.B, p1.B, p2.B];
        var C = [p0.C, p1.C, p2.C];
        var D = [p0.D, p1.D, p2.D];

        return new Vector(
            -h3(B, C, D) / h3(B, C, A),
            -h3(C, A, D) / h3(C, A, B),
            -h3(A, B, D) / h3(A, B, C)
        );

        function h3(A, B, C) {
            return 0 +
                C[0] * (A[2] * B[1] - A[1] * B[2]) +
                C[1] * (A[0] * B[2] - A[2] * B[0]) +
                C[2] * (A[1] * B[0] - A[0] * B[1]);
        }
    },
    twoPlanesAndSphere: function (plane0, plane1/*, radius, center*/) {
        //center = center || Vector(0);
        //radius = radius || 1;
        /**
         * вычел 1) из 2, 3) получается такая система:
         * 5) Px*A + Py*B + Pz*C = Pd
         * 6) Qx*A + Qy*B + Qz*C = Qd
         * +4)  A*A +  B*B +  C*C = 1
         */
        var P = plane0.normal(), //.subtract(center),
            Pd = -plane0.D;
        var Q = plane1.normal(), //.subtract(center),
            Qd = -plane1.D;
        // крутанем
        var mapi = 0, value = 0;
        for (var i = 0; i < 3; i++) {
            P.carousel();
            Q.carousel();
            //mapi++;
            /**
             * 5 =>    7) A = (Pd - Py*B - Pz*C) / Px
             * 6 =>    8) A = (Qd - Qy*B - Qz*C) / Qx
             * 7,8 =>  9) (Pd - Py*B - Pz*C) * Qx = (Qd - Qy*B - Qz*C) * Px
             * 9 =>   10) (Qy*Px - Py*Qx)*B + (Qz*Px - Pz*Qx)*C = Qd*Px - Pd*Qx
             * 5,6 => 11) (Qx*Py - Px*Qy)*A + (Qz*Py - Pz*Qy)*C = Qd*Py - Pd*Qy
             * => переход к новым буквам
             */
            var _Ry = Q.y * P.x - P.y * Q.x, _Rz = Q.z * P.x - P.z * Q.x, _Rd = Qd * P.x - Pd * Q.x;
            var _Sx = Q.x * P.y - P.x * Q.y, _Sz = Q.z * P.y - P.z * Q.y, _Sd = Qd * P.y - Pd * Q.y;

            var val = Math.min(Math.abs(_Ry), Math.abs(_Sx));

            if (val > value) {
                var Ry = _Ry, Rz = _Rz, Rd = _Rd;
                var Sx = _Sx, Sz = _Sz, Sd = _Sd;
                mapi = i + 1;
                value = val;
            }
            ;
        }
        for (var i = 0; i < mapi; i++) {
            P.carousel();
            Q.carousel();
        }

        if (value < 1e-4)
            console.log('twoPlanesAndSphere(): error');

        for (var i = 0; i < mapi; i++) {
            P.carousel();
            Q.carousel();
        }

        /**
         * 12)        Ry*B + Rz*C = Rd
         * 13) Sx*A +      + Sz*C = Sd
         * +4)  A*A +  B*B +  C*C = 1
         */
        var Ry2 = Ry * Ry, Sx2 = Sx * Sx, Ry2Sx2 = Sx2 * Ry2;
        var Sz2 = Sz * Sz, Rz2 = Rz * Rz;
        /**
         * 12 =>    14) B = (Rd - Rz*C) / Ry
         * 13 =>    15) A = (Sd - Sz*C) / Sx
         * 12,13,4=>16) Ry2*(Sd - Sz*C)^2 + Sx2*(Rd - Rz*C)^2 + Ry2Sx2*C^2 = Ry2Sx2
         * 16 => 17) (Ry2*Sz2 + Sx2*Rz2 + Ry2Sx2)*C^2 - 2*(Ry2*Sd*Sz + Sx2*Rd*Rz)*C + (Ry2*Sd^2 + Sx2*Rd^2 - Ry2Sx2) = 0
         */
        var a = Ry2 * Sz2 + Sx2 * Rz2 + Ry2Sx2, b = -2 * (Ry2 * Sd * Sz + Sx2 * Rd * Rz),
            c = Ry2 * Sd * Sd + Sx2 * Rd * Rd - Ry2Sx2;
        var d = b * b / 4 - a * c;

        if (d < -1e-16)
            return [];
        else if (d < 1e-16)
            d = 0;

        d = Math.sqrt(d);
        //console.log(d);
        var sols = d ? [(-b / 2 - d) / a, (-b / 2 + d) / a] : [(-b / 2) / a];

        if (sols.length != 2)
            console.log('sols.length=' + sols.length);

        //console.log([(-b/2 - d)/a, (-b/2 + d)/a]);
        // var lineNormal = Vector.crossProduct(
        // pp[0].subtract(pp[0].center || Vector(0)),
        // pp[1].subtract(pp[1].center || Vector(0)));
        for (var i = 0; i < sols.length; i++) {
            var C = sols[i],
                A = (Sd - Sz * C) / Sx, // 15)
                B = (Rd - Rz * C) / Ry, // 14)
                n = new Vector(A, B, C);
            // debug
            n.mapi = mapi;
            n.sols = sols;
            // крутим обратно (через вперед)))
            for (var j = mapi; j < 3; j++) {
                n.carousel();
            }
            sols[i] = n;
        }
        return sols;
    }
};

//##############################################################################

Orientation =
    Metrics.Orientation = function () {
        this.right = new Metrics.Vector(1, 0, 0);
        this.up = new Metrics.Vector(0, 1, 0);
        this.forward = new Metrics.Vector(0, 0, 1);
    };

//using("Metrics.Orientation");

Metrics.Orientation.prototype.clone = function () {
    var clone = new Metrics.Orientation();
    clone.right = this.right.clone();
    clone.up = this.up.clone();
    clone.forward = this.forward.clone();
    return clone;
}

Metrics.Orientation.prototype.translateVector = function (v) {
    var
        x = this.right.x * v.x + this.up.x * v.y + this.forward.x * v.z,
        y = this.right.y * v.x + this.up.y * v.y + this.forward.y * v.z,
        z = this.right.z * v.x + this.up.z * v.y + this.forward.z * v.z;
    if (isNaN(x) || isNaN(y) || isNaN(z))
    //alert('Orientation'+this+'.translateVector('+v+'): NaN result ('+x+','+y+','+z+')');
    //alert('x = '+this.right.x+' * '+v.x+' + '+this.up.x+' * '+v.y+' + '+this.forward.x+' * '+v.z);
    //alert('v='+v+', v.x='+v.x+' , v.y='+v.y+' , v.z='+v.z);
    {
        var rep = [];
        for (var i in v)
            rep[rep.length] = i;
        error.error.error.error
    }

    return new Metrics.Vector(
        //this.right.x	 * v.x + this.right.y	 * v.y + this.right.z	 * v.z,
        //this.up.x			* v.x + this.up.y			* v.y + this.up.z			* v.z,
        //this.forward.x * v.x + this.forward.y * v.y + this.forward.z * v.z
        x, y, z
    );
};

Metrics.Orientation.prototype.rotate = function (angle, axis) {
    this.right = Metrics.Vector.rotate(this.right, angle, axis).normalize();
    this.up = Metrics.Vector.rotate(this.up, angle, axis).normalize();
    this.forward = Metrics.Vector.rotate(this.forward, angle, axis).normalize();
    return this;
};

Metrics.Orientation.prototype.rotateFrom = function (vector1, vector2) {
    vector1 = vector1.normalize();
    vector2 = vector2.normalize();
    axis = Vector.crossProduct(vector1, vector2);
    var l12 = Vector.subtract(vector1, vector2).length();
    if (l12 < 0.0001) return false;
    axis.normalize();

    var alpha = (Math.PI - Math.atan2(axis.y, axis.z)); // -patched
    axis = Vector.rotate(axis, alpha, 'x');
    var betta = (Math.PI - Math.atan2(axis.x, axis.z));
    var gamma = Math.acos((2 - l12 * l12) / 2);

    this.rotate(alpha, 'x');
    this.rotate(betta, 'y');
    this.rotate(gamma / 1.4142, 'z');
    this.rotate(-betta, 'y');
    this.rotate(-alpha, 'x');

    return true;
};

Metrics.Orientation.prototype.toString = function () {
    return "(" + this.right + ", " + this.up + ", " + this.forward + ")";
};
//##############################################################################

Quat =
    Metrics.Quat = function (w, x, y, z) {
        if (arguments.length == 4) {
            this.w = w;
            this.x = x;
            this.y = y;
            this.z = z;
        } else {
            this.w = 1;
            this.x = 0;
            this.y = 0;
            this.z = 0;
        }
    };

//using("Metrics.Quat");

Metrics.Quat.prototype.clone = function () {
    return new Metrics.Quat(this.w, this.x, this.y, this.z);
};

Metrics.Quat.prototype.normalize = function () {
    var length = this.w * this.w + this.x * this.x + this.y * this.y + this.z * this.z;

    if (Math.abs(length - 1) > 0.001) {
        length = Math.sqrt(length);

        this.w /= length;
        this.x /= length;
        this.y /= length;
        this.z /= length;
    }
};

Metrics.Quat.prototype.translateVector = function (v) {
    var xx = 2 * this.x * this.x;
    var yy = 2 * this.y * this.y;
    var zz = 2 * this.z * this.z;

    var xw = 2 * this.x * this.w;
    var xy = 2 * this.x * this.y;
    var xz = 2 * this.x * this.z;

    var yw = 2 * this.y * this.w;
    var yz = 2 * this.y * this.z;

    var zw = 2 * this.z * this.w;

    return new Metrics.Vector(
        v.x * (1 - yy - zz) + v.y * (xy + zw) + v.z * (xz - yw),
        v.x * (xy - zw) + v.y * (1 - xx - zz) + v.z * (yz + xw),
        v.x * (xz + yw) + v.y * (yz - xw) + v.z * (1 - xx - yy)
    );
};

Metrics.Quat.multiply = function (q1, q2) {
    var result = new Quat(
        q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z,
        q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y,
        q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x,
        q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w
    );

    result.normalize();

    return result;
};

Metrics.Quat.fromAxisRotation = function (axisVector, angle) {
    var sinAngle = Math.sin(angle / 2);

    return new Metrics.Quat(
        Math.cos(angle / 2),
        axisVector.x * sinAngle,
        axisVector.y * sinAngle,
        axisVector.z * sinAngle
    );
};

Metrics.Quat.rotate = function (quat, axisVector, angle) {
    return Metrics.Quat.multiply(Metrics.Quat.fromAxisRotation(axisVector, angle), quat);
};

//##############################################################################

Rectangle =
    Metrics.Rectangle = function (pos, dims) {
        this.pos = pos.clone();
        this.dims = dims.clone();
    };

//using("Metrics.Rectangle");

Metrics.Rectangle.prototype.clone = function () {
    return new Metrics.Rectangle(this.pos, this.dims);
};

Metrics.Rectangle.prototype.getPos = function () {
    return this.pos.clone();
};

Metrics.Rectangle.prototype.getDims = function () {
    return this.dims.clone();
};

Metrics.Rectangle.prototype.getTopLeft = function () {
    return this.pos.clone();
};

Metrics.Rectangle.prototype.getTopRight = function () {
    return new Metrics.Vector(this.pos.x + this.dims.x - 1, this.pos.y);
};

Metrics.Rectangle.prototype.getBottomLeft = function () {
    return new Metrics.Vector(this.pos.x, this.pos.y + this.dims.y - 1);
};

Metrics.Rectangle.prototype.getBottomRight = function () {
    return new Metrics.Vector(this.pos.x + this.dims.x - 1, this.pos.y + this.dims.y - 1);
};

Metrics.Rectangle.prototype.overlaps = function (rect) {
    return (this.pos.x + this.dims.x > rect.pos.x) &&
        (this.pos.y + this.dims.y > rect.pos.y) &&
        (rect.pos.x + rect.dims.x > this.pos.x) &&
        (rect.pos.y + rect.dims.y > this.pos.y);
};

Metrics.Rectangle.prototype.containsPoint = function (point) {
    return point.x >= this.pos.x && point.x + this.pos.x < this.dims.x &&
        point.y >= this.pos.y && point.y + this.pos.y < this.dims.y;
};

Metrics.Rectangle.prototype.containsRect = function (rect) {
    return rect.pos.x >= this.pos.x && rect.pos.x + rect.dims.x <= this.pos.x + this.dims.x &&
        rect.pos.y >= this.pos.y && rect.pos.y + rect.dims.y <= this.pos.y + this.dims.y;
};

Metrics.Rectangle.prototype.clipPoint = function (point) {
    var result = point.clone();

    if (result.x < this.pos.x) {
        result.x = this.pos.x;
    } else if (result.x >= this.pos.x + this.dims.x) {
        result.x = this.pos.x + this.dims.x - 1;
    }

    if (result.y < this.pos.y) {
        result.y = this.pos.y;
    } else if (result.y >= this.pos.y + this.dims.y) {
        result.y = this.pos.y + this.dims.y - 1;
    }

    return result;
};

Metrics.Rectangle.prototype.clipRect = function (rect) {
    var x = Math.max(this.pos.x, rect.pos.x);
    var y = Math.max(this.pos.y, rect.pos.y);

    var w = Math.min(this.pos.x + this.dims.x, rect.pos.x + rect.dims.x) - x;
    var h = Math.min(this.pos.y + this.dims.y, rect.pos.y + rect.dims.y) - y;

    if (w > 0 && h > 0) {
        return new Rectangle(new Vector(x, y), new Vector(w, h));
    } else {
        return null;
    }
};

Metrics.Rectangle.prototype.toString = function () {
    return this.pos + ", " + this.dims;
};

//##############################################################################

Metrics.getViewportWidth = function (win) {
    win = win || self;

    if (win.innerWidth) {
        // all except IE
        return win.innerWidth;
    }

    if (win.document.documentElement && win.document.documentElement.clientWidth) {
        // IE6 strict
        return Math.min(win.document.documentElement.clientWidth, win.document.body.clientWidth);
    }

    // other IE
    return win.document.body.clientWidth;
};

Metrics.getViewportHeight = function (win) {
    win = win || self;

    if (win.innerHeight) {
        // all except IE
        return win.innerHeight;
    }

    if (win.document.documentElement && win.document.documentElement.clientHeight) {
        // IE6 strict
        return Math.min(win.document.documentElement.clientHeight, win.document.body.clientHeight);
    }

    // other IE
    return win.document.body.clientHeight;
};

Metrics.getViewportDims = function (win) {
    win = win || self;
    var width, height;

    if (win.innerWidth) {
        width = win.innerWidth;
        height = win.innerHeight;
    } else if (win.document.documentElement && win.document.documentElement.clientWidth) {
        // IE6 strict
        if (win.document.documentElement.clientWidth < win.document.body.clientWidth) {
            width = win.document.documentElement.clientWidth;
            height = win.document.documentElement.clientHeight;
        } else {
            width = win.document.body.clientWidth;
            height = win.document.body.clientHeight;
        }
    } else {
        // other IE
        width = win.document.body.clientWidth;
        height = win.document.body.clientHeight;
    }

    return new Metrics.Vector(width, height);
};

Metrics.getViewportTop = function (win) {
    win = win || self;

    if (win.pageYOffset !== undefined) {
        return win.pageYOffset;
    }

    if (win.document.documentElement && win.document.documentElement.scrollTop) {
        return win.document.documentElement.scrollTop;
    }

    return win.document.body.scrollTop;
};

Metrics.getViewportLeft = function (win) {
    win = win || self;

    if (win.pageXOffset !== undefined) {
        return win.pageXOffset;
    }

    if (win.document.documentElement && win.document.documentElement.scrollLeft) {
        return win.document.documentElement.scrollLeft;
    }

    return win.document.body.scrollLeft;
};

Metrics.getViewportPos = function (win) {
    win = win || self;
    var x, y;

    if (win.pageYOffset !== undefined) {
        x = win.pageXOffset;
        y = win.pageYOffset;
    } else if (win.document.documentElement && win.document.documentElement.scrollTop) {
        x = win.document.documentElement.scrollLeft;
        y = win.document.documentElement.scrollTop;
    } else {
        x = win.document.body.scrollLeft;
        y = win.document.body.scrollTop;
    }

    return new Metrics.Vector(x, y);
};

Metrics.getViewportRect = function (win) {
    return new Metrics.Rectangle(Metrics.getViewportPos(win), Metrics.getViewportDims(win));
};

Metrics.getDocumentWidth = function (win) {
    win = win || self;

    return Math.max(win.document.body.scrollWidth, win.document.offsetWidth);
};

Metrics.getDocumentHeight = function (win) {
    win = win || self;

    return Math.max(win.document.body.scrollHeight, win.document.offsetHeight);
};

Metrics.getDocumentDims = function (win) {
    win = win || self;
    var width, height;

    if (win.document.body.scrollHeight > win.document.body.offsetHeight) {
        width = win.document.body.scrollWidth;
        height = win.document.body.scrollHeight;
    } else {
        width = win.document.body.offsetWidth;
        height = win.document.body.offsetHeight;
    }

    return new Metrics.Vector(width, height);
};

Metrics.getWidth = function (node) {
    if (node.offsetWidth !== undefined) {
        return node.offsetWidth;
    } else if (node.clientWidth !== undefined) {
        return node.clientWidth;
    } else if (node.width !== undefined) {
        return node.width;
    } else {
        throw "Metrics.getWidth doesn't work in this browser";
    }
};

Metrics.getHeight = function (node) {
    if (node.offsetHeight !== undefined) {
        return node.offsetHeight;
    } else if (node.clientHeight !== undefined) {
        return node.clientHeight;
    } else if (node.height !== undefined) {
        return node.height;
    } else {
        throw "Metrics.getHeight doesn't work in this browser";
    }
};

Metrics.getDims = function (node) {
    var width, height;

    if (node.offsetWidth !== undefined) {
        width = node.offsetWidth;
        height = node.offsetHeight;
    } else if (node.clientWidth !== undefined) {
        width = node.clientWidth;
        height = node.clientHeight;
    } else if (node.width !== undefined) {
        width = node.width;
        height = node.height;
    } else {
        throw "Metrics.getDims doesn't work in this browser";
    }

    return new Metrics.Vector(width, height);
};

Metrics.setDims = function (node, dims) {
    node.style.width = dims.x + "px";
    node.style.height = dims.y + "px";

    var adjustment = Vector.subtract(dims, Metrics.getDims(node));

    if (!adjustment.isZero()) {
        dims = Vector.add(dims, adjustment);

        node.style.width = dims.x + "px";
        node.style.height = dims.y + "px";
    }
};

Metrics.getInnerWidth = function (node) {
    if (node.clientWidth !== undefined) {
        return node.clientWidth;
    } else if (node.width !== undefined) {
        return node.width;
    } else {
        throw "Metrics.getInnerWidth doesn't work in this browser";
    }
};

Metrics.getInnerHeight = function (node) {
    if (node.clientHeight !== undefined) {
        return node.clientHeight;
    } else if (node.height !== undefined) {
        return node.height;
    } else {
        throw "Metrics.getInnerHeight doesn't work in this browser";
    }
};

Metrics.getInnerDims = function (node) {
    var width, height;

    if (node.clientWidth !== undefined) {
        width = node.clientWidth;
        height = node.clientHeight;
    } else if (node.width !== undefined) {
        width = node.width;
        height = node.height;
    } else {
        throw "Metrics.getInnerDims doesn't work in this browser";
    }

    return new Metrics.Vector(width, height);
};

Metrics.getOffsetLeft = function (node) {
    return node.offsetLeft;
};

Metrics.getOffsetTop = function (node) {
    return node.offsetTop;
};

Metrics.getOffsetPos = function (node) {
    return new Metrics.Vector(node.offsetLeft, node.offsetTop);
};

Metrics.getOffsetRect = function (node) {
    return new Metrics.Rectangle(Metrics.getOffsetPos(node), Metrics.getDims(node));
};

Metrics.setOffsetPos = function (node, pos) {
    node.style.left = pos.x + "px";
    node.style.top = pos.y + "px";
};

Metrics.setOffsetRect = function (node, rect) {
    Metrics.setOffsetPos(node, rect.pos);
    Metrics.setDims(node, rect.dims);
};

Metrics.getLeft = function (node) {
    var x = 0;

    do {
        x += node.offsetLeft;
        node = node.offsetParent;
    } while (node);

    return x;
};

Metrics.getTop = function (node) {
    var y = 0;

    do {
        y += node.offsetTop;
        node = node.offsetParent;
    } while (node);

    return y;
};

Metrics.getPos = function (node) {
    var x = 0;
    var y = 0;

    do {
        x += node.offsetLeft;
        y += node.offsetTop;
        node = node.offsetParent;
    } while (node);

    return new Metrics.Vector(x, y);
};

Metrics.getRect = function (node) {
    return new Metrics.Rectangle(Metrics.getPos(node), Metrics.getDims(node));
};

Metrics.setPos = function (node, pos) {
    if (node.offsetParent) {
        var offsetPos = Vector.subtract(pos, Metrics.getPos(node.offsetParent));
        Metrics.setOffsetPos(node, offsetPos);
    } else {
        Metrics.setOffsetPos(node, pos);
    }
};

Metrics.setRect = function (node, rect) {
    Metrics.setPos(node, rect.pos);
    Metrics.setDims(node, rect.dims);
};

</script>
<script type="text/javascript">/**
 * Figure is an object with properties:
 *   .$points  $([points])
 *   .type     string
 *
 * Vertex, Line, Face and Polygon definition.
 *
 * @author   popitch@yandex.ru
 */

function Figure(params) {
    // self as origin
    this.origin = this;

    // origin may be updated here
    $.extend(this, params);

    // базовые точки
    this.$points = $(params.$points || params.points ||
        console.log('Figure(): points not present'));
    this.$points.length > 0 ||
    console.log('Figure(): wrong count of points');

    // тип
    this.type = this.type ||
        (this.$points.length < 4 ?
            [null, 'vertex', 'line', 'face'][this.$points.length] : 'polygon');

    this.removed = false;

    this._enum = Figure.__enum++;
}

Figure.__enum = 0;

Figure.prototype = {
    // поворот точек фигуры вокруг оси
    rotate: function (angle, axis) {
        this.$points.each(function () {
            var v = Vector.rotate(this, angle, axis);
            this.x = v.x;
            this.y = v.y;
            this.z = v.z;
        });
        return this;
    },
    // возвращает массив примитивов
    primitives: function () {
        return $([this]);
    },

    /**
     * Деление фейсов фигуры (равные хорды)
     * @return this
     */
    splitFaces: function (N) {
        N = N || 2;
        var figure = this;

        var points = {};
        figure.$points.each(function (i) {
            points[this._enum = this._enum || i] = this;
        });
        var $faces = $([]);

        function addFace(a, b, c) {
            $faces.push(new Figure({
                type: 'face',
                $points: $([a, b, c]).map(function () {
                    return points[this]
                })
            }));
            addLine(a, b);
            addLine(b, c);
            addLine(c, a);
        }

        var lines = {};

        function addLine(a, b) {
            var uniq = [a, b].sort().join('-');
            lines[uniq] = lines[uniq] || new Figure({
                type: 'line',
                $points: $([a, b]).map(function () {
                    return points[this]
                })
            });
        }

        var verts = {}, $splitted = $([]);
        figure.$primitives.each(function (i) {
            if (this.type == 'face') {
                var face = this;
                var f = face.$points.map(function () {
                    return this._enum;
                }).get();
                var mini = f.slice(0).sort(function (a, b) {
                    return a - b
                })[0];
                while (f[0] != mini) f.push(f.shift());
                var ai = f[0], bi = f[1], ci = f[2];
                var up = [ai];
                for (var n = 1; n <= N; n++) {
                    var lu = [ai, bi, n].join('-'),
                        li = verts[lu] || (verts[lu] =
                            figure.splitFaces_middlePointEnum(ai, bi, n / N, points));
                    var ru = [ai, ci, n].join('-'),
                        ri = verts[ru] || (verts[ru] =
                            figure.splitFaces_middlePointEnum(ai, ci, n / N, points));
                    var down = [], i;
                    for (i = 0; i <= n; i++) {
                        var mu = (li < ri) ? '' + li + '-' + ri + '-' + i : '' + ri + '-' + li + '-' + (n - i);
                        var mi = verts[mu] ? verts[mu] : (verts[mu] =
                            figure.splitFaces_middlePointEnum(li, ri, i / n, points));
                        down.push(mi);
                    }
                    addFace(up[0], down[0], down[1]);
                    for (i = 1; i < up.length; i++) {
                        addFace(up[i], up[i - 1], down[i]);
                        addFace(up[i], down[i], down[i + 1]);
                    }
                    up = down;
                }
                $splitted.push(face);
            }
        });

        // заливаем новую форму
        figure.$primitives = $faces;
        figure.$points.each(function () {
            figure.$primitives.push(new Figure({
                type: 'vertex',
                points: [this],
                center: this.center
            }));
        });
        for (var i in lines) {
            figure.$primitives.push(lines[i]);
        }

        // добавляем преобразование к названию фигуры
        this.type += '.' + 'v(' + N + ')';

        return this;
    },

    splitFaces_middlePointEnum: function (ai, bi, q, enum2point) {
        if (q == 0) return ai;
        if (q == 1) return bi;
        var a = enum2point[ai], b = enum2point[bi];
        // определение центра кривизны
        var center, selvage = a.center && (center = b.center);
        center = selvage ? center : new Vector();
        /*selvage || (center.radius = 1);*/
        var point = Vector.add(
            Vector.subtract(a, center).scale(1 - q),
            Vector.subtract(b, center).scale(q)
        ).normalize()./*scale(center.radius).*/add(center);
        selvage && (point.center = center);
        point._enum = this.$points.length;
        this.$points.push(point);
        enum2point[point._enum] = point;
        return point._enum;
    },

    /**
     * срез фигуры по координате
     * @return this
     */
    sliceByAxis: function (axis, partial, filtrate) {
        if (partial == '1' || partial == '1/1') {
            _.each(this.$primitives, function (p) {
                p.live = true;
            });
            return this;
        }

        var threshold = 1 - 2 * eval(partial);

        this.$points.each(function () {
            this._remain = 0;
        });

        this.$primitives.each(function () {
            var $pp = this.$points, centroid = 0;

            if ($pp.length >= 3) {
                $.map($pp, function (p) {
                    centroid += p[axis];
                });
                centroid /= $pp.length;
                if (Math.abs(centroid - threshold) > 1e-6 && centroid > threshold) {
                    $.map($pp, function (p) {
                        p._remain = 1;
                    });
                }
            }
        });

        this.$primitives = this.$primitives.map(function () {
            var saved = 0;
            this.$points.each(function () {
                saved += this._remain || 0;
            });
            this.live = (saved == this.$points.length);
            return (filtrate === false) || this.live ? this : null;
        });

        this.$points = this.$points.map(function () {
            var live = (filtrate === false) || this._remain;
            delete this._remain;
            return live ? this : null
        });
        this.points = this.$points.get();

        return this;
    },

    /**
     * срез фигуры по остающейся доле фейсов в результате
     */
    sliceByFraction: function (startVertex, fraction, filtrate) {
        if (fraction == '1' || fraction == '1/1') {
            _.each(this.$primitives, function (p) {
                p.live = true;
            });
            return this;
        }

        fraction = eval(fraction);

        var $all = this.$primitives;
        var facesTotal = this.subs('face').length;
        var $wave = startVertex.$super.face;

        for (var faces = 0; faces / facesTotal < fraction;) {
            $wave.each(function () {
                $(this).add(this.$sub.line).add(this.$sub.vertex).each(function () {
                    this.live = true;
                });
                this.$sub.line.each(function () {
                    this.$super.face.each(function () {
                        this._ready = true;
                    });
                    this.$points.each(function () {
                        this.live = true;
                    });
                });
                faces++;
            });
            $wave = $all.filter(function () {
                return this._ready && !this.live;
            });
        }

        // live if live origin
        $all.each(function () {
            this.live = this.origin.live;
            delete this._ready;
        });

        if (filtrate !== false) {
            this.$primitives = $all.filter(function () {
                return this.live;
            });

            this.$points = this.$points.filter(function () {
                var leave = this.live;
                delete this.live;
                return leave;
            });
            this.points = this.$points.get();
        }

        return this;
    },

    /**
     * Making a (inscribed) fulleren
     */
    fulleren: function () {
        var pointsBefore = this.$points.length;
        var fig = this,
            newVertexByLine = {},
            fullerPrimitives = [],
            nil = Vector(0);

        function divideLine(near, far, line) {/*
			var vertex;

			if (!near.H) {
				near.H = _.reduce(near.$super.line, function(H, line) {
					var l = Vector.distance(line.$points[0], line.$points[1]);
					line.H = l * l / 2;
					return H + line.H;
				}, 0);
				near.H = near.H / near.$super.line.length; */
            /*Math.pow(near.H, 1 / near.$super.line.length);*/
            /*
			}
			var q = near.H / line.H / 3; // 1/3;

			var mid = near.$points[0].clone().scale(1 - q).add(far.$points[0].clone().scale(q));
			//mid.scale(1 / mid.length()).add(line.center);*/
            var Near = near, vertex;
            near = near.$points[0].clone().subtract(near.center);
            far = far.$points[0].clone().subtract(far.center);

            //var alfa = near.angleWith(far);
            var q = 1 / 3;//0.5 - Math.tan(alfa / 6) * ( 0.5 / Math.tan(alfa / 2) );
            var mid = Vector.add(near.scale(1 - q), far.scale(q));
            mid.scale(1 / mid.length()).add(line.center);

            mid.center = line.center;
            mid.selvage = line.selvage;
            mid.sliced = line.sliced;

            // вершины рождаются здесь
            fig.$points.push(mid);
            fullerPrimitives.push(vertex = new Figure({
                type: 'vertex',
                points: [mid],
                center: near.center || nil,
                selvage: near.selvage,
                sliced: near.sliced
            }));
            return vertex;
        }

        // вершины образуются из ребер, по две на каждое
        fig.$primitives.each(function () {
            if (this.type == 'line') {
                var le = this._enum, vv = this.$sub.vertex;
                var vA = divideLine(vv[0], vv[1], this);
                var vB = divideLine(vv[1], vv[0], this);
                newVertexByLine[le] = [vA, vB];

                // ребра-остатки (середина базовых), половина всех ребер
                fullerPrimitives.push(new Figure({
                    type: 'line',
                    points: [vA.$points[0], vB.$points[0]],
                    center: this.center,
                    selvage: this.selvage,
                    sliced: this.sliced/*,
					baseLineLength: Math.round(Vector.distance(vv[0].$points[0], vv[1].$points[0]) * 1e-6)*/
                }));
            }
        });

        // ищет в фейсе новые вершины, образованные от заданных, возвращает в том же порядке
        function getNewPoints(p0, p1, face) {
            var ret;
            face.$sub.line.each(function () {
                var vv = this.$sub.vertex;
                var nvv = newVertexByLine[this._enum];
                ret = ret ||
                    (vv[0].$points[0] == p0 && vv[1].$points[0] == p1 && nvv) ||
                    (vv[0].$points[0] == p1 && vv[1].$points[0] == p0 && [nvv[1], nvv[0]]);
            });
            return [ret[0].$points[0], ret[1].$points[0]];
        }

        // для вершинных фейсов
        var newVertexesByVertex = {};

        // грани и половина ребер
        fig.$primitives.each(function () {
            if (this.type == 'face') {
                var face = this;
                var p0 = face.$points[1], p1 = face.$points[2];
                var v6 = [];
                face.$points.each(function () {
                    var p2 = this;
                    /*var baseLine = face.$sub.line.filter(function() {
							return (this.$points[0] === p0 && this.$points[1] === p2) ||
								(this.$points[1] === p0 && this.$points[0] === p2);
						})[ 0 ];*/

                    // точки нового фейса-остатка
                    var nvv01 = getNewPoints(p0, p1, face);
                    v6.push(nvv01[0]);
                    v6.push(nvv01[1]);

                    // ребра-скосы (образованные срезанием вершины), половина всех ребер
                    var nvv12 = getNewPoints(p1, p2, face);
                    fullerPrimitives.push(new Figure({
                        type: 'line',
                        points: [nvv01[1], nvv12[0]]/*,
						baseLineLength: Math.round(Vector.distance(baseLine.$points[0], baseLine.$points[1]) * 1e+6)*/
                    }));

                    // для вершинных фейсов
                    newVertexesByVertex[p1._enum] = newVertexesByVertex[p1._enum] || {};
                    newVertexesByVertex[p1._enum][p2._enum] = {
                        point: nvv12[0],
                        next: p0._enum
                    };

                    p0 = p1;
                    p1 = p2;
                });

                if (v6.length != 6)
                    console.log('v6.length != 6');

                // фейсы-остатки (середина фейсов-предшественников), столько же сколько было фейсов
                fullerPrimitives.push(new Figure({
                    type: 'face',
                    points: v6
                }));
            }
        });

        // фейсы-скосы (при вершинах, кроме крайних пока)
        fig.$primitives.each(function () {
            if (this.type == 'vertex' && !this.selvage) {
                var aster = newVertexesByVertex[this.$points[0]._enum],
                    c = 0;
                for (var i in aster) c++;
                if (c >= 5) {
                    var last = i, pp = [];
                    do {
                        pp.push(aster[i].point);
                        i = aster[i].next;
                    } while (i != last);

                    if (pp.length < 5)
                        console.log('face points.length < 5');

                    // фейсы-скосы (при вершинах), столько же сколько было вершин
                    fullerPrimitives.push(new Figure({
                        type: 'face',
                        points: pp
                    }));
                }
            }
        });

        this.$primitives = $(fullerPrimitives);
        this.$points = $(Array.prototype.slice.call(this.$points, pointsBefore));

        /*Figure.equalizeLineGroups(
			_.groupBy(_.where(this.$primitives, { type: 'line' }), 'baseLineLength')
		);*/

        return this;
    },

    /**
     * @required prepareUnify() before
     */
    outerFulleren: function () {
        var prevVertexes = this.subs('vertex'),
            prevFaces = this.subs('face'),
            nextLines = {},
            center = new Vector; // todo: remove center from project

        var nextVertexes = _.map(prevFaces, function (face) {
            face.newPoint = Solutions.planesCross.apply(this,
                _.map(face.$points, function (point) {
                    return new Plane(point, point);
                })
            );
            var nextVertex = new Figure({
                type: 'vertex',
                points: [face.newPoint],
                center: center
            });

            face.newPoint._enum = nextVertex._enum;

            return nextVertex;
        });

        var nextFaces = _.map(prevVertexes, function (vertex) {
            var points = _.compact(_.map(vertex.$scheme, function (mem) {
                return mem.isFace && mem.source.newPoint;
            }));

            var prevPoint = _.last(points);
            _.each(points, function (point) {
                var lineIndex = [point._enum, prevPoint._enum].sort().join();
                nextLines[lineIndex] = nextLines[lineIndex] ||
                    new Figure({
                        type: 'line',
                        points: [prevPoint, point]
                    });
                prevPoint = point;
            });

            return new Figure({
                type: 'face',
                points: points,
                _protoPoint: vertex.$points[0]
            });
        });

        this.$primitives = $(nextFaces.concat(_.values(nextLines)).concat(nextVertexes));
        this.$points = $(_.pluck(_.pluck(nextVertexes, '$points'), '0'));

        return this;
    },

    /**
     * Устанавливает отношения (вложенность) примитивов фигуры
     * @return this
     */
    relations: function () {
        var $primitives = this.$primitives;
        // sub & super
        $primitives.each(function () {
            var prim = this;
            prim.$sub = {vertex: $([]), line: $([])};
            var a = this.$points;
            this.$subsets = $primitives.map(function (pi) {
                var b = this.$points;
                if (b.length < a.length) {
                    for (var i = 0, length = b.length; i < length; i++)
                        if (-1 == $.inArray(b[i], a)) {
                            return null;
                        }

                    if (!prim.$sub[this.type])
                        console.log('!prim.$sub[this.type]');

                    prim.$sub[this.type].push(this);
                    return this;
                }
                return null;
            });
            this.$supersets = $([]);
            this.$super = {line: $([]), face: $([])};
        });
        $primitives.each(function () {
            var prim = this;
            this.$subsets.each(function () {
                this.$supersets.push(prim);
                this.$super[prim.type].push(prim);
            });
        });

        // figure subsets, supersets is null
        this.$subsets = $primitives;
        this.$supersets = null;

        // sort face's sub-vertexes in self-points order
        this.subs('face').each(function () {
            var face = this;

            face.$sub.vertex = $(
                _.sortBy(face.$sub.vertex, function (vertex) {
                    return _.indexOf(face.$points, vertex.$points[0]);
                })
            );
        });

        // selvage detection
        this.detectSelvage(); // why this?

        return this;
    },

    // selvage detection
    detectSelvage: function (advAttr) {
        this.$primitives.each(function () {
            this.selvage = false;

            if (this.type == 'line')
                this.faceCount = 0;
        });

        this.$primitives.each(function () {
            if (this.type == 'face')
                this.$sub.line.each(function () {
                    this.faceCount++;
                });
        });

        this.$primitives.each(function () {
            if (this.type == 'line')
                if (this.selvage = (this.faceCount == 1)) {
                    this.$subsets.each(function () {
                        this.selvage = true;
                        if (advAttr) {
                            this[advAttr] = true;
                        }
                    });
                    if (advAttr) {
                        this[advAttr] = true;
                    }
                }
        });
        return this;
    },

    // adv relations
    prepareUnify: function () {
        // order line/face/line/face/.. around vertex
        this.subs('vertex').each(function () {
            var vertex = this;
            var point = vertex.$points[0];
            var chain = [];

            // список звеньев цепочки, с указанием остальных составляющих звено точек
            vertex.$supersets.each(function (i) {
                var pp = this.$points.get();
                var pos = $.inArray(point, pp);
                var others = pp.slice(pos + 1).concat(pp.slice(0, pos));
                var isFace = (others.length > 1);
                chain[i] = {
                    i: i,
                    source: this,
                    others: others,
                    isFace: isFace
                };
                if (isFace)
                    chain[i].faceAngle = Vector.angle(others[0], point, others[others.length - 1]);
                else {// line
                    chain[i].length = point.distance(others[0]);
                    if (chain[i].length < 1e-6)
                        console.log('rib length is nil');
                }
            });
            var iLost = undefined, countNext = 0, countLost = 0;

            // каждому звену прописывается индекс следующего
            vertex.$supersets.each(function (i) {
                var iOthers = chain[i].others;
                var iFace = iOthers.length > 1;

                var found = vertex.$supersets.map(function (j) {
                    var jOthers = chain[j].others;
                    return (jOthers.length > 1 ^ iOthers.length > 1) ? (
                        iOthers[iOthers.length - 1] === jOthers[0] ? ++countNext && j : null
                    ) : null;
                });

                if (found.length > 1)
                    console.log('gt one next chain nodes this');

                var h = found[0];
                if (h !== undefined) {
                    chain[i]._next = h;
                } else {
                    iLost = i;
                    ++countLost;
                }
            });

            // asserts
            if (countNext > vertex.$supersets.length)
                console.log(countNext + ' countNext > vertex.$supersets.length');
            if ((countNext == vertex.$supersets.length) == vertex.selvage)
                console.log('(countNext == vertex.$supersets.length) == vertex.selvage');
            if ((countNext == vertex.$supersets.length - 1) != vertex.selvage)
                console.log('(countNext == vertex.$supersets.length - 1) != vertex.selvage');
            if (countNext < vertex.$supersets.length - 1)
                console.log('countNext < vertex.$supersets.length - 1');

            if (countLost > 1)
                console.log({countLost: countLost, countNext: countNext, chain: chain});
            (iLost !== undefined) === vertex.selvage ||
            console.log('selvage is not salve: ' + iLost + ', ' + countLost);
            (iLost !== undefined) === (countLost == 1) ||
            console.log('selvage is not salve2: ' + iLost + ', ' + countLost);

            // break the chain
            var sortChains = [];
            $(chain).each(function (i) {
                var start = this, curr = this;
                for (sortChains[i] = [];
                     curr && sortChains[i].push(curr) && (curr = chain[curr._next]) !== start;
                ) ;
            });
            sortChains.sort(function (a, b) {
                if (b.length - a.length)
                    return b.length - a.length;
                for (var i = 0, l = a.length, d; i < l; i++) {
                    d = (b[i].faceAngle - a[i].faceAngle) || (b[i].length - a[i].length);
                    if (Math.abs(d) > 1e-3)
                        return d;
                }
                return 0;
            });

            // сохраняем упорядоченный, выбранный список надмножеств
            vertex.$scheme = $(sortChains[0]);
        });

        // для линий: вершины, дополняющие соседние фейсы
        this.subs('line').each(function () {
            var line = this;
            line.$vertexes = line.$sub.vertex;
            line.$adjoinVertexes = $([]);
            line.$adjoinPoints = $([]);
            line.$super.face.each(function () {
                this.$sub.vertex.each(function () {
                    if (-1 == $.inArray(this, line.$vertexes))
                        line.$adjoinVertexes.push(this);
                    line.$adjoinPoints.push(this.$points[0]);
                });
            });

            // для линий: для каждой вершины список линий соседних к линии-сабжу
            line.$nearLinesByVertex = $([]);
            line.$vertexes.each(function () {
                var vertex = this,
                    point = this.$points[0],
                    cont = $([]);
                vertex.$super.line.each(function () {
                    if (this === line) return;
                    if (this.$points[0] === point && -1 != $.inArray(this.$points[1], line.$adjoinPoints))
                        cont.push(this);
                    if (this.$points[1] === point && -1 != $.inArray(this.$points[0], line.$adjoinPoints))
                        cont.push(this);
                });
                line.$nearLinesByVertex.push(cont);
            });

            if (line.$nearLinesByVertex.length != 2)
                console.log('line.$nearLinesByVertex.length != 2');
            if (-1 == $.inArray(line.$nearLinesByVertex[0].length, [1, 2]))
                console.log('line.$nearLinesByVertex.length[0].length: ' + line.$nearLinesByVertex[0].length);
            if (-1 == $.inArray(line.$nearLinesByVertex[1].length, [1, 2]))
                console.log('line.$nearLinesByVertex.length[1].length: ' + line.$nearLinesByVertex[1].length);
        });

        // фейсам выстроим sub- отрезки и вершины по-порядку
        this.subs('face').each(function () {
            var $vv = this.$sub.vertex;
            var mid = new Vector;
            $vv.each(function () {
                mid.add(this.$points[0]);
            });
            mid.scale(1 / $vv.length);

            function orderedVertexes(line) {
                var $vv = line.$sub.vertex;
                var dir = Vector.crossProduct(
                    Vector.subtract($vv[0].$points[0], mid),
                    Vector.subtract($vv[1].$points[0], mid)
                );
                return Vector.dotProduct(dir, mid) > 0 ? $vv : $([$vv[1], $vv[0]]);
            }

            var order = {}, i;
            this.$sub.line.each(function () {
                var vv = orderedVertexes(this);
                order[vv[0]._enum] = {
                    line: this,
                    vv: vv,
                    next: i = vv[1]._enum
                };
            });

            var vertexes = [];
            var lines = [];
            for (var end = i;
                 lines.push(order[i].line) &&
                 vertexes.push(order[i].vv[0]) &&
                 order[i].next != end;
                 i = order[i].next) ;
            this.$sub.line = $(lines);
            this.$sub.vertex = $(vertexes);
        });

        return this;
    },

    // pseudo-self-remove the member
    remove: function () {
        if (this.removed) return [];

        var removed = [this];

        this.removed = true;

        this.$supersets.each(function () {
            removed = removed.concat(this.remove());
        });
        this.$subsets.each(function () {
            var superPresent = this.$supersets.filter(function () {
                return !this.removed;
            });
            if (!superPresent.length)
                removed = removed.concat(this.remove());
        });

        return removed;
    },

    // restore pseudo-removed member
    restore: function () {
        if (!this.removed) return [];

        var restored = [this];

        this.removed = false;

        this.$subsets.each(function () {
            restored = restored.concat(this.restore());
        });

        return restored;
    },

    subs: function (type) {
        return type ? this.$subsets.filter(function () {
            return this.type === type;
        }) : this.$subsets;
    },

    supers: function (type) {
        return type ? this.$supersets.filter(function () {
            return this.type === type;
        }) : this.$supersets;
    },

    /**
     * Приземление кромки фигуры
     */
    groundSliced: function (axis) {
        var points = $.map(this.$primitives, function (p) {
            if (p.type !== 'vertex' || !p.live)
                return null;

            return _.some(p.$supersets, function (s) {
                return !s.live
            }) ? p.$points[0] : null;
        });

        var aval = false;

        // find minimal-absolute value by axis
        _.each(points, function (p) {
            aval = aval && Math.abs(aval) < Math.abs(p[axis]) ? aval : p[axis];
        });

        _.each(points, function (p) {
            if (Math.abs(p[axis] - aval) > 1e-6) {
                p[axis] = aval;
                var len = p.length(), q = Math.sqrt((1 - aval * aval) / (len * len - aval * aval));

                if (axis != 'x') p.x *= q;
                if (axis != 'y') p.y *= q;
                if (axis != 'z') p.z *= q;
            }
        });
    },

    /**
     * Унификация примитивов, поиск одинаковых
     * @return Object stat
     *
     * $todo insert product eacher
     */
    unify: function () {
        var result = {total: {}, count: {}};

        // для некоторых соединений, перед унификацией "крайних" узлов
        // требуется аккумулятор для накопление инфы об остальных узлах
        var accum = {};

        // сначала те, что не на краю,
        // и в порядке: коннекторы, ребра, грани
        var hashOrder = _.where(this.$primitives, {removed: false});

        _.each(['vertex', 'line', 'face'], function (type) {
            var stat = {};

            var ordered = $.map(hashOrder, function (f) {
                return f.type == type && f.live ? f : null
            })
                .sort(function (a, b) {
                    if (a.selvage != b.selvage)
                        return (a.selvage ? 1 : 0) - (b.selvage ? 1 : 0);
                    if (a.type == 'vertex')
                        return a.$points[0].y - b.$points[0].y;
                    return 0;
                });

            $(ordered).each(function (i) {
                var unifier = this.unifier = this.unifier || (this.product ? this.product.unify(accum) : '<unkn>');
                unifier = unifier.replace(/\(\d+\)/g, '');
                stat[unifier] = stat[unifier] || [];
                stat[unifier].push(this);
            });

            // сортировка коллекции по убыванию кол-ва элементов данного типа
            var keys = [];
            $.each(stat, function (key, cont) {
                keys.push({key: key, cont: cont});
            });

            // сортировка
            keys.sort(function (a, b) {
                // по убыванию кол-ва элементов данного типа
                if (b.cont.length != a.cont.length)
                    return (b.cont.length - a.cont.length);

                // line: compare lengths
                if (type == 'line') {
                    var diff = b.cont[0].product.maxLength() - a.cont[0].product.maxLength();
                    if (diff) return diff;
                }

                // сравнение строк-унификаторов
                if (a.key !== b.key) {
                    return ([a.key, b.key].sort()[0] === a.key) ? 1 : -1;
                }

                console.warn('Undefined sort order detected');
            });

            result[type] = {};
            result.total[type] = 0;
            result.count[type] = 0;
            $.each(keys, function (order) {
                var index = Product.index(this.cont[0].type, order);
                result[type][index] = {
                    count: this.cont.length,
                    unifier: this.key,
                    index: index,
                    order: order,
                    collect: this.cont
                };
                $(this.cont).each(function () {
                    this.index = index;
                    this.order = order;
                    //this.groups = result[type];
                });
                result.total[type] += this.cont.length;
                result.count[type]++;
            });
        });

        // вычисление максимальной длины ребра
        var maxOuter = 0, minOuter = Infinity, minInner = null;
        $.each(result.line, function (index, stat) {
            var outer = stat.collect[0].product.maxLength();
            var inner = stat.collect[0].product.minLength();
            maxOuter = outer > maxOuter ? outer : maxOuter;
            minOuter = outer < minOuter ? outer : minOuter;
            minInner = inner < minInner || minInner === null ? inner : minInner;
        });
        $.each(result.line, function (index, stat) {
            stat.collect[0].product.maxOuterLength = maxOuter;
            stat.collect[0].product.minOuterLength = minOuter;
            stat.collect[0].product.minInnerLength = minInner;
        });

        return this.stat = result;
    },

    visible: function () {
        if (this.type == 'polygon' || this.type == 'face') {
            var $p = this.$points.map(function () {
                return this.plane
            });
            return 0 <
                $p[1].x * $p[2].y - $p[1].y * $p[2].x +
                $p[2].x * $p[0].y - $p[2].y * $p[0].x +
                $p[0].x * $p[1].y - $p[0].y * $p[1].x;
        }
        return true;
    },

    prepareVisibility: function () {
        this.$primitives.each(function () {
            this.isVisible = false;
        });
        this.$primitives.each(function () {
            if (this.type == 'face') {
                if (this.isVisible = this.visible()) {
                    this.$subsets.each(function () {
                        this.isVisible = true;
                    });
                }
            }
        });
    }
}

// делает точки окружности равноудаленными с соседями (точки в начале должны быть в одной плоскости)
Figure.pointsEquidistant = function ($points, center, radius) {
    // приведение точек к окружности
    function circle() {
        $points.each(function () {
            this.subtract(center);
            this.scale(radius / this.length());
            this.add(center);
        });
    }

    // соседи
    var $near = [];
    $points.each(function (i) {
        var point = this, arr, $dist = [];
        $near[i] = (
            (arr = $points.get()
                .sort(function (a, b) {
                    return a.distance(point) - b.distance(point);
                }))
                .slice(1, 3)
        );
    });

    // итерируем пока не понравится результат
    function aberration() {
        return $points.map(function (i) {
            return Math.abs($near[i][0].distance(this) - $near[i][1].distance(this));
        }).get().sort(function (a, b) {
            return a - b;
        }).pop();
    }

    do {
        // уводим точки в сторону более удаленного соседа
        var $dirs = $points.map(function (i) {
            return $near[i][0].clone().subtract(this).add($near[i][1]).subtract(this);
        });
        $points.each(function (i) {
            this.add(
                $dirs[i].scale(1 / 5)
            );
        });
        circle();
    } while (aberration() > 1e-15);
}

/**
 * @constructor Container
 */
Figure.Container = function (params) {
    params = params || {};
    var $points = params.points ? $(params.points) : $params.points || $([]);
    var $figures = params.figures ? $(params.figures) : $params.figures || $([]);
    var $primitives = $([]);
    $figures.each(function (i) {
        $primitives = $.merge($primitives, this.primitives());
        //this.source = params.source || console.log('Figure.Container: !source');
    });
    // parent::construct()
    Figure.apply(this, [$.extend({
        type: 'container',
        $points: $points,
        $figures: $figures,
        $primitives: $.unique($primitives)
    }, params)]);
}
    .inherits(Figure);

/**
 * @constructor Icosahedron
 */
Figure.Icosahedron = function (params) {
    var center = Vector(0);

    // defaults
    params = $.extend(
        {
            symmetry: 'Pentad'
        },
        params || {}
    );

    // золотой ключик
    var a = 4 / Math.sqrt(2 * (5 + Math.sqrt(5))) / 2,
        b = Math.sqrt(1 - a * a),
        primitives = [];
    // вершины
    var points = [
        [-a, 0.0, b], [a, 0.0, b], [-a, 0.0, -b], [a, 0.0, -b],
        [0.0, b, a], [0.0, b, -a], [0.0, -b, a], [0.0, -b, -a],
        [b, a, 0.0], [-b, a, 0.0], [b, -a, 0.0], [-b, -a, 0.0]
    ];
    for (var i = 0; i < points.length; i++) {
        points[i] = $.extend(
            new Vector(points[i][0], points[i][1], points[i][2]), {
                _enum: i,
                pptPoint: true
            }
        );
        // вершина
        primitives.push(new Figure({
            type: 'vertex',
            points: [points[i]],
            center: center
        }));
    }
    // грани & ребра
    var faces = [
        [0, 4, 1], [0, 9, 4], [9, 5, 4], [4, 5, 8], [4, 8, 1],
        [8, 10, 1], [8, 3, 10], [5, 3, 8], [5, 2, 3], [2, 7, 3],
        [7, 10, 3], [7, 6, 10], [7, 11, 6], [11, 0, 6], [0, 1, 6],
        [6, 1, 10], [9, 0, 11], [9, 11, 2], [9, 2, 5], [7, 2, 11]
    ], snakeTail = {0: 1, 1: 2, 2: 0};
    for (var i = 0; i < faces.length; i++) {
        var f = faces[i];
        // грань
        primitives.push(new Figure({
            type: 'face',
            points: [points[f[0]], points[f[1]], points[f[2]]]
        }));
        for (var m in snakeTail) {
            var n = snakeTail[m];
            if (f[m] < f[n]) {
                // ребро
                primitives.push(new Figure({
                    type: 'line',
                    points: [points[f[m]], points[f[n]]]
                }));
            }
        }
    }
    // parent::construct()
    Figure.apply(this, [$.extend({
        type: 'Icosahedron',
        $points: $(points),
        $primitives: $(primitives)
    }, params)]);

    // orientate figure for given rotational symmetry around given axis
    switch (params.symmetry) {
        case 'Pentad':
            this.rotate(Math.atan(a / b), {y: 'x', z: 'y', x: 'z'}[params.axis]);
            break;
        case 'Cross':
            break
        case 'Triad':
            this.rotate(Math.asin(2 / (Math.sqrt(3) + Math.sqrt(15))), {y: 'z', z: 'x', x: 'y'}[params.axis]);
            break;
    }
}
    .inherits(Figure)
    .override({
        primitives: function () {
            return this.$primitives;
        }
    });

/**
 * @constructor http://en.wikipedia.org/wiki/Pentakis_dodecahedron
 */
Figure.PentakisDodecahedron = function (params) {
    Figure.Icosahedron.call(this, params);

    this.relations();

    var base = this;
    var points = this.$points;
    primitives = this.subs('vertex');

    _.each(this.subs('face'), function (f) {
        var p = _.reduce(f.$points, Vector.add, new Vector).normalize();
        points.push(p);
        p._enum = primitives.length;
        primitives.push(new Figure({points: [p]}));
        f._dodCenter = p;
    });

    _.each(this.subs('face'), function (f) {
        var C = f._dodCenter;
        var lines = f.subs('line');

        var B = f.$points[2];
        _.each(f.$points, function (A) {
            var line = _.filter(lines, function (l) {
                var pair = l.$points;
                return (pair[0] === A && pair[1] === B) || (pair[1] === A && pair[0] === B);
            })[0];
            var anotherFace = _.difference(line.supers('face'), [f])[0];

            addFace([C, anotherFace._dodCenter, A])
            B = A;
        });
    });

    this.$points = $(this.points = points);
    this.$primitives = $(primitives);

    this.type = 'PentakisDodecahedron';

    function addFace(tri) {
        primitives.push(new Figure({points: tri}));
        for (var i = 0, j = 2; i < 3; j = i++) {
            var A = tri[j], B = tri[i];
            if (A._enum < B._enum) {
                // ребро
                primitives.push(new Figure({points: [A, B]}));
            }
        }
    }
}
    .inherits(Figure);
</script>
<script type="text/javascript">/**
 * Абстракция изделия, необходимого для постройки кислосферы.
 *
 * @author   popitch@yandex.ru
 */

Product = function (params) {
    $.extend(this, params);
    this.cache = {};
}
    .override({
        // точность вычислений
        //PRECISION: 10000,
        //round: function(value) {
        //	return Math.round(value * this.PRECISION) / this.PRECISION;
        //},

        // @return string that defines the product
        unify: function () {
            console.log(['abstract method unify()', this]);
        },

        // @return figure that defines the product
        model: function () {
            //console.log(['abstract method model()', this]); //alcohol yad
            // default null model
            return null;
        },

        // draw product scheme on canvas
        //plot: function(canvas){},

        // product meter
        meter: function () {
            return {};
        }
    })
    .statics({
        characters: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
        index: function (type, order) {
            if (type == 'line')
                return (order >= 26 ? Product.index(type, Math.floor(order / 26) - 1) : '') + Product.characters[order % 26];
            else
                return order + 1;
        },

        selectChain: function (chain, nodeAttrScalar, cyclic, bilateral) {
            chain = $(chain).get();

            function breaked(i) {
                var sample = chain.slice(i).concat(chain.slice(0, i));

                var arr = sample.slice();
                var forward = [], el, i = 0;
                do {
                    if (el = arr.shift()) {
                        forward.push({
                            forward: true,
                            sequence: forward,
                            i: i++,
                            node: el
                        });
                    }
                } while (el && !el.terminate);
                forward[0].node.dontStart ||
                list.push(forward);

                if (!bilateral) {
                    arr = sample.slice();
                    var reversed = [];
                    i = 0;
                    do {
                        if (el = arr.pop()) {
                            reversed.push({
                                forward: false,
                                sequence: reversed,
                                i: i++,
                                node: el
                            });
                        }
                    } while (el && !el.terminate);
                    reversed[0].node.dontStart ||
                    list.push(reversed);
                }
            };

            var list = [];
            cyclic ? $(chain).each(breaked) : breaked(0);

            function zeroDetector(v) {
                return (Math.abs(v) < 1e-5) ? 0 : v;
            }

            var cmp;
            switch (typeof nodeAttrScalar) {
                case 'string':
                    cmp = function (a, b) {
                        return zeroDetector(b.node[nodeAttrScalar] - a.node[nodeAttrScalar]);
                    };
                    break;
                case 'function':
                    cmp = function (a, b) {
                        return zeroDetector(
                            nodeAttrScalar.call(b.node, b.i, b.sequence, b.forward) -
                            nodeAttrScalar.call(a.node, a.i, a.sequence, a.forward));
                    };
                    break;
                default:
                    console.log('Product.selectChain(): wrong comparator')
            }

            // caching sequences of actual values
            $.map(list, function (seq) {
                seq.used = $.map(seq, function (a) {
                    return a.node.sortIgnore ? null : a;
                });
            });

            // get sorted first
            // TODO: use one-cicle algorithm to search
            var selected = list.sort(function (A, B) {
                if (B.used.length - A.used.length)
                    return (B.used.length - A.used.length);

                for (
                    var i = 0, res = 0;
                    i < A.used.length && !(res = cmp(A.used[i], B.used[i]));
                    i++
                ) ;

                return res;
            })[0];

            return $(selected).map(function () {
                this.node.forward = this.forward;
                return this.node;
            }).get();
        },

        // number unifiers
        ANGLE_PRECISION: 0.1, // degrees
        LENGTH_PRECISION: 0.001, // m (to 1 mm)

        angleUnify: function (angle) {
            angle = angle * 180 / Math.PI;
            return Math.round(angle / Product.ANGLE_PRECISION);
        },

        lengthUnify: function (length) {
            return Math.round(length / Product.LENGTH_PRECISION);
        }
    });</script>
<script type="text/javascript">/**
 * Определение продукта многоугольник
 *
 * @author   popitch [at yandex.ru]
 */

Product.Polygon = function () {
    Product.apply(this, arguments);
    // expected
    this.face || console.log('face not specified');
}
    .inherits(Product)
    .override({

        /** отдает вершины продуктового полигона
         *  в сложном случае (например, Joint) вершины не совпадают с вершинами базовой фигуры, задающей форму купола
         */
        realPoints: function () {
            var face = this.face;

            return this.cache.$reals = this.cache.$reals ||
                face.$sub.vertex.map(function () {
                    var vertex = this,
                        point = vertex.$points[0];

                    var planes = $.map(face.bindedLines || face.$sub.line, function (line) {
                        line = line.origin;
                        return (line.$points[0] == point || line.$points[1] == point) ?
                            line.product.getPlane() : null;
                    });

                    var sols = $(Solutions.twoPlanesAndSphere(planes[0], planes[1])).each(function () {
                        this._offset = this.distance(point);
                    });

                    sols.length == 2 ||
                    console.log('polygon unify(): sols.length=' + sols.length);

                    var result = sols[0]._offset < sols[1]._offset ? sols[0] : sols[1];

                    // ~
                    result.scale(point.length());

                    result.vertex = vertex;
                    vertex.real = result;
                    return result;
                });
        },

        realCenter: function () {
            if (this.cache.realCenter)
                return this.cache.realCenter;
            var center = new Vector, $points = this.realPoints();
            $.map($points, function (p) {
                center.add(p);
            });
            return this.cache.realCenter = center.scale(1 / $points.length);
        }
    });

Product.Polygon.Simple = function (face, params) {
    // parent()
    Product.Polygon.apply(this, [$.extend({
        type: 'Polygon',
        title: 'Многоугольник',
        bilateral: false,
        face: face
    }, params || {})]);
}
    .inherits(Product.Polygon)
    .override({
        unify: function (accum) {
            var product = this;
            var $reals = this.realPoints();

            if (this.face._protoPoint) {
                var center = this.face._protoPoint;
            } else {
                center = new Vector;
                $reals.each(function () {
                    center.add(this);
                });
                center.scale(1 / $reals.length);
            }

            // normal
            this.normal = Vector.crossProduct(
                Vector.subtract($reals[0], center),
                Vector.subtract($reals[1], center)
            );
            if (Vector.dotProduct(this.normal, $reals[0]) < 0)
                this.normal.scale(-1);

            var prevPoint = $reals[$reals.length - 1],
                prevVertex = product.face.$sub.vertex[$reals.length - 1],
                chain = [];

            var angleSum = 0;

            product.maxR = 0;

            $reals.each(function (vertexIter) {
                // vertex raduis
                var R = prevPoint.distance(center) * product.R;
                chain.push({
                    value: R,
                    mark: 'R',
                    vertex: prevVertex,
                    center: center
                });

                // side angle
                var prevAngle = Vector.subtract(this, center).angleWith(Vector.subtract(prevPoint, center));
                chain.push({
                    dontStart: true,
                    value: prevAngle,
                    mark: 'A'
                });
                angleSum += prevAngle;

                // max R
                product.maxR = Math.max(product.maxR, R);

                // side length
                chain.push({
                    sortIgnore: true,
                    dontStart: true,
                    vertex: this.vertex,
                    value: this.distance(prevPoint) * product.R,
                    line: _.findWhere(product.face.bindedLines || product.face.$sub.line, {
                        origin: this.vertex.$super.line.map(function () {
                            return this.$vertexes[0] === prevVertex ||
                            this.$vertexes[1] === prevVertex ? this : null;
                        })[0]
                    }),
                    mark: 'L'
                });

                if (!_.last(chain).line) {
                    console.warn('binded line not found');
                }

                prevVertex = this.vertex;
                prevPoint = this;
            });

            // assert: polygon on plane
            if (Math.abs(angleSum - 2 * Math.PI) > 1e-3) {
                console.log('Polygon.unify(): sum of polygon angles is not equal full circle (' + angleSum + ' != 2*PI)');
            }

            // select chain
            this.chain = Product.selectChain(chain, 'value', true, product.bilateral);

            return $(this.chain).map(function () {
                if (this.mark == 'L') return null;
                return this.mark + Product.lengthUnify(this.value);
            }).get().join('-');
        },

        model: function (onExport) {
            if (!onExport)
                return // console.error(this, 'on export only');

            var face = this.face.$points.get();

            // todo: resolve std orientation
            var reverted = [];
            for (var i = 0, p; p = face.pop(); reverted.push(p)) ;

            // one reverted face
            return [reverted];
        },

        plot: function (canvas) {
            var product = this;
            var R = this.maxR;
            var plotter = new Plotter(canvas, {
                width: 2 * R,
                height: 2 * R,
                margin: 20, // px
                resize: true,
                offset: {x: R, y: R}
            });
            //plotter.circle({x: 0, y: 0}, R, ['gray']);

            // plotter point by angle & R
            function pos(a, r) {
                return {
                    x: r * Math.cos(a),
                    y: r * Math.sin(a)
                };
            }

            var center = {x: 0, y: 0};

            // draw polygon
            var a = Math.PI / 2, prev = {};

            // previous (last) segment data
            for (var i = 1; i <= 3; i++) {
                var match = this.chain[this.chain.length - i];
                prev[match.mark] = match.line || match.value;
            }
            // set last plotter-point as previous
            prev.point = pos(a - prev.A, prev.R);

            $(this.chain).each(function () {
                switch (this.mark) {
                    case 'L':
                        prev.L = this.line;
                        break;
                    case 'A':
                        a += this.value;
                        break;
                    case 'R':
                        var point = pos(a, this.value);
                        var worldPoints = $.map(prev.L.$sub.vertex, function (v) {
                            return v.$points[0]
                        });

                        // side
                        var length = Product.lengthUnify(plotter.distance(point, prev.point));
                        var basedOnLineNormal = Vector.crossProduct(worldPoints[0], worldPoints[1]);
                        var segmentNormal = Vector.crossProduct(
                            Vector.subtract(worldPoints[0], product.realCenter()),
                            Vector.subtract(worldPoints[1], product.realCenter())
                        );

                        var byLineAngle = Math.round(segmentNormal.angleWith(basedOnLineNormal) * 1800 / Math.PI) / 10;
                        // basedOnLineNormal orientation unknown
                        byLineAngle = (byLineAngle > 90) ? 180 - byLineAngle : byLineAngle;

                        plotter.line(prev.point, point, {
                            strokeStyle: "black",
                            lineWidth: 3,
                            strokeStyle: productPalette.line[prev.L.order].css
                        });
                        plotter.textByLine(length + ' (' + prev.L.index + ') ∟' + byLineAngle + '°',
                            point, prev.point, {
                                fontSize: 1.2,
                                fillStyle: productPalette.line[prev.L.order].css
                            });

                        // R
                        plotter.line(center, point, 'division');
                        plotter.textByLine(Product.lengthUnify(plotter.distance(point, center)),
                            point, center, {fontSize: 1});

                        // vertex label
                        var vertex = this.vertex;
                        setTimeout(function () {
                            plotter.vertexLabel(vertex.index, point, vertex.$super.line.length);
                        });

                        prev.point = point;
                        break;
                }
            });
        },

        meter: function () {
            var res = {};
//		res['Многоугольники'] = {
//		};
            return res;
        }
    });
</script>
<script type="text/javascript">/**
 * Определение продукта треугольник
 *
 * @author   popitch [at yandex.ru]
 */

Product.Triangle = {};

Product.Triangle.Simple = function (face, params) {
    // parent()
    Product.Polygon.Simple.call(this, face, $.extend({
        type: 'Triangle',
        title: 'Треугольник'
    }, params || {}));
}
    .inherits(Product.Polygon.Simple)
    .override({
        unify: function () {
            var product = this;
            var unifier = Product.Polygon.Simple.prototype.unify.call(this, 'without radius, brother');

            /* stats */
            var $reals = this.realPoints();

            // длины сторон a, b, c
            var abc = this.abc = new Array(3);
            var lines = this.lines = new Array(3);
            // для углов
            var abcEdges = this.abcEdges = new Array(3);
            var n = Vector.crossProduct(
                $reals[0].clone().subtract($reals[1]),
                $reals[2].clone().subtract($reals[1])
            );
            $reals.each(function (i) {
                var A = this, B = $reals[(i + 1) % 3];
                abc[(i + 2) % 3] = product.R * A.distance(B);
                lines[(i + 2) % 3] = _.find(product.face.bindedLines || product.face.$sub.line, function (line) {
                    var vv = line.origin.$sub.vertex;
                    return 0 ||
                        (vv[0] == A.vertex && vv[1] == B.vertex) ||
                        (vv[1] == A.vertex && vv[0] == B.vertex);
                });
                // угол между плоскостью треугольника и смотрящей в центр
                var r = A.vertex.points[0].clone().add(B.vertex.points[0]).scale(.5);
                abcEdges[(i + 2) % 3] = Math.asin(Math.abs(r.cosWith(n)));
            });

            // Формула Герона позволяет вычислить площадь треугольника (S) по его сторонам
            var p = this.p = (abc[0] + abc[1] + abc[2]) / 2;
            this.S = Math.sqrt(p * (p - abc[0]) * (p - abc[1]) * (p - abc[2]));

            // углы
            this.angles = new Array(3);
            for (var a = 0; a < 3; a++) {
                var b = (a + 1) % 3, c = (a + 2) % 3;
                product.angles[a] = Math.acos((abc[b] * abc[b] + abc[c] * abc[c] - abc[a] * abc[a]) / (2 * abc[b] * abc[c]));
            }

            // S = abc/4R
            this.triR = abc[0] * abc[1] * abc[2] / (4 * this.S);
            /* end for stats */

            return unifier;
        },

        model: function (onExport) {
            if (!onExport)
                return // console.error(this, 'on export only');

            var face = this.face.$points.get();

            // todo: resolve std orientation
            face = [face[2], face[1], face[0]];

            // one reverted face
            return [face];
        },

        plot: function (canvas) {
            var product = this;

            new Plotter.Triangle(canvas, {
                $points: this.realPoints(),
                $vertexes: product.face.$sub.vertex,
                $lines: this.lines,
                R: this.R,
                angles: this.angles,
                radiuses: [this.triR, this.triR, this.triR],
                abcEdges: this.abcEdges,
                textZoom: 2
            })
                .triangle()
                .vertexes();
        },

        meter: function () {
            var meter = {};

            meter[__('Coverage area, m2')] = this.S;
            meter[__('Triangles')] = {};
            meter[__('Triangles')]['range:' + __('Min. height, mm')] = Product.lengthUnify(
                _.min([
                    2 * this.S / this.abc[0],
                    2 * this.S / this.abc[1],
                    2 * this.S / this.abc[2]
                ])
            );
            meter[__('Triangles')]['range:' + __('Max. side, mm')] = Product.lengthUnify(
                _.max(this.abc)
            );

            return meter;
        }
    });
</script>
<script type="text/javascript">/**
 * Определение продукта коннекторов
 *
 * @author   popitch [at yandex.ru]
 */

Product.Connector = function () {
    Product.apply(this, arguments);
    // expected
    this.vertex || console.log('vertex not specified');
    this.point || console.log('point not specified');
}
    .inherits(Product)
    .override({
        // расстояние от вершины коннектора до плоскости ребра (средняя его плоскость)
        ribPlaneOffset: function (line) {
            return console.log('Product.Connector.ribPlaneOffset() abstract called', this) & 0;
        },

        // ребро, в которое упирается заданное ребро
        ribWall: function (line) {
            return null;
        },

        // расстояние от вершины коннектора до плоскости стены (в которую ребро упирается)
        ribWallOffset: function (line) {
            return 0;
        },

        // вектор от текущей точки вдоль line
        toOtherTail: function (line) {
            var OTHER = (line.$points[0] != this.point) ? (line.$points[1] != this.point) ?
                console.log('Product.Connector.toOtherTail: wrong line') : 0 : 1;
            return line.$points[OTHER].clone().subtract(this.point);
        },

        otherVertex: function (line) {
            var vertex = this.vertex;
            return line.$vertexes.map(function () {
                return this !== vertex ? this : null;
            })[0];
        },

        // вектор к центру от тукущей точки
        toCenter: function () {
            return this.point.center ?
                Vector.subtract(this.point.center, this.point) :
                (new Vector).subtract(this.point);
        },

        // угол к радиусу в тукущей точке
        angleByRaduis: function (line) {
            return this.toOtherTail(line).angleWith(this.toCenter());
        },

        // коллекция ребер данного фейса
        linesByFace: function (face) {
            var point = this.vertex.$points[0];
            return face.$sub.line.map(function () {
                return this.$points[0] === point || this.$points[1] === point ? this : null;
            });
        },

        afterTailsGiven: function (tail, i, line) {
            return tail;
        },

        unify: function () {
            var product = this;

            // подготовка схемы
            this.vertex.$scheme.each(function () {
                if (this.isFace) {
                    /**
                     * @todo брать не угол реал-фейса, а угол к остальным вершинам
                     */
                    var reals = this.source.product.realPoints().get();
                    var i = this.source.$sub.vertex.index(product.vertex);
                    this.faceAngle = Vector.angle(reals[(i - 1 + reals.length) % reals.length], reals[i], reals[(i + 1) % reals.length]);
                } else {
                    this.radiusAngle = product.angleByRaduis(this.source);
                }
            });

            // прокрутим цепочку и выберем единственный вариант
            if (!this.vertex.selvage) {
                var chain = this.vertex.$scheme.get();
                var sortChains = [];
                $(chain).each(function (i) {
                    sortChains.push(chain.slice(i).concat(chain.slice(0, i)));
                });
                sortChains.sort(function (a, b) {
                    for (var i = 0, l = a.length, d; i < l; i++) {
                        d = (b[i].faceAngle || b[i].radiusAngle) - (a[i].faceAngle || a[i].radiusAngle);
                        if (-1e-6 > d || d > 1e-6)
                            return d;
                    }
                    return 0;
                });
                this.vertex.$scheme = $(sortChains[0]);
            }

            // цепочка уголов ребра_к_радиусу - треугольника ...
            // префикс "ground " означает что это край
            return (this.vertex.selvage ? 'Ground ' : '') +
                this.vertex.$scheme.map(function () {
                    return this.isFace ?
                        'F' + Product.angleUnify(this.faceAngle) :
                        'R' + Product.angleUnify(this.radiusAngle);
                }).get().join(' ');
        },

        meter: function () {
            var p = this.vertex.$points[0];
            var meter = {};

            meter['swing:' + __('Height from base, m')] = p.y * this.R;
            if (this.vertex.selvage)
                meter['range:' + __('Base radius, m')] = Math.sqrt(p.x * p.x + p.z * p.z) * this.R;
            return meter;
        }
    });

/**
 * Joint connector http://popitch1.livejournal.com/1840.html
 */
Product.Connector.Joint = function (vertex, params) {
    Product.Connector.apply(this, [$.extend({
        type: 'Joint',
        vertex: vertex,
        point: vertex.$points[0],
        whirlAsClock: '1'
    }, params || {})]);
}
    .inherits(Product.Connector)
    .override({
        ribPlaneOffset: function (line) {
            if (line.sliced || this.vertex.sliced)
                return 0;
            return (this.whirlAsClock != 0 ? 1 : -1) * line.product.thickness / 2; // @todo any product
        },

        // ребро, в которое упирается заданное ребро
        ribWall: function (line) {
            var vertexIndex = $.inArray(this.vertex, line.$vertexes);

            vertexIndex >= 0 ||
            console.log('its not my line', this);

            var $nearLines = line.$nearLinesByVertex[vertexIndex];
            var nearSlicedLines = $nearLines.filter(function () {
                return this.sliced
            });
            return nearSlicedLines[0];
        },

        // оконцовщик хвоста ребра
        getTail: function (line) {
            var vertex = this.vertex,
                point = vertex.$points[0],
                plane = line.product.getPlane(), // @todo
                outer;

            var vertexIter = (line.origin.$vertexes[0] == vertex) ? 0 : 1;

            // todo: copypasted from Rib.getTails() by refactor getTail mechanism

            var anotherPoint = line.origin.$sub.vertex.filter(function () {
                return this !== vertex
            })[0].$points[0];
            var center = line.origin.center || vertex.center || Vector(0);

            var result = plane.result(point);
            if (Math.abs(result) > 1e-6) {
                // базовая плоскость смещена (Joint + GoodKarma, Semicone)
                var $nearLines = line.origin.$nearLinesByVertex[vertexIter];
                var wall;
                $nearLines.each(function () {
                    var otherVertex = this.$sub.vertex.filter(function () {
                        return this !== vertex
                    })[0];
                    var otherPoint = otherVertex.$points[0];
                    var otherResult = plane.result(otherPoint);
                    if (otherResult * result < 0) {
                        wall && console.log('too many walls for one tail');
                        // по другую сторону от плоскости ребра, значит сюда уперлись
                        wall = {
                            plane: this.product.getPlane().clone(),
                            line: this
                        };
                        // ориентируем плоскость стены относительно незадействованной здесь вершины лайна
                        if (wall.plane.result(anotherPoint) < 0)
                            wall.plane.revert();
                    }
                });

                if (!wall)
                    console.log(['Product.Rib::model(): no wall', this]);

                // двигаем стену в сторону сабжевого ребра, учитывая толщину ребра
                wall.plane.D -= wall.line.product.thickness / 2;


                var sols = Solutions.twoPlanesAndSphere(plane, wall.plane);

                sols.length == 2 ||
                console.log(['Product.Rib::model(): not two solutions', this]);

                outer = sols[0].distance(point) < sols[1].distance(point) ? sols[0] : sols[1];

                // refactor candidate
                //outer = Solutions.planesCross(line.product.getOuterPlane(), plane, wallPlane);

                // for points placed out of sphere (outer Fulleren)
                // ~
                outer.scale(point.length());

                return {
                    // точка внешней поверхности
                    outer: outer,
                    center: center,
                    wall: wall.plane,
                    vertex: vertex
                };
            }
            // смещения нет

            if (!line.sliced) {
                wallLine = vertex.product.ribWall(line);

                // ребро не на краю
                if (wallLine) {
                    // есть ребро, в которое упирается данное (Joint)
                    var wall = {
                        plane: wallLine.product.getPlane().clone(),
                        line: wallLine
                    };

                    // ориентируем плоскость стены относительно незадействованной здесь вершины лайна
                    if (wall.plane.result(anotherPoint) < 0)
                        wall.plane.revert();

                    // двигаем стену в сторону сабжевого ребра, учитывая толщину ребра
                    wall.plane.D -= wall.line.product.thickness / 2;
                }
                else {
                    console.error('This code branch must no usage');

                    // нет ребра в которое упирается данное (Piped)
                    center = vertex.center || line.center || center;
                    var lineVector = Vector.subtract(point, anotherPoint);
                    var pointVector = Vector.subtract(point, center);
                    var sideVector = Vector.crossProduct(lineVector, pointVector);
                    var wallNormal = Vector.crossProduct(sideVector, pointVector).normalize();
                    var wall = {
                        plane: new Plane(wallNormal, point)
                    };
                }

                // двигаем стену на радиус трубы
                var offset = vertex.product.ribWallOffset(line);
                if (offset) {
                    wall.plane.D += (wall.plane.result(anotherPoint) > 0 ? -offset : offset);
                }


                var sols = Solutions.twoPlanesAndSphere(plane, wall.plane);

                sols.length == 2 ||
                console.log(['Product.Rib->getTails()', 'not 2 solutions', this]);

                outer = sols[0].distance(point) < sols[1].distance(point) ? sols[0] : sols[1];

                // for points placed out of sphere (outer Fulleren)
                // ~
                outer.scale(point.length());

                return {
                    // точка внешней поверхности
                    outer: outer,
                    center: center,
                    wall: wall.plane,
                    vertex: vertex
                };
            }

            // В НОВОЙ ВЕРСИИ СЮДА НЕ ПОПАДАЕМ
            console.error('// В НОВОЙ ВЕРСИИ СЮДА НЕ ПОПАДАЕМ');
        },

        unify: function () {
            return 'Joint ' + Product.Connector.prototype.unify.apply(this);
        }
    });


// Труба (расположена вдоль радиуса) с лучами для крепления ребер, классика для любителей сварки и стыков металл+дерево
Product.Connector.Piped = function (vertex, params) {
    Product.Connector.apply(this, [$.extend({
        type: 'Piped',
        vertex: vertex,
        point: vertex.$points[0],
        bilateral: false
    }, params || {})]);

    // габариты приводим к долям радиуса, а получаем в неких единицах измерения
    this.Dpipe *= (this.measure || 1) / this.R;
}
    .inherits(Product.Connector)
    .override({
        ribPlaneOffset: function (line) {
            return 0;
        },

        // расстояние от вершины коннектора до плоскости стены (в которую ребро упирается)
        ribWallOffset: function (line) {
            return this.Dpipe / 2;
        },

        // оконцовщик хвоста ребра
        getTail: function (line) {
            // take origin line figure
            line = line.origin;

            // cache
            this.cache.pipedTail = this.cache.pipedTail || {};
            if (this.cache.pipedTail[line._enum])
                return this.cache.pipedTail[line._enum];

            var vertex = this.vertex, point = vertex.$points[0];
            var plane = line.product.getPlane();

            var vertexIter = line.$vertexes[0] == vertex ? 0 : 1;
            var anotherPoint = line.$vertexes[1 - vertexIter].$points[0];
            var center = line.center || vertex.center || Vector(0);

            if (!line.sliced) {
                // ребро не на краю
                center = vertex.center || line.center || center;
                var lineVector = Vector.subtract(point, anotherPoint);
                var pointVector = Vector.subtract(point, center);
                var sideVector = Vector.crossProduct(lineVector, pointVector);
                var wallNormal = Vector.crossProduct(sideVector, pointVector).normalize();
                var wall = {
                    plane: new Plane(wallNormal, point)
                };

                // двигаем стену на радиус трубы
                var offset = vertex.product.ribWallOffset(line);
                wall.plane.D += (wall.plane.result(anotherPoint) > 0 ? -offset : offset);

                return this.cache.pipedTail[line._enum] = {
                    // точка внешней поверхности
                    outer: Solutions.planesCross(line.product.getOuterPlane(), plane, wall.plane),
                    center: center,
                    wall: wall.plane,
                    vertex: vertex
                };
            }

            // ребро целиком на краю (Piped, Joint)
            line.sliced ||
            console.log(['Product.Rib->getTails()', 'fail']);

            var $anotherSelvageLines = vertex.$super.line.filter(function () {
                return this.selvage && this !== line
            });

            if ($anotherSelvageLines.length != 1)
                console.log(['Product.Rib->getTails()', 'not 1 anotherSelvageLines: ', $anotherSelvageLines]);

            var linePlane = line.product.getPlane(),
                line2Plane = $anotherSelvageLines[0].product.getPlane();

            Vector.dotProduct(linePlane.normal(), line2Plane.normal()) < 0 &&
            line2Plane.revert();

            var outer = point,
                offset = this.ribWallOffset(line);

            center = vertex.center || line.center || center;
            var lineVector = Vector.subtract(point, anotherPoint);
            var pointVector = Vector.subtract(point, center);
            var sideVector = Vector.crossProduct(lineVector, pointVector);
            var wallNormal = Vector.crossProduct(sideVector, pointVector).normalize();
            var wallPlane = new Plane(wallNormal, point);


            // двигаем стену на радиус трубы
            if (offset) {
                wallPlane.D += (wallPlane.result(anotherPoint) > 0 ? -offset : offset);

                outer = Solutions.planesCross(line.product.getOuterPlane(), plane, wallPlane);
            }

            return this.cache.pipedTail[line._enum] = {
                outer: outer,
                wall: wallPlane,
                center: center,
                vertex: vertex
            };
        },

        unify: function () {
            var product = this;

            // проекция ребер на основание радиус-вектора вершины
            var prev, pinAngleSum = 0;
            var radius = Vector.subtract(this.vertex.$points[0], this.vertex.center);

            this.$hedgehog = this.vertex.$scheme.map(function () {
                if (this.source.type == 'line') {
                    var vector = product.toOtherTail(this.source);
                    return prev = {
                        vector: vector,
                        source: this.source,
                        pin: Vector.subtract(vector, Vector.project(vector, radius)),
                        terminate: this.selvage
                        //product: product
                    };
                }
                return null;
            });

            // углы
            this.$hedgehog.each(function () {
                var angle = (Vector.add(prev.pin, this.pin).length() < 1e-9) ? Math.PI : prev.pin.angleWith(this.pin);
                if (Vector.dotProduct(Vector.crossProduct(this.pin, prev.pin), radius) < 0)
                    angle = Math.PI * 2 - angle;
                pinAngleSum += (
                    prev.forwardAngle = this.backAngle = angle);
                prev = this;
            });

            // total control
            if (Math.abs(pinAngleSum - Math.PI * 2) > 1e-7)
                console.log('Piped.unify(): pin angles sum is not full circle ' + pinAngleSum);

            // sort & select
            this.$hedgehog = $(
                Product.selectChain(
                    this.$hedgehog,
                    function (i, arr, forward) {
                        return forward ? this.backAngle : this.forwardAngle;
                    },
                    true,
                    product.bilateral
                )
            );

            return this.$hedgehog.map(
                function () {
                    return Product.angleUnify(this.forward ? this.backAngle : this.forwardAngle);
                }
            ).get().join('-');
        },

        // draw product scheme on canvas
        // @param DomElement canvas
        // @param Object plotter options
        plot: function (canvas, opts) {
            var product = this;
            var C = {x: 1, y: 1};
            var plotter = new Plotter(canvas, $.extend(opts, {
                width: 2,
                height: 2,
                margin: 20, // px
                resize: true,
                R: this.R,
                textZoom: 2
            }))
                .circle(C, .8, ['solid', 'fillWhite']);

            var angle = 0, prev;//Math.PI;
            this.$hedgehog.each(function () {
                prev = angle;
                angle += this.forward ? this.backAngle : this.forwardAngle;

                var A = {x: 1 + Math.cos(angle), y: 1 + Math.sin(angle)};

                // line
                plotter.line(C, A, 'bbb');

                // index
                plotter.textByLine(this.source.index, C, A, {q: .9, sup: true});

                // degree
                var mid = (prev + angle) / 2;
                var degree = Math.round(1800 * (angle - prev) / Math.PI) / 10 + '°';
                plotter.text(
                    {x: 1 + .38 * Math.cos(mid), y: 1 + .38 * Math.sin(mid)},
                    degree
                );
            });
        }
    });

/**
 * Корабль, карандаш, конус... ибо шишка
 */
Product.Connector.Cone = function (vertex, params) {
    Product.Connector.apply(this, [$.extend({
        type: 'Cone',
        vertex: vertex,
        point: vertex.$points[0],
        bilateral: false
    }, params || {})]);
}
    .inherits(Product.Connector.Piped)
    .override({
        ribPlaneOffset: function (line) {
            return 0;
        },

        // расстояние от вершины коннектора до плоскости стены (в которую ребро упирается)
        ribWallOffset: function (line) {
            return 0;
        },

        // ключ аккумулятора
        pinAccumKey: function (pin, finaly) {
            var back = pin.forward ? pin.backAngle : pin.forwardAngle;
            var forward = pin.forward ? pin.forwardAngle : pin.backAngle;
            var pp = pin.source.$points;
            var length = Product.lengthUnify(pp[0].distance(pp[1]) * this.R);
            var suffix;

            if (!finaly) {
                var otherProduct = this.otherVertex(pin.source).product;

                if (otherProduct === this)
                    console.log('otherProduct === this product');

                // prepare other hedgehog
                if (!otherProduct.$hedgehog)
                    Product.Connector.Piped.prototype.unify.call(otherProduct);

                var otherHedgehog = otherProduct.$hedgehog;
                var otherPin = otherHedgehog.map(function () {
                    return this.source === pin.source ? this : null;
                })[0];
                suffix = otherProduct.pinAccumKey(otherPin, true);
            }

            return (back >= Math.PI ? 'none' : Product.angleUnify(back)) + ' ' + length + ' ' +
                (forward >= Math.PI ? 'none' : Product.angleUnify(forward)) + (suffix ? ' => ' + suffix : '');
        },

        unify: function (accum) {
            if (this.cache.coneUnifier)
                return this.cache.coneUnifier;

            var product = this;
            var unifier = Product.Connector.Piped.prototype.unify.apply(this, arguments);

            accum.cone = accum.cone || {};

            function mirrorKey(key) {
                //key = key.replace(/(\S+) (\S+) (\S+)$/g, '$3 $2 $1');
                key = key.replace(/^(\S+) (\S+) (\S+)/g, '$3 $2 $1');
                return key;
            }

            function AngleSet() {
            }

            AngleSet.prototype = {
                add: function (angle, weight) {
                    // sense a near value
                    for (var a in this)
                        if (this.hasOwnProperty(a))
                            angle = Math.abs(a - angle) > 1e-6 ? angle : a;
                    // increment
                    this[angle] = (this[angle] || 0) + weight;
                    return angle;
                },
                merge: function (set, maxus) {
                    for (var a in set)
                        if (set.hasOwnProperty(a)) {
                            this.add(a, set[a]);
                            maxus = Math.max(maxus, this[a]);
                        }
                    return maxus;
                }
            };

            if (!this.vertex.selvage) {
                // решение зацикленной системы линейных уравнений
                if (this.$hedgehog.length % 2) {
                    // единственно, если лучей нечетное кол-во
                    this.$hedgehog.each(function () {
                        this.coneAngle = 0;
                    });
                    this.$hedgehog.each(function (i) {
                        var subj = this;
                        product.$hedgehog.each(function (j) {
                            var pin = product.$hedgehog[(i + j) % product.$hedgehog.length];
                            subj.coneAngle += (j % 2 ? -1 : 1) * pin[pin.forward ? 'forwardAngle' : 'backAngle'];
                            // assertion
                            if (isNaN(subj.coneAngle))
                                console.log('isNaN(subj.coneAngle)');
                        });
                    });
                    this.$hedgehog.each(function (i) {
                        if (this.coneAngle < 0)
                            console.log('<0');
                        this.coneAngle = Math.abs(this.coneAngle / 2);
                        // assertion
                        if (isNaN(this.coneAngle))
                            console.log('isNaN(this.coneAngle)');
                    });
                }
                else {
                    // при четном кол-ве сходящихся лучей (ребер)
                    var sum = [0, 0];
                    this.$hedgehog.each(function (i) {
                        sum[i % 2] += this.forwardAngle;
                    });
                    if (Math.abs(sum[0] - sum[1]) < 1e-6 && Math.abs(sum[0] - Math.PI) < 1e-6) {
                        // если сумма углов через один равна Пи, то беск. кол-во решений
                        // выбираем лучшее...
                        var alfa = 0;
                        var mins = [9, 9];
                        this.$hedgehog.each(function (i) {
                            this.coneAngle = alfa;
                            mins[i % 2] = Math.min(mins[i % 2], alfa);
                            var forwardAngle = this.forward ? this.forwardAngle : this.backAngle;
                            alfa = forwardAngle - alfa;
                            // assertion
                            if (isNaN(alfa))
                                console.log('isNaN(alfa)');
                        });
                        var mid = (mins[0] + mins[1]) / 2;
                        this.$hedgehog.each(function (i) {
                            this.coneAngle += mid - mins[i % 2];
                            // assertion
                            if (isNaN(this.coneAngle))
                                console.log('isNaN(this.coneAngle)');
                        });
                    }
                    else {
                        // иначе решений нет
                        console.log('Cone.unify(): zero solution');
                    }
                }

                // аккумуляция значений вычисленых coneAngle для различных соседних углов лучу (ребру)
                this.$hedgehog.each(function (i) {
                    var angle = this.coneAngle;

                    var key = product.pinAccumKey(this);
                    accum.cone[key] = accum.cone[key] || new AngleSet;
                    accum.cone[key].add(angle, 1000);
                    var revLeft = mirrorKey(key);
                    accum.cone[revLeft] = accum.cone[revLeft] || new AngleSet;
                    accum.cone[revLeft].add(angle, 1);
                });

                //console.log('direct', this.$hedgehog.map(function(){ return this.coneAngle }))
            }
            else {
                // край фигуры
                // попытка вытащить из аккумулятора
                var found, foundMaxus;
                this.$hedgehog.each(function (i) {
                    var curr = this;
                    var key = product.pinAccumKey(curr);
                    var keySense = new RegExp(
                        key
                            .replace(/none/g, '(?:\\d+|none)')
                            .replace(/(\d+)/g, '(?:$1|none)')
                    );

                    var angle, maxus = 0;
                    var angleSols = new AngleSet;
                    for (var k in accum.cone)
                        if (keySense.test(k))
                            maxus = angleSols.merge(accum.cone[k], maxus);

                    var solus = 0;
                    for (var a in angleSols) {
                        if (angleSols[a] == maxus) {
                            solus++;
                            angle = a;
                        }
                    }

                    if (solus == 1 && (!found || maxus > foundMaxus)) {
                        curr.coneAngle = parseFloat(angle);

                        // registration
                        //accum.cone[key] = accum.cone[key] || new AngleSet;
                        //accum.cone[key].add(angle, 1000);
                        //var left = mirrorKey(key);
                        //accum.cone[left] = accum.cone[left] || new AngleSet;
                        //accum.cone[left].add(angle, 1);

                        //console.log('add solution for key', key, 'with solutions set', angleSols)

                        // assertion
                        if (isNaN(curr.coneAngle))
                            console.log('isNaN(curr.coneAngle)');

                        // order founded
                        found = i;
                        foundMaxus = maxus;
                        //return false;
                    }
                    else {
                        //console.log('Cone.unify():', solus, 'solutions for ', key, angleSols, 'by', accum.cone);
                    }
                });

                if (found !== undefined) {
                    var length = this.$hedgehog.length;

                    //if (length == 2)
                    //console.log('found', found, 'length', length);

                    for (var incr = 1, back = 0; incr >= -1; incr -= 2, back = 1) {
                        var i = found;
                        var curr = this.$hedgehog[i];
                        while ((i = (i + incr + length) % length) != found) {
                            var prev = curr;
                            curr = this.$hedgehog[i];

                            var sum = back ?
                                (prev.forward ? prev.backAngle : prev.forwardAngle) :
                                (prev.forward ? prev.forwardAngle : prev.backAngle);

                            // hardcode for situation when non-fuller must be breaked (PI * 4 / 5 gt 2.5)
                            if (sum > 2.5) {
                                //console.log('cones sum > 2.5, sum is', sum);
                                break;
                            }

                            curr.coneAngle = (sum > Math.PI ? Math.PI * 2 - sum : sum) - prev.coneAngle;

                            if (curr.source.selvage && prev.source.selvage)
                                break;
                        }
                    }
                }
                else {
                    var keys = this.$hedgehog.map(function (i) {
                        return product.pinAccumKey(this)
                    });
                    var lens = $.map(keys, function (key) {
                        return key.replace(/^\S+ (\d+).*/, '$1')
                    });
                    var acc = {};
                    $.each(accum.cone, function (key, as) {
                        for (var i = 0, l = lens.length; i < l; i++)
                            if (key.split(lens[i]).length > 1)
                                acc[key] = as;
                    });
                    console.log('Cone angle for keys', keys, 'not found in accum', acc);
                }
            }

            // control shot
            if (this.$hedgehog.filter(function () {
                    return isNaN(this.coneAngle)
                }).length)
                console.log('Cone-' + unifier, 'cone without angle detected', this.$hedgehog);
            if (this.$hedgehog.filter(function () {
                    return this.coneAngle > Math.PI / 2
                }).length)
                console.log('Cone-' + unifier, 'cone with angle gt PI/2', this.$hedgehog);

            return this.cache.coneUnifier =
                'Cone-' + unifier;
        },

        // оконцовщик ребра
        getTail: function (line) {
            // take origin line figure
            line = line.origin;

            $.inArray(this.vertex, line.$vertexes) >= 0 || console.log('its not my line', this);

            var tail = Product.Connector.Piped.prototype.getTail.apply(this, arguments);

            this.unify();
            var vertex = this.vertex, product = this,
                pinIndex, pinCount = this.$hedgehog.length;

            this.$hedgehog.each(function (i) {
                if (this.source == line) {
                    pinIndex = i;
                    tail.coneAngle = this.coneAngle;
                    tail.coneSide = (i != 0) ? (i != pinCount - 1) ? 'both' : 'left' : 'right';
                    return false;
                }
            });

            //var $nearLines = line.$nearLinesByVertex[vertexIndex];

            return tail;
        },

        plot: function (canvas, opts) {
            Product.Connector.Piped.prototype.plot.apply(this, arguments);

            var plotter = new Plotter(canvas, $.extend(opts, {
                width: 2,
                height: 2,
                margin: 20, // px
                R: this.R,
                textZoom: 2
            }));

            var angle = 0;
            this.$hedgehog.each(function () {
                angle += this.forward ? this.backAngle : this.forwardAngle;

                // degree
                var degree = Math.round(1800 * this.coneAngle / Math.PI) / 10;
                plotter.text(
                    {x: 1 + .618 * Math.cos(angle), y: 1 + .618 * Math.sin(angle)},
                    degree * 2 + '°'
                );
            });
        }
    });

/**
 * Пол-шишки тоже шишка
 * - это просто нос, полуконус дальше будет
 */
Product.Connector.Nose = function (vertex, params) {
    Product.Connector.apply(this, [$.extend({
        type: 'Nose',
        vertex: vertex,
        point: vertex.$points[0],
        //bilateral: true,
        whirlAsClock: 0
    }, params || {})]);
    // type cast
    this.whirlAsClock = (1 == this.whirlAsClock);
    // no pipe
    this.Dpipe = 0;
}
    .inherits(Product.Connector.Piped)
    .override({
        afterTailsGiven: function (tail, i, line) {
            var product = this;
            var wall = this.$hedgehog.map(function () {
                if (this.source === line)
                    return null;
                //if (!this.pin || !tail.backDir)
                //	console.error('must to be');
                if (this.pin.cosWith(tail.backDir) > 0) // angle must be < 90 degrees
                    return null;
                var is = Vector.dotProduct(
                    Vector.crossProduct(this.pin, tail.backDir),
                    product.point // strangen center not found
                ) > 0;
                if (is ^ !product.whirlAsClock)
                    return null;
                return product.getTail(this.source).wall;
            })[0];

            if (wall) {
                //var oi = Vector.subtract(tail.inner, tail.outer);
                //tail.walker0clone = tail.walker0clone || tail.walkers[0].clone();
                //var revert = Vector.dotProduct(Vector.crossProduct(tail.walker0clone, oi), tail.backDir) < 0 ? 0 : 1;
                var pp = [],
                    dd = $.map(tail.walkers, function (w, j) {
                        pp[j] = Vector.add(product.point, w);
                        return wall.result(pp[j]) //Math.abs( wall.result(pp[j]) );
                    }),
                    index = (dd[0] > dd[1]) ? 0 : 1,
                    p = pp[index],
                    d = dd[index],
                    walker = tail.walkers[index];

                // wall.result(p + X * tail.backDir) = 0
                var td = wall.result(Vector.add(p, tail.backDir));
                var X = d / (d - td);

                tail.walkers[index].add(Vector.scale(tail.backDir, X));
            }
            return tail;
        }
    });


/**
 * GoodKarma
 */
Product.Connector.GoodKarma = function (vertex, params) {
    Product.Connector.apply(this, [$.extend({
        type: 'GoodKarma',
        vertex: vertex,
        point: vertex.$points[0],
        whirlAsClock: 0
    }, params || {})]);
    // type cast
    this.whirlAsClock = (1 == this.whirlAsClock);
    // no pipe
    this.Dpipe = 0;
}
    .statics({
        lineSeparatelyForFaces: true
    })
    .inherits(Product.Connector.Joint)
    .override({
        ribPlaneOffset: function (line) {
            return 0;
        },

        // ребро, в которое упирается заданное ребро
//	ribWall: function(line) {
//		var product = this;
//
//		return _.filter(figure.$primitives, function(p) {
//			return 1
//				&& p.bindedFace === line.bindedFace
//				&& p !== line
//				&& _.indexOf(p.origin.$vertexes, product.vertex) > -1;
//		})[0];
//	},

        // оконцовщик хвоста ребра
        getTail: function (line) {
            var vertex = this.vertex,
                point = vertex.$points[0],
                secondVertex = line.origin.$vertexes[line.origin.$vertexes[0] === vertex ? 1 : 0],
                secondPoint = secondVertex.$points[0],

                plane = line.product.getPlane(true),
                planeNormal = plane.normal();

            var wallLine = _.find(line.bindedFace.bindedLines, function (any) {
                    return 1
                        && any !== line
                        && _.contains(any.origin.$vertexes, vertex);
                }),
                wallPlane = wallLine.product.getPlane(true).clone().normalize(),

                thirdVertex = wallLine.origin.$vertexes[wallLine.origin.$vertexes[0] === vertex ? 1 : 0],
                thirdPoint = thirdVertex.$points[0];

            // ориентируем плоскость стены относительно незадействованной здесь вершины лайна
            if (wallPlane.result(secondPoint) < 0)
                wallPlane.revert();

            // двигаем стену в сторону сабжевого ребра, учитывая толщину ребра
            wallPlane.D += (
                this.whirlAsClock ^
                (Vector.dotProduct(
                    Vector.crossProduct(
                        secondPoint.clone().subtract(point),
                        thirdPoint.clone().subtract(point)
                    ),
                    point
                ) > 0)
                    ? -1 : 1) * wallLine.product.thickness / 2;

            var outer = plane.crossWithRay(
                wallPlane.crossWithRay(
                    point,
                    secondPoint.clone().subtract(point)
                ),
                Vector.crossProduct(line.product.getOuterPlane().normal(), wallPlane.normal())
            );

            return {
                // точка внешней поверхности
                outer: outer,
                center: Vector(0),
                wall: wallPlane,
                vertex: vertex
            };
        },

        unify: function () {
            return 'GoodKarma ' + Product.Connector.prototype.unify.apply(this);
        }
    });


/**
 * Semicone
 */
Product.Connector.Semicone = function (vertex, params) {
    Product.Connector.apply(this, [$.extend({
        type: 'Semicone',
        vertex: vertex,
        point: vertex.$points[0]
    }, params || {})]);
    // no pipe ?
    //this.Dpipe = 0;
}
    .statics({
        lineSeparatelyForFaces: true
    })
    .inherits(Product.Connector.Joint)
    .override({
        ribPlaneOffset: function (line) {
            return 0;
        },

        // оконцовщик хвоста ребра
        getTail: function (line) {
            var vertex = this.vertex,
                point = vertex.$points[0],
                secondVertex = line.origin.$vertexes[line.origin.$vertexes[0] === vertex ? 1 : 0],
                secondPoint = secondVertex.$points[0],

                plane = line.product.getPlane(true),
                planeNormal = plane.normal(),

                thirdVertex = _.difference(line.bindedFace.$sub.vertex.get(), [vertex, secondVertex])[0],
                thirdPoint = thirdVertex.$points[0],

                wallPlane = new Plane(
                    Vector.crossProduct(
                        // bisectrix
                        Vector.add(
                            Vector.subtract(secondPoint, point).normalize(),
                            Vector.subtract(thirdPoint, point).normalize()
                        ),
                        // radius vector
                        point
                    ),
                    point
                ).normalize();

            // ориентируем плоскость стены относительно незадействованной здесь вершины лайна
//		if (wallPlane.result(secondPoint) < 0)
//			wallPlane.revert();

            var outer = plane.crossWithRay(
                wallPlane.crossWithRay(
                    point,
                    secondPoint.clone().subtract(point)
                ),
                Vector.crossProduct(line.product.getOuterPlane().normal(), wallPlane.normal())
            );

            return {
                // точка внешней поверхности
                outer: outer,
                center: Vector(0),
                wall: wallPlane,
                vertex: vertex
            };
        },

        unify: function () {
            return 'Semicone ' + Product.Connector.prototype.unify.apply(this);
        }
    });
</script>
<script type="text/javascript">/**
 * Определение продукта "ребро"
 *
 * @author   popitch [at yandex.ru]
 */

Product.Rib = function () {
    Product.apply(this, arguments);

    // expected
    this.line || console.log('no line specified');

    // init plane cache
    this.cache.plane = {};
}
    .inherits(Product)
    .override({
        // отдает плоскость, рассекающую ребро пополам и проходящую через центры радиусов вершин
        getPlane: function (useBindedFace) {
            if (this.cache.plane[useBindedFace])
                return this.cache.plane[useBindedFace];

            var line = this.line,
                vv = line.origin.$subsets.get(),
                pp = [vv[0].$points[0], vv[1].$points[0]],
                center = line.origin.center || pp[0].center || Vector(0);// ? pp[1].center ? pp[0].center.equals(pp[1].center) ? pp[0].center :
            /**
             * 1) A*cx + B*cy + C*cz + D = 0
             * 2) A*x0 + B*y0 + C*z0 + D = d0
             * 3) A*x1 + B*y1 + C*z1 + D = -d1
             * 4) A*A  + B*B  + C*C      = 1
             */
                // если line на краю, либо данный конец на краю, то смещение 0
            var d0 = vv[0].product.ribPlaneOffset(line),
                d1 = -vv[1].product.ribPlaneOffset(line);
            var sols = Solutions.twoPlanesAndSphere(
                new Plane(Vector.subtract(pp[0], center), -d0),
                new Plane(Vector.subtract(pp[1], center), -d1)
            );
            sols.length ||
            console.log('no plane no cry');

            var lineNormal = Vector.crossProduct(Vector.subtract(pp[0], center), Vector.subtract(pp[1], center));

            for (var i = 0; i < sols.length; i++) {
                var n = sols[i],
                    D = -Vector.dotProduct(n, center); // 1) D
                var pl = sols[i] = new Plane(n, D);
                pl.sign = n.cosWith(lineNormal) > 0 ? 1 : -1;

                // debug
                pl.reflect = vv[i].reflection;
                pl.mapi = n.mapi;
                pl.solutions = n.solutions;

                /** tests
                 * 1) A*cx + B*cy + C*cz + D = 0
                 * 2) A*x0 + B*y0 + C*z0 + D = d0
                 * 3) A*x1 + B*y1 + C*z1 + D = -d1
                 */
                var testc = pl.A * center.x + pl.B * center.y + pl.C * center.z + pl.D;
                var test0 = pl.A * pp[0].x + pl.B * pp[0].y + pl.C * pp[0].z + pl.D;
                var test1 = pl.A * pp[1].x + pl.B * pp[1].y + pl.C * pp[1].z + pl.D;
                if (testc != 0)
                    console.warn('!' + testc);
                if (Math.abs(d0 - test0) > 1e-6)
                    console.warn('d0:' + d0 + ' != ' + test0);
                if (Math.abs(d1 - test1) > 1e-6)
                    console.warn('d1:' + d1 + ' != ' + test1);
            }

            if (sols.length == 2 && sols[0].sign == sols[1].sign)
                console.log({sols: sols, lineNormal: lineNormal});

            // выбор решения
            var plane = (sols.length == 2 ? sols[0].sign == 1 ? sols[0] : sols[1] : sols[0]).normalize();

            // Good Karma's (+Semicone) workaround
            if (useBindedFace === true && line.bindedFace) {
                // move plane to side of binded face
                var testPoint = _.filter(line.bindedFace.$sub.vertex, function (vertex) {
                    return !_.contains(line.origin.$vertexes, vertex);
                })[0].$points[0];
                plane.D += (plane.result(testPoint) < 0 ? 1 : -1) * this.thickness / 2;
            }

            return this.cache.plane[useBindedFace] = plane;
        },

        // отдает конфигурацию оконцовок
        /*final*/ getTails: function () {
            var line = this.line;

            if (this.cache.tails)
                return this.cache.tails;

            // 1. get tails separately
            var tails = line.origin.$vertexes.map(function () {
                var vertex = this,
                    tail = vertex.product.getTail(line);

                tail.product = vertex.product;
                return tail;
            });

            // 2. common calculator
            tails = this.afterGetTails(tails);

            // 3. separate again
            tails = tails.map(function (i) {
                if (!this.iwas) {
                    this.product.afterTailsGiven(this, i, line);
                    this.iwas = 1;
                }
                return this;
            });

            return this.cache.tails = tails;
        },

        afterGetTails: function (tails) {
            return tails;
        },

        getOuterPlane: function () {
            // !! допущения:
            // 1. both centers is (0,0,0)

            var A = this.line.$points[0], a = A.length(),
                B = this.line.$points[1], b = B.length(),
                AB = B.clone().subtract(A).normalize();
            //var doubleSquare = Vector.crossProduct(A, B);

            var comp = Vector.dotProduct(A, AB);
            var P = AB.scale(-comp).add(A);

            return new Plane(P, P);
        },

        // максимальная длина продукта,
        maxLength: function () {
        }
    });

/**
 * Брус
 * @param Figure line
 * @param {} params
 */
Product.Rib.Beam = function (params) {
    Product.Rib.apply(this, [
        $.extend({
            type: 'Beam',
            title: 'Брус',
            width: 0,     // ширина бруса (в радиусах) - параллельна радиусу
            thickness: 0, // толщина
            R: 0          // радиус сферы должен быть известен!
        }, params || {})
    ]);

    // габариты приводим к долям радиуса, а получаем в неких единицах измерения
    this.width *= (this.measure || 1) / this.R;
    this.thickness *= (this.measure || 1) / this.R;
}
    .inherits(Product.Rib)
    .override({
        afterGetTails: function (tails) {
            var product = this;
            var outerPlane = this.getOuterPlane();
            var plane = this.getPlane(true);

            return tails.each(function (i) {
                var dir = Vector.subtract(tails[1 - i].outer, this.outer);

                // save for given
                this.backDir = dir.clone();

                // .outer принадлежат срединной плоскости (ориентирующей продукт)
                // .inner считаем
                var vect = Vector.crossProduct(plane.normal(), this.wall.normal());
                vect.scale(
                    Vector.dotProduct(Vector.subtract(this.outer, this.center), vect) > 0 ? -1 : 1
                );
                var cos = vect.cosWith(dir),
                    sin = Math.sqrt(1 - cos * cos);
                vect.normalize().scale(product.width / sin);
                this.inner = Vector.add(this.outer, vect);

                // для определения отреза требуются вектора отклонения вершин от этой плоскости к бокам бруса
                this.walkers = [Vector.crossProduct(this.wall.normal(), outerPlane.normal())];
                this.walkers[0].normalize();
                this.walkers[0].scale(0.5 * product.thickness / plane.normal().cosWith(this.walkers[0]));
                this.walkers[1] = this.walkers[0].clone().scale(-1);

                if (this.coneAngle) {
                    // Cone this
                    dir.normalize().scale(product.thickness / 2 / Math.tan(this.coneAngle) / sin);
                    this.walkers[0].add(dir);
                    this.walkers[1].add(dir);
                }
            });
        },

        unify: function () {
            var product = this;
            var tails = this.getTails();

            var unifiers = tails.map(function (i) {
                var tail = this;
                var dir = Vector.subtract(tails[1 - i].outer, this.outer).normalize();
                var hOI = Vector.subtract(this.inner, this.outer).scale(.5);
                this.middle = Vector.add(tail.outer, hOI);

                var direct = Vector.dotProduct(Vector.crossProduct(this.walkers[0], hOI), dir) > 0;

                // [left, right] bevels
                this.bevelSides = [
                    Vector.component(this.walkers[direct ? 0 : 1], dir),
                    Vector.component(this.walkers[direct ? 1 : 0], dir)
                ];

                // если меньше градуса отклонение, то считаем что его нет
                for (var j = 0; j < 2; j++) {
                    if (Math.abs(this.bevelSides[j] * 50) < product.thickness / 2)
                        this.bevelSides[j] = 0;
                }

                // left = right = 0
                this.bevelZero = !this.bevelSides[0] && !this.bevelSides[1];
                // left + right = 0
                this.bevelStraight = Math.abs(this.bevelSides[0] + this.bevelSides[1]) < 1e-6;
                // left = right
                this.bevelsEquals = Math.abs(this.bevelSides[0] - this.bevelSides[1]) < 1e-6;
                // else
                this.bevelChaos = !this.bevelZero && !this.bevelStraight && !this.bevelsEquals;

                // inner bevel (outer bevel = 0)
                this.bevelInner = Vector.component(hOI, dir);

                var PI2 = Math.PI / 2;
                var inner = Math.atan2(this.bevelInner, product.width / 2);
                var left = Math.atan2(this.bevelSides[0], product.thickness / 2);
                var right = Math.atan2(this.bevelSides[1], product.thickness / 2);

                return (!Math.round(inner * 500) ? 'T' : (inner < PI2 ? 'A' : 'B') + Product.angleUnify(inner < PI2 ? PI2 - inner : inner - PI2)) +
                    (this.bevelZero ?
                        'Piped' :
                        (this.bevelStraight ?
                            'Joint/GoodKarma/Semicone' + (left > 0 ? 'L' : 'R') + Product.angleUnify(left > 0 ? left : -left) :
                            (this.coneAngle ?
                                    'Cone' + Product.angleUnify(this.coneAngle)
                                    : // Nose only
                                    (Math.round(left * 500) ? '-L' + Product.angleUnify(left) + dihedralMark(left, inner) : '') +
                                    (Math.round(right * 500) ? '-R' + Product.angleUnify(right) + dihedralMark(right, inner) : '')
                            ))) +
                    '(' + tail.vertex.index + ')';

                function dihedralMark(side, inner) {
                    var normalOuter = new Vector(0, 0, 1);
                    var byOuter = new Vector(Math.sin(side), Math.cos(side), 0);
                    var toInner = new Vector(Math.sin(inner), 0, Math.cos(inner));
                    var normalCut = Vector.crossProduct(toInner, byOuter);
                    var dihedral = normalCut.angleWith(normalOuter);
                    var mark = Product.angleUnify(PI2 - dihedral);
                    return mark == 0 ? '' : '-I' + mark;
                }
            });
            unifiers.sort();

            // for calc min/max
            this.midLength = tails[0].middle.distance(tails[1].middle);

            return 'Length' + Product.lengthUnify(this.maxLength() * product.R) +
                ' ' + unifiers.get().join(' ');
        },

        maxLength: function () {
            var maxLength = this.midLength;
            this.getTails().each(function () {
                if (_.contains(['Joint', 'GoodKarma', 'Semicone'], this.vertex.product.type))
                    maxLength += Math.abs(this.bevelSides[0]);
                //if (this.bevelInner < 0)
                maxLength += Math.abs(this.bevelInner);
            });
            return maxLength;
        },

        minLength: function () {
            var minLength = this.midLength;
            this.getTails().each(function () {
                if (_.contains(['Joint', 'GoodKarma', 'Semicone'], this.vertex.product.type))
                    minLength -= Math.abs(this.bevelSides[0]);
                //if (this.bevelInner > 0)
                minLength -= Math.abs(this.bevelInner);
            });
            return minLength;
        },

        model: function (onExport) {
            var product = this;
            var tails = this.getTails();

            var pp = [];
            $([tails[0].outer, tails[0].inner, tails[1].inner, tails[1].outer]).each(function (i) {
                var tail = tails[i >> 1];

                if (isNaN(tail.walkers[0].x) || isNaN(tail.inner.x) || isNaN(tail.outer.x))
                    console.log('isNaN(tail.walker.x) || isNaN(tail.inner.x) || isNaN(tail.outer.x)');

                pp.push(this.clone().add(tail.walkers[0]));
                pp.push(this.clone());
                pp.push(this.clone().add(tail.walkers[1]));
            });

            var faces = [
                [pp[1], pp[0], pp[3], pp[4]], // one tail sides
                [pp[2], pp[1], pp[4], pp[5]],
                [pp[10], pp[11], pp[8], pp[7]], // second tail sides
                [pp[9], pp[10], pp[7], pp[6]],
                [pp[4], pp[3], pp[6], pp[7], pp[8], pp[5]], // inner face
                [pp[0], pp[9], pp[6], pp[3]], // side
                [pp[2], pp[5], pp[8], pp[11]], // side
                [pp[1], pp[2], pp[11], pp[10], pp[9], pp[0]], // outer face
            ];

            // model on export
            if (onExport) {
                return faces;
            }

            var cont = new Figure.Container({
                figures: $([
                    faces.pop() // outer only
                ]).map(function () {
                    // ориентация полигона
                    var polypp = this, outerp;

                    $(pp).each(function () {
                        if (-1 == $.inArray(this, polypp))
                            outerp = this;
                    });

                    var normal = Vector.crossProduct(
                        Vector.subtract(polypp[1], polypp[0]),
                        Vector.subtract(polypp[2], polypp[0]));
                    if (Vector.dotProduct(Vector.subtract(outerp, polypp[0]), normal) < 0) {
                        var reverted = [], p;
                        while (p = polypp.shift())
                            reverted.push(p);
                        polypp = reverted;
                    }

                    return new Figure({
                        type: 'polygon',
                        points: polypp
                    });
                }),
                points: pp,
                source: this.line
            });
            cont.$primitives.each(function () {
                this.figure = product.line.figure;
            });
            return cont;
        },

        // draw product scheme on canvas
        // @param DomElement canvas
        // @param Object opts  plotter options
        plot: function (canvas, opts) {
            var product = this;
            var tails = this.getTails();
            var sizeOfTail = _.map(tails, function (tail) {
                    return 2 * _.max(_.map(tail.bevelSides, Math.abs)) + Math.abs(tail.bevelInner);
                }),
                xGaps = [];

            var margin = 20, // px
                milkRight = 20, // px
                xyRatio = ($(canvas).width() - 2 * margin - milkRight) / ($(canvas).height() - 2 * margin),
                maxLength = product.maxLength(),
                maxVisibleLength = xyRatio * product.thickness,
                gapVisibleWidth = product.thickness / 4;

            if (maxLength > maxVisibleLength) {
                var productDelta = product.maxOuterLength - product.minOuterLength;

                if (productDelta) {
                    var visibleProductLength = maxVisibleLength * (.6 + .4 * (maxLength - product.minOuterLength) / productDelta),
                        gapDelta = maxLength - visibleProductLength;
                } else {
                    var gapDelta = maxLength - maxVisibleLength;
                }

                var gapStart = sizeOfTail[0] + (maxLength - sizeOfTail[0] - sizeOfTail[1]) / 2 - gapDelta / 2;

                if (gapStart > sizeOfTail[0]) {
                    xGaps.push({
                        x: gapStart,
                        from: gapDelta + gapVisibleWidth,
                        to: gapVisibleWidth
                    });
                } else {
                    gapDelta = maxLength - sizeOfTail[0] - sizeOfTail[1];
                    xGaps.push({
                        x: sizeOfTail[0],
                        from: gapDelta + gapVisibleWidth,
                        to: gapVisibleWidth
                    });
                }

                //console.log(product.line.index, maxVisibleLength, maxLength, xGaps[0])
            }

            var plotter = new Plotter.Beam(canvas, $.extend(opts, {
                depth: this.width,
                width: this.maxOuterLength,
                length: this.maxLength(),
                height: this.thickness, // толщина доски (бруса) рисуется в высоту
                margin: margin,
                milkRight: milkRight,
                resize: false,
                R: this.R,
                xGaps: xGaps,
                textZoom: 2,
                fixOutXProportion: true
            }))
                .ydivision(0, 'left', 'top')
                .ydivision(this.thickness, 'left', 'bottom')
                .start(this, tails[0])
                .tail(tails[0], false, 'left', this)
                .offset({x: this.midLength})
                .tail(tails[1], true, 'right', this)
                .end();

            // side angles of face
            var line = this.line;
            var byLineNormal = Vector.crossProduct(tails[0].vertex.$points[0], tails[1].vertex.$points[0]);

            $.each(line.origin.$super.face, function (i, face) {
                if (line.bindedFace && line.bindedFace !== face) return;

                var a = Product.angleUnify(face.product.normal.angleWith(byLineNormal)) / 10;
                a = (a > 90) ? 180 - a : a; // figure is convex
                var outer = face.$points.not(line.$points)[0];

                var y = (outer.cosWith(byLineNormal) <= 0) ? product.thickness : 0;

                plotter.textByLine(
                    '∟' + a + '°',
                    {y: y, x: 0},
                    {y: y, x: xGaps[0] ? xGaps[0].x + product.thickness * 3 : product.thickness * 5},
                    {
                        otherSide: !y
                    }
                );
            });
        },

        materialName: function () {
            return __('Beams') + ' ' +
                Math.round(this.width * this.R / .001) + 'x' +
                Math.round(this.thickness * this.R / .001) + __('mm');
        },

        meter: function () {
            var mat = this.materialName(), meter = {};

            // площадь основания (допущение: срез был по оси Y)
            if (this.line.selvage) {
                var pp = this.line.$points;
                meter[__('Base area, m2')] = Math.abs((pp[0].x * pp[1].z - pp[1].x * pp[0].z) * this.R * this.R / 2);
            }

            meter[mat] = {};
            meter[mat][__('Total length of beams, m')] = this.maxLength() * this.R;
            meter[mat][__('Total volume of beams, m3')] = this.midLength * this.width * this.thickness * this.R * this.R * this.R;
            meter[mat]['max:' + __('Max. beam length, mm')] = Product.lengthUnify(this.maxLength() * this.R);

            // угол сопряжения граней
            var $faces = this.line.origin.$super.face;
            var $normals = $faces.length == 2 ? $faces.map(function () {
                return Vector.crossProduct(
                    Vector.subtract(this.$points[0], this.$points[1]),
                    Vector.subtract(this.$points[2], this.$points[1])
                );
            }) : false;
            var edgel = $normals ? $normals[0].angleWith($normals[1]) : false;
            if (edgel) {
                meter[mat]['range:' + __('Angle between faces, °')] = 180 - 180 * edgel / Math.PI;
            }

            return meter;
        }
    });
</script>
<script type="text/javascript">/**
 * Доска для чертежей
 */

Plotter = function (canvas, opts) {
    var plotter = this;

    // canvas
    this._$canvas = $(canvas);
    this._context = this._$canvas[0].getContext('2d');
    this._width = this._$canvas.width() - (opts.milkRight || 0);
    this._height = this._$canvas.height();

    // canvas style
    this._context.lineCap = 'round',
        this._context.lineJoin = 'bevel',
        this._context.miterLimit = 10;
    this._context.globalAlpha = .9;

    // styles
    this._styles = {
        solid: {
            strokeStyle: "black",
            lineWidth: 2
        },
        backside: {
            strokeStyle: "gray",
            lineWidth: 2
        },
        division: {
            strokeStyle: "rgba(141,141,141,.5)",
            lineWidth: 1
        },
        white: {
            strokeStyle: "white",
            lineWidth: 2
        },
        fillWhite: {
            fillStyle: 'white'
        }
    };

    this._dashedLine = false;
    this._dashedStep = 5;

    this._currentStyle = {
        line: {},
        point: {}
    };

    // props & defaults
    $.extend(this, opts);

    // text zooming
    this.textZoom = this.textZoom || 1;

    this.R = opts.R || 1;
    this.margin = opts.margin || 16; // px

    // input frame (data)
    this._frameIn = {
        l: 0,
        t: 0,
        w: opts.width || console.log('Plotter: no width'),
        h: opts.height || console.log('Plotter: no height')
    };

    // output frame (canvas)
    this._frameOut = {
        l: this.margin,
        t: this.margin,
        w: this._width - 2 * this.margin,
        h: this._height - 2 * this.margin
    };

    // plot current offset
    this._offset = {
        x: opts.offset && opts.offset.x || 0,
        y: opts.offset && opts.offset.x || 0
    };

    // gaps on x
    this.xGaps = opts.xGaps || [];
    this.xGapDelta = 0;
    $(this.xGaps).each(function () {
        plotter.xGapDelta += this.to - this.from;
    });
    this._frameIn.w += this.xGapDelta;

    // пропорции
    if (opts.fixOutXProportion) {
        this._frameOut.w = this._frameOut.h * (this._frameIn.w / this._frameIn.h);
    } else {
        this._frameOut.h = this._frameOut.w * (this._frameIn.h / this._frameIn.w);
    }

    // resize
    if (opts.resize) {
        var of = this._offset;
        this._offset = {x: 0, y: 0};
        this.resizeCanvasHeightByInputProportion();
        this._offset = of;
    }

    // ratio
    this.ratio = {
        x: this._frameOut.w / this._frameIn.w,
        y: this._frameOut.h / this._frameIn.h
    };
    this.ratio.l = Math.sqrt(this.ratio.x * this.ratio.y);
}
    .override({
        resizeCanvasHeightByInputProportion: function () {
            var height = this._plane({x: 0, y: this._frameIn.h}, {y: this.margin}).y;
            this._frameOut.h = height - 2 * this.margin;
            this._$canvas[0].height = height;
        },

        _setStyle: function (subj, style) {
            if (style instanceof Array) {
                for (var i = 0; i < style.length; i++) {
                    this._setStyle(subj, style[i]);
                }
                return;
            }
            var values = typeof style == 'object' ? style : this._styles[style] || {};
            for (var k in values) {
                this._context[k] = values[k];
            }
            if (subj == 'line') {
                this._dashedLine = (style == 'backside' || style == 'dashed');
            }
        },

        _plane: function (p, offset) {
            var plotter = this;
            var xGapDelta = 0;
            $(this.xGaps).each(function () {
                if (plotter._offset.x + p.x > this.x + this.from)
                    xGapDelta += this.to - this.from;
            });
            return {
                x: Math.round(
                    this._frameOut.l +
                    ((this._offset.x + p.x + xGapDelta) - this._frameIn.l) * this._frameOut.w / this._frameIn.w +
                    (offset && offset.x || 0)
                ),
                y: Math.round(
                    this._frameOut.t +
                    ((this._offset.y + p.y) - this._frameIn.t) * this._frameOut.h / this._frameIn.h +
                    (offset && offset.y || 0)
                )
            };
        },

        _moveTo: function (p) {
            this._context.moveTo(p.x + .5, p.y + .5);
        },

        _lineTo: function (p) {
            this._context.lineTo(p.x + .5, p.y + .5);
        },

        offset: function (o) {
            if (!o) return this._offset;
            this._offset.x += o.x || 0;
            this._offset.y += o.y || 0;
            return this;
        },

        line: function (p1, p2, style, outputXY) {
            var ctx = this._context;
            ctx.save();
            this._setStyle('line', style);
            ctx.beginPath();

            if (this._dashedLine) {
                var s = outputXY ? p1 : this._plane(p1);
                var f = outputXY ? p2 : this._plane(p2);
                var length = Math.sqrt((s.x - f.x) * (s.x - f.x) + (s.y - f.y) * (s.y - f.y));
                var step = this._dashedStep;
                var v = {x: (f.x - s.x) * step / length, y: (f.y - s.y) * step / length};
                var p = {x: s.x + v.x / 2, y: s.y + v.y / 2};
                for (var i = 0, l = 0; l < length; i++) {
                    this[(i % 2) ? '_lineTo' : '_moveTo'](p);
                    l += step;
                    p.x += l < length - 1 ? v.x : v.x / 2;
                    p.y += l < length - 1 ? v.y : v.y / 2;
                }
            } else {
                this._moveTo(outputXY ? p1 : this._plane(p1));
                this._lineTo(outputXY ? p2 : this._plane(p2));
            }

            ctx.closePath();
            ctx.stroke();
            ctx.restore();
            return this;
        },

        circle: function (pos, r, style) {
            pos = this._plane(pos);
            r *= this.ratio.l;

            var ctx = this._context;
            ctx.save();
            this._setStyle('circle', style);

            ctx.beginPath();
            ctx.arc(pos.x, pos.y, r, 0, Math.PI * 2, true);
            ctx.closePath();
            ctx.fill();
            ctx.stroke();
            ctx.restore();
            return this;
        },

        text: function (pos, text, opts) {
            opts = $.extend({
                planePos: true,
                fillStyle: 'black',
                strokeStyle: 'black'
            }, opts || {});
            pos = opts.planePos ? this._plane(pos) : pos;

            var ctx = this._context;
            ctx.save();
            ctx.textBaseline = opts.valign || 'middle';
            ctx.textAlign = opts.align || 'center';
            ctx.font = (opts.fontSize || 0.5) * this.textZoom + "em monospace, Optimer, verdana";
            ctx.fillStyle = opts.fillStyle;
            ctx.strokeStyle = opts.strokeStyle;
            ctx.strokeWidth = 2;
            ctx.translate(pos.x, pos.y);
            ctx.rotate(opts.rotate || 0);
            ctx.fillText(text, 0, 0);
            ctx.restore();
            return this;
        },

        textByLine: function (text, A, B, opts) {
            if (text === undefined) {
                debugger;
            }

            opts = opts || {};
            var pos = this.average(A, B, opts.q || 1 / 2),
                length = Math.sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y)),
                normal = {x: (A.y - B.y) / length, y: (B.x - A.x) / length};
            var rev = (opts.sup && normal.y) ^ opts.otherSide > 0 ? -1 : 1;
            pos.x += rev * normal.x * 10 / this.ratio.x;
            pos.y += rev * normal.y * 10 / this.ratio.y;
            var angle = Math.atan2(A.y - B.y, A.x - B.x);
            this.text(pos, text, $.extend({
                rotate: angle < Math.PI / 2 ? angle > -Math.PI / 2 ? angle : angle + Math.PI : angle - Math.PI,
                fontSize: 0.6
            }, opts));
        },

        vertical: function (x, style) {
            this.line(
                this._plane({x: x, y: 0}, {y: -this.margin}),
                this._plane({x: x, y: this._frameIn.h}, {y: this.margin}),
                style,
                true // dont plane again
            );
            return this;
        },

        xdivision: function (x, align, valign, anchor) {
            this.vertical(x, 'division');

            // division
            var floatDiv = (x + this._offset.x) * this.R / 0.001;

            // rounding
            var div = Math.round(
                (typeof anchor != 'undefined') ? anchor + Math.round(floatDiv - anchor) : floatDiv
            );

            var pos = this._plane({
                x: x,
                y: (valign == 'top') ? 0 : this._frameIn.h
            }, {
                y: (valign == 'top') ? -this.margin : this.margin
            });

            //this.text(pos, div, {valign: valign, align: align});
            var ctx = this._context;
            ctx.textBaseline = valign;
            ctx.textAlign = align;
            ctx.font = "14px monospace, Optimer, verdana";//(0.5) * this.textZoom + "em Optimer, verdana";
            ctx.fillStyle = "black";
            ctx.strokeStyle = "black";
            ctx.strokeWidth = 2;
            ctx.fillText(div, pos.x, pos.y);
            return floatDiv;
        },

        horizontal: function (y, style) {
            this.line(
                this._plane(
                    {x: 0, y: y},
                    {x: -this.margin}
                ),
                this._plane(
                    {x: this._frameIn.xmax || this._frameIn.w, y: y},
                    {x: this.margin}
                ),
                style,
                true // dont plane coordinates again
            );
            // gaps on x
            var plotter = this;
            setTimeout(function () {
                $(plotter.xGaps).each(function () {
                    plotter.line(
                        {x: this.x, y: y},
                        {x: this.x + this.from + 0.00001, y: y},
                        ['white', 'dashed']
                    );
                });
            }, 1);
            return this;
        },

        ydivision: function (y, align, valign) {
            this.horizontal(y, 'division');

            // division
            var div = Math.round((y + this._offset.y) * this.R / 0.001);
            var pos = this._plane({
                x: (align == 'left') ? 0 : this._frameIn.w,
                y: y
            }, {
                x: (align == 'left') ? -this.margin : this.margin
            });
            var ctx = this._context;
            ctx.textBaseline = valign;
            ctx.textAlign = align;
            ctx.font = "14px monospace, Optimer, verdana"; //(0.5) * this.textZoom + "em Optimer, verdana";
            ctx.fillStyle = "black";
            this._context.fillText(div, pos.x, pos.y);
            return this;
        },

        vertexLabel: function (index, pos, ribs) {
            var rayCount = _.filter(ribs, function (rib) {
                return rib === rib.origin;
            }).length;

            pos = this._plane(pos);

            // sun-connector
            var ctx = this._context, r = 12;
            ctx.strokeStyle = "#ccc";
            ctx.strokeWidth = 1;
            ctx.fillStyle = "#fff";
            ctx.beginPath();
            ctx.arc(pos.x, pos.y, r, 0, Math.PI * 2, true);
            ctx.closePath();
            ctx.fill();
            ctx.stroke();
            // rays
            rayCount = rayCount || 6;
            for (var i = 0.3; i < rayCount; i++) {
                var offset = {
                    x: r * Math.cos(i * 2 * Math.PI / rayCount),
                    y: r * Math.sin(i * 2 * Math.PI / rayCount)
                };
                ctx.beginPath();
                ctx.moveTo(pos.x + offset.x, pos.y + offset.y);
                ctx.lineTo(pos.x + 2 * offset.x, pos.y + 2 * offset.y);
                ctx.closePath();
                ctx.stroke();
            }

            // index
            ctx.fillStyle = "#000";
            ctx.textBaseline = 'middle';
            ctx.textAlign = 'center';
            ctx.textWeight = 'bold';
            ctx.font = "12px monospace, Optimer, verdana";
            ctx.fillText(index, pos.x, pos.y);
        },

        planeAngle: function (A, B, C, style) {
            style = $.extend({
                strokeStyle: 'gray',
                fillStyle: 'black'
            }, style);

            var AB = this.distance(A, B);
            var BC = this.distance(B, C);
            var CA = this.distance(C, A);
            var angle = Math.acos((AB * AB + BC * BC - CA * CA) / (2 * AB * BC));
            angle = Math.round(angle * 180 * 5 / Math.PI) / 5;

            var Ar = Math.atan2(A.y - B.y, A.x - B.x);
            var Cr = Math.atan2(C.y - B.y, C.x - B.x);
            var R = 62;

            A = this._plane(A);
            B = this._plane(B);
            C = this._plane(C);

            var ctx = this._context;
            ctx.save();
            this._setStyle('circle', style);

            ctx.beginPath();
            ctx.arc(B.x, B.y, R, Ar, Cr, true);
            ctx.stroke();
            ctx.restore();

            // degrees
            var bstx = (Ar + Cr) / 2 + (Math.abs(Ar - Cr) > Math.PI ? Math.PI : 0);
            //bstx += bstx > Math.PI + 1e-5 ? -Math.PI : 0;
            if (angle) {
                this.text(
                    {
                        x: B.x + (R + 10) * Math.cos(bstx),
                        y: B.y + (R + 10) * Math.sin(bstx)
                    },
                    angle + '°',
                    _.extend({
                        rotate: bstx + Math.PI / 2 + (bstx > 0 ? Math.PI : 0),
                        planePos: false,
                        fontSize: 0.6
                    }, style)
                );
            }
            return this;
        },

        average: function (a, b, q) {
            q = (q === undefined) ? .5 : q;
            return {
                x: a.x * (1 - q) + b.x * q,
                y: a.y * (1 - q) + b.y * q
            };
        },

        distance: function (A, B) {
            return Math.sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
        }
    });

/**
 * Чертеж отрезов бруса
 */

Plotter.Beam = function (canvas, opts) {
    Plotter.apply(this, arguments);

    this._frameIn.xmax = opts.length || opts.width;
}
    .inherits(Plotter)
    .override({
        start: function (product, tail) {
            this._offset = {
                x: tail.coneAngle ? Math.abs(tail.bevelInner) :
                    Math.max(
                        Math.abs(tail.bevelInner + (!tail.bevelChaos && tail.bevelSides[0])),
                        Math.abs(tail.bevelInner + (!tail.bevelChaos && tail.bevelSides[1]))),
                y: 0
            };

            this._left = [null, null];
            this._right = [null, null];

            return this;
        },

        tail: function (tail, turnMe, whichSide/*nothing*/, product) {
            var TURN = turnMe ? -1 : 1;
            var outerLink, innerLink;

            var outerCenter = {x: -TURN * tail.bevelInner, y: this._frameIn.h / 2},
                innerCenter = {x: TURN * tail.bevelInner, y: this._frameIn.h / 2};

            if (tail.bevelZero || tail.bevelsEquals || tail.bevelChaos) {
                outerLink = this.xdivision(outerCenter.x, turnMe ? 'left' : 'right', 'top');
                innerLink = this.xdivision(innerCenter.x, turnMe ? 'right' : 'left', 'bottom');
            }

            var outerBevels = [];

            for (var i = 0; i < 2; i++) {
                var TOP = (turnMe ^ i) ? 1 : 0;
                var outerBevel = {x: outerCenter.x + TURN * tail.bevelSides[i], y: TOP ? 0 : this._frameIn.h},
                    innerBevel = {x: innerCenter.x + TURN * tail.bevelSides[i], y: TOP ? 0 : this._frameIn.h};

                outerBevels.push(outerBevel);

                if (!tail.bevelZero && tail.bevelsEquals && i || tail.bevelChaos) {
                    outerLink = this.xdivision(outerBevel.x, turnMe ? 'left' : 'right', 'top', outerLink);
                    innerLink = this.xdivision(innerBevel.x, turnMe ? 'right' : 'left', 'bottom', innerLink);
                }
                if (tail.bevelStraight && !tail.bevelsEquals) {
                    outerLink = this.xdivision(outerBevel.x, turnMe ? 'left' : 'right', TOP ? 'top' : 'bottom', outerLink);
                    innerLink = this.xdivision(innerBevel.x, turnMe ? 'right' : 'left', TOP ? 'top' : 'bottom', innerLink);
                }

                //var style = (whichSide == 'middle') || (whichSide == 'right' ^ outerCenter.x < innerCenter.x) ? 'backside' : 'solid';
                this.line(outerCenter, outerBevel, 'solid');
                this.line(innerCenter, innerBevel, 'dashed');

                function accum(accum, val, method) {
                    return accum == null ? val : Math[method](accum, val);
                }

                this._left[TOP] = accum(this._left[TOP], this._offset.x + outerBevel.x, 'min');
                this._right[TOP] = accum(this._left[TOP], this._offset.x + outerBevel.x, 'max');

                // angle between outer plane and plane to cut
                if (
                    (tail.bevelZero && !i) || // Piped (once)
                    (!tail.bevelStraight && !i) || // Cone (once)
                    (!tail.bevelZero && tail.bevelStraight && (turnMe ^ outerBevel.x > outerCenter.x)) // side what near to rub center (Joint, GoodKarma, Semicone)
                ) {
                    var byOuter = new Vector(outerBevel.x - outerCenter.x, outerBevel.y - outerCenter.y, 0);
                    var toInner = new Vector(innerCenter.x - outerCenter.x, innerCenter.y - outerCenter.y, this.depth);
                    var normalCut = Vector.crossProduct(byOuter, toInner).scale(i ? 1 : -1);
                    var normalOuter = new Vector(0, 0, this.depth);
                    var angleBetween = normalOuter.angleWith(normalCut);
                    var pp = i ? [outerBevel, outerCenter] : [outerCenter, outerBevel];
                    this.textByLine(
                        '∟' + Math.round(1800 * angleBetween / Math.PI) / 10 + '°',
                        pp[0], pp[1],
                        {
                            q: turnMe ? 0.382 : 0.618,
                            fillStyle: 'blue'
                        }
                    );
                } else if (
                    tail.bevelStraight && // side what far from rib center (Joint, GoodKarma)
                    (!tail.bevelZero || i) // <exclude> Piped (once)
                ) {
                    // angle on outer plane
                    var planeAngle = [outerCenter, outerBevel, {x: outerBevel.x, y: outerCenter.y}];
                    if (turnMe ^ i ^ outerBevel.x > outerCenter.x)
                        planeAngle.reverse();
                    this.planeAngle.apply(this, planeAngle.concat([{fillStyle: 'red', strokeStyle: 'gray'}]));
                }
            }

            if (!tail.bevelZero && tail.bevelsEquals || tail.bevelChaos) {
                this.line(innerCenter, outerCenter, 'dashed');
            }

            // label
            this.vertexLabel(tail.vertex.index, outerCenter, tail.vertex.$super.line);

            // angle on outer plane (Cone)
            if (!tail.bevelStraight) {
                var planeAngle = [outerBevels[0], outerCenter, outerBevels[1]];
                if (!tail.bevelChaos ^ tail.bevelsEquals)
                    planeAngle = planeAngle.reverse();
                this.planeAngle.apply(this, planeAngle.concat([{fillStyle: 'red', strokeStyle: 'gray'}]));
            }

            return this;
        },

        end: function () {
            this._offset = {x: 0, y: 0};
            this.line({x: this._left[1], y: 0}, {x: this._right[1], y: 0}, 'solid');
            this.line({x: this._left[0], y: this._frameIn.h}, {x: this._right[0], y: this._frameIn.h}, 'solid');
            return this;
        }
    });

/**
 * Чертеж треугольника
 */

Plotter.Triangle = function (canvas, opts) {
    var plotter = this;

    var angles = this.angles = opts.angles;
    var radiuses = this.radiuses = opts.radiuses;
    this.maxR = Math.max.apply(null, radiuses);
    this.$lines = opts.$lines;

    // вершины A, B, C
    var ABC = this.ABC = [];
    a = -Math.PI / 2;
    for (var i = 0; i < 3; i++) {
        ABC[i] = {
            x: radiuses[i] * Math.cos(a),
            y: radiuses[i] * Math.sin(a)
        }
        a -= 2 * angles[(i + 2) % 3];
    }

    // основания высот от вершин на чертеже Ah, Bh, Ch
    var ABCh = this.ABCh = [];
    for (var a = 0; a < 3; a++) {
        var b = (a + 1) % 3, c = (a + 2) % 3;
        ABCh[a] =
            Vector.project(
                Vector.subtract(ABC[a], ABC[c]),
                Vector.subtract(ABC[b], ABC[c])
            ).add(ABC[c]);
    }

    // размер полотна
    opts = $.extend({
        width: 2 * this.maxR,
        height: 2 * this.maxR,
        margin: 20, // px
        resize: true,
        offset: {x: this.maxR, y: this.maxR}
    }, opts);

    // parent::constructor()
    Plotter.apply(this, [canvas, opts]);
}
    .inherits(Plotter)
    .override({
        outerCircle: function () {
            return this.circle({x: 0, y: 0}, this.maxR, ['gray', 'fillWhite']);
        },

        triangle: function () {
            for (var i = 0; i < 3; i++) {
                var A = this.ABC[i], B = this.ABC[(i + 1) % 3], C = this.ABC[(i + 2) % 3];
                var Ah = this.ABCh[i];
                this.line(B, C, {
                    strokeStyle: productPalette.line[this.$lines[i].order].css,
                    lineWidth: 3
                });
                this.line(A, Ah, {
                    strokeStyle: '#bbb'
                });
            }
            for (var i = 0; i < 3; i++) {
                var A = this.ABC[i], B = this.ABC[(i + 1) % 3], C = this.ABC[(i + 2) % 3];
                var Ah = this.ABCh[i];

                this.textByLine(Product.lengthUnify(this.distance(B, Ah)), B, Ah, {q: .38});
                this.textByLine(Product.lengthUnify(this.distance(Ah, C)), Ah, C, {q: .62});

                var edge = Math.round(this.abcEdges[i] * 180 * 10 / Math.PI) / 10;
                this.textByLine(this.$lines[i].index + '  ∟' + edge + '°', B, C, {
                    fontSize: 0.9,
                    fillStyle: productPalette.line[this.$lines[i].order].css
                });

                this.textByLine(Product.lengthUnify(this.distance(A, Ah)), A, Ah, {q: .9});
            }
            return this;
        },

        heights: function () {

        },

        vertexes: function () {
            var plotter = this;
            $(this.ABC).each(function (i) {
                // todo: странная проблема разной направленности массивов angles и $vertexes, хотя источник у них один..
                // подогнал в ручную сверяя со схемой
                var n = 2 - i;
                plotter.vertexLabel(plotter.$vertexes[n].index, this, plotter.$vertexes[n].$super.line)
            });
        }
    });</script>
<script type="text/javascript">/**
 * Накопитель значений и их статистика
 *
 * var meter = new Meter;
 *
 * meter.push( [ groupKey ], { key: value, .. } );
 *
 */

Meter = function () {
    this.values = {};
}
    .override({
        // push()
        push: function (map, context) {
            if (typeof map == 'string') {
                var h = {}, args = [].slice.call(arguments, 0);
                h[args[0]] = args[1];
                args[1] = h;
                return this.push.apply(this, args.slice(1));
            }

            context = context || this.values;
            for (var k in map) {
                var matchFn = /^(\w+):(.*)$/.exec(k),
                    fn = (matchFn && this.operators[matchFn[1]]) ? matchFn[1] : 'default';

                if (fn == 'default' && typeof(map[k]) == 'object') {
                    context[k] = context[k] || {};
                    this.push(map[k], context[k]);
                }
                else {
                    context[k] = this.operators[fn](context[k], map[k]);
                }
            }
        },

        operators: {
            default: function (accum, value) { // sum
                return (accum || (isNaN(value) ? '' : 0)) + value;
            },

            min: function (accum, value) {
                return (accum == undefined) ? value : Math.min(accum, value);
            },

            max: function (accum, value) {
                return (accum == undefined) ? value : Math.max(accum, value);
            },

            swing: function (accum, value) {
                return {
                    min: (accum && accum.min != undefined) ? Math.min(accum.min, value) : value,
                    max: (accum && accum.max != undefined) ? Math.max(accum.max, value) : value
                };
            },

            range: function (accum, value) {
                return {
                    min: (accum && accum.min != undefined) ? Math.min(accum.min, value) : value,
                    max: (accum && accum.max != undefined) ? Math.max(accum.max, value) : value
                };
            }
        },

        reporters: {
            swing: function (value) {
                return (value.max || 0) - (value.min || 0);
            },

            range: function (value) {
                return (value.max - value.min < 1e-6) ? value.min : [value.min, '-', value.max];
            }
        },

        reportText: function (context, indent, report) {
            indent = indent || '';
            context = context || this.values;
            report = report || [];

            var objects = [];

            for (var k in context) {
                var matchFn = /^(\w+):(.*)$/.exec(k),
                    key = k, value = context[k];

                if (matchFn) {
                    if (this.reporters[matchFn[1]])
                        value = this.reporters[matchFn[1]](value);
                    key = matchFn[2];
                }

                if (typeof value == 'object' && !(value instanceof Array)) {
                    objects.push({
                        key: '\n' + indent + key.bold(),
                        val: false
                    });
                    this.reportText(value, indent + '    ', objects);
                }
                else {
                    report.push({
                        key: indent + key,
                        val: value
                    });
                }
            }

            $.each(objects, function () {
                report.push(this);
            });

            var maxKeyLength = 0;
            return (arguments.length == 3) ? 0 :
                $(report).each(function () {
                    maxKeyLength = Math.max(this.key.length, maxKeyLength);
                    return this;
                }).map(function () {
                    while (this.key.length < maxKeyLength)
                        this.key += ' ';
                    var key = this.key.replace(/(,\s*.)(\d)(\D|$)/, '$1&sup$2;$3');
                    return key + '  ' + (this.val ?
                        $.map(
                            $.makeArray(this.val),
                            function (val) {
                                return typeof val == 'number' ? Meter.numberFormat(val) : val;
                            }
                        ).join('') :
                        '');
                }).get().join('\n');
        }
    });

Meter.numberFormat = function (val) {
    if (!val) return '';
    var val100 = Math.round(val / .01);
    return Math.floor(val100 / 100) + (
        (val100 % 100) ?
            '.' + (
                (val100 % 100) < 10 ? '0' : ''
            ) +
            (val100 % 100)
            : ''
    );
}</script>

<!-- extra -->
<script type="text/javascript">!function () {
    var __pointsEnum, __points, __faces, __lines;

    /**
     * Деление фейсов фигуры (равные дуги)
     * @return this
     */
    Figure.prototype.splitFaces_EA = function (N) {
        var figure = this;

        // init
        __faces = [];
        __lines = {};
        __points = {};
        __pointsEnum = 0;

        // save prev points
        _.each(figure.$points, function (p) {
            __points[p._enum = __pointsEnum++] = p;
        });

        var triIndex = 0;
        _.each(this.$primitives, function (p) {
            if (p.type == 'face') {
                // init edge points
                var A = p.points[0], B = p.points[1], C = p.points[2];
                var AB = [A, B], BC = [B, C], CA = [C, A];
                _.each([AB, BC, CA], function (segment) {
                    var a = segment[0], b = segment.pop();
                    for (var i = 1; i < N; i++)
                        segment.push(edgePoint(a, b, i, N));
                    segment.push(b);
                });

                // main
                var up = [A], down;
                for (var n = 1; n <= N; n++) {
                    down = [CA[N - n]];
                    for (var i = 1; i < n; i++) {
                        if (n == N) {
                            down.push(BC[N - i]);
                        } else {
                            var point = findPoint(
                                AB[n], CA[N - n],
                                BC[N - i], AB[i],
                                CA[N - n + i], BC[n - i],
                                A, i
                            );
                            down.push(point);
                        }
                    }
                    down.push(AB[n]);

                    // collect faces
                    for (i = 0; i < n; i++) {
                        addFace(up[i], down[i + 1], down[i], [n, N - i, N - n + i + 1], triIndex);
                        if (i > 0)
                            addFace(down[i], up[i - 1], up[i], [n - 1, N - i, N - n + i], triIndex);
                    }
                    up = down;
                }
                triIndex++;
            }
        });

        __points = _.map(_.values(__points), function (point) {
            return new Figure({
                type: 'vertex',
                points: [point],
                center: point.center
            });
        });

        console.log('points', __points.length, 'lines', _.values(__lines).length, 'faces', __faces.length);

        this.$primitives = $(__points.concat(_.values(__lines)).concat(__faces));

        return this;
    }

    Figure.prototype.splitFaces_EA_updateToMexican = function (N) {
        var lines = _.filter(this.$primitives, function (p) {
            return (p.type == 'line') // && (2 <= p.sideDistance && p.sideDistance < N);
        });

        Figure.equalizeLineGroups(
            _.groupBy(lines, 'sideDistance')
        );

        return this;
    }

    Figure.prototype.splitFaces_updateToClassII = function () {
        console.time('Update to Class II');

        // prepare relations
        this.relations();

        // first wave
        var prevVertexWave = this.$primitives.map(function () {
            this.sideDistance = 0;
            this.underWave = (this.type == 'vertex') && this.$points[0].pptPoint;
            this.underLine = this.underWave ? 5 : 0;
            this.underLine1 = 0;
            return this.underWave ? this : null;
        });
        var lines = [];

        do {
            var vertexWave = [], lineWave = [];
            _.each(prevVertexWave, function (prevVertex) {
                var prevSideDistance = prevVertex.sideDistance;
                _.each(prevVertex.$super.line, function (line) {
                    if (!line.underWave) {
                        lineWave.push(line);
                        _.each(line.$sub.vertex, function (vertex) {
                            if (vertex != prevVertex && !vertex.lastWave) {
                                if (!vertex.underLine)
                                    vertexWave.push(vertex);
                                vertex.sideDistance += prevSideDistance;
                                if (1 == prevSideDistance)
                                    vertex.underLine1++;
                                vertex.underLine++;
                            }
                        });
                        line.underWave = true;
                    }
                });
            });

            _.each(vertexWave, function (vertex) {
                vertex.sideDistance /= vertex.underLine;
                if (vertex.underLine % 2)
                    vertex.sideDistance++;
                else if (vertex.underLine == vertex.underLine1)
                    vertex.sideDistance = 0;
                vertex.lastWave = true;
            });

            _.each(lineWave, function (line) {
                var lengths = _.pluck(line.$sub.vertex, 'sideDistance');
                if (lengths[0] == lengths[1] && lengths[0] == 1)
                    line.sideDistance = 0;
                else
                    line.sideDistance = lengths[0] + lengths[1];
            });

            lines = lines.concat(lineWave);

            prevVertexWave = vertexWave;
        } while (lineWave.length);

        console.log(_.groupBy(lines, 'sideDistance'));

        Figure.equalizeLineGroups(
            _.groupBy(lines, 'sideDistance')
        );

        // clean
        _.each(this.$primitives, function (f) {
            delete f.sideDistance;
            delete f.underLine;
            delete f.underWave;
            delete f.lastWave;
        });

        console.timeEnd('Update to Class II');

        return this;
    };

    Figure.prototype.splitFaces_updateToClassIII = function (M, N) {
        var figure = this,
            subdivisionScheme = [],
            pointsEnum = _.chain(this.$points).pluck('_enum').max().value();

        isNaN(pointsEnum) && console.error('points enum is', pointsEnum);

        M = Number(M);
        N = Number(N);
        var S = M * M + M * N + N * N;

        // prepare entities relation info
        this.relations();

        // make a Class III M,N subdivision scheme
        !function () {
            var base = [
                    [-M - N, N, M], // n vector
                    [-M, M + N, -N] // m vector
                ],
                trio = [[0, 0], [N, M], [M + N, -N]],
                trioSquare = square.apply(null, trio);

            for (var y = -N; y < M; y++) {
                for (var x = 0; x < M + N; x++) {
                    triangle([x, y], [x, y + 1], [x + 1, y]);
                    triangle([x + 1, y + 1], [x + 1, y], [x, y + 1]);
                }
            }

            // assertions
            if (subdivisionScheme.length != S) {
                console.error('Class III: bad scheme with size', subdivisionScheme.length, 'expected', S);
            }
            var integer = _.chain(subdivisionScheme).map(function (f) {
                    return _.map(f, roundS);
                }).value(),
                stat = _.chain(integer).flatten(true).map(function (p) {
                    return _.sortBy(p).join()
                }).countBy().value();
            _.each(stat, function (count) {
                if (count % 3) {
                    console.error('Class III: wrong symmetry on scheme', integer);
                }
            });

            // end

            function triangle(A, B, C) {
                var center = alter2base([(A[0] + B[0] + C[0]) / 3, (A[1] + B[1] + C[1]) / 3]),
                    points = _.map([A, B, C], alter2base),
                    inFace = _.countBy(points, function (p) {
                        return _.all(p, positive)
                    });

                if (
                    _.all(center, positive) // center belongs to face
                    || (
                        !_.some(center, negative) && // center belongs to edge of face
                        inFace["true"] > inFace["false"] // inner vertices GT outer
                    )
                ) {
                    subdivisionScheme.push(points);
                    /*if (inFace["false"] == 2 || (!_.some(center, negative) && !_.all(center, positive))) {
						console.log(
							'scheme face: center', center, //_.map(center, function(c){ return Math.round(c*S*3) / 3 }),
							inFace, '=>', points
						);
					}*/
                }
            }

            // translate coords to face-based
            function alter2base(alter) {
                var coords = _.reduce(base, function (result, axis, axisIndex) {
                    _.each(axis, function (axisCoord, axisCoordIndex) {
                        result[axisCoordIndex] += axisCoord * alter[axisIndex] / S;
                    });
                    return result;
                }, [1, 0, 0]);

                // assertions
                Math.abs(_.reduce(coords, sum, 0) - 1) < 1e-9
                || console.error('Class III: bad coords of subdivision point', coords);
                _.filter(coords, negative).length <= 1
                || console.error('Class III: bad coords with greater than 1 negative', coords);

                return coords;
            }

            // 2d
            function diff(B, A) {
                return [B[0] - A[0], B[1] - A[1]];
            }

            function square(A, B, C) {
                A = diff(A, C);
                B = diff(B, C);
                return A[0] * B[1] - A[1] * B[0];
            }
        }();

        //console.log(_.map(faces, function(pp) { return _.map(pp, function(p) { return p[0] + p[1] + p[2] }) }));

        // subdivision each face by scheme
        !function () {
            var prevPrimitivesLength = figure.$primitives.length,
                addedLines = {};

            _.each(_.where(figure.$primitives, {type: 'vertex'}), function (vertex) {
                vertex.$points[0].vertex = vertex;
            });

            _.each(_.where(figure.$primitives, {type: 'face'}), function (face) {
                var facePoints = face.$points;

                _.each(subdivisionScheme, function (pointsCoords) {
                    var points = _.map(pointsCoords, coords2point);

                    figure.$primitives.push(new Figure({
                        type: 'face',
                        points: points
                    }));
                    //console.log('+ face');

                    _.each([1, 2, 0], function (p1, p2) {
                        var lineIndex = [points[p1].vertex._enum, points[p2].vertex._enum].sort().join();

                        if (!addedLines[lineIndex]) {
                            if (_.find(figure.$primitives, function (p) {
                                    return p.type == 'line' && (
                                        (points[p1] === p.$points[0] && points[p2] === p.$points[1]) ||
                                        (points[p1] === p.$points[1] && points[p2] === p.$points[0])
                                    );
                                })) {
                                console.error('doublet line detected for', points[p1], points[p2]);
                            }
                            figure.$primitives.push(new Figure({
                                type: 'line',
                                points: [points[p1], points[p2]],
                                __enums: [points[p1].vertex._enum, points[p2].vertex._enum]
                            }));
                            addedLines[lineIndex] = true;
                            //console.log('+ line');
                        } else {
                            //console.log('- decline line', points[p1].vertex._enum, points[p2].vertex._enum);
                        }
                    });
                });

                function coords2point(coords) {
                    var baseIndex = _.indexOf(coords, 1);
                    if (baseIndex != -1) {
                        // PPT point
                        //console.log('PPT point happens')
                        return facePoints[baseIndex];
                    }

                    var negativeIndex = _.indexOf(coords, _.filter(coords, negative)[0]);
                    if (negativeIndex != -1) {
                        // point outside of the face
                        var linePoints = _.filter(facePoints, function (point, index) {
                                return index !== negativeIndex;
                            }),
                            negativeLine = _.find(face.$sub.line, function (line) {
                                var points = line.$points;
                                return 0
                                    || (points[0] === linePoints[0] && points[1] === linePoints[1])
                                    || (points[0] === linePoints[1] && points[1] === linePoints[0]);
                            }),
                            negativeFace = _.find(negativeLine.$super.face, function (f) {
                                return f !== face
                            }),
                            negativePoint = _.difference(negativeFace.$points.get(), face.$points.get())[0],
                            negativeOrdinate = coords[negativeIndex],
                            base = [];

                        /**
                         * Translate coordinates to other face by negative ordinate
                         *
                         *  (A, B, C) * (a, b, c) === (A, B, A + B - C) * (x, y, z)
                         * =>
                         *  z = -c
                         *  y = b + c
                         *  x = a + c
                         * where
                         *  (A + B - C)  new base ordinate
                         */
                        coords = _.map(coords, function (coord, index) {
                            base[index] = (negativeIndex === index) ? negativePoint : facePoints[index];
                            return (negativeIndex === index) ? -coord : coord + negativeOrdinate;
                        });

                        // assert
                        Math.abs(_.reduce(coords, sum, 0) - 1) < 1e-9
                        || console.error('Class III: bad coords for negative face', coords);
                    }

                    // find or calc new point
                    return (function (face, base) {
                        face.classIIIPoints = face.classIIIPoints || [];

                        // make stamp of face-based coordinates of point
                        var stamp = _.map(base, function (point, index) {
                            return {
                                order: point.vertex._enum,
                                value: coords[index]
                            };
                        });
                        stamp = _.pluck(_.sortBy(stamp, 'order'), 'value');

                        // try to find similar class III point produced previous to
                        var found = _.find(face.classIIIPoints, function (memo) {
                            return _.all(memo.stamp, function (memoOrdinate, index) {
                                return Math.abs(memoOrdinate - stamp[index]) < 1e-9;
                            });
                        });
                        if (found) {
                            //console.log('memoized point happens')
                            return found.point;
                        }

                        // else calc new point
                        var point = _.reduce(base, function (absolute, axis, index) {
                            absolute.x += axis.x * coords[index];
                            absolute.y += axis.y * coords[index];
                            absolute.z += axis.z * coords[index];
                            return absolute;
                        }, new Vector);

                        // memoize
                        face.classIIIPoints.push({
                            stamp: stamp,
                            coords: coords,
                            point: point
                        });

                        // new vertex promitive
                        point.vertex = new Figure({
                            type: 'vertex',
                            points: [point.normalize()],
                            negative: !!negativeFace
                        });
                        figure.$primitives.push(point.vertex);
                        //console.log('+ vertex');

                        point._enum = ++pointsEnum;

                        return point;
                    })(negativeIndex != -1 ? negativeFace : face, negativeIndex != -1 ? base : facePoints);
                }
            });

            // remove previous primitives
            figure.$primitives = $(
                _.filter(figure.$primitives, function (primitive, index) {
                    // clear points memo
                    delete primitive.classIIIPoints;

                    return index >= prevPrimitivesLength ||
                        primitive.type == 'vertex'; // save PPT points only
                })
            );

            // figure points
            figure.$points = $(
                _.chain(figure.$primitives).pluck('$points').invoke('get').flatten().uniq().value()
            );
        }();

        return this;

        // any helpers
        function positive(v) {
            return v > 1e-9
        }

        function negative(v) {
            return v < -1e-9
        }

        function zero(v) {
            return Math.abs(v) < 1e-9
        }

        function sum(a, b) {
            return a + b
        }

        function roundS(p) {
            return _.map(p, function (c) {
                return Math.round(c * S)
            })
        }
    };


    /**
     * Lines length equalizer
     * @static
     * @return undefined
     */
    Figure.equalizeLineGroups = function (lineGroups, diffPrecision, stepLimit) {
        console.time('Lengths equalizer');

        diffPrecision = diffPrecision || 1e-12;
        stepLimit = stepLimit || 300;

        var usedPoints = _.chain(lineGroups).flatten().pluck('$points').invoke('get').flatten().uniq().value();

        var step = 0;
        do {
            var delta = 0;
            // prepare
            _.each(lineGroups, function (lines, group) {
                _.each(lines, function (line) {
                    line.__dirs = [
                        Vector.subtract(line.$points[0], line.$points[1]),
                        Vector.subtract(line.$points[1], line.$points[0])
                    ];
                    line.length = line.__dirs[0].length();
                });
            });
            // change
            _.each(lineGroups, function (lines, group) {
                var lengths = _.pluck(lines, 'length'),
                    diff = _.max(lengths) - _.min(lengths);

                if (diff < diffPrecision) return;
                delta = Math.max(delta, diff);

                var avgLength = _.reduce(lengths, function (a, l) {
                    return a + l
                }, 0) / lengths.length;

                // try to move points
                _.each(lines, function (line) {
                    var q = (avgLength - line.length) / line.length / 4;
                    _.each(line.$points, function (point, i) {
                        point.add(Vector.scale(line.__dirs[i], q));
                    });
                });
            });
            // normalize
            _.each(usedPoints, function (point) {
                point.normalize();
            });
        } while (delta && ++step < stepLimit);

        // clean
        _.each(lineGroups, function (lines, group) {
            _.each(lines, function (line) {
                delete line.__dirs;
            });
        });

        console.log('Lengths equalizer: lines reduced by', step, 'steps, final delta', delta);
        console.timeEnd('Lengths equalizer');
    }

    // private helpers
    function findPoint(a1, a2, b1, b2, c1, c2, control, step) {
        var planes = _.map([[a1, a2], [b1, b2], [c1, c2]], function (pair) {
            var normal = Vector.crossProduct(pair[0], pair[1]).normalize();
            return new Plane(normal, 0);
        });

        var ABC = [];
        for (var i = 0, j = 2; i < 3; j = i, i++) {
            var vars = $.map(Solutions.twoPlanesAndSphere(planes[i], planes[j]), function (v) {
                return Vector.dotProduct(v, control) > 0 ? v : null;
            });
            if (vars.length != 1)
                console.log('super bug');
            ABC.push(vars[0]);
        }

        // todo: calc triangle's center point optionally (Euler set)

        // 1) in-center +normalize
        //var point = incenter.apply(this, ABC).normalize();

        // 2) in-center3d
        var point = incenter3d(planes[0], planes[1], planes[2], control);

        // 3) centeroid + normalize
        //	V6 {vertex: 11, line: 10, face: 12}
        //var point = ABC[0].add(ABC[1]).add(ABC[2]).scale(1/3).normalize();

        // 4) out-center3d
//		var pl = [];
//		for (var i = 0, j = 2; i < 3; j = i++) {
//			var ACmid = Vector.add(ABC[i], ABC[j]).scale(1/2);
//			var ACnorm = Vector.subtract(ABC[i], ABC[j]).normalize();
//			pl.push( new Plane(ACnorm, ACmid).normalize() );
//		}
//
//		var solpp = [];
//		for (var i = 0, j = 2; i < 3; j = i++) {
//			var vars = $.map(Solutions.twoPlanesAndSphere(pl[i], pl[j]), function(v) {
//				return Vector.dotProduct(v, control) > 0 ? v : null;
//			});
//			if (vars.length != 1)
//				console.log('super bug');
//			solpp.push( vars[0] );
//		}
        //var point = solpp[0];

        // equals distances assertion
        //var testDist = _.map(planes, function(pl) { return pl.result(point) });
        //console.log( testDist, _.max(testDist) - _.min(testDist) );

        point._enum = __pointsEnum++;
        var key = [a1._enum, a2._enum, step].join();
        __points[key] = point;
        //console.log(key)

        return point;
    }

    function edgePoint(A, B, i, N, key) {
        key = key || (
            A._enum < B._enum ?
                [A._enum, B._enum, i].join() :
                [B._enum, A._enum, N - i].join()
        );

        if (i * 2 > N) return edgePoint(B, A, N - i, N, key);

        if (__points[key]) return __points[key];

        var cos = A.cosWith(B);
        var Y = Vector.subtract(B, A.clone().scale(cos)).normalize();
        var angle = Math.acos(cos) * i / N;
        var point = Y.scale(Math.sin(angle)).add(A.clone().scale(Math.cos(angle)));

        point._enum = __pointsEnum++;
        point.triSide = true;
        __points[key] = point;
        //console.log(key)

        return point;
    }

    function addFace(A, B, C, sideDistances, triIndex) {
        __faces.push(new Figure({
            type: 'face',
            points: [A, B, C]
        }));
        _.each([[B, C], [C, A], [A, B]], function (pair, i) {
            var key = _.pluck(pair, '_enum').sort().join();
            //console.log(key);
            __lines[key] = __lines[key] || new Figure({
                type: 'line',
                points: pair,
                sideDistance: sideDistances[i],
                sideIndex: i,
                triIndex: triIndex
            });
        });
    }

    // plain incenter
    function incenter(A, B, C) {
        var AB = Vector.subtract(B, A);
        var AC = Vector.subtract(C, A);
        var BC = Vector.subtract(C, B);
        var Ab = AB.clone().normalize().add(AC.clone().normalize());
        var angle = AB.angleWith(AC);
        var l = [AB.length(), AC.length(), BC.length()];
        var p = (l[0] + l[1] + l[2]) / 2
        var S = Math.sqrt(p * (p - l[0]) * (p - l[1]) * (p - l[2]));
        var r = S / p;
        var incenter = Ab.scale(
            (r / Math.sin(angle / 2)) / Ab.length()
        ).add(A);

        // assertion
        var a = Vector.distance(B, C), b = Vector.distance(A, C), c = Vector.distance(A, B);
        _.each([[A, B, C], [B, C, A], [C, A, B]], function (test) {
            var v1 = Vector.subtract(test[0], test[1]);
            var v2 = Vector.subtract(test[2], test[1]);
            var bisect = v1.clone().normalize().add(v2.clone().normalize());
            var angle = v1.angleWith(v2);
            var a1 = v1.angleWith(bisect);
            var a2 = v2.angleWith(bisect);

            if (Math.abs(a1 + a2 - angle) > 1e-13)
                console.warn('a1 + a2 != angle');
            if (Math.abs(a1 - a2) > 1e-13)
                console.warn('a1 != a2');
        });

        return incenter;
    }

    // spherically incenter
    function incenter3d(p1, p2, p3, control) {
        var bisect1 = new Plane(p1.A - p2.A, p1.B - p2.B, p1.C - p2.C, p1.D - p2.D);
        var bisect2 = new Plane(p3.A - p2.A, p3.B - p2.B, p3.C - p2.C, p3.D - p2.D);

        var vars = $.map(Solutions.twoPlanesAndSphere(bisect1, bisect2), function (v) {
            return Vector.dotProduct(v, control) > 0 ? v : null;
        });
        if (vars.length != 1)
            console.error('incenter3d bug');
        return vars[0];
    }
}();</script>
<script type="text/javascript">Figure.prototype.clientDownload = function () {
    var figureUnifier = document.location.href.split('#').pop() || "";
    var figureName = figureUnifier.replace(/[^\w\.]/g, '_');
    var obj = {
            points: [],
            faces: [],
            add: function (what, entity) {
                what += 's';
                if (!entity.index) {
                    this[what].push(entity);
                    entity.index = this[what].length;
                }
                return entity.index;
            },
            output: []
        },

        R = parseFloat(window.form.state.radius),
        lines = _.unique(_.flatten(
            _.map(figure.subs('face'), function (face) {
                return face.bindedLines || face.subs('line').get();
            })
        )),
        ribs = _.compact(_.map(lines, function (line) {
            return line.removed ? null : {
                faces: line.product.model(true),
                unifier: line.product.unify().replace(/\s/g, '_') +
                ' ' + line.index
            }
        }));

    $.each(ribs, function (r, rib) {
        obj.output.push('g ' + figureName + ' ' + rib.unifier);
        var faces = $.map(rib.faces, function (face) {
            var points = $.map(face, function (vect) {
                if (!vect.index) {
                    vect.scale(R);
                    obj.output.push('v ' + vect.x + ' ' + vect.y + ' ' + vect.z);
                }
                return obj.add('point', vect);
            });
            obj.output.push('f ' + points.join(' '));
            return obj.add('face', points);
        });
        obj.output.push('surf 0.0 1.0 0.0 1.0 ' + faces.join(' '));
        obj.output.push('end');
    });

    // with header
    obj = [
        '#',
        '# http://acidome.ru/lab/calc/#' + figureUnifier,
        '#',
        '# (c) ' + (new Date).getFullYear() + ' acidome.ru/lab/calc',
        '# Under CC-BY-SA license',
        '#', ''
    ].concat(obj.output).join('\r\n');

    // output
    var form = $('<form>').attr({
        action: 'fileback.php?filename=' + figureName + '.obj',
        method: 'POST'/*,
		target: '_blank'*/
    })
        .appendTo('body').hide();
    $('<textarea name="content">').val(obj)
        .appendTo(form);
    form.submit();
    //$.post('fileback.php?filename=' + figureName, { content: obj });
};</script>

<!-- calc ui -->
<script type="text/javascript">/**
 * Helper for usage url's #fragment as source of params.
 *
 * @license  Legalize Cannabis License
 * @author   popitch ya ru
 */

function Stringifier(options) {
    this.options = options = $.extend(true, {
        // format: string,
        // mapping: hashmap
    }, options);

    var format = (options.format instanceof Array) ? options.format.join('') : options.format;
    this._compile(format);

    // Backbone.Events
    _.extend(this, Backbone.Events);
}

(function (root, _, $, undefined) {
    Stringifier.prototype = {
        fromString: function (string) {
            var matches = this.fullRegexp.exec(string);
            if (!matches) return {};

            var params = {};
            var options = this.options, i = 1;
            $.each(this.compiled, function (j, chunk) {
                if (typeof chunk == 'string') return;

                var mapper = options.mapping[chunk.name];
                if (mapper)
                    value = mapper.value.apply(params, matches.slice(i, i + chunk.argc + 1));
                else
                    value = matches[i];
                i += chunk.argc + 1;
                if (typeof value == 'object')
                    params = $.extend(true, params, value);
                else
                    params[chunk.name] = value;
            });

            // fire event
            var moreArgs = [].slice.call(arguments, 1);
            this.trigger.apply(this, ['params', params].concat(moreArgs));

            return params;
        },

        fromParams: function (params) {
            var options = this.options;
            var string = _.map(this.compiled, function (chunk) {
                if (typeof chunk == 'string') return chunk;

                var mapper = options.mapping[chunk.name];
                return mapper ?
                    mapper.string.call(params, params[chunk.name])
                    : params[chunk.name] || '';
            }).join('');

            // fire event
            var moreArgs = [].slice.call(arguments, 1);
            this.trigger.apply(this, ['string', string].concat(moreArgs));

            return string;
        },

        _compile: function (formatString) {
            // reg exp helpers
            function rescape(str) {
                return str.replace(/[\\\/\.\-+*?^$|{}\[\]]/g, '\\$&');
            }

            var reGroups = /\([^?][^\)]*\)/g;

            var compiled = this.compiled = [], pose = 1;

            // make chain
            formatString.replace(/([^<]+)|<([\w.]+):([^>]+)>/g,
                function (frag, simple, name, find) {
                    if (simple) {
                        compiled.push(simple);
                        return;
                    }
                    var argc = find.split(reGroups).length - 1;
                    compiled.push({
                        name: name,
                        find: find,
                        produce: function (argv) {
                            var i = 0;
                            return find.replace(reGroups, function () {
                                return argv[i++];
                            });
                        },
                        argc: argc,
                        pose: pose + 1
                    });
                    pose += argc + 1;
                }
            );

            // make full string regexp
            this.fullRegexp = new RegExp('^' +
                $.map(this.compiled, function (frag) {
                    return (typeof frag == 'string') ? rescape(frag) : '(' + frag.find + ')';
                }).join('') +
                '$');

            return compiled;
        }
    };
})(this, _, jQuery);
</script>
<script type="text/javascript">// exports
__ = i18n;

// simple text translater
function i18n(text, lang, translate) {
    lang = lang ? lang.toLowerCase() : i18n.lang();
    text = String(text);

    // translate, if present
    translate = translate || i18n.translate[lang];

    if (translate) {
        var namespace = text.replace(/^(\w+:)\S.*$|^.*$/, '$1');

        if (namespace) {
            text = text.replace(/^\w+:(\S.*)$/, '$1');

            return _.has(translate, namespace) && _.has(translate[namespace], text) ? translate[namespace][text] : i18n(text, lang, translate[namespace]);
        }
        return _.has(translate, text) ? translate[text] : text;
    }
    return text;
}

_.extend(i18n, {
    BASE_LANG: 'en'
});

_.extend(i18n, {
    lang: (function () {
        var lang = ko.observable(null);

        // try to get lang from cookie
        var cookieLang = (document.cookie.split('i18n')[1] || '=;').replace(/^=([^;]*);.*$/, '$1');
        if (cookieLang) {
            lang(cookieLang);
        }
        // listen observable, set cookie
        lang.subscribe(function () {
            document.cookie = 'i18n=' + lang();
        });
        return lang;
    }()),
    langList: [],
    translate: {},
    addLang: function (lang, data) {
        this.translate[lang] = data;
        this.langList.push(lang);
    },
    isLangKnown: function (lang) {
        return _.has(this.translate, lang.toLowerCase());
    },
    langOptions: function () {
        return _.chain(this.langList)
            .map(function (lang) {
                return {
                    id: lang,
                    name: __(':lang', lang)
                };
            })
            .sortBy('name')
            .value();
    }
});

// ko.binding "text" adapter
ko.bindingHandlers.text = (function (textBinding) {
    return {
        /*init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
			return textBinding.init.apply(this, arguments);
		},*/
        update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
            $(element).text(
                i18n(
                    ko.unwrap(valueAccessor())
                )
            );
        }
    };
})(ko.bindingHandlers.text);


/** Translate extensions
 */
i18n.addLang('en', {
    ':lang': 'English',

    "budget:": {
        "title:": {
            "line": "Edges",
            "face": "Faces",
            "vertex": "Vertices"
        }
    }
});


/** Translate extensions
 */
i18n.addLang('ru', {
    ':lang': 'Русский',

    "Carcass": "Каркас",
    "Schema": "Схема",
    "Cover": "Кровля",
    "Base": "План",

    "Figure options": "Исходная форма",
    "Level of detail, V": "Частота, V",
    "Subdivision class": "Класс разбиения",
    "Subdivision method": "Метод разбиения",
    "method:": {
        "Equal Chords": 'Равные хорды',
        "Equal Arcs": 'Равные дуги',
        "Mexican": 'Мексиканец'
    },
    "Rotational symmetry": "Осевая симметрия",
    "Fullerene": "Фуллерен",
    "fulleren:": {
        "None": 'Нет',
        "Inscribed in": 'Вписанный',
        "Described around": 'Описанный'
    },
    "Part of full sphere": "Часть сферы",
    "Align the base": "Выравнивать основание",

    "Product options": "Продукция",
    "Sphere radius, m": "Радиус сферы, м",
    "Connection type": "Способ соединения",
    "Pipe diameter, mm": "Диаметр трубы, мм",
    "Spinning clockwise": "По-часовой",
    "else counter": "или против",

    "Timber size": "Материал ребер",
    "Width, mm": "Ширина, мм",
    "Thickness, mm": "Толщина, мм",

    "Resulting": "В результате имеем",
    "Height from base, m": "Высота от основания, м",
    "Base radius, m": "Радиус основания, м",
    "Sizes (units)": "Размеры",
    "Faces": "Граней",
    "Edges": "Ребер",
    "Vertices": "Вершин",
    "Beams": "Балки (ребра)",
    "Total length of beams, m": "Суммарная длина, м",
    "Total volume of beams, m3": "Объем ребер, м3",
    "Max. beam length, mm": "Макс. длина ребра, мм",
    "Angle between faces, °": "Угол смежных граней, °",
    "Base area, m2": "Площадь основания, м2",
    "Coverage area, m2": "Площадь покрытия, м2",
    "Triangles": "Треугольники",
    "Min. height, mm": "Мин. высота, мм",
    "Max. side, mm": "Макс. сторона, mm",

    "mm": "мм",
    "pcs": "шт.",

    "buyme:": {
        "Product offering": "Предложения продукции",
        "Buy it": "Купить",
        "Sorry, error happens while we trying to order. Please, try again later.": 'К сожалению, при попытке заказать происходит ошибка. Пожалуйста, попробуйте повторить заказ позже.',
        "Thank you, the order has been successfully created.": 'Спасибо, заказ успешно создан. Проверьте вашу электронную почту.',
        "Check your email, if the letter is marked as spam, mark it as not spam.": 'Проверьте почту, если письмо случайно попало в папку "Спам", вытащите его оттуда.',
        "E-mail is not valid": "Вы ввели неправильный e-mail"
    },

    "share:": {
        "Share": 'Поделиться',
        "from acidome.ru": 'от acidome.ru'
    },

    "Donate": 'Помочь проекту',

    "modified": 'изменен',

    "budget:": {
        "title:": {
            "line": "Ребра",
            "face": "Грани",
            "vertex": "Вершины"
        }
    },

    "clothier:": {
        "Clothier": "Портной"
    }
});

/**
 * @thanks https://www.facebook.com/enrique.desanjulian
 */
i18n.addLang('es', {
    ':lang': 'Español',

    "Carcass": "Carcasa",
    "Schema": "Esquema",
    "Cover": "Cubierta",
    "Base": "Base",

    "Figure options": "Opciones de geometría",
    "Level of detail, V": "Frecuencia, V",
    "Subdivision class": "Tipo de subdivisión",
    "Subdivision method": "Método de subdivisión",
    "method:": {
        "Equal Chords": 'Cuerdas iguales',
        "Equal Arcs": 'Arcos iguales',
        "Mexican": 'Mexicano'
    },
    "Rotational symmetry": "Simetría rotacional",
    "Fullerene": "Circunscripción",
    "fulleren:": {
        "None": 'Ninguna',
        "Inscribed in": 'Inscrita dentro',
        "Described around": 'Descrita alrededor'
    },
    "Part of full sphere": "Porción de la esfera",
    "Align the base": "Alinear la base",

    "Product options": "Opciones de Proyecto",
    "Sphere radius, m": "Radio de la esfera, m",
    "Connection type": "Tipo de conexión",
    "Pipe diameter, mm": "Diámetro del tubo, mm",
    "Spinning clockwise": "Girando en sentido horario",
    "else counter": "sentido contrario",

    "Timber size": "Tamaño de las piezas",
    "Width, mm": "Anchura, mm",
    "Thickness, mm": "Grosor, mm",

    "Resulting": "Resultados",
    "Height from base, m": "Altura desde la base, m",
    "Base radius, m": "Radio de la base, m",
    "Sizes (units)": "Tamaños (unidades)",
    "Faces": "Caras",
    "Edges": "Aristas",
    "Vertices": "Vértices",
    "Beams": "Travesaños",
    "Total length of beams, m": "Longitud total de los travesaños, m",
    "Total volume of beams, m3": "Volumen total de los travesaños, m3",
    "Max. beam length, mm": "Longitud máx. del travesaño, mm",
    "Angle between faces, °": "Ángulo entre caras, °",
    "Base area, m2": "Área de la base, m2",
    "Coverage area, m2": "Área de la cubierta, m2",
    "Triangles": "Triángulos",
    "Min. height, mm": "Altura mín., mm",
    "Max. side, mm": "Longitud máx. del lado, mm",

    "mm": "mm",
    "pcs": "piezas"
});

/**
 * @thanks https://www.facebook.com/bezruchko.arkadiy
 */
i18n.addLang('ua', {
    ':lang': 'Українська',

    "Carcass": "Каркас",
    "Schema": "Схема",
    "Cover": "Покрівля",
    "Base": "План",

    "Figure options": "Вихідна форма",
    "Level of detail, V": "Частота, V",
    "Subdivision class": "Клас розбивки",
    "Subdivision method": "Метод розбивки",
    "method:": {
        "Equal Chords": 'Рівні хорди',
        "Equal Arcs": 'Рівні дуги',
        "Mexican": 'Мексиканський'
    },
    "Rotational symmetry": "Осьова симетрія",
    "Fullerene": "Фуллерен",
    "fulleren:": {
        "None": 'Немає',
        "Inscribed in": 'Вписаний',
        "Described around": 'Описаний'
    },
    "Part of full sphere": "Частина сфери",
    "Align the base": "Вирівнювати основу",

    "Product options": "Продукція",
    "Sphere radius, m": "Радіус сфери, м",
    "Connection type": "Спосіб з'єднаня",
    "Pipe diameter, mm": "Діаметр труби, мм",
    "Spinning clockwise": "За-годинниковою",
    "else counter": "або проти",

    "Timber size": "Матеріал ребер",
    "Width, mm": "Ширина, мм",
    "Thickness, mm": "Товщина, мм",

    "Resulting": "В результаті маємо",
    "Height from base, m": "Висота від основи, м",
    "Base radius, m": "Радіус основи, м",
    "Sizes (units)": "Разміри",
    "Faces": "Граней",
    "Edges": "Ребер",
    "Vertices": "Вершин",
    "Beams": "Балки (ребра)",
    "Total length of beams, m": "Сумарна довжина, м",
    "Total volume of beams, m3": "Об'єм ребер, м3",
    "Max. beam length, mm": "Макс. довжина ребра, мм",
    "Angle between faces, °": "Кут суміжних граней, °",
    "Base area, m2": "Площа основи, м2",
    "Coverage area, m2": "Площа покриття, м2",
    "Triangles": "Трикутники",
    "Min. height, mm": "Мін. висота, мм",
    "Max. side, mm": "Макс. сторона, mm",

    "mm": "мм",
    "pcs": "шт."
});


/**
 * @thanks https://www.facebook.com/pascal.belzunce
 */
i18n.addLang('fr', {
    ':lang': 'Français',

    "Carcass": "Squelette",
    "Schema": "Schéma",
    "Cover": "Couverture",
    "Base": "Sol",

    "Figure options": "Paramètres de la forme",
    "Level of detail, V": "Fréquence, V",
    "Subdivision class": "Classe de subdivision",
    "Subdivision method": "Méthode de subdivision",
    "method:": {
        "Equal Chords": 'Cordes égales',
        "Equal Arcs": 'Arcs égaux',
        "Mexican": 'Mexicaine'
    },
    "Rotational symmetry": "Axe de symétrie",
    "Fullerene": "Fullerène",
    "fulleren:": {
        "None": 'Aucune',
        "Inscribed in": 'Intérieur',
        "Described around": 'Extérieur'
    },
    "Part of full sphere": "Découpage de la sphère",
    "Align the base": "Aligner le bas",

    "Product options": "Caractéristiques du matériel",
    "Sphere radius, m": "Rayon de la sphère, m",
    "Connection type": "Type de connecteur",
    "Pipe diameter, mm": "Diamètre du tube, mm",
    "Spinning clockwise": "Sens horaire",
    "else counter": "antihoraire",

    "Timber size": "Taille des montants",
    "Width, mm": "Largeur, mm",
    "Thickness, mm": "Epaisseur, mm",

    "Resulting": "Résultats",
    "Height from base, m": "Hauteur au sol, m",
    "Base radius, m": "Rayon au sol, m",
    "Sizes (units)": "Quantités",
    "Faces": "Faces",
    "Edges": "Montants",
    "Vertices": "Nœuds",
    "Beams": "Montants",
    "Total length of beams, m": "Longueur totale des montants, м",
    "Total volume of beams, m3": "Volume total des montants, м3",
    "Max. beam length, mm": "Montant le plus grand, мм",
    "Angle between faces, °": "Angle entre les faces, °",
    "Base area, m2": "Surface au sol, м2",
    "Coverage area, m2": "Surface de la couverture, м2",
    "Triangles": "Triangles",
    "Min. height, mm": "Hauteur mini, мм",
    "Max. side, mm": "Côté maxi, mm",

    "mm": "mm",
    "pcs": "pcs",

    "Donate": 'Donation',

    "modified": 'modifié',

    "budget:": {
        "title:": {
            "line": "Montants",
            "face": "Faces",
            "vertex": "Nœuds"
        }
    }
});

/**
 * @thanks https://www.facebook.com/gencho.genchev.50
 */
i18n.addLang('bg', {
    ':lang': 'Български',

    "Carcass": "Конструкция",
    "Schema": "Схема",
    "Cover": "Покрив",
    "Base": "Основа",

    "Figure options": "Изходна форма",
    "Level of detail, V": "Честота V",
    "Subdivision class": "Делови клас",
    "Subdivision method": "Метод на делене",
    "method:": {
        "Equal Chords": "Равни Хорди",
        "Equal Arcs": "Равни дъги",
        "Mexican": "Мексикански"
    },
    "Rotational symmetry": "Осева симетрия",
    "Fullerene": "Фулерен",
    "fulleren:": {
        "None": "Не",
        "Inscribed in": "Вписан",
        "Described around": "Описан"
    },
    "Part of full sphere": "Част от сферата",
    "Align the base": "Изравни на основата",

    "Product options": "Параметри на купола",
    "Sphere radius, m": "Радиус на сферата, м",
    "Connection type": "Тип връзки",
    "Pipe diameter, mm": "Диаметър на тръбата, мм",
    "Spinning clockwise": "По часовата стрелка",
    "else counter": "Обратно на ч.с.",

    "Timber size": "Материал на ребрата",
    "Width, mm": "Ширина, мм",
    "Thickness, mm": "Дебелина, мм",

    "Resulting": "Резултат",
    "Height from base, m": "Височина от основата, м",
    "Base radius, m": "Радиус на основата, м",
    "Sizes (units)": "Размери (бройки)",
    "Faces": "Лица",
    "Edges": "Краища",
    "Vertices": "Върхове",
    "Beams": "Греди",
    "Total length of beams, m": "Обща дължина греди, м",
    "Total volume of beams, m3": "Обем греди, м3",
    "Max. beam length, mm": "Максимална дължина греда, мм",
    "Angle between faces, °": "Ъгъл между съседни повърхности",
    "Base area, m2": "Площ на основата, м2",
    "Coverage area, m2": "Покривна площ, м2",
    "Triangles": "Триъгълници",
    "Min. height, mm": "Мин. Височина, мм",
    "Max. side, mm": "Минимална страна, мм",

    "mm": "мм",
    "pcs": "бр."
});

i18n.addLang('pl', {
    ':lang': 'Polski',

    "Carcass": "Konstrukcja",
    "Schema": "Schemat",
    "Cover": "Pokrycie",
    "Base": "Podstawa",

    "Figure options": "Opcje kształtu",
    "Level of detail, V": "Stopień aproksymacji, V",
    "Subdivision class": "Klasa podziału",
    "Subdivision method": "Metoda podziału",
    "method:": {
        "Equal Chords": 'Równych cięciw',
        "Equal Arcs": 'Równych łuków',
        "Mexican": 'Meksykańska'
    },
    "Rotational symmetry": "Оś symetrii",
    "Fullerene": "Wzór fulerenu",
    "fulleren:": {
        "None": 'Nie',
        "Inscribed in": 'Wpisany',
        "Described around": 'Opisany'
    },
    "Part of full sphere": "Część sfery",
    "Align the base": "Wyrównaj do podstawy",

    "Product options": "Opcje produktu",
    "Sphere radius, m": "Promień sfery, m",
    "Connection type": "Typ łączenia",
    "Pipe diameter, mm": "Średnica rury, mm",
    "Spinning clockwise": "Obrót w prawo ",
    "else counter": "lub w lewo",

    "Timber size": "Rozmiar belki",
    "Width, mm": "Szerokość, mm",
    "Thickness, mm": "Grubość, mm",

    "Resulting": "W wyniku otrzymujemy",
    "Height from base, m": "Wysokość od podstawy, m",
    "Base radius, m": "Promień podstawy, m",
    "Sizes (units)": "Elementy",
    "Faces": "Ścianki",
    "Edges": "Krawędzie",
    "Vertices": "Wierzchołki",
    "Beams": "Belki",
    "Total length of beams, m": "Całkowita długość belek, m",
    "Total volume of beams, m3": "Całkowita objętość belek, m3",
    "Max. beam length, mm": "Maksymalna długość belki, mm",
    "Angle between faces, °": "Kąt pomiędzy ściankami, °",
    "Base area, m2": "Powierzchnia podstawy, m2",
    "Coverage area, m2": "Powierzchnia pokrycia, m2",
    "Triangles": "Тrójkąty",
    "Min. height, mm": "Maksymalna wysokość, mm",
    "Max. side, mm": "Maksymalna długość boku, mm",

    "mm": "mm",
    "pcs": "szt.",

    "budget:": {
        "title:": {
            "line": "Belki",
            "face": "Ścianki",
            "vertex": "Wierzchołki"
        }
    }
});
</script>
<script type="text/javascript">var viewer = (function () {
    var zoom = {current: 3, progress: 1, target: 4},
        ZOOM_RANGE = [0, 16],
        ZOOM_LATENCY = 400, // ms
        NEAR = 1, FAR = 5;

    var viewer = _.clone(Backbone.Events);

    var $dom = viewer.$dom = $('.geodesic'),
        $canvas = $dom.find('canvas.preview'),
        $form = $dom.find('form.options');

    // resize spy
    !function () {
        $(window).resize(_.debounce(function () {
            var w = $(window).width(),
                h = $(window).height(),
                fb = $form.outerHeight(true) + $form.offset().top;

            if (w != canvasW || h != canvasH) {
                canvasH = Math.max(fb, h * (IS_IFRAME ? 1 : 1.2));
                canvasW = w;
                viewer.trigger('render');
            }
        }, 300));

        // init
        $(function () {
            $dom.show();
            $(window).resize();
        });
    }();

    // mouse spy
    !function () {
        var touchStart, touchCurrent, overObject, hasRotate;

        $canvas
            .bind('mousedown touchstart', function (e) {
                if (!commonGroup) return;
                touchStart = touchScene(e);
            })
            .bind('mouseup touchend', function (e) {
                touchStart = null;
                _.defer(function () {
                    hasRotate = false;
                });
            })
            .bind('mousemove touchmove', function (e) {
                touchCurrent = touchScene(e);
                if (touchStart) {
                    // rotate startTouch.matrix, from startTouch.sphereCross to touch.sphereCross
                    rotateSphere(touchStart, touchCurrent);
                    hasRotate = true;
                }
                return false;
            })
            .click(function (e) {
                if (!hasRotate) {
                    trigger('click', e);
                }
            });

        // mouse wheel / zooming
        $canvas[0].addEventListener('DOMMouseScroll', handleScroll, false);
        $canvas[0].addEventListener('mousewheel', handleScroll, false);

        function handleScroll(e) {
            var evt = e || window.event;
            var delta = (evt.detail < 0 || evt.wheelDelta > 0) ? 1 : -1;
            if (delta) {
                var before = touchScene(e);

                if (!before.sphereTouch) return;

                zoom.target = Math.min(Math.max(zoom.target + delta, ZOOM_RANGE[0]), ZOOM_RANGE[1]);
                if (zoom.progress == 1) {
                    zoom = _.extend(zoom, {
                        progress: 0,
                        start: zoom.current,
                        startTouch: before,
                        startAt: new Date,
                        startEvent: e
                    });
                }
            }
            return e.preventDefault() && false;
        }


        // auto zoom progresser
        zoomProgress();

        function zoomProgress() {
            var now = (new Date).getTime(),
                timeout = 100,
                current = zoom.current;

            if (zoom.startAt) {
                zoom.progress = Math.min(now - zoom.startAt.getTime(), ZOOM_LATENCY) / ZOOM_LATENCY;
                zoom.current = zoom.start + (zoom.target - zoom.start) * zoom.progress;
                if (zoom.current != current) {
                    viewer.trigger('render');
                    var touch = touchScene(zoom.startEvent);
                    rotateSphere(zoom.startTouch, touch);
                    timeout = 0;
                }
            }
            _.delay(zoomProgress, timeout);
        }

        function rotateSphere(touch1, touch2) {
            var axis = touch1.sphereCross.clone().cross(touch2.sphereCross);
            var angle = touch1.sphereCross.angleTo(touch2.sphereCross);

            if (Math.abs(angle) < 1e-4 || Math.abs(axis.length()) < 1e-9) return;

            commonGroup.matrix = (new THREE.Matrix4).rotateByAxis(axis, angle)
                .multiply(touch1.matrix);
            viewer.trigger('render');
        }

        function touchScene(e) {
            var raycaster = getRaycaster(e);

            // fix Threejs: prepare mesh's vertices position to world coordinates
            _.each(scene.getDescendants(), function (mesh) {
                if (mesh instanceof THREE.Mesh) {
                    mesh._geometryVertices = mesh.geometry.vertices;
                    mesh.geometry.vertices = mesh._geometryWorldVertices = mesh._geometryWorldVertices ||
                        _.map(mesh.geometry.vertices, function (v) {
                            return v.clone().add(mesh.position);
                        });
                }
            });
            var intersects = raycaster.intersectObjects(scene.children, true);
            // from fix
            _.each(scene.getDescendants(), function (mesh) {
                if (mesh instanceof THREE.Mesh) {
                    mesh.geometry.vertices = mesh._geometryVertices;
                }
            });


            // cross with shere
            var R = SCALE * form.state.radius,
                p = raycaster.ray.origin,
                v = raycaster.ray.direction,
                a = v.dot(v),
                b = v.dot(p) * 2,
                c = p.dot(p) - R * R,
                D = b * b - 4 * a * c,
                q = -b / 2 / a;
            if (D > 0) {
                D = Math.sqrt(D) / 2 / a;
                q = (c > 0) ? q - D : q + D;
            }

            var sphereCross = v.clone().multiplyScalar(q).add(p);

            if (_.isEmpty(intersects)) {
                trigger('mouseout', e);
                overObject = null;
            } else {
                if (overObject === intersects[0].object) {
                    trigger('mousemove', e);
                }
                else {
                    trigger('mouseout', e);
                    overObject = intersects[0].object;
                    trigger('mousein', e);
                }
            }

            return {
                raycaster: raycaster,
                intersect: intersects[0],
                product: intersects[0] && intersects[0].object._product,
                sphereCross: sphereCross,
                sphereTouch: D > 0,
                matrix: commonGroup && commonGroup.matrix
            };
        }

        function trigger(eventName, e) {
            if (overObject && overObject.trigger && overObject._events[eventName]) { // handler present
                overObject.trigger(eventName, e);
                viewer.trigger('render');
            }
        }

        var projectionMatrixInverse = new THREE.Matrix4;

        function getRaycaster(e) {
            // ray
            // FF fix: used page's [X,Y] with canvas position [0,0]
            var oe = e.originalEvent || e,
                x = (e.offsetX || e.pageX) || (oe.touches && oe.touches[0] && oe.touches[0].pageX) || 0,
                y = (e.offsetY || e.pageY) || (oe.touches && oe.touches[0] && oe.touches[0].pageY) || 0,
                v = new THREE.Vector3(
                    (x / canvasW) * 2 - 1,
                    -(y / canvasH) * 2 + 1,
                    0.5
                );
            //v = projector.unprojectVector(v, camera);
            projectionMatrixInverse.getInverse(camera.projectionMatrix);
            v.applyProjection(projectionMatrixInverse).applyMatrix4(camera.matrixWorld);

            var p = camera.position;
            v.sub(p).normalize();

            return new THREE.Raycaster(p, v);
        }
    }();

    // view mode switcher
    var activeMode, currentFigure;

    !function () {
        var $modeList = $('.mode', $dom),
            $activeMode = $modeList.filter('.active');

        activeMode = $activeMode.data('mode');

        // track initial mode
        Tracker.push('view', activeMode);

        $modeList.click(function () {
            var $mode = $(this);
            if (this != $activeMode[0]) {
                $activeMode.removeClass('active');
                $activeMode = $mode;
                $activeMode.addClass('active');

                activeMode = $activeMode.data('mode');

                // track mode switched
                Tracker.push('view', activeMode);

                updateCameraPosition();
                viewer.trigger('render', currentFigure);
            }
        });
    }();

    // stat switcher
    !function () {
        var active;

        $('.stat .toggle', $dom).click(function () {
            active = !active;
            $('.stat', $dom).add(this).toggleClass('active', active);
        });
    }();

    // adapt to tree.js
    var canvasW, canvasH,
        camera, scene, light, renderer, commonGroup;

    // const
    var SCALE = 1000;

    // projection helper
    var projector = new THREE.Projector();

    function updateCameraPosition() {
        var pos = (ZOOM_RANGE[1] - zoom.current) / ZOOM_RANGE[1], // [0..1]
            R = form.state.radius;

        // todo: zoom by logarithmic scale
        //pos = Math.pow(2, pos * 10) / 1025;

        pos = NEAR + (FAR - NEAR) * pos; // (NEAR..FAR]

        pos = new THREE.Vector3(
            0,
            0, // man's eyes average height
            R * pos //form.state.radius * 5
        );
        pos[AXIS] = (figure ? _.min(_.pluck(figure.$points, AXIS)) : 0) * R + // dome base
            1.6; // avg eyes height

        var part = viewer.drivers[activeMode].particle;

        var dist = (pos.length() + (part * 3 - 1) * R) * SCALE;

        camera.far = dist;
        camera.position.copy(pos.multiplyScalar(SCALE));
        camera.updateMatrixWorld();

        // light distance
        light.distance = dist;
    }

    // renderer init
    !function () {
        canvasW = $canvas.innerWidth();
        canvasH = $canvas.innerHeight();
        window.camera = // debug
            camera = new THREE.PerspectiveCamera(30, canvasW / canvasH, 10, 1e+5);

        scene = new THREE.Scene();

        window.renderer = // debug
            renderer = new THREE.CanvasRenderer({canvas: $canvas[0], antialias: true});

        window.light = // debug
            light = new THREE.PointLight(0xffffff, 1.5);
        scene.add(light);
    }();

    // renderer
    viewer.on('render', function (figure) {
        var startAt = new Date;

        camera.aspect = canvasW / canvasH;
        renderer.setSize(canvasW, canvasH);
        camera.updateProjectionMatrix();

        //console.log(canvasW, canvasH);;

        // set camera distance position
        updateCameraPosition();

        // actualize light position
        light.position.copy(camera.position);

        // update figure, if required
        if (figure) {
            // renew common group object, with saving matrix rotation
            if (commonGroup) {
                var prevMatrix = commonGroup.matrix.clone();
                scene.remove(commonGroup);
            }

            window.commonGroup = // debug
                commonGroup = new THREE.Object3D();
            commonGroup.matrixAutoUpdate = false;

            commonGroup.matrix.copy(prevMatrix || commonGroup.matrix);
            var mel = commonGroup.matrix.elements;
            commonGroup.matrix.multiplyScalar(SCALE * form.state.radius / Math.sqrt(mel[0] * mel[0] + mel[4] * mel[4] + mel[8] * mel[8])); //commonGroup.matrix.getColumnX().length());

            scene.add(commonGroup);

            // refill common group
            _.each(figure.$primitives, function (f) {
                var showRemoved = viewer.drivers[activeMode].showRemoved;

                if (!showRemoved && f.removed) return;

                var color = productPalette[f.type][f.order].integer;

                var object = viewer.drivers[activeMode].present(f, color);
                var objects = _.compact(object instanceof Array ? object : [object]);

                _.each(objects, function (object) {
                    if (object.geometry) {
                        // vertices relatively center
                        averageVertex3(object.position, object.geometry.vertices);
                        _.each(object.geometry.vertices, function (v) {
                            v.sub(object.position);
                        });
                    }

                    // back link
                    object.sourceFigure = f;

                    commonGroup.add(object);
                });
            });

            currentFigure = figure;
        }

        // update matrixWorld
        scene.updateMatrixWorld(true);

        camera.lookAt(scene.position);
        renderer.render(scene, camera);
    });

    return viewer;

    function averageVertex3(target, vertices) {
        target.x =
            target.y =
                target.z = 0;
        _.reduce(vertices, function (center, v) {
            return center.add(v);
        }, target)
            .multiplyScalar(1 / vertices.length);
    }
})();
</script>
<script type="text/javascript">// any methods how figure may be present

viewer.drivers = (function () {
    var geometry;

    // new vertices registration helper
    function pushVertex(v) {
        var index = _.indexOf(_.pluck(geometry.vertices, 'source'), v);
        if (-1 != index) return index;

        var vertice = new THREE.Vector3().copy(v);
        //vertice.w = 1;
        vertice.source = v;
        geometry.vertices.push(vertice);
        return geometry.vertices.length - 1;
    }

    function pushFace(face, appendRevertedVersion) {
        if (appendRevertedVersion) {
            pushFace(_.reduceRight(face, function (memo, p) {
                return memo.concat([p])
            }, []));
        }

        face = _.map(face, pushVertex);
        if (face.length == 3) {
            geometry.faces.push(new THREE.Face3(face[0], face[1], face[2]));
        }
        else if (face.length >= 4) {
            for (var i = 1, l = face.length; i < l - 2; i += 2) {
                var face4 = new THREE.Face4(face[0], face[i + 0], face[i + 1], face[i + 2]);
                geometry.faces.push(face4);
                if (face.length > 4) {
                    face4._source = face;
                    (face._collect = face._collect || []).push(face4);
                }
            }
            if (face.length % 2) {
                var face3 = new THREE.Face3(face[0], face[i + 0], face[i + 1]);
                geometry.faces.push(face3);
                if (face.length > 4) {
                    face3._source = face;
                    (face._collect = face._collect || []).push(face3);
                }
            }
        }
        else {
            console.error('wrong face', face);
        }
    }

    function averageVertex3(target, vertices) {
        target.x =
            target.y =
                target.z = 0;
        _.reduce(vertices, function (center, v) {
            return center.add(v);
        }, target)
            .multiplyScalar(1 / vertices.length);
    }

    function makeParticleLabelProgram(options) {
        var font = options.fontSize + 'px ' + options.fontFamily;

        return function (ctx) {
            ctx.transform(1, 0, 0, -1, 0, 0);

            if (options.bgRadius) {
                ctx.beginPath();

                var gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, options.bgRadius);
                gradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
                gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');

                ctx.arc(0, 0, options.bgRadius, 0, Math.PI * 2);
                ctx.closePath();

                ctx.fillStyle = gradient;
                ctx.fill();
            }

            ctx.textBaseline = 'middle';
            ctx.textAlign = 'center';
            ctx.fillStyle = 'black';
            ctx.font = font;
            ctx.fillText(options.text, 0, 0);
        };
    }

    return {
        "carcass": {
            particle: 1,
            present: function (figure, color) {
                // lines only
                if (figure.type != 'line') return null;

                geometry = new THREE.Geometry();

                // get product model
                var model = figure.product.model(true);

                var material = new THREE.MeshLambertMaterial({
                    color: color,
                    shading: THREE.FlatShading,
                    overdraw: true,
                    wireframe: false,
                    opacity: 0.95
                });

                _.each(model, function (face) {
                    pushFace(face, false);
                });

                //geometry.normalsNeedUpdate = true;
                geometry.computeFaceNormals();

                // restore normals of face4 like face3
                _.each(geometry.faces, function (face) {
                    var sface = face._source;
                    if (sface) {
                        var normal = _.filter(sface._collect, function (face4) {
                            return face4.normal.length() > 0;
                        })[0].normal;
                        _.each(sface._collect, function (face4) {
                            face4.normal.copy(normal);
                        });
                    }
                });

                return new THREE.Mesh(geometry, material);
            }
        },

        "cover": {
            showRemoved: true,
            particle: 1,
            present: function (figure, color) {
                if (figure.type != 'face') return null;

                geometry = new THREE.Geometry();

                // get product model
                var model = figure.product.model(true);

                _.each(model, function (face) {
                    pushFace(face, true);
                });

                //geometry.normalsNeedUpdate = true;
                geometry.computeFaceNormals();

                // restore normals of face4 like face3
                _.each(geometry.faces, function (face) {
                    var sface = face._source;
                    if (sface) {
                        var normal = _.filter(sface._collect, function (face4) {
                            return face4.normal.length() > 0;
                        })[0].normal;
                        _.each(sface._collect, function (face4) {
                            face4.normal.copy(normal);
                        });
                    }
                });

                var material = new THREE.MeshLambertMaterial({
                        color: color,
                        shading: THREE.FlatShading,
                        overdraw: true,
                        wireframe: false
                    }),
                    contourMaterial = new THREE.LineDashedMaterial({
                        color: 0,
                        linewidth: 5,
                        dashSize: 8,
                        gapSize: 5,
                        vertexColors: THREE.VertexColors
                    }),
                    contourGeometry = new THREE.Geometry();

                _.each(geometry.vertices, function (vertice) {
                    contourGeometry.vertices.push(
                        vertice.clone().multiplyScalar(1000 * form.state.radius)
                    );
                });
                contourGeometry.vertices.push(
                    geometry.vertices[0].clone().multiplyScalar(1000 * form.state.radius)
                );

                var contour = new THREE.Line(contourGeometry, contourMaterial);

                updateOpacity();

                var object = new THREE.Mesh(geometry, material),
                    over = false;

                _.extend(object, Backbone.Events)
                    .on('mousein', function (e) {
                        over = true;
                        updateOpacity();
                    })
                    .on('mouseout', function (e) {
                        over = false;
                        updateOpacity();
                    })
                    .on('click', function (e) {
                        if (figure.removed) {
                            var restored = _.pluck(figure.restore(), 'removeIndex');
                            form.removedList.removeAll(restored);
                        } else {
                            var removed = _.pluck(figure.remove(), 'removeIndex');
                            form.removedList.push.apply(form.removedList, removed);
                        }
                        updateOpacity();
                    });

                return [object, contour];

                function updateOpacity() {
                    material.opacity = figure.removed ? .03 : (over ? 1 : .97);
                    contourMaterial.opacity = over ? .5 : 0;
                }
            }
        },

        "schema": {
            particle: 1 / 3,
            present: function (figure, color) {
                geometry = new THREE.Geometry();

                _.each(figure.$points, pushVertex);

                var object = new THREE.Object3D();

//				if (this[figure.type]) {
//					var adv = this[figure.type](figure, color);
//					if (adv) object.add(adv);
//				}

                // product index
                var fontSize = (form.state.radius / form.state.detail * 100) * (form.state.subdivClass == 'I' ? 1 : 1.33),
                    particle = new THREE.Particle(new THREE.ParticleCanvasMaterial({
                        color: color,
                        program: makeParticleLabelProgram({
                            fontSize: fontSize,
                            fontFamily: 'Optimer, verdana',
                            bgRadius: fontSize,
                            text: figure.index
                        })
                    }));

                if (figure.type == 'vertex') {
                    averageVertex3(particle.position, figure.$points);
                    particle.position.multiplyScalar(1 + 0.1 / figure.product.R); // 1cm to outside
                } else {
                    var points = _.unique(_.flatten(figure.product.model(true)));

                    if (figure.type == 'line' && /^GoodKarma|Semicone$/.test(form.state.connType)) {
                        //points = points.concat()
                        points = points.concat(figure.bindedFace.$points.get());
                    }

                    averageVertex3(particle.position, points);
                    particle.position.normalize();
                }
                particle.position.multiplyScalar(1.001);
                //particle.updateMatrix();

                object.add(particle);

                return [viewer.drivers["carcass"].present(figure, color), object];
            }
        },

        "base": {
            particle: 1 / 3,
            present: function () {
                return function (figure, color) {
                    if (figure.type != 'line' || figure.origin.removed || (figure.bindedFace && figure.bindedFace.removed)) return;

                    var isActive = (_.where(figure.origin.$super.face, {removed: false}).length == 1), // basically
                        isBase = isActive;

                    var lineObject = viewer.drivers["carcass"].present(figure, color),
                        objects = [lineObject];

                    begin(objects);

                    if (isBase) {
                        baseLines.push(figure);
                    }

                    lineObject.material.opacity = isActive ? .5 : .027;

                    _.extend(lineObject, Backbone.Events)
                        .on('mousein', function (e) {
                            this.material.opacity = .263;
                        })
                        .on('mouseout', function (e) {
                            this.material.opacity = isActive ? .5 : .027;
                        })
                        .on('click', function (e) {
                            // toggle activity
                            isActive = !isActive;

                            if (isActive) {
                                activeLines.push(figure);
                            } else {
                                activeLines = _.difference(activeLines, [figure]);
                            }

                            if (isBase) {
                                recalcBaseMetricContainer();
                            }

                            _.each(figure.origin.$sub.vertex, updateVertexMetric);

                            this.material.opacity = isActive ? .5 : .027;
                        });

                    _.each(figure.origin.$sub.vertex, function (vertex) {
                        if (once(vertex)) {
                            vertex.metricObjects = vertexMetric(vertex);

                            objects = objects.concat(vertex.metricObjects);
                        }
                        updateVertexMetric(vertex);
                    });

                    if (isActive) {
                        activeLines.push(figure)
                    }

                    return objects;
                };

                var inited, axisDown, R,
                    vertexRegister, activeLines, baseLines, baseVertexes,
                    baseMetricContainer;

                function begin(objects) {
                    if (inited) return;

                    vertexRegister = [];
                    activeLines = [];
                    baseLines = [];

                    // release mem
                    _.each(figure.subs('vertex'), function (vertex) {
                        delete vertex.metricObjects;
                    });

                    // init base metric container
                    baseMetricContainer = new THREE.Object3D();
                    objects.push(baseMetricContainer);

                    R = form.state.radius;

                    // get down position by axis uses global figure object
                    axisDown = _.chain(figure.subs('vertex')).where({removed: false}).pluck('$points').pluck('0').pluck(AXIS).min().value();

                    // destroy inited state after driver been fully applied by current runtime
                    _.defer(end);

                    inited = true;
                }

                function end() {
                    // order base vertexes collection as polygon
                    var lines = _.tail(baseLines, 0),
                        line = lines[0],
                        vertex = line.$sub.vertex[0];
                    baseVertexes = [];
                    while (line) {
                        baseVertexes.push(vertex);
                        lines = _.difference(lines, [line]);
                        line = _.find(lines, function (l) {
                            return _.contains(l.$sub.vertex, vertex);
                        });
                        vertex = line && line.$sub.vertex[line.$sub.vertex[0] === vertex ? 1 : 0];
                    }

                    _.isEmpty(lines) || console.error('Achtung happens');

                    recalcBaseMetricContainer();
                    viewer.trigger('render');

                    inited = false;
                    2
                }

                function once(entity) {
                    if (!_.contains(vertexRegister, entity)) {
                        vertexRegister.push(entity);
                        return true;
                    }
                }

                function recalcBaseMetricContainer() {
                    var cont = baseMetricContainer;

                    // clear
                    for (var i = cont.children.length - 1; i >= 0; i--)
                        cont.remove(cont.children[i]);

                    // fill
                    var vertexes = _.intersection(baseVertexes, _.chain(activeLines).pluck('origin').unique().pluck('$sub').pluck('vertex').invoke('get').flatten().value());
                    if (vertexes.length > 1) {
                        var lineColor = 0x808080,
                            lineMaterial = new THREE.LineBasicMaterial({
                                color: lineColor,
                                opacity: 1,
                                linewidth: 1,
                                vertexColors: THREE.VertexColors
                            }),
                            prevVertex = _.last(vertexes);

                        _.each(vertexes, function (vertex) {
                            var lineGeometry = new THREE.Geometry,
                                vertices = _.map([prevVertex, vertex], function (vertex) {
                                    var point = vertex.$points[0],
                                        vertice = new THREE.Vector3(point.x, point.y, point.z);
                                    vertice[AXIS] = axisDown;
                                    lineGeometry.vertices.push(vertice.multiplyScalar(1000 * R));
                                    return vertice;
                                });
                            baseMetricContainer.add(new THREE.Line(lineGeometry, lineMaterial));

                            // line length, mm
                            var lineParticle = new THREE.Particle(new THREE.ParticleCanvasMaterial({
                                color: 'black',
                                program: makeParticleLabelProgram({
                                    fontSize: (24 * R),
                                    fontFamily: 'monospace, Optimer, verdana',
                                    text: Math.round(vertices[0].clone().sub(vertices[1]).length())
                                })
                            }));
                            lineParticle.position.addVectors(vertices[0], vertices[1]).multiplyScalar(.5 / 1000 / R); // todo: refactor for no coords magic
                            baseMetricContainer.add(lineParticle);

                            prevVertex = vertex;
                        });
                    }

                    // metric polygon members
                    var members = _.intersection()
                }

                function updateVertexMetric(vertex) {
                    _.each(vertex.metricObjects, function (object) {
                        object.visible = _.any(vertex.$super.line, function (line) {
                            return _.contains(activeLines, line);
                        });
                    });
                }

                function vertexMetric(vertex) {
                    var point = vertex.$points[0],
                        baseDelta = Math.abs(point[AXIS] - axisDown) * R;

                    // radius
                    var vertexColor = productPalette[vertex.type][vertex.order].integer,
                        radiusGeometry = new THREE.Geometry,
                        radiusVertice = new THREE.Vector3(point.x, point.y, point.z),
                        radiusCenter = new THREE.Vector3,
                        vertexMaterial = new THREE.LineBasicMaterial({
                            color: vertexColor,
                            opacity: 1,
                            linewidth: 1,
                            vertexColors: THREE.VertexColors
                        });

                    radiusCenter[AXIS] = axisDown;
                    radiusVertice[AXIS] = axisDown;

                    radiusGeometry.vertices.push(radiusCenter.multiplyScalar(1000 * R));
                    radiusGeometry.vertices.push(radiusVertice.multiplyScalar(1000 * R));

                    var radiusLine = new THREE.Line(radiusGeometry, vertexMaterial);

                    // radius length, mm
                    var radiusParticle = new THREE.Particle(new THREE.ParticleCanvasMaterial({
                        color: 'black',
                        program: makeParticleLabelProgram({
                            fontSize: (24 * R),
                            fontFamily: 'monospace, Optimer, verdana',
                            text: Math.round(radiusVertice.clone().sub(radiusCenter).length())
                        })
                    }));
                    radiusParticle.position.addVectors(radiusCenter, radiusVertice).multiplyScalar(.5 / 1000 / R); // todo: refactor for no coords magic

                    if (baseDelta > 1e-6) {
                        var deltaBase = new THREE.Vector3(point.x, point.y, point.z),
                            deltaVertice = deltaBase.clone(),
                            deltaGeometry = new THREE.Geometry;

                        deltaBase[AXIS] = axisDown;

                        deltaGeometry.vertices.push(deltaBase.multiplyScalar(1000 * R));
                        deltaGeometry.vertices.push(deltaVertice.multiplyScalar(1000 * R));

                        var deltaLine = new THREE.Line(deltaGeometry, vertexMaterial);

                        // delta, mm
                        var deltaParticle = new THREE.Particle(new THREE.ParticleCanvasMaterial({
                            color: 'black',
                            program: makeParticleLabelProgram({
                                fontSize: (24 * R),
                                fontFamily: 'monospace, Optimer, verdana',
                                text: Math.round(deltaBase.clone().sub(deltaVertice).length())
                            })
                        }));
                        deltaParticle.position.subVectors(deltaVertice, deltaBase).setLength(32 * R).add(deltaVertice).multiplyScalar(1 / 1000 / R);
                    }

                    return _.compact([radiusLine, radiusParticle, deltaLine, deltaParticle]);
                }
            }()
        }
    };
})();</script>
<script type="text/javascript">var form = (function () {

    // teach knockout to animate visibility toggling
    (function (visible) {
        var originUpdate = visible.update;
        visible.update = function (element, valueAccessor) {
            if (!form.animate || !$(element).closest('.slow-toggle-visibility').length)
                return originUpdate.apply(this, arguments);
            var show = ko.utils.unwrapObservable(valueAccessor());
            show ?
                $(element).slideDown('fast') :
                $(element).slideUp('fast');
        }
    })(ko.bindingHandlers.visible);

    // teach knockout to plot product on canvas
    ko.bindingHandlers.plot = {
        update: function (canvas, valueAccessor) {
            // get bound callback (don't care about context, it's ready-to-use ref to function)
            var product = valueAccessor();
            // fire callback with new value of an observable bound via 'html' binding
            _.defer(_.bind(product.plot, product, canvas));
        }
    }

    // figure defaults
    FigureOptionsVM.defaults = {
        radius: '4.2',
        connType: 'Piped',
        pipeD: '108',
        clockwise: true,
        detail: 3,
        subdivClass: 'I',
        M: 0,
        N: 0,
        subdivMethod: 'Chords',
        symmetry: 'Pentad',
        fullerenType: 'none',
        partial: '7/12',
        alignTheBase: false,
        beamsWidth: '150',
        beamsThickness: '50',
        clothier: {
            width: 2100,
            height: 12000
        }
    };

    var form = new FigureOptionsVM(_.throttle(function (state) {
        form.trigger('change', state);
    }, 100, {leading: false}));

    // used as dependency by reporting product statistics
    form.resultFigure = ko.observable();

    form.resultMeter = ko.computed(function () {
        var meter = new Meter,
            figure = form.resultFigure(),
            removed = form.removedList(); // depends

        if (!figure) return;

        meter.push(__('Sizes (units)'), _.reduce({
            face: 'Faces',
            line: 'Edges',
            vertex: 'Vertices'
        }, function (stat, title, type) {
            var sizes = 0, total = 0;

            _.chain(figure.$primitives)
                .where({type: type, removed: false})
                .countBy('index')
                .each(function (count, index) {
                    sizes += count > 0;
                    total += count;
                });
            stat[__(title)] = sizes + ' (' + total + ')';

            return stat;
        }, {}));

        _.each(['vertex', 'line', 'face'], function (type) {
            _.chain(figure.$primitives)
                .where({type: type, removed: false})
                .groupBy('index')
                .each(function (list, index) {
                    _.each(list, function (figure) {
                        meter.push(figure.product.meter());
                    });
                });
        });

        return meter;
    }, null, {deferEvaluation: true});

    form.reportText = ko.computed(function () {
        var meter = form.resultMeter();

        return meter ? meter.reportText() : 'Please, wait...';
    }, null, {deferEvaluation: true});

    // set flag
    $('form.options').addClass('slow-toggle-visibility');

    // flag to animate form elements
    form.animate = true;

    form.update = function (data) {
        _.each(data, function (value, key) {
            if (ko.isObservable(form[key])) {
                form[key](value);
            }
        });
    };

    return form;


    // view model of form
    function FigureOptionsVM(stateReceiver, timeout) {
        // const
        this.FULLEREN_TYPE_LIST = ko.computed(function () {
            return [{
                id: 'inscribed',
                name: __('fulleren:Inscribed in')
            }, {
                id: 'described',
                name: __('fulleren:Described around')
            }];
        });

        var form = this;

        this.state = {}; // of current

        // manual/auto mode
        !function () {
            var forcibly = 0; // counter for non-manual value change detection

            form.forceBegin = function () {
                forcibly++
            };
            form.forceEnd = function () {
                forcibly--
            };
            form.isModeForcibly = function () {
                return !!forcibly
            };
        }();

        // common observables
        _.each(FigureOptionsVM.defaults, function (value, key) {
            form[key] = ko.observable();
            form[key].subscribe(function (value) {
                form.state[key] = value;

                // protect the values of the change is not manually
                if (!form.isModeForcibly())
                    form[key].manual = value;

                fireChange();
            });
        });

        // deferred state-changed firer
        var lastFiredState;

        fireChange = _.debounce(fireChange, timeout || 300);

        function fireChange() {
            var state = _.values(form.state).join(' ');
            if (state != lastFiredState) {
                stateReceiver(form.state);
            }
            lastFiredState = state;
        }

        // option lists
        _.each('connTypeList detailList partialList subdivClassList'.split(' '), function (key) {
            form[key] = ko.observable();
        });

        // true subdivMethodList
        this.subdivMethodList = ko.computed(function () {
            var options = [{
                id: 'Chords',
                name: __('method:Equal Chords')
            }].concat(
                form.subdivClass() == 'I' && form.detail() > 2 ? [
                    {
                        id: 'Arcs',
                        name: __('method:Equal Arcs')
                    }, {
                        id: 'Mexican',
                        name: __('method:Mexican')
                    }
                ] : []
            );

            form.subdivMethod(maybe(form.subdivMethod.manual, _.pluck(options, 'id')));

            return options;
        }, this, {deferEvaluation: true});


        // option lists fresher
        function refreshList(what) {
            form.forceBegin();
            _.each(what.split(/\s+/), function (what) {
                refreshList[what]();
            });
            form.forceEnd();
        }

        _.extend(refreshList, {

            detail: function () {
                var list = form.fullerenType() ? _.range(1, 9) : _.range(1, 11);
                if (form.subdivClass() == 'II') {
                    list = _.filter(list, function (v) {
                        return v % 2 != 1;
                    });
                }

                form.detailList(list);
                form.detail(luckyNumber(form.detail.manual, list));
            },

            subdivClass: function () {
                var classes = [{
                        id: 'I',
                        name: 'I'
                    }],
                    V = form.detail();

                for (var p = 1, q = V - p; p < V / 2; p++, q--) {
                    classes.push({
                        id: 'III_' + p + ',' + q,
                        name: 'III ' + p + ',' + q
                    });
                }

                if (V % 2 == 0) {
                    classes.push({
                        id: 'II',
                        name: 'II'
                    });
                }

                for (var p = Math.floor(V / 2 + 1), q = V - p; p < V; p++, q--) {
                    classes.push({
                        id: 'III_' + p + ',' + q,
                        name: 'III ' + p + ',' + q
                    });
                }

                form.subdivClassList(classes);
                form.subdivClass(maybe(form.subdivClass.manual, _.pluck(classes, 'id')));
            },

            partial: function () {
                var partialList = partialList_ClassI(form.detail());

                form.partialList(partialList);
                form.partial(nearestRational(form.partial.manual, partialList));
            },

            connType: function () {
                var list = ['Piped', 'GoodKarma', 'Semicone'];

                if (form.fullerenType() || form.subdivClass() == 'II' || form.subdivMethod() == 'Chords')
                    list.push('Cone');

                list.push(form.fullerenType() ? 'Nose' : 'Joint');

                form.connTypeList(list);
                form.connType(maybe(form.connType.manual, list));
            }
        });

        // lists binding
        form.detail.subscribe(function () {
            refreshList('partial subdivClass');
        });

        form.fullerenType.subscribe(function () {
            refreshList('partial connType detail');
        });

        form.subdivClass.subscribe(function () {
            refreshList('detail');
        });

        form.subdivMethod.subscribe(function () {
            refreshList('connType');
        });

        form.canAlignTheBase = ko.computed(function () {
            var can =
                (form.subdivClass() == 'I')
                && !form.fullerenType()
                && !/^1\/[12]$/.test(form.partial())
                && /Piped|Joint|GoodKarma|Semicone/.test(form.connType());

            // force set align
            form.forceBegin();
            form.alignTheBase(can && form.alignTheBase.manual);
            form.forceEnd();

            return can;
        });

        // clothier
        form.clothier = {
            width: ko.observable(),
            height: ko.observable(),

            onRun: ko.observable(false),
            onPause: ko.observable(false),

            run: function () {
                form.clothier.onRun(true);
                form.clothier.onPause(false);
            },
            pause: function () {
                form.clothier.onPause(true);
            },
            stop: function () {
                form.clothier.onPause(false);
                form.clothier.onRun(false);
            }
        };

        // removed figure entities
        form.removedList = ko.observableArray([]);
        form.removedList.subscribe(function (list) {
            form.state.removedList = list;
            fireChange();
        });

        // figure budget
        form.budgetList = ko.observable();

        // set defaults
        !function () {
            defaults(FigureOptionsVM.defaults, form);

            function defaults(object, subject) {
                _.each(object, function (value, key) {
                    _.isObject(value) && !_.isArray(value) ?
                        defaults(value, subject[key]) :
                        subject[key](value);
                });
            }
        }();


        // events support
        _.extend(this, Backbone.Events);
    }


    // helpers
    function maybe(value, list) {
        return _.contains(list, value) ? value : list[0];
    }

    function luckyNumber(best, variants) {
        var lucky, quality = -1;
        _.each(variants, function (number) {
            var distance = Math.abs(number - best);
            if (-1 == quality || distance < quality) {
                lucky = number;
                quality = distance;
            }
        });
        return lucky;
    }

    function partialList_ClassI(V) {
        for (var list = [], p = V, q = V * 4; p <= V * 4; p += (p != V * 3) ? 2 : V)
            list.push(rational(p, q));
        return list;
    }

    function partialList_ClassII(V, symmetry) {
        console.warn('partialList_ClassII')

        var v = V / 2,
            faceCount = 60 * v * v;

        switch (symmetry) {
            case 'Cross':
            case 'Triad':
            case 'Pentad':
        }
    }

    function rational(p, q) {
        var pp = p, qq = q;
        // simplify rational fraction
        for (var m = 2; m <= pp && m <= qq;)
            if (pp % m == 0 && qq % m == 0) {
                pp /= m;
                qq /= m;
            } else
                m++;
        return pp + '/' + qq;
    }

    function nearestRational(rational, list) {
        var result, lastValue,
            target = eval(rational);

        _.each(list, function (rational) {
            var value = eval(rational);
            if (!result || Math.abs(value - target) < Math.abs(lastValue - target)) {
                result = rational;
                lastValue = value;
            }
        });
        return result;
    }
})();
</script>
<script type="text/javascript">// calculation process

!function (root) {
    var chain = [], io, startLength, timeout;

    var proc = root.calcProc = _.extend({
        stop: function () {
            if (chain.length) {
                proc.trigger('cancel', chain);
            }
            chain = [];
            io = null;
            clearTimeout(timeout);
            return this;
        },
        start: function (stages) {
            this.stop();
            chain = chain.concat(_.compact(_.flatten(stages)));
            startLength = chain.length;
            proc.trigger('start', chain);
            _.defer(tick);
            return this;
        },
        length: function () {
            return chain.length;
        }
    }, Backbone.Events);

    function tick() {
        var progress = startLength - chain.length;
        var stage = chain.shift();
        if (stage) {
            proc.trigger('stage', stage, progress, io);
            timeout = _.defer(function () {
                io = stage.process(io);
                proc.trigger('stageOk', stage, progress, io);
                _.defer(tick);
            });
        } else {
            proc.trigger('complete', chain);
        }
    }
}(window);
</script>
<script type="text/javascript">// offline version
$('body').toggleClass('offline', IS_OFFLINE);

// iframe version
$('body').toggleClass('iframe', IS_IFRAME);
if (IS_IFRAME) {
    $('html').css({overflow: 'hidden'});
    $('.geodesic').height($(window).height());
}

// download
var downloadUrl;
$('.download').click(function () {
    downloadUrl = downloadUrl || $(this).attr('href')
    $(this).attr('href',
        downloadUrl + '?figure=' + encodeURIComponent(fragmentRouter.fromParams(form.state, 'prevent #hash update'))
    );
});


// product color palettes
var productPalette = _.reduce(Palette.collections, function (memo, colors, type) {
    memo[type] = _.map(/*colors*/Palette.collections.line, function (cssColor) {
        return {
            integer: (new Function('return 0x' + cssColor.substr(1)))(),
            css: cssColor
        };
    });
    return memo;
}, {});


// const
var CALC_PATH = document.location.pathname;
var DEFAULT_SYMMETRY_BY_CLASS = {I: 'Pentad', II: 'Cross', III: 'Pentad'};


// url#fragment router                                           * * *
var fragmentRouter = new Stringifier({
    format: [
        '<alignTheBase:ground_|[Aa]lign_|>',
        '<partial:[1-9]\\d*/[1-9]\\d*|1>_',
        '<subdivClass:|Class_(II)_|Class_III_(\\d+),(\\d+)_>',
        '<subdivMethod:|(Chords|Arcs|Mexican)_>',
        '<symmetry:|(Pentad|Cross|Triad)_>',
        '<fullerenType:|(Described_|Inscribed_|)Fuller(?:ene?)?_(?:with|in|on|of|smoke)_>',
        '<connType:Piped_D(?:0|[1-9]\\d*)(?:\\.\\d+)?|(?:Joint|Nose|GoodKarma)(?:_back|_counter)?|Cone|Semicone|>_',
        '<detail:[1-9]\\d?>V',
        '<radius:|_R(?:0|[1-9]\\d*)(?:\\.\\d+)?>',
        '<material:|_beams_?(\\d+(?:\\.\\d+)?)x(\\d+(?:\\.\\d+)?)>',
        '<removedList:|~([vlf]\\d+)+>'
    ],
    mapping: {
        alignTheBase: {
            value: function (align) {
                return !!align;
            },
            string: function (align) {
                return align && this.partial != '1/1' && this.partial != '1/2' ? 'Align_' : '';
            }
        },
        subdivClass: {
            value: function (string, II, III_M, III_N) {
                return III_M ? {
                    subdivClass: 'III_' + III_M + ',' + III_N,
                    M: III_M, N: III_N
                } : {
                    subdivClass: II || 'I'
                };
            },
            string: function (klass) {
                var all = klass.split(/_|,/),
                    subdivClass = all[0];
                switch (subdivClass) {
                    case 'I':
                        return '';
                    case 'II':
                        return 'Class_II_';
                    case 'III':
                        return 'Class_III_' + all[1] + ',' + all[2] + '_';
                }
                console.error('unknown subdivision class', klass);
            }
        },
        subdivMethod: {
            value: function (string, method) {
                return method || 'Chords';
            },
            string: function (method) {
                return method == 'Chords' ? '' : method + '_';
            }
        },
        symmetry: {
            value: function (string, symmetry) {
                var subdivClass = this.subdivClass.split('_')[0];
                return symmetry || DEFAULT_SYMMETRY_BY_CLASS[subdivClass];
            },
            string: function (symmetry) {
                var subdivClass = this.subdivClass.split('_')[0];
                return symmetry != DEFAULT_SYMMETRY_BY_CLASS[subdivClass] ? symmetry + '_' : '';
            }
        },
        // partial is simple
        fullerenType: {
            value: function (fulleren, type) {
                return fulleren ? type.match(/^descr/i) ? 'described' : 'inscribed' : false;
            },
            string: function (type) {
                if (type)
                    return (type == 'inscribed' ? 'Inscribed_' : 'Described_') + 'Fullerene_on_';
                return '';
            }
        },
        connType: {
            value: function (string) {
                var matches = /^(Piped|Joint|Cone|Semicone|Nose|GoodKarma|)(.*)?$/.exec(string);
                var value = {
                    connType: matches[1]
                };
                if (value.connType == 'Piped')
                    value.pipeD = matches[2].replace(/\D/g, '');
                else if (/^Joint|Nose|GoodKarma$/.test(value.connType))
                    value.clockwise = !matches[2];
                return value;
            },
            string: function (connType) {
                switch (connType) {
                    case 'Piped':
                        connType += '_D' + this.pipeD;
                        break;
                    case 'Joint':
                    case 'Nose':
                    case 'GoodKarma':
                        connType += this.clockwise ? '' : '_counter';
                        break;
                }
                return connType;
            }
        },
        // detail is simple
        radius: {
            value: function (string) {
                return new Number(string.substr(2)).valueOf() || 69;
            },
            string: function (value) {
                return '_R' + value;
            }
        },
        material: {
            value: function (string, width, height) {
                return {
                    beamsWidth: width,
                    beamsThickness: height
                };
            },
            string: function () {
                return '_beams_' + this.beamsWidth + 'x' + this.beamsThickness;
            }
        },
        removedList: {
            value: function (string) {
                if (!string) return [];
                var list = [];
                string.replace(/[vlf]\d+/g, function (index) {
                    list.push(index);
                });
                return {
                    removedList: list
                };
            },
            string: function (list) {
                if (!figure)
                    return _.isEmpty(list) ? '' : '~' + list.join('');

                // pack a list
                var removing = _.pluck(_.where(figure.$primitives, {removed: true, type: 'face'}), 'removeIndex'),
                    groups = _.reduce(figure.$primitives, function (memo, item) {
                        if (item.removed && (item.type == 'face' || _.all(item.$super.face, _.property('removed')))) {
                            memo.push({
                                index: item.removeIndex,
                                faces: item.type == 'face' ? [item.removeIndex] : _.pluck(item.$super.face, 'removeIndex')
                            });
                        }
                        return memo;
                    }, []),
                    packed = [],
                    removed = [];

                while (removing.length > 0) {
                    var group = _.reduce(groups, function (best, group) {
                            return (best && best.faces.length >= group.faces.length) ? best : group;
                        }, null),
                        removedFaces = group.faces;

                    // filter groups
                    groups = _.filter(groups, function (group) {
                        return _.all(group.faces, function (face) {
                            return !_.contains(removedFaces, face);
                        });
                    });

                    // release removed
                    removing = _.difference(removing, removedFaces);

                    packed.push(group.index);
                }

                return _.isEmpty(list) ? '' : '~' + packed.join('')
            }
        }
    }
});


// binding url fragment with form                                  * * *
$(window).on('popstate', function (e) {
    if (CALC_PATH == document.location.pathname) {
        var string = document.location.hash.substr(1);

        // try to restore figure configuration from file:path
        if (!string && IS_OFFLINE) {
            document.location.pathname.replace(/^.*\$(\d+)_([^$/]+)(?:\$([^ %/]+))?(?:(?: |%20)\(\d+\))?\.html$/, function (pathname, numerator, more, removed) {
                string = numerator + '/' + more + (removed && '~' + removed);
            });
        }

        fragmentRouter.fromString(string);

        // auto map params correction to url #fragment
        string = fragmentRouter.fromParams(form.state);

        // update page title
        updateTitle(string);
        return false;
    }
});

// router params => form state
fragmentRouter.on('params', function (state) {
    form.animate = false;

    // mark values as setted manually
    _.each(state, function (value, key) {
        form[key].manual = value;
    });

    // set form values
    _.each(state, function (value, key) {
        form[key](value);
    });

    // lame-force, todo: resolve depends in any way
    form.partial(state.partial);

    form.animate = true;
});

form.urlFragment = ko.observable('');
form.figureUrl = ko.computed(function () {
    return 'http://acidome.ru/lab/calc/' + (form.urlFragment() ? '#' + form.urlFragment() : '');
});
form.figureName = ko.computed(function () {
    return form.urlFragment().replace(/_/g, ' ');
});

form.on('change', function (state) {
    // form state => router string
    var string = fragmentRouter.fromParams(state);
    form.urlFragment(string);
});

// router string => url fragment
fragmentRouter.on('string', function (string) {
    var currFragment = document.location.hash.substr(1);

    if (currFragment == string) {
        // prevent history repeating
        return;
    }

    // update history
    if (!IS_OFFLINE) {
        history.replaceState(null, 'no effect', '#' + string);
        console.log('* push state with', string);
    }

    // update page title
    updateTitle(string);
});

// page title helper
var initialTitle = $('head title').text();

function updateTitle(spec) {
    if (!spec) return;
    var hasRemoved = /~/.test(spec),
        title = spec.split('~')[0].replace(/_/g, ' ') + (hasRemoved ? ' (' + __('modified') + ')' : '');
    $('head title').text([title, initialTitle].join(' - '));

    // update figure dowload name
    var $link = $('#js-download-link');
    $link.attr({
        href: $link.data('href-base') + '?figure=' + encodeURIComponent(spec)
    });
}


// init form params from url fragment
// called before form been bounded with figure calculation process
$(window).trigger('popstate');


// figure calculator
var AXIS = 'y',
    CENTER = new Vector,
    figure;

form._lastState = {};
form.on('change', function () {
    if (_.isEqual(
            _.omit(form.state, 'removedList'),
            _.omit(form._lastState, 'removedList')
        )) return;

    if (!_.isEmpty(form._lastState) &&
        !_.isEqual(
            _.omit(form.state, 'radius', 'connType', 'pipeD', 'beamsWidth', 'beamsThickness', 'removedList'),
            _.omit(form._lastState, 'radius', 'connType', 'pipeD', 'beamsWidth', 'beamsThickness', 'removedList')
        )
    ) {
        // reset removed on figure form option was changed
        form.removedList([]);
    }

    var state = _.clone(form.state);
    var mm = (1 / 1000); // state.radius; // mm/R
    var calcStartedAt = +new Date;

    calcProc.start(_.compact([
        {
            name: 'base figure',
            process: function () {
                if (state.subdivClass == 'II') {
                    figure = new Figure.PentakisDodecahedron({
                        axis: AXIS,
                        symmetry: state.symmetry
                    });
                }
                else {
                    figure = new Figure.Icosahedron({
                        axis: AXIS,
                        symmetry: state.symmetry
                    });
                }

                return figure;
            }
        }, {
            name: 'subdivision',
            process: function (figure) {
                var V = state.detail,
                    subdivAll = state.subdivClass.split(/_|,/),
                    subdivClass = subdivAll[0];

                switch (subdivClass) {
                    case 'I':
                        switch (state.subdivMethod) {
                            case 'Chords':
                                return figure.splitFaces(V);

                            case 'Arcs':
                                return figure.splitFaces_EA(V);

                            case 'Mexican':
                                return figure
                                    .splitFaces_EA(V)
                                    .splitFaces_EA_updateToMexican(V);

                            default:
                                throw 'Unknown subdivision method: ' + state.subdivMethod;
                        }

                    case 'II':
                        figure.splitFaces(V / 2);
                        return figure.splitFaces_updateToClassII();

                    case 'III':
                        return figure.splitFaces_updateToClassIII(subdivAll[1], subdivAll[2]);

                    default:
                        throw 'Unknown subdivision class: ' + state.subdivClass;
                }
            }
        }, {
            name: 'primitive relations',
            process: function (figure) {
                // figure primitives initial
                figure.$primitives.each(function () {
                    // radius-center of points
                    this.center = CENTER;
                    this.$points.each(function () {
                        this.center = CENTER;
                    });
                });
                return figure.relations();
            }
        },
        (state.fullerenType) && {
            name: 'transmutation figure to fulleren', // if required
            process: function (figure) {
                switch (state.fullerenType) {
                    case 'inscribed':
                        figure.fulleren();
                        break;
                    case 'described':
                        figure.prepareUnify();
                        figure.outerFulleren();
                        break;
                    default:
                        console.error('strange fulleren type:', state.fullerenType);
                }

                // recalc relations
                figure.relations();

                // assert faces vertexes has common plane
                var badFactor = 0;
                _.each(figure.subs('face'), function (face) {
                    var points = face.$points,
                        bad = _.filter(
                            _.map(points, function (a, i) {
                                var b = points[(i - 1 + points.length) % points.length],
                                    c = points[(i + 2) % points.length],
                                    normal = Vector.crossProduct(
                                        Vector.subtract(b, a),
                                        Vector.subtract(c, a)
                                    ),
                                    plane = new Plane(normal, a).normalize();

                                plane.badMax = _.max(_.map(points, function (point) {
                                    var result = Math.abs(plane.result(point));
                                    return result < 1e-9 ? 0 : result;
                                }));
                                return plane;
                            }),
                            function (plane) {
                                return plane.badMax;
                            }
                        );
                    if (!_.isEmpty(bad)) {
                        console.error('bad face');
                        badFactor = Math.max(badFactor, _.min(_.compact(_.pluck(bad, 'badMax'))));
                    }
                });
                if (badFactor) {
                    console.error('bad factor', badFactor * state.radius);
                }

                return figure;
            }
        }, {
            name: 'set items remove indexes',
            process: function (figure) {
                // set items remove indexes
                _.each(_.groupBy(figure.$primitives, 'type'), function (collection, type) {
                    var typeIndex = type.substr(0, 1).toLowerCase();
                    _.each(collection, function (figure, index) {
                        figure.removeIndex = typeIndex + index;
                    });
                });
                return figure;
            }
        }, {
            name: 'pre-slice',
            process: function (figure) {
                if (state.subdivClass == 'I' && !state.fullerenType && state.symmetry == 'Pentad') {
                    var upper = _.sortBy(figure.subs('vertex'), function (v) {
                        return v.$points[0][AXIS];
                    }).pop();
                    //console.log(upper);
                    // wrong: .sliceByFraction() call .relations() too
                    figure.sliceByFraction(upper, state.partial, false);
                }
                else {
                    figure.sliceByAxis(AXIS, state.partial, false);
                }
                return figure; //.detectSelvage('sliced');
            }
        },
        state.alignTheBase && {
            name: 'align the base', // if required
            process: function (figure) {
                figure.groundSliced(AXIS);
                return figure;
            }
        }, {
            name: 'init product objects',
            process: function (figure) {
                var Connector = Product.Connector[state.connType],
                    Rib = Product.Rib["Beam"],
                    Face = Product[state.fullerenType ? 'Polygon' : 'Triangle']["Simple"];

                figure.$primitives = $(_.flatten(_.map(figure.$primitives, function (primitive) {
                    switch (primitive.type) {
                        case 'vertex':
                            primitive.product = new Connector(primitive, {
                                R: state.radius,
                                Dpipe: state.pipeD * mm,
                                whirlAsClock: state.clockwise
                            });
                            break;

                        case 'line':
                            if (Connector.lineSeparatelyForFaces) {
                                // GoodKarma, Semicone
                                var lineData = {
                                    $points: primitive.$points,
                                    origin: primitive
                                };

                                primitive = _.map(primitive.$super.face, function (face, i) {
                                    if (i > 0) {
                                        // clone primitive related to origin
                                        primitive = new Figure(lineData);
                                    }
                                    primitive.bindedFace = face;
                                    primitive.live = face.live;
                                    primitive.product = new Rib({
                                        R: state.radius,
                                        width: state.beamsWidth * mm,
                                        thickness: state.beamsThickness * mm,
                                        line: primitive
                                    });

                                    face.bindedLines = face.bindedLines || [];
                                    face.bindedLines.push(primitive);

                                    return primitive;
                                });
                            }
                            else {
                                primitive.product = new Rib({
                                    R: state.radius,
                                    width: state.beamsWidth * mm,
                                    thickness: state.beamsThickness * mm,
                                    line: primitive
                                });
                            }
                            break;

                        case 'face':
                            primitive.product = new Face(primitive, {
                                R: state.radius,
                                bilateral: _.contains(['GoodKarma', 'Semicone'], state.connType)
                            });
                            break;

                        default:
                            throw 'Product type unknown: ' + primitive.type;
                    }
                    return primitive;
                })));

                return figure;
            }
        }, {
            name: 'preparing to unify',
            process: function (figure) {
                figure.prepareUnify();
                return figure;
            }
        }, {
            name: 'unify',
            process: function (figure) {
                figure.unify();
                return figure;
            }
        }, {
            name: 'slice',
            process: function (figure) {
                figure.$points = $([]);
                figure.$primitives = figure.$primitives.filter(function () {
                    if (this.type == 'vertex' && this.live) {
                        figure.$points.push(this.$points[0]);
                    }
                    return this.bindedFace ? this.bindedFace.live : this.live;
                });
                figure.relations();

                // GoodKarma/Semicone workaround, slice dark-side clones binded to removed face
                if (state.connType == 'GoodKarma' || state.connType == 'Semicone') {
                    // filter lines binded to removed face
                    var faces = _.filter(figure.$primitives, function (prim) {
                        return !prim.removed && prim.type == 'face';
                    });

                    figure.$primitives = figure.$primitives.filter(function () {
                        return 0
                            || this.type != 'line'
                            || _.contains(faces, this.bindedFace);
                    });
                }
                return figure; //.detectSelvage('sliced');
            }
        }, {
            name: 'statistics',
            process: function (figure) {
                // remove items
                _.each(figure.$primitives, function (figure, index) {
                    if (_.contains(state.removedList, figure.removeIndex)) {
                        figure.remove();
                    }
                });

                // translate figure value to start compute report
                form.resultFigure(figure);

                return figure;
            }
        }, {
            name: 'plot product',
            process: function (figure) {
                // по типам
                var productAreaWidth = $('.budget-list').width();

                form.budgetList(_.compact(_.map([
                    {
                        type: 'line',
                        canvasAttr: {width: productAreaWidth, height: state.connType == 'Semicone' ? 150 : 220}
                    },
                    {type: 'face', canvasAttr: {width: productAreaWidth / 3}},
                    {type: 'vertex', canvasAttr: {width: productAreaWidth / 4}}
                ], function (budget) {
                    budget.units = ko.computed(function () {
                        var dependsFrom = form.removedList();
                        return _.where(figure.$primitives, {type: budget.type, removed: false});
                    });
                    budget.sizeList = _.compact(_.map(figure.stat[budget.type], function (size) {
                        /*_.chain(size.collect) todo: implement plot for fullfilled instances (wrong lines plot)
								.sortBy(function(inst) {
									return inst.$subsets.length + inst.$supersets.length;
								})
								.last()
								.value()*/

                        switch (budget.type) {
                            case 'vertex':
                                var sample = _.sortBy(size.collect, function (one) {
                                    return - // find with max count of live lines around
                                        _.chain(one.product.$hedgehog)
                                            .pluck('source')
                                            .where({live: true})
                                            .value().length;
                                })[0];
                                break;
                            default:
                                sample = size.collect[0];
                        }

                        var product = sample.product;

                        if (product.plot) {
                            return {
                                index: size.index,
                                color: productPalette[budget.type][size.order].css,
                                product: product,
                                units: ko.computed(function () {
                                    return _.filter(budget.units(), function (unit) {
                                        return unit.index === size.index;
                                    });
                                })
                            };
                        }
                    }));
                    return _.isEmpty(budget.sizeList) ? null : budget;
                })));

                return figure;
            }
        }, {
            name: 'render scene',
            process: function (figure) {
                viewer.trigger('render', figure);
                return figure;
            }
        }, {
            name: 'push log',
            process: function (figure) {
                // state and last state diff
                var diff = _.reduce(state, function (diff, value, key) {
                    if (form._lastState[key] !== state[key])
                        diff[key] = value;
                    return diff;
                }, {});

                // save last
                form._lastState = _.clone(state);

                // track figure change
                Tracker.push('figure', diff, {
                    dura: (+new Date) - calcStartedAt
                });

                // fix initial page height (auto scroll position refreshed page issue)
                $('body').css('height', 'auto');

                console.log('figure diff:', diff);

                return figure;
            }
        }
    ]));

    form._lastState = state;
});


// progress bar of calculation process
!function () {
    var $progress = $('.geodesic .progress'), progressTotal;

    function progress(step, msg) {
        $progress.text(msg + '  ' + (step + 1) + '/' + progressTotal);
    }

    calcProc
        .on('start', function (chain) {
            $progress.show();
            progressTotal = chain.length;
            progress(0, 'calculation started...');
            console.time && console.time('process time');
        })
        .on('cancel', function (chain) {
            progress(null, 'calculation was brutal cancelled');
            console.timeEnd && console.timeEnd('process time');
        })
        .on('complete', function (chain) {
            progress(chain.length, 'calculation complete');
            console.timeEnd && console.timeEnd('process time');
            $progress.hide();
        })
        .on('stage', function (stage, step) {
            progress(step, stage.name);
        })
        .on('stageOk', function (stage, step) {
            progress(step, stage.name + ' ok');
        });
}();


// geo receiver
$(document).bind('geo-complete', function (event, geo) {
    console.log('geo complete with', geo);
});


// bind form layout with view-model instance
$(function () {
    ko.applyBindings(form);
});


// ad
//$.getScript('./js/offer.js');</script>

<!-- /pack me -->

<!-- service offer -->
<script type="text/javascript">OfferFactory = (function () {
    var list = [];

    function OfferFactory(options) {
        _.extend(this, options);
    }

    OfferFactory.add = function (options) {
        list.push(new OfferFactory(options));
    };

    OfferFactory.resultList = ko.computed(function () {
        if (!form.resultMeter()) return;

        form.removedList(); // depends

        var R = form.state.radius,
            R2 = R * R,
            live = _.where(figure.$primitives, {removed: false}),
            total = _.groupBy(live, 'type'),
            sizes = _.reduce(total, function (sizes, list, type) {
                sizes[type] = _.union(_.pluck(list, 'index')).length;
                return sizes;
            }, {}),
            context = _.extend({
                R: R,
                all: live,
                total: total,
                sizes: sizes,
                ribMaxLengthSum: _.reduce(total.line, function (sum, line) {
                    return sum + line.product.maxLength();
                }, 0) * R, // => m
                maxRibProductLength:
                _.max(_.map(total.line, function (line) {
                    return line.product.maxLength();
                }))
                * R
                * 1000, // => mm
                ribVolume:
                    _.reduce(total.line, function (memo, line) {
                        return memo +
                            line.product.midLength * line.product.R *
                            line.product.thickness * line.product.R *
                            line.product.width * line.product.R;
                    }, 0), // => m3
                skinArea:
                    _.reduce(figure.stat.face, function (area, stat) {
                        var sample = stat.collect[0],
                            count = _.where(stat.collect, {removed: false}).length,
                            pp = sample.$points,
                            square = 0;

                        for (var i = 1; i <= pp.length - 2; i++) {
                            square += Vector.crossProduct(Vector.subtract(pp[i], pp[0]), Vector.subtract(pp[i + 1], pp[0])).length() / 2
                        }

                        return area + count * square * R2;
                    }, 0), // => m2
                baseArea:
                    _.reduce(total.line, function (area, line) {
                        if (_.where(line.origin.$super.face, {removed: false}).length == 1) {
                            var pp = _.invoke(line.$points, 'clone');
                            _.each(pp, function (p) {
                                p[figure.axis] = 0;
                            });
                            area += Vector.crossProduct(pp[0], pp[1]).length() / 2 * R2;
                        }
                        return area;
                    }, 0),
                beamSection: form.state.beamsWidth + 'x' + form.state.beamsThickness
            }, form.state);

        return _.compact(_.flatten(
            _.map(list, function (offer) {
                var producer = !offer.condition || offer.condition(context) ? offer.result(context) : null;
                if (producer && !_.isEmpty(producer.offer))
                    return producer;
            })
        ));
    }, null, {deferEvaluation: true});

    OfferFactory.Offer = Offer;

    function Offer(data) {
        _.extend(this, data);
    }

    OfferFactory.formatPrice = function (number) {
        number = String(Math.round(number));
        for (var prev; number != prev; prev = number, number = number.replace(/(\S)(\d{3})(\s|$)/, '$1 $2$3')) ;
        return number;
    };

    OfferFactory.Order = function (producer) {
        var order = this;

        this.email = ko.observable(localStorage["client-email"] || '');

        this.producer = producer;
        this.selectedPositions = ko.observableArray();

        this.pending = ko.observable(false);

        this.submit = function (order, event) {
            var $email = $(event.target).siblings('input[type="email"]'),
                email = order.email();

            if (!/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(email)) {
                alert(__('buyme:E-mail is not valid') + ': ' + email);
                $email.focus();
                return;
            }

            localStorage["client-email"] = email;

            order.pending(true);

            $.ajax({
                url: 'http://acidome.ru/lab/calc/order-flow/?action=create',
                type: 'POST',
                data: {
                    order: _.pick(ko.toJS(order), 'email', 'producer', 'selectedPositions'),
                    url: 'http://acidome.ru/lab/calc/' + location.hash,
                    form: form.state
                },
                dataType: 'JSON',
                complete: function () {
                    order.pending(false);
                },
                error: function () {
                    alert(__('buyme:Sorry, error happens while we trying to order. Please, try again later.'));
                },
                success: function (error) {
                    if (error) {
                        alert(__('buyme:' + error));
                    } else {
                        alert(
                            __('buyme:Thank you, the order has been successfully created.') +
                            '\n-------\n' +
                            __('buyme:Check your email, if the letter is marked as spam, mark it as not spam.')
                        );
                    }

                    if (!error) {
                        // reset selected positions
                        order.selectedPositions([]);
                    }
                }
            });
        };
    };

    return OfferFactory;
})();

/*
OfferFactory.add({
	price: {
		singleSlice: 35, // per item
		sliceTuning: 2000, // per size
		cutEdgePair: 25,
		sosnaCube: 20000, // RUR
		berezaCube: 18000, // RUR
		nalogIP: 0.06,
		nalogNDS: 0.20
	},
	condition: function(data) {
		return 1
			&& 20 <= data.beamsWidth && data.beamsWidth <= 300
			&& 20 <= data.beamsThickness && data.beamsThickness <= 300
			&& 500 <= data.maxRibProductLength && data.maxRibProductLength <= 3000
			//&& _.contains(['ru', 'ua'], i18n.lang());
	},
	result: function(data) {
		var q1 = Math.max(data.beamsWidth, 100) / 200 + 0.5, // mm
			q2 = Math.max(data.beamsThickness, 30) / 120 + 0.75, // mm
			q3 = Math.max(data.maxRibProductLength, 1000) / 2400 + 0.5, // mm
			cutsPerTail = {
				Piped: 1,
				Cone: 2,
				Joint: 1,
				Semicone: 1,
				GoodKarma: 1
			}[ data.connType ],

			cutCost = this.price.singleSlice * q1 * q2 * q3 * cutsPerTail
					* 2 // 2 tails
					* data.total.line.length // ribs total
					* 1 / (1 - this.price.nalogNDS), // НДС
			tuningCost  = this.price.sliceTuning
					* data.sizes.line // sizes
					/ ( data.connType == 'GoodKarma' ? 2 : 1 ) // тоесть гуд карма на 3 частоту настройка угла 3 пары * 2000, или 6 ребер / 2 * 2000
					* 1 / (1 - this.price.nalogNDS), // НДС

			cutEdgeCost = this.price.cutEdgePair * q3
					* data.total.line.length
					* 1 / (1 - this.price.nalogNDS); // НДС

		return new OfferFactory.Offer({
			producerName: 'Мастер гео дом',
			offer: [{
				name: (data.connType == 'Piped' ? 'Ребра каркаса' : 'Каркас') + ', сосна',
				price: cutCost + tuningCost
					+ data.ribVolume * this.price.sosnaCube * 1 / (1 - this.price.nalogIP), // material (ИП)
				currency: 'RUR'
			}, {
				name: (data.connType == 'Piped' ? 'Ребра каркаса' : 'Каркас') + ', береза',
				price: cutCost + tuningCost
					+ data.ribVolume * this.price.berezaCube * 1 / (1 - this.price.nalogIP), // material (ИП)
				currency: 'RUR'
			}],
			feedback: {
				email: 'beethowen@mail.ru'
			}
		});
	}
});
*/

// Kupolok.com
OfferFactory.add({
    price: {
        rib: {
            Piped: 5, // 200x50, 150х50
            GoodKarma: 4,
            Semicone: 5,
            Cone: 5,
            Joint: 5
        },
        connectors: {
            "Сокол": {
                url: 'https://vk.com/market-50380859?w=product-50380859_13147%2Fquery',
                price: 19, // 200x50 only!
                conditions: [{
                    beamsWidth: "200",
                    beamsThickness: "50",
                    pipeD: "108"
                }]
            },
            "Клешня-партизан": {
                price: 23,
                url: 'https://vk.com/market-50380859?w=product-50380859_13201%2Fquery',
                conditions: [{
                    beamsWidth: "150",
                    beamsThickness: "50",
                    pipeD: "118"
                }, {
                    beamsWidth: "200",
                    beamsThickness: "50",
                    pipeD: "118"
                }]
            }
        },
        wood: 140, // m3
        КРОВЛЯ_ФАНЕРА_ФСФ_15мм: 10,
        КРОВЛЯ_ДОСКА_25мм: 9
    },
    result: function (data) {
        var ribsCost = this.price.rib[data.connType] * data.total.line.length
            + data.ribVolume * this.price.wood,
            offer = [{
                name: (data.connType == 'Piped' ? 'Ребра каркаса' : 'Каркас'),
                price: ribsCost,
                currency: 'USD'
            }];

        if (data.connType == 'Piped') {
            offer = offer.concat(_.map(this.price.connectors, function (connector, connectorName) {
                var name = '«' + connectorName + '»',
                    price = connector.price * data.total.vertex.length;

                if (connector.url) {
                    name = '<a href="' + connector.url + '" target="_blank">' + name + '</a>';
                }
                name = 'Комплект коннекторов ' + name;

                if (_.some(connector.conditions, _.bind(_.findWhere, null, [data]))) {
                    // any condition is met
                    return {
                        name: name,
                        price: price,
                        currency: 'USD'
                    };
                } else {
                    return {
                        name: name,
                        price: price,
                        currency: 'USD',
                        conditions: _.map(connector.conditions, function (condition) {
                            return {
                                condition: condition,
                                name: '&empty;' + condition.pipeD + ' ' + condition.beamsWidth + 'x' + condition.beamsThickness
                            };
                        })
                    };
                }
            }));
        }

        offer.push({
            name: 'Кровля, фанера ФСФ 15мм',
            price: this.price.КРОВЛЯ_ФАНЕРА_ФСФ_15мм * data.skinArea,
            currency: 'USD'
        });

        offer.push({
            name: 'Кровля, доска 25мм',
            price: this.price.КРОВЛЯ_ДОСКА_25мм * data.skinArea,
            currency: 'USD'
        });

        return new OfferFactory.Offer({
            producerName: 'Kupolok.com',
            producerLink: 'http://kupolok.com/',
            offer: offer,
            feedback: {
                email: 'kotiara82@gmail.com'
            }
        });
    }
});

// Geosota.ru
OfferFactory.add({
    price: {
        //ИЗГОТОВЛЕНИЕ_ТРЕУГОЛЬНИКА_ОБШИВКИ: 400,
        М2_ОБШИВКИ: 900,
        ИЗГОТОВЛЕНИЕ_РЕБРА: {
            Piped: 400/*,
			Cone: 600*/
        },
        М3_ДЕРЕВА: {
            Piped: 10000,
            Cone: 18000
        },
        КОННЕКТОР_ГЕОСОТА: 230
    },
    result: function (form) {
        var offer = [];

        // I. "Обшивка из треугольников"
        if (
            true
        /*!form.fullerenType &&
			1.5 <= form.R && form.R <= 16*/
        ) {
            offer.push({
                name: 'Кровля, фанера ФСФ 15–18мм',
                price: /*form.total.face.length * this.price.ИЗГОТОВЛЕНИЕ_ТРЕУГОЛЬНИКА_ОБШИВКИ + */form.skinArea * this.price.М2_ОБШИВКИ,
                currency: 'RUR'
            });
        }

        //  II. Ребра (балки) каркаса:
        if (
            _.has(this.price.ИЗГОТОВЛЕНИЕ_РЕБРА, form.connType) /*&&
			form.maxRibProductLength <= 2200 &&
			1.5 <= form.R && form.R <= 16 &&
			form.detail <= 7*/
        ) {
            offer.push({
                name: 'Ребра каркаса',
                price: form.total.line.length * this.price.ИЗГОТОВЛЕНИЕ_РЕБРА[form.connType] + form.ribVolume * this.price.М3_ДЕРЕВА[form.connType],
                currency: 'RUR'
            });
        }

        // III. Комплект коннекторов "Партизан"
        if (
            form.connType == 'Piped' /*&&
			3 <= form.R && form.R <= 10*/
        ) {
            var conditions = [{
                    beamsWidth: "150",
                    beamsThickness: "50",
                    pipeD: "118"
                }, {
                    beamsWidth: "200",
                    beamsThickness: "50",
                    pipeD: "118"
                }],
                connectorName = 'Комплект коннекторов <a href="http://geosota.ru/solutions/kit/2-x/" target="_blank">«Партизан»</a>';

            if (_.some(conditions, _.bind(_.findWhere, null, [form]))) {
                // any condition is met
                offer.push({
                    name: connectorName,
                    price: form.total.line.length * 2 * (194 + 43) + form.total.vertex.length * 300, // magic prices?
                    currency: 'RUR'
                });
            } else {
                offer.push({
                    name: connectorName,
                    conditions: _.map(conditions, function (condition) {
                        return {
                            condition: condition,
                            name: '&empty;' + condition.pipeD + ' ' + condition.beamsWidth + 'x' + condition.beamsThickness
                        };
                    })
                });
            }
        }

        // Комплект коннекторов "Геосота"
        var conditions = [{
                pipeD: "90"
            }],
            connectorName = 'Комплект коннекторов <a href="http://geosota.ru/solutions/kit/1-3" target="_blank">«Геосота»</a>';

        if (
            form.connType == 'Piped' &&
            1.5 <= form.R && form.R <= 10 &&
            45 <= form.beamsWidth && form.beamsWidth <= 195 &&
            45 <= form.beamsThickness && form.beamsThickness <= 50
        ) {
            if (_.some(conditions, _.bind(_.findWhere, null, [form]))) {
                offer.push({
                    name: connectorName,
                    price: form.total.vertex.length * this.price.КОННЕКТОР_ГЕОСОТА,
                    currency: 'RUR'
                });
            } else {
                offer.push({
                    name: connectorName,
                    conditions: _.map(conditions, function (condition) {
                        return {
                            condition: condition,
                            name: '&empty;' + condition.pipeD
                        };
                    })
                });
            }
        }
        else if (
            form.connType == 'Piped' &&
            1.5 <= form.R && form.R <= 10
        ) {
            offer.push({
                name: connectorName,
                conditions: _.map(conditions, function (condition) {
                    return condition.pipeD == form.pipeD ? {
                        condition: {
                            beamsWidth: (45 <= form.beamsWidth ? form.beamsWidth <= 195 ? form.beamsWidth : 195 : 45),
                            beamsThickness: (45 <= form.beamsThickness ? form.beamsThickness <= 50 ? form.beamsThickness : 50 : 45)
                        },
                        name:
                        (45 <= form.beamsWidth ? form.beamsWidth <= 195 ? form.beamsWidth : 195 : 45) + 'x' +
                        (45 <= form.beamsThickness ? form.beamsThickness <= 50 ? form.beamsThickness : 50 : 45)
                    } : {
                        condition: condition,
                        name: '&empty;' + condition.pipeD
                    };
                })
            });
        }

        return new OfferFactory.Offer({
            producerName: 'Geosota.ru',
            producerLink: 'http://geosota.ru/',
            offer: offer,
            feedback: {
                email: 'mail@geosota.ru'
            }
        });
    }
});

// Геодом BY
OfferFactory.add({
    price: {
        V: {
            3: {
                '5/12': [90155, 120005], // without / with 2nd floor
                '7/12': [115580, 145430]
            },
            4: {
                '1/2': [164255, 204055]
            }
        },
        beam: {
            '150x50': 240,
            '200x50': 300
        }
    },
    result: function (form) {
        if (form.connType != 'Piped') return;

        // коннекторы
        var offer = _.map(
            this.price.V[form.detail] &&
            this.price.V[form.detail][form.partial],

            function (price, with2dnFloor) {
                return {
                    name: '<a href="https://pp.vk.me/c624220/v624220318/16d1c/OTysMlOtGoE.jpg" target="_blank">Комплект коннекторов</a>'
                    + (with2dnFloor ? ' +&nbsp;коннекторы перекрытия 2-го этажа' : ''),
                    price: price,
                    currency: 'RUR'
                };
            }
        );

        // ребра
        if (form.connType == 'Piped') {
            if (this.price.beam[form.beamSection]) {
                offer.push({
                    name: 'Ребра каркаса',
                    price: form.ribMaxLengthSum * this.price.beam[form.beamSection],
                    currency: 'RUR'
                });
            } else {
                offer.push({
                    name: 'Ребра каркаса',
                    conditions: [{
                        condition: {
                            beamsWidth: "150",
                            beamsThickness: "50",
                            pipeD: "133"
                        },
                        name: '&empty;133 150x50'
                    }, {
                        condition: {
                            beamsWidth: "200",
                            beamsThickness: "50",
                            pipeD: "133"
                        },
                        name: '&empty;133 200x50'
                    }],
                    currency: 'RUR'
                });
            }
        }

        return new OfferFactory.Offer({
            producerName: 'Геодом BY',
            producerLink: 'http://www.dome-by.com/',
            offer: offer,
            feedback: {
                email: 'sabishy@gmail.com'
            }
        });
    }
});

// www.ecodome.com.ua
OfferFactory.add({
    price: {
        carcassAndCover: 150,
        carcass: 100,
        calc: Math.E / 100
    },
    result: function (form) {
        var offer = [];

        /*
		 Предложение #1 - Каркас + обшивка
		 * GoodKarma (до диаметра 8м) и Piped (все размеры)
		 * V2-4
		 * Максимальная длинна ребра - 1900
		 * Брус может быть любым, но можно показывать псевдолинк где размер будет - 46х150
		 * Диаметр трубы - 100. Хотя коннектор у нас не на базе трубы 🙂
		 * Цена: площадь_основания * $150 + 2.718%
		*/
        if (
            ((form.connType == 'GoodKarma' && form.R <= 4) || form.connType == 'Piped') &&
            2 <= form.detail && form.detail <= 4 &&
            form.maxRibProductLength <= 1900
        ) {
            if (form.beamSection == '150x46' && form.pipeD == 100) {
                offer.push({
                    name: 'Каркас + обшивка',
                    price: form.baseArea * this.price.carcassAndCover * (1 + this.price.calc),
                    currency: 'USD'
                });
            } else {
                offer.push({
                    name: 'Каркас + обшивка',
                    conditions: [{
                        condition: {
                            beamsWidth: "150",
                            beamsThickness: "46",
                            pipeD: "100"
                        },
                        name: '&empty;100 150x46'
                    }],
                    currency: 'RUR'
                });
            }
        }

        /*
		 Предложение #2 - Каркас
		 Фильтр: любой размер, любая доска
		 * Piped
		 * V2-5
		 Цена: площадь_основания * $100
		 */
        if (
            form.connType == 'Piped' &&
            2 <= form.detail && form.detail <= 5
        ) {
            offer.push({
                name: 'Каркас',
                price: form.baseArea * this.price.carcass * (1 + this.price.calc),
                currency: 'USD'
            });
        }

        return new OfferFactory.Offer({
            producerName: 'ecodome.com.ua',
            producerLink: 'http://ecodome.com.ua/',
            offer: offer,
            feedback: {
                email: 'mail@ecodome.com.ua'
            }
        });
    }
});
</script>
<script type="text/javascript">/**
 * Clothier helper
 *
 * With figure #1/4_Class_III_1,2_Triad_Semicone_3V_R3.5_beams_200x30:
 *
 * OPERATION                                          USEC      MEMORY
 * create + start with face + applying 1 transition   ~20ms     25 Kb
 * create
 * start with one face                                32-53
 * applying 1 transition                              256-274
 *
 */

!function (exports) {
    var IS_LIVE = {live: true, removed: false}, // condition for used figure primitives
        min = Math.min, max = Math.max,
        pointsCounter = 0;

    // exports
    /**
     * Gives graph of figure surface (cutting helper)
     */
    Figure.prototype.createSurfaceGraph = function () {
        return (new Graph).initFromFigure(this);
    };
    exports.FigureSurfaceGraph = Graph;

    Graph.validator = {
        Sheet: (function () {
            function DirectedRangeValidator(directionSegment) {
                this.matrix = segment2matrix(directionSegment);
                this.xrange = [Infinity, -Infinity];
                this.yrange = [Infinity, -Infinity];
                Rollable(this);
            }

            DirectedRangeValidator.prototype = {
                pushPoint: function (axisSizes, point) {
                    var M = this.matrix,
                        x = point[0] * M[0] + point[1] * M[2],
                        y = point[0] * M[1] + point[1] * M[3],// todo: catch valid x,y here (speed)
                        xrange = [min(this.xrange[0], x), max(this.xrange[1], x)],
                        yrange = [min(this.yrange[0], y), max(this.yrange[1], y)],
                        sizes = [xrange[1] - xrange[0], yrange[1] - yrange[0]].sort(),
                        valid = sizes[0] < axisSizes[0] && sizes[1] < axisSizes[1];

                    if (valid) {
                        this.xrange = xrange;
                        this.yrange = yrange;
                    }
                    return valid
                        && (sizes[0] * sizes[1]) / (axisSizes[0] * axisSizes[1]);
                },
                begin: function () {
                    return {
                        xrange: this.xrange,
                        yrange: this.yrange
                    };
                },
                rollback: function (state) {
                    this.xrange = state.xrange;
                    this.yrange = state.yrange;
                }
            };

            function SheetValidator(axisSizes) {
                this._axisSizes = axisSizes;
                this._rangeValidators = [];
                this._weight = undefined;
                Rollable(this);
            }

            SheetValidator.prototype = {
                pushPoint: function (point) {
                    var sheet = this,
                        weight = Infinity;

                    if (!this._rangeValidators.every(function (rangeValidator, index) {
                            var validWeight = rangeValidator.begin().pushPoint(sheet._axisSizes, point);

                            if (false === validWeight) {
                                // auto-rollback previous validators
                                _.invoke(sheet._rangeValidators.slice(0, index + 1), 'rollback');

                                return false;
                            }

                            weight = min(validWeight, weight);

                            return true;
                        })) return false;

                    _.invoke(this._rangeValidators, 'end');

                    return weight;
                },
                pushSegment: function (segment, graphPoints) {
                    var axisSizes = this._axisSizes,
                        rangeValidator = new DirectedRangeValidator(segment),
                        valid = graphPoints.every(function (point) {
                            return false !== rangeValidator.pushPoint(axisSizes, point);
                        });

                    return valid && !!this._rangeValidators.push(rangeValidator.begin());
                },
                applyTransition: function (overSegment, nextPoint, graphPoints) {
                    var sheet = this;

                    // add new segments
                    if (!overSegment.every(function (segmentPoint, index) {
                            if (!sheet.pushSegment([segmentPoint, nextPoint], graphPoints)) {
                                // rollback segments
                                sheet._rangeValidators.splice(-index, index);

                                return false;
                            }
                            return true;
                        })) return false;

                    // add new point
                    var resultWeight = this.pushPoint(nextPoint);
                    if (!resultWeight) {
                        // auto-rollback segments
                        sheet._rangeValidators.splice(-2, 2);
                        return false;
                    }

                    this._weight = resultWeight;

                    return true;
                },

                begin: function () {
                    return {
                        length: _.invoke(this._rangeValidators, 'begin').length,
                        weight: this._weight
                    };
                },
                rollback: function (state) {
                    this._rangeValidators.length = state.length;
                    _.invoke(this._rangeValidators, 'rollback');
                    this._weight = state.weight;
                },
                end: function () {
                    _.invoke(this._rangeValidators, 'end');
                },

                getWeight: function () {
                    return this._weight;
                }
            };

            return function (width, height) {
                var axisSizes = [width, height].sort();
                return new SheetValidator(axisSizes);
            };
        })(),

        HasNoCross: (function () {
            function HasNoCrossValidator() {
                this._segments = [];
                Rollable(this);
            }

            HasNoCrossValidator.prototype = {
                pushSegment: function (segment) {
                    var x1 = segment[0][0], y1 = segment[0][1],
                        x2 = segment[1][0], y2 = segment[1][1];

                    return this._segments.every(validateSegment)
                        && !!this._segments.push(segment);

                    // http://algolist.manual.ru/maths/geom/intersect/lineline2d.php
                    function validateSegment(segment) {
                        var x3 = segment[0][0], y3 = segment[0][1],
                            x4 = segment[1][0], y4 = segment[1][1];

                        var denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
                        if (!denom) return true;

                        var result = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
                        if (result < -1e-9 && (1 + 1e-9) < result) return false;

                        return true;
                    }
                },
                applyTransition: function (overSegment, nextPoint) {
                    var segments = this._segments;

                    return overSegment.every(function (segmentPoint, index) {
                        if (!this.pushSegment([segmentPoint, nextPoint])) {
                            // auto-rollback
                            segments.splice(-index, index);

                            return false;
                        }
                        return true;
                    }, this);
                },

                begin: function () {
                    return this._segments.length;
                },
                rollback: function (state) {
                    this._segments.length = state;
                }
            };

            return function () {
                return new HasNoCrossValidator();
            };
        })()
    };

    function Graph() {
        this.reset();
        this.validators = [
            Graph.validator.HasNoCross(),
            Graph.validator.Sheet(2.1 / form.state.radius, 6 / form.state.radius)
        ];
        Rollable(this);
    }

    _.extend(Graph.prototype, {
        reset: function () {
            this.front = [];
            this.group = [];
            this.points = [];
            this.faces = [];
        },

        begin: function () {
            return {
                front: _.clone(this.front),
                group: _.clone(this.group),
                points: _.clone(this.points)
            };
        },

        rollback: function (state) {
            this.front = state.front;
            this.group = state.group;
            this.points = state.points;
        },

        startWith: function (face) {
            var // first triangle points
                lengths = _.pluck(face.borders, 'length'),
                lengths2 = _.map(lengths, square),
                cos0 = (lengths2[2] + lengths2[1] - lengths2[0]) / (2 * lengths[1] * lengths[2]),
                facePoints = [
                    [0, 0],
                    [cos0 * lengths[2], Math.sqrt(1 - cos0 * cos0) * lengths[2]],
                    [lengths[1], 0]
                ];

            _.each(facePoints, function (point, i) {
                point.index = pointsCounter++
            })

            this.validators.forEach(function (validator) {
                for (var i = 0, j = 2; i < 3; j = i++) {
                    validator.pushSegment([facePoints[j], facePoints[i]], []);
                }
            });

            this.validators.forEach(function (validator) {
                if (!validator.pushPoint) return;
                for (var i = 0; i < 3; i++) {
                    validator.pushPoint(facePoints[i]);
                }
            });

            this.addGroupFace(face, facePoints);

            return this;
        },

        addGroupFace: function (face, facePoints) {
            var graph = this;

            //console.log('++', face.borders, facePoints);

            //this.faces.splice(this.faces.indexOf(face), 1);
            this.group.push({
                face: face,
                points: facePoints
            });

            _.each(facePoints, function (point) {
                if (!point.addedToGraph) {
                    graph.points.push(point);
                    point.addedToGraph = true;
                }
            });

            // update front
            _.each(face.borders, function (border, index) {
                if (border.nextFacePoint && !_.findWhere(graph.group, {face: border.nextFace})) {
                    // calc segment of border
                    var segment = shiftIndex(facePoints, index).reverse(); // copy
//					var segment = facePoints.slice(0); // copy
//					segment.splice(index, 1); // remove point from previous face
//					segment.reverse(); // reverse order

                    graph.front.push({
                        facePoints: facePoints,
                        border: border,
                        segment: segment,
                        nextFace: border.nextFace
                    });
                }
            });
        },

        applyTransition: function (transition) {
            var graph = this,

                segment = transition.segment,
                point = transition.border.nextFacePoint(transition.facePoints),
                nextFacePoints = pushOrderIndex(segment, point, transition.border.nextPointIndex),

                graphPoints = this.points,
                validators = this.validators,
                weight,
                valid = validators.every(function (validator, index) {
                    validator.begin();

                    if (!validator.applyTransition(segment, point, graphPoints)) {
                        // auto-rollback
                        _.invoke(validators.slice(0, index + 1), 'rollback');
                        return false;
                    }

                    weight = validator.getWeight ? validator.getWeight() : weight; // todo: what if not happens?
                    return true;
                });

            point.index = pointsCounter++;

            if (valid) {
                _.invoke(validators, 'end');

                this.addGroupFace(transition.border.nextFace, nextFacePoints);

                // filter front from happy recieved transitions
                this.front = this.front.filter(function (tr) {
                    return !_.findWhere(graph.group, {face: tr.border.nextFace});
                });
            }

            return valid && weight;
        },

        initFromFigure: function (figure) {
            this.reset();
            this.faces = _.chain(figure.subs('face')).where(IS_LIVE)
            // init faces/borders
                .reduce(function (faceById, face) {
                    var borders = _.chain(face.$sub.line)
                            .filter(function (line) {
                                return !line.bindedFace || line.bindedFace === face;
                            })
                            .sortBy(function (line) {
                                var thirdPoint = _.difference(face.$points.get(), line.$points.get())[0];
                                return _.indexOf(face.$points, thirdPoint);
                            })
                            .map(function (line) {
                                var points = line.origin.$points.get();
                                return {
                                    lineOriginIndex: line.origin._enum,
                                    length: points[0].distance(points[1]),
                                    nextFace: _.find(line.origin.$super.face, function (f) {
                                        return f !== face && f.live && !f.removed;
                                    }),
                                    line: line
                                };
                            })
                            .value(),
                        // area of triangle by Heron
                        p = _.reduce(borders, function (P, line) {
                            return P + line.length
                        }, 0) / 2,
                        area = Math.sqrt(p * (p - borders[0].length) * (p - borders[1].length) * (p - borders[2].length));

                    faceById[face._enum] = {
                        borders: borders,
                        area: area,
                        origin: face
                    };
                    return faceById;
                }, {})

                // direct links between graph faces / border mirrors
                .reduce(function (faces, face, index, faceById) {
                    _.each(face.borders, function (border) {
                        if (!border.nextFace) return;

                        border.nextFace = faceById[border.nextFace._enum];
                        border.overBorder = _.findWhere(border.nextFace.borders, {lineOriginIndex: border.lineOriginIndex});
                    });
                    faces.push(face);
                    return faces;
                }, [])

                // transitions beyond border handlers
                .each(function (face, faceIndex) {
                    _.each(face.borders, function (border, index) {
                        if (!border.nextFace) return;

                        var length2 = border.length * border.length,

                            self2 = _.chain(shiftIndex(face.borders, index))
                                .pluck('length').map(square).value(),

                            overIndex = _.indexOf(border.nextFace.borders, border.overBorder),
                            over2 = _.chain(shiftIndex(border.nextFace.borders, overIndex))
                                .pluck('length').map(square).value();

                        var	// matrix of transition from self- to over-points
                            matrix = pushOrderIndex([
                                // height base by self-face       // height base by over-face
                                ((length2 + self2[0] - self2[1]) + (length2 + over2[1] - over2[0]) * border.nextFace.area / face.area) / (2 * length2),
                                ((length2 - self2[0] + self2[1]) + (length2 - over2[1] + over2[0]) * border.nextFace.area / face.area) / (2 * length2)
                            ], -border.nextFace.area / face.area, index);

                        border.nextFacePoint = function (points) {
                            return [
                                points[0][0] * matrix[0] + points[1][0] * matrix[1] + points[2][0] * matrix[2],
                                points[0][1] * matrix[0] + points[1][1] * matrix[1] + points[2][1] * matrix[2]
                            ];
                        };
                        border.nextPointIndex = overIndex;
                        border.selfIndex = index;
                        border.matrix = matrix;
                    });
                })
                .value();

            return this;
        }
    });

    // rollable mixin
    function Rollable(object) {
        var stack = [];

        object.begin = (function (beginState) {
            return function () {
                stack.push(beginState.call(object));
                //console.log('roll begin ' + stack.length);
                Rollable.stat.begin++;
                return object;
            };
        })(object.begin);

        object.rollback = (function (rollbackState) {
            return function () {
                rollbackState.call(object, stack.pop());
                //console.log('roll back ' + stack.length);
                Rollable.stat.back++;
                return object;
            };
        })(object.rollback);

        object.end = (function (endState) {
            return function () {
                endState && endState.call(object, stack.pop());
                //console.log('roll end ' + stack.length);
                Rollable.stat.end++;
                return object;
            };
        })(object.end);
    };
    Rollable.stat = {
        begin: 0,
        back: 0,
        end: 0
    };
    exports.Rollable = Rollable;

    // some helpers
    function segment2matrix(segment) {
        var O = segment[0],
            V = [segment[1][0] - segment[0][0], segment[1][1] - segment[0][1]],
            Vlength = Math.sqrt(V[0] * V[0] + V[1] * V[1]),
            X = [V[0] / Vlength, V[1] / Vlength],
            Y = [-X[1], X[0]];

        return [Y[1], -X[1], -Y[0], X[0]]; // inverse 2x2 matrix
    }

    function pushOrderIndex(array, item, index) { // worked only for array.length === 2
        if (index < 0) console.error('bad index');
        if (array.length !== 2) console.error('bad array');

        switch (index) {
            case 0:
                return [item].concat(array);
            case 1:
                return [array[1], item, array[0]];
            case 2:
                return array.concat([item]);
        }

//		array.push(item); // insert new last
//		return array.slice(2 - index).concat(array.slice(0, 2 - index)); // rotate to be inserted item placed by index
    }

    function shiftIndex(array, index) {
        if (index < 0) console.error('bad index');

        var length = array.length;
        return array.concat(array).slice(index + 1, index + length);
    }

    function square(a) {
        return a * a
    }
}(this);


// debug
$canvas = $('<canvas>').appendTo('body').attr({width: $(window).width(), height: $(window).height()});
canvas = $canvas.get(0);

function trp(p) {
    return [
        canvas.width / 2 + p[0] * 160,
        canvas.height / 2 - p[1] * 160
    ];
}

FigureSurfaceGraph.prototype.show = function () {
    var context = canvas.getContext('2d');

    context.clearRect(0, 0, canvas.width, canvas.height);

    sg.group.forEach(function (node) {
        node.face.borders.forEach(function (border, index) {
            var points = node.points.slice(0);
            points.splice(index, 1);
            points = points.map(trp);

            context.beginPath();
            context.moveTo(points[0][0], points[0][1]);
            context.lineTo(points[1][0], points[1][1]);

            if (_.findWhere(sg.front, {border: border})) {
                context.lineWidth = 3;
                context.strokeStyle = 'red';
            } else {
                context.lineWidth = 2;
                context.strokeStyle = 'lightblue';
            }
            context.stroke();

            context.font = '16px Arial';
            context.textAlign = 'center';
            context.textBaseline = 'middle';
            context.fillText(border.line.index, (points[0][0] + points[1][0]) / 2, (points[0][1] + points[1][1]) / 2);
        });
    });

    return this;
};

/**
 sg = figure.createSurfaceGraph();
 sg.startWith(sg.faces[0]).show();

 w = sg.applyTransition(sg.front[0]); w === false && sg.front.splice(0, 1); console.log(sg); sg.show(); w
 */</script>

<!-- timing end -->
<script type="text/javascript">
    Tracker.timeEnd('js-loading', _jsLoadingStartAt);
    delete window._jsLoadingStartAt;
</script>

<!-- Google Analytics -->
<script type="text/javascript">
    var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
    document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
    try {
        var pageTracker = _gat._getTracker("UA-7847979-1");
        pageTracker._trackPageview();
    } catch (err) {
    }
</script>

<!-- timing end -->
<script type="text/javascript">
    Tracker.timeEnd('page-loading', _pageLoadingStartAt);
    delete window._pageLoadingStartAt;
</script>
</body>
</html>