1/** 2 * Panolens.js 3 * @author pchen66 4 * @namespace PANOLENS 5 */ 6 7var PANOLENS = { REVISION: '9' }; 8;/*! npm.im/iphone-inline-video 2.2.2 */ 9var enableInlineVideo=function(){"use strict";/*! npm.im/intervalometer */ 10function e(e,i,n,r){function t(n){d=i(t,r),e(n-(a||n)),a=n}var d,a;return{start:function(){d||t(0)},stop:function(){n(d),d=null,a=0}}}function i(i){return e(i,requestAnimationFrame,cancelAnimationFrame)}function n(e,i,n){function r(r){n&&!n(e,i)||r.stopImmediatePropagation()}return e.addEventListener(i,r),r}function r(e,i,n,r){function t(){return n[i]}function d(e){n[i]=e}r&&d(e[i]),Object.defineProperty(e,i,{get:t,set:d})}function t(e,i,n){n.addEventListener(i,function(){return e.dispatchEvent(new Event(i))})}function d(e,i){Promise.resolve().then(function(){e.dispatchEvent(new Event(i))})}function a(e){var i=new Audio;return t(e,"play",i),t(e,"playing",i),t(e,"pause",i),i.crossOrigin=e.crossOrigin,i.src=e.src||e.currentSrc||"data:",i}function u(e,i,n){(m||0)+200<Date.now()&&(e[h]=!0,m=Date.now()),n||(e.currentTime=i),k[++T%3]=100*i|0}function o(e){return e.driver.currentTime>=e.video.duration}function s(e){var i=this;i.video.readyState>=i.video.HAVE_FUTURE_DATA?(i.hasAudio||(i.driver.currentTime=i.video.currentTime+e*i.video.playbackRate/1e3,i.video.loop&&o(i)&&(i.driver.currentTime=0)),u(i.video,i.driver.currentTime)):i.video.networkState===i.video.NETWORK_IDLE&&0===i.video.buffered.length&&i.video.load(),i.video.ended&&(delete i.video[h],i.video.pause(!0))}function c(){var e=this,i=e[g];if(e.webkitDisplayingFullscreen)return void e[E]();"data:"!==i.driver.src&&i.driver.src!==e.src&&(u(e,0,!0),i.driver.src=e.src),e.paused&&(i.paused=!1,0===e.buffered.length&&e.load(),i.driver.play(),i.updater.start(),i.hasAudio||(d(e,"play"),i.video.readyState>=i.video.HAVE_ENOUGH_DATA&&d(e,"playing")))}function v(e){var i=this,n=i[g];n.driver.pause(),n.updater.stop(),i.webkitDisplayingFullscreen&&i[w](),n.paused&&!e||(n.paused=!0,n.hasAudio||d(i,"pause"),i.ended&&!i.webkitDisplayingFullscreen&&(i[h]=!0,d(i,"ended")))}function p(e,n){var r={};e[g]=r,r.paused=!0,r.hasAudio=n,r.video=e,r.updater=i(s.bind(r)),n?r.driver=a(e):(e.addEventListener("canplay",function(){e.paused||d(e,"playing")}),r.driver={src:e.src||e.currentSrc||"data:",muted:!0,paused:!0,pause:function(){r.driver.paused=!0},play:function(){r.driver.paused=!1,o(r)&&u(e,0)},get ended(){return o(r)}}),e.addEventListener("emptied",function(){var i=!r.driver.src||"data:"===r.driver.src;r.driver.src&&r.driver.src!==e.src&&(u(e,0,!0),r.driver.src=e.src,i||!n&&e.autoplay?r.driver.play():r.updater.stop())},!1),e.addEventListener("webkitbeginfullscreen",function(){e.paused?n&&0===r.driver.buffered.length&&r.driver.load():(e.pause(),e[E]())}),n&&(e.addEventListener("webkitendfullscreen",function(){r.driver.currentTime=e.currentTime}),e.addEventListener("seeking",function(){k.indexOf(100*e.currentTime|0)<0&&(r.driver.currentTime=e.currentTime)}))}function l(e){var i=e[h];return delete e[h],!e.webkitDisplayingFullscreen&&!i}function f(e){var i=e[g];e[E]=e.play,e[w]=e.pause,e.play=c,e.pause=v,r(e,"paused",i.driver),r(e,"muted",i.driver,!0),r(e,"playbackRate",i.driver,!0),r(e,"ended",i.driver),r(e,"loop",i.driver,!0),n(e,"seeking",function(e){return!e.webkitDisplayingFullscreen}),n(e,"seeked",function(e){return!e.webkitDisplayingFullscreen}),n(e,"timeupdate",l),n(e,"ended",l)}function y(e,i){if(void 0===i&&(i={}),!e[g]){if(!i.everywhere){if(!b)return;if(!(i.iPad||i.ipad?/iPhone|iPod|iPad/:/iPhone|iPod/).test(navigator.userAgent))return}e.pause();var n=e.autoplay;e.autoplay=!1,p(e,!e.muted),f(e),e.classList.add("IIV"),e.muted&&n&&(e.play(),e.addEventListener("playing",function i(){e.autoplay=!0,e.removeEventListener("playing",i)})),/iPhone|iPod|iPad/.test(navigator.platform)||console.warn("iphone-inline-video is not guaranteed to work in emulated environments")}}var m,b="object"==typeof document&&"object-fit"in document.head.style&&!matchMedia("(-webkit-video-playable-inline)").matches,g="bfred-it:iphone-inline-video",h="bfred-it:iphone-inline-video:event",E="bfred-it:iphone-inline-video:nativeplay",w="bfred-it:iphone-inline-video:nativepause",k=[],T=0;return y}(); 11;/** 12 * Tween.js - Licensed under the MIT license 13 * https://github.com/tweenjs/tween.js 14 * ---------------------------------------------- 15 * 16 * See https://github.com/tweenjs/tween.js/graphs/contributors for the full list of contributors. 17 * Thank you all, you're awesome! 18 */ 19 20var TWEEN = TWEEN || (function () { 21 22 var _tweens = []; 23 24 return { 25 26 getAll: function () { 27 28 return _tweens; 29 30 }, 31 32 removeAll: function () { 33 34 _tweens = []; 35 36 }, 37 38 add: function (tween) { 39 40 _tweens.push(tween); 41 42 }, 43 44 remove: function (tween) { 45 46 var i = _tweens.indexOf(tween); 47 48 if (i !== -1) { 49 _tweens.splice(i, 1); 50 } 51 52 }, 53 54 update: function (time, preserve) { 55 56 if (_tweens.length === 0) { 57 return false; 58 } 59 60 var i = 0; 61 62 time = time !== undefined ? time : TWEEN.now(); 63 64 while (i < _tweens.length) { 65 66 if (_tweens[i].update(time) || preserve) { 67 i++; 68 } else { 69 _tweens.splice(i, 1); 70 } 71 72 } 73 74 return true; 75 76 } 77 }; 78 79})(); 80 81 82// Include a performance.now polyfill.
83// In node.js, use process.hrtime. 84if (typeof (window) === 'undefined' && typeof (process) !== 'undefined') { 85 TWEEN.now = function () { 86 var time = process.hrtime(); 87 88 // Convert [seconds, nanoseconds] to milliseconds. 89 return time[0] * 1000 + time[1] / 1000000; 90 }; 91} 92// In a browser, use window.performance.now if it is available. 93else if (typeof (window) !== 'undefined' && 94 window.performance !== undefined && 95 window.performance.now !== undefined) { 96 // This must be bound, because directly assigning this function 97 // leads to an invocation exception in Chrome. 98 TWEEN.now = window.performance.now.bind(window.performance); 99} 100// Use Date.now if it is available. 101else if (Date.now !== undefined) { 102 TWEEN.now = Date.now; 103} 104// Otherwise, use 'new Date().getTime()'. 105else { 106 TWEEN.now = function () { 107 return new Date().getTime(); 108 }; 109} 110 111 112TWEEN.Tween = function (object) { 113 114 var _object = object; 115 var _valuesStart = {}; 116 var _valuesEnd = {}; 117 var _valuesStartRepeat = {}; 118 var _duration = 1000; 119 var _repeat = 0; 120 var _repeatDelayTime; 121 var _yoyo = false; 122 var _isPlaying = false; 123 var _reversed = false; 124 var _delayTime = 0; 125 var _startTime = null; 126 var _easingFunction = TWEEN.Easing.Linear.None; 127 var _interpolationFunction = TWEEN.Interpolation.Linear; 128 var _chainedTweens = []; 129 var _onStartCallback = null; 130 var _onStartCallbackFired = false; 131 var _onUpdateCallback = null; 132 var _onCompleteCallback = null; 133 var _onStopCallback = null; 134 135 this.to = function (properties, duration) { 136 137 _valuesEnd = properties; 138 139 if (duration !== undefined) { 140 _duration = duration; 141 } 142 143 return this; 144 145 }; 146 147 this.start = function (time) { 148 149 TWEEN.add(this); 150 151 _isPlaying = true; 152 153 _onStartCallbackFired = false; 154 155 _startTime = time !== undefined ? time : TWEEN.now(); 156 _startTime += _delayTime; 157 158 for (var property in _valuesEnd) { 159 160 // Check if an Array was provided as property value 161 if (_valuesEnd[property] instanceof Array) { 162 163 if (_valuesEnd[property].length === 0) { 164 continue; 165 } 166 167 // Create a local copy of the Array with the start value at the front 168 _valuesEnd[property] = [_object[property]].concat(_valuesEnd[property]); 169 170 } 171 172 // If `to()` specifies a property that doesn't exist in the source object, 173 // we should not set that property in the object 174 if (_object[property] === undefined) { 175 continue; 176 } 177 178 // Save the starting value. 179 _valuesStart[property] = _object[property]; 180 181 if ((_valuesStart[property] instanceof Array) === false) { 182 _valuesStart[property] *= 1.0; // Ensures we're using numbers, not strings 183 } 184 185 _valuesStartRepeat[property] = _valuesStart[property] || 0; 186 187 } 188 189 return this; 190 191 }; 192 193 this.stop = function () { 194 195 if (!_isPlaying) { 196 return this; 197 } 198 199 TWEEN.remove(this); 200 _isPlaying = false; 201 202 if (_onStopCallback !== null) { 203 _onStopCallback.call(_object, _object); 204 } 205 206 this.stopChainedTweens(); 207 return this; 208 209 }; 210 211 this.end = function () { 212 213 this.update(_startTime + _duration); 214 return this; 215 216 }; 217 218 this.stopChainedTweens = function () { 219 220 for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) { 221 _chainedTweens[i].stop(); 222 } 223 224 }; 225 226 this.delay = function (amount) { 227 228 _delayTime = amount; 229 return this; 230 231 }; 232 233 this.repeat = function (times) { 234 235 _repeat = times; 236 return this; 237 238 }; 239 240 this.repeatDelay = function (amount) { 241 242 _repeatDelayTime = amount; 243 return this; 244 245 }; 246 247 this.yoyo = function (yoyo) { 248 249 _yoyo = yoyo; 250 return this; 251 252 }; 253 254 255 this.easing = function (easing) { 256 257 _easingFunction = easing; 258 return this; 259 260 }; 261 262 this.interpolation = function (interpolation) { 263 264 _interpolationFunction = interpolation; 265 return this; 266 267 }; 268 269 this.chain = function () { 270 271 _chainedTweens = arguments; 272 return this; 273 274 }; 275 276 this.onStart = function (callback) { 277 278 _onStartCallback = callback; 279 return this; 280 281 }; 282 283 this.onUpdate = function (callback) { 284 285 _onUpdateCallback = callback; 286 return this; 287 288 }; 289 290 this.onComplete = function (callback) { 291 292 _onCompleteCallback = callback; 293 return this; 294 295 }; 296 297 this.onStop = function (callback) { 298 299 _onStopCallback = callback; 300 return this; 301 302 }; 303 304 this.update = function (time) { 305 306 var property; 307 var elapsed; 308 var value; 309 310 if (time < _startTime) { 311 return true; 312 } 313 314 if (_onStartCallbackFired === false) { 315 316 if (_onStartCallback !== null) { 317 _onStartCallback.call(_object, _object); 318 } 319 320 _onStartCallbackFired = true; 321 } 322
323 elapsed = (time - _startTime) / _duration; 324 elapsed = elapsed > 1 ? 1 : elapsed; 325 326 value = _easingFunction(elapsed); 327 328 for (property in _valuesEnd) { 329 330 // Don't update properties that do not exist in the source object 331 if (_valuesStart[property] === undefined) { 332 continue; 333 } 334 335 var start = _valuesStart[property] || 0; 336 var end = _valuesEnd[property]; 337 338 if (end instanceof Array) { 339 340 _object[property] = _interpolationFunction(end, value); 341 342 } else { 343 344 // Parses relative end values with start as base (e.g.: +10, -3) 345 if (typeof (end) === 'string') { 346 347 if (end.charAt(0) === '+' || end.charAt(0) === '-') { 348 end = start + parseFloat(end); 349 } else { 350 end = parseFloat(end); 351 } 352 } 353 354 // Protect against non numeric properties. 355 if (typeof (end) === 'number') { 356 _object[property] = start + (end - start) * value; 357 } 358 359 } 360 361 } 362 363 if (_onUpdateCallback !== null) { 364 _onUpdateCallback.call(_object, value); 365 } 366 367 if (elapsed === 1) { 368 369 if (_repeat > 0) { 370 371 if (isFinite(_repeat)) { 372 _repeat--; 373 } 374 375 // Reassign starting values, restart by making startTime = now 376 for (property in _valuesStartRepeat) { 377 378 if (typeof (_valuesEnd[property]) === 'string') { 379 _valuesStartRepeat[property] = _valuesStartRepeat[property] + parseFloat(_valuesEnd[property]); 380 } 381 382 if (_yoyo) { 383 var tmp = _valuesStartRepeat[property]; 384 385 _valuesStartRepeat[property] = _valuesEnd[property]; 386 _valuesEnd[property] = tmp; 387 } 388 389 _valuesStart[property] = _valuesStartRepeat[property]; 390 391 } 392 393 if (_yoyo) { 394 _reversed = !_reversed; 395 } 396 397 if (_repeatDelayTime !== undefined) { 398 _startTime = time + _repeatDelayTime; 399 } else { 400 _startTime = time + _delayTime; 401 } 402 403 return true; 404 405 } else { 406 407 if (_onCompleteCallback !== null) { 408 409 _onCompleteCallback.call(_object, _object); 410 } 411 412 for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) { 413 // Make the chained tweens start exactly at the time they should, 414 // even if the `update()` method was called way past the duration of the tween 415 _chainedTweens[i].start(_startTime + _duration); 416 } 417 418 return false; 419 420 } 421 422 } 423 424 return true; 425 426 }; 427 428}; 429 430 431TWEEN.Easing = { 432 433 Linear: { 434 435 None: function (k) { 436 437 return k; 438 439 } 440 441 }, 442 443 Quadratic: { 444 445 In: function (k) { 446 447 return k * k; 448 449 }, 450 451 Out: function (k) { 452 453 return k * (2 - k); 454 455 }, 456 457 InOut: function (k) { 458 459 if ((k *= 2) < 1) { 460 return 0.5 * k * k; 461 } 462 463 return - 0.5 * (--k * (k - 2) - 1); 464 465 } 466 467 }, 468 469 Cubic: { 470 471 In: function (k) { 472 473 return k * k * k; 474 475 }, 476 477 Out: function (k) { 478 479 return --k * k * k + 1; 480 481 }, 482 483 InOut: function (k) { 484 485 if ((k *= 2) < 1) { 486 return 0.5 * k * k * k; 487 } 488 489 return 0.5 * ((k -= 2) * k * k + 2); 490 491 } 492 493 }, 494 495 Quartic: { 496 497 In: function (k) { 498 499 return k * k * k * k; 500 501 }, 502 503 Out: function (k) { 504 505 return 1 - (--k * k * k * k); 506 507 }, 508 509 InOut: function (k) { 510 511 if ((k *= 2) < 1) { 512 return 0.5 * k * k * k * k; 513 } 514 515 return - 0.5 * ((k -= 2) * k * k * k - 2); 516 517 } 518 519 }, 520 521 Quintic: { 522 523 In: function (k) { 524 525 return k * k * k * k * k; 526 527 }, 528 529 Out: function (k) { 530 531 return --k * k * k * k * k + 1; 532 533 }, 534 535 InOut: function (k) { 536 537 if ((k *= 2) < 1) { 538 return 0.5 * k * k * k * k * k; 539 } 540 541 return 0.5 * ((k -= 2) * k * k * k * k + 2); 542 543 } 544 545 }, 546 547 Sinusoidal: { 548 549 In: function (k) { 550 551 return 1 - Math.cos(k * Math.PI / 2); 552 553 }, 554 555 Out: function (k) { 556 557 return Math.sin(k * Math.PI / 2); 558 559 }, 560 561 InOut: function (k) { 562 563 return 0.5 * (1 - Math.cos(Math.PI * k)); 564 565 } 566 567 }, 568 569 Exponential: { 570 571 In: function (k) { 572 573 return k === 0 ? 0 : Math.pow(1024, k - 1); 574 575 }, 576 577 Out: function (k) { 578 579 return k === 1 ? 1 : 1 - Math.pow(2, - 10 * k); 580 581 }, 582 583 InOut: function (k) { 584 585 if (k === 0) { 586 return 0; 587 } 588 589 if (k === 1) { 590 return 1; 591 } 592 593 if ((k *= 2) < 1) {
vendor: 26,488 bytes, lines 594-1778
594 return 0.5 * Math.pow(1024, k - 1); 595 } 596 597 return 0.5 * (- Math.pow(2, - 10 * (k - 1)) + 2); 598 599 } 600 601 }, 602 603 Circular: { 604 605 In: function (k) { 606 607 return 1 - Math.sqrt(1 - k * k); 608 609 }, 610 611 Out: function (k) { 612 613 return Math.sqrt(1 - (--k * k)); 614 615 }, 616 617 InOut: function (k) { 618 619 if ((k *= 2) < 1) { 620 return - 0.5 * (Math.sqrt(1 - k * k) - 1); 621 } 622 623 return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1); 624 625 } 626 627 }, 628 629 Elastic: { 630 631 In: function (k) { 632 633 if (k === 0) { 634 return 0; 635 } 636 637 if (k === 1) { 638 return 1; 639 } 640 641 return -Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI); 642 643 }, 644 645 Out: function (k) { 646 647 if (k === 0) { 648 return 0; 649 } 650 651 if (k === 1) { 652 return 1; 653 } 654 655 return Math.pow(2, -10 * k) * Math.sin((k - 0.1) * 5 * Math.PI) + 1; 656 657 }, 658 659 InOut: function (k) { 660 661 if (k === 0) { 662 return 0; 663 } 664 665 if (k === 1) { 666 return 1; 667 } 668 669 k *= 2; 670 671 if (k < 1) { 672 return -0.5 * Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI); 673 } 674 675 return 0.5 * Math.pow(2, -10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI) + 1; 676 677 } 678 679 }, 680 681 Back: { 682 683 In: function (k) { 684 685 var s = 1.70158; 686 687 return k * k * ((s + 1) * k - s); 688 689 }, 690 691 Out: function (k) { 692 693 var s = 1.70158; 694 695 return --k * k * ((s + 1) * k + s) + 1; 696 697 }, 698 699 InOut: function (k) { 700 701 var s = 1.70158 * 1.525; 702 703 if ((k *= 2) < 1) { 704 return 0.5 * (k * k * ((s + 1) * k - s)); 705 } 706 707 return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2); 708 709 } 710 711 }, 712 713 Bounce: { 714 715 In: function (k) { 716 717 return 1 - TWEEN.Easing.Bounce.Out(1 - k); 718 719 }, 720 721 Out: function (k) { 722 723 if (k < (1 / 2.75)) { 724 return 7.5625 * k * k; 725 } else if (k < (2 / 2.75)) { 726 return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; 727 } else if (k < (2.5 / 2.75)) { 728 return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; 729 } else { 730 return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; 731 } 732 733 }, 734 735 InOut: function (k) { 736 737 if (k < 0.5) { 738 return TWEEN.Easing.Bounce.In(k * 2) * 0.5; 739 } 740 741 return TWEEN.Easing.Bounce.Out(k * 2 - 1) * 0.5 + 0.5; 742 743 } 744 745 } 746 747}; 748 749TWEEN.Interpolation = { 750 751 Linear: function (v, k) { 752 753 var m = v.length - 1; 754 var f = m * k; 755 var i = Math.floor(f); 756 var fn = TWEEN.Interpolation.Utils.Linear; 757 758 if (k < 0) { 759 return fn(v[0], v[1], f); 760 } 761 762 if (k > 1) { 763 return fn(v[m], v[m - 1], m - f); 764 } 765 766 return fn(v[i], v[i + 1 > m ? m : i + 1], f - i); 767 768 }, 769 770 Bezier: function (v, k) { 771 772 var b = 0; 773 var n = v.length - 1; 774 var pw = Math.pow; 775 var bn = TWEEN.Interpolation.Utils.Bernstein; 776 777 for (var i = 0; i <= n; i++) { 778 b += pw(1 - k, n - i) * pw(k, i) * v[i] * bn(n, i); 779 } 780 781 return b; 782 783 }, 784 785 CatmullRom: function (v, k) { 786 787 var m = v.length - 1; 788 var f = m * k; 789 var i = Math.floor(f); 790 var fn = TWEEN.Interpolation.Utils.CatmullRom; 791 792 if (v[0] === v[m]) { 793 794 if (k < 0) { 795 i = Math.floor(f = m * (1 + k)); 796 } 797 798 return fn(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i); 799 800 } else { 801 802 if (k < 0) { 803 return v[0] - (fn(v[0], v[0], v[1], v[1], -f) - v[0]); 804 } 805 806 if (k > 1) { 807 return v[m] - (fn(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m]); 808 } 809 810 return fn(v[i ? i - 1 : 0], v[i], v[m < i + 1 ? m : i + 1], v[m < i + 2 ? m : i + 2], f - i); 811 812 } 813 814 }, 815 816 Utils: { 817 818 Linear: function (p0, p1, t) { 819 820 return (p1 - p0) * t + p0; 821 822 }, 823 824 Bernstein: function (n, i) { 825 826 var fc = TWEEN.Interpolation.Utils.Factorial; 827 828 return fc(n) / fc(i) / fc(n - i); 829 830 }, 831 832 Factorial: (function () { 833 834 var a = [1]; 835 836 return function (n) { 837 838 var s = 1; 839 840 if (a[n]) { 841 return a[n]; 842 } 843 844 for (var i = n; i > 1; i--) { 845 s *= i; 846 } 847 848 a[n] = s; 849 return s; 850 851 }; 852 853 })(), 854 855 CatmullRom: function (p0, p1, p2, p3, t) { 856 857 var v0 = (p2 - p0) * 0.5; 858 var v1 = (p3 - p1) * 0.5; 859 var t2 = t * t; 860 var t3 = t * t2; 861 862 return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (- 3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1; 863 864 } 865 866 } 867 868}; 869 870// UMD (Universal Module Definition) 871(function (root) { 872 873 if (typeof define === 'function' && define.amd) { 874 875 // AMD 876 define([], function () { 877 return TWEEN; 878 }); 879 880 } else if (typeof module !== 'undefined' && typeof exports === 'object') { 881 882 // Node.js 883 module.exports = TWEEN; 884 885 } else if (root !== undefined) { 886 887 // Global variable 888 root.TWEEN = TWEEN; 889 890 } 891 892})(this); 893;/** 894 * @author qiao / https://github.com/qiao 895 * @author mrdoob / http://mrdoob.com 896 * @author alteredq / http://alteredqualia.com/ 897 * @author WestLangley / http://github.com/WestLangley 898 * @author erich666 / http://erichaines.com 899 */ 900/*global THREE, console */ 901 902// This set of controls performs orbiting, dollying (zooming), and panning. It maintains 903// the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is 904// supported. 905// 906// Orbit - left mouse / touch: one finger move 907// Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 908// Pan - right mouse, or arrow keys / touch: three finter swipe 909 910THREE.OrbitControls = function ( object, domElement ) { 911 912 this.object = object; 913 this.domElement = ( domElement !== undefined ) ? domElement : document; 914 this.frameId; 915 916 // API 917 918 // Set to false to disable this control 919 this.enabled = true; 920 921 // "target" sets the location of focus, where the control orbits around 922 // and where it pans with respect to. 923 this.target = new THREE.Vector3(); 924 925 // center is old, deprecated; use "target" instead 926 this.center = this.target; 927 928 // This option actually enables dollying in and out; left as "zoom" for 929 // backwards compatibility 930 this.noZoom = false; 931 this.zoomSpeed = 1.0; 932 933 // Limits to how far you can dolly in and out ( PerspectiveCamera only ) 934 this.minDistance = 0; 935 this.maxDistance = Infinity; 936 937 // Limits to how far you can zoom in and out ( OrthographicCamera only ) 938 this.minZoom = 0; 939 this.maxZoom = Infinity; 940 941 // Set to true to disable this control 942 this.noRotate = false; 943 this.rotateSpeed = -0.15; 944 945 // Set to true to disable this control 946 this.noPan = true; 947 this.keyPanSpeed = 7.0; // pixels moved per arrow key push 948 949 // Set to true to automatically rotate around the target 950 this.autoRotate = false; 951 this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 952 953 // How far you can orbit vertically, upper and lower limits. 954 // Range is 0 to Math.PI radians. 955 this.minPolarAngle = 0; // radians 956 this.maxPolarAngle = Math.PI; // radians 957 958 // Momentum 959 this.momentumDampingFactor = 0.90; 960 this.momentumScalingFactor = -0.005; 961 this.momentumKeydownFactor = 20; 962 963 // Fov 964 this.minFov = 30; 965 this.maxFov = 120; 966 967 // How far you can orbit horizontally, upper and lower limits. 968 // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. 969 this.minAzimuthAngle = - Infinity; // radians 970 this.maxAzimuthAngle = Infinity; // radians 971 972 // Set to true to disable use of the keys 973 this.noKeys = false; 974 975 // The four arrow keys 976 this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 977 978 // Mouse buttons 979 this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; 980 981 //////////// 982 // internals 983 984 var scope = this; 985 986 var EPS = 10e-8; 987 var MEPS = 10e-5; 988 989 var rotateStart = new THREE.Vector2(); 990 var rotateEnd = new THREE.Vector2(); 991 var rotateDelta = new THREE.Vector2(); 992 993 var panStart = new THREE.Vector2(); 994 var panEnd = new THREE.Vector2(); 995 var panDelta = new THREE.Vector2(); 996 var panOffset = new THREE.Vector3(); 997 998 var offset = new THREE.Vector3(); 999 1000 var dollyStart = new THREE.Vector2(); 1001 var dollyEnd = new THREE.Vector2(); 1002 var dollyDelta = new THREE.Vector2(); 1003 1004 var theta; 1005 var phi; 1006 var phiDelta = 0; 1007 var thetaDelta = 0; 1008 var scale = 1; 1009 var pan = new THREE.Vector3(); 1010 1011 var lastPosition = new THREE.Vector3(); 1012 var lastQuaternion = new THREE.Quaternion(); 1013 1014 var momentumLeft = 0, momentumUp = 0; 1015 var eventCurrent, eventPrevious; 1016 var momentumOn = false; 1017 1018 var keyUp, keyBottom, keyLeft, keyRight; 1019 1020 var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; 1021 1022 var state = STATE.NONE; 1023 1024 // for reset 1025 1026 this.target0 = this.target.clone(); 1027 this.position0 = this.object.position.clone(); 1028 this.zoom0 = this.object.zoom; 1029 1030 // so camera.up is the orbit axis 1031 1032 var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); 1033 var quatInverse = quat.clone().inverse(); 1034 1035 // events 1036 1037 var changeEvent = { type: 'change' }; 1038 var startEvent = { type: 'start' }; 1039 var endEvent = { type: 'end' }; 1040 1041 this.setLastQuaternion = function ( quaternion ) { 1042 lastQuaternion.copy( quaternion ); 1043 scope.object.quaternion.copy( quaternion ); 1044 }; 1045 1046 this.getLastPosition = function () { 1047 return lastPosition; 1048 } 1049 1050 this.rotateLeft = function ( angle ) { 1051 1052 if ( angle === undefined ) { 1053 1054 angle = getAutoRotationAngle(); 1055 1056 } 1057 1058 thetaDelta -= angle; 1059 1060 1061 }; 1062 1063 this.rotateUp = function ( angle ) { 1064 1065 if ( angle === undefined ) { 1066 1067 angle = getAutoRotationAngle(); 1068 1069 } 1070 1071 phiDelta -= angle; 1072 1073 }; 1074 1075 // pass in distance in world space to move left 1076 this.panLeft = function ( distance ) { 1077 1078 var te = this.object.matrix.elements; 1079 1080 // get X column of matrix 1081 panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] ); 1082 panOffset.multiplyScalar( - distance ); 1083 1084 pan.add( panOffset ); 1085 1086 }; 1087 1088 // pass in distance in world space to move up 1089 this.panUp = function ( distance ) { 1090 1091 var te = this.object.matrix.elements; 1092 1093 // get Y column of matrix 1094 panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] ); 1095 panOffset.multiplyScalar( distance ); 1096 1097 pan.add( panOffset ); 1098 1099 }; 1100 1101 // pass in x,y of change desired in pixel space, 1102 // right and down are positive 1103 this.pan = function ( deltaX, deltaY ) { 1104 1105 var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 1106 1107 if ( scope.object instanceof THREE.PerspectiveCamera ) { 1108 1109 // perspective 1110 var position = scope.object.position; 1111 var offset = position.clone().sub( scope.target ); 1112 var targetDistance = offset.length(); 1113 1114 // half of the fov is center to top of screen 1115 targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); 1116 1117 // we actually don't use screenWidth, since perspective camera is fixed to screen height 1118 scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight ); 1119 scope.panUp( 2 * deltaY * targetDistance / element.clientHeight ); 1120 1121 } else if ( scope.object instanceof THREE.OrthographicCamera ) { 1122 1123 // orthographic 1124 scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth ); 1125 scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight ); 1126 1127 } else { 1128 1129 // camera neither orthographic or perspective 1130 console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 1131 1132 } 1133 1134 }; 1135 1136 this.momentum = function(){ 1137 1138 if ( !momentumOn ) return; 1139 1140 if ( Math.abs( momentumLeft ) < MEPS && Math.abs( momentumUp ) < MEPS ) { 1141 1142 momentumOn = false; 1143 return; 1144 } 1145 1146 momentumUp *= this.momentumDampingFactor; 1147 momentumLeft *= this.momentumDampingFactor; 1148 1149 thetaDelta -= this.momentumScalingFactor * momentumLeft; 1150 phiDelta -= this.momentumScalingFactor * momentumUp; 1151 1152 }; 1153 1154 this.dollyIn = function ( dollyScale ) { 1155 1156 if ( dollyScale === undefined ) { 1157 1158 dollyScale = getZoomScale(); 1159 1160 } 1161 1162 if ( scope.object instanceof THREE.PerspectiveCamera ) { 1163 1164 scale /= dollyScale; 1165 1166 } else if ( scope.object instanceof THREE.OrthographicCamera ) { 1167 1168 scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom * dollyScale ) ); 1169 scope.object.updateProjectionMatrix(); 1170 scope.dispatchEvent( changeEvent ); 1171 1172 } else { 1173 1174 console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 1175 1176 } 1177 1178 }; 1179 1180 this.dollyOut = function ( dollyScale ) { 1181 1182 if ( dollyScale === undefined ) { 1183 1184 dollyScale = getZoomScale(); 1185 1186 } 1187 1188 if ( scope.object instanceof THREE.PerspectiveCamera ) { 1189 1190 scale *= dollyScale; 1191 1192 } else if ( scope.object instanceof THREE.OrthographicCamera ) { 1193 1194 scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / dollyScale ) ); 1195 scope.object.updateProjectionMatrix(); 1196 scope.dispatchEvent( changeEvent ); 1197 1198 } else { 1199 1200 console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 1201 1202 } 1203 1204 }; 1205 1206 this.update = function ( ignoreUpdate ) { 1207 1208 var position = this.object.position; 1209 1210 offset.copy( position ).sub( this.target ); 1211 1212 // rotate offset to "y-axis-is-up" space 1213 offset.applyQuaternion( quat ); 1214 1215 // angle from z-axis around y-axis 1216 1217 theta = Math.atan2( offset.x, offset.z ); 1218 1219 // angle from y-axis 1220 1221 phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 1222 1223 if ( this.autoRotate && state === STATE.NONE ) { 1224 1225 this.rotateLeft( getAutoRotationAngle() ); 1226 1227 } 1228 1229 this.momentum(); 1230 1231 theta += thetaDelta; 1232 phi += phiDelta; 1233 1234 // restrict theta to be between desired limits 1235 theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) ); 1236 1237 // restrict phi to be between desired limits 1238 phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 1239 1240 // restrict phi to be betwee EPS and PI-EPS 1241 phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 1242 1243 var radius = offset.length() * scale; 1244 1245 // restrict radius to be between desired limits 1246 radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 1247 1248 // move target to panned location 1249 this.target.add( pan ); 1250 1251 offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 1252 offset.y = radius * Math.cos( phi ); 1253 offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 1254 1255 // rotate offset back to "camera-up-vector-is-up" space 1256 offset.applyQuaternion( quatInverse ); 1257 1258 position.copy( this.target ).add( offset ); 1259 1260 this.object.lookAt( this.target ); 1261 1262 thetaDelta = 0; 1263 phiDelta = 0; 1264 scale = 1; 1265 pan.set( 0, 0, 0 ); 1266 1267 // update condition is: 1268 // min(camera displacement, camera rotation in radians)^2 > EPS 1269 // using small-angle approximation cos(x/2) = 1 - x^2 / 8 1270 if ( lastPosition.distanceToSquared( this.object.position ) > EPS 1271 || 8 * (1 - lastQuaternion.dot(this.object.quaternion)) > EPS ) { 1272 1273 ignoreUpdate !== true && this.dispatchEvent( changeEvent ); 1274 1275 lastPosition.copy( this.object.position ); 1276 lastQuaternion.copy (this.object.quaternion ); 1277 1278 } 1279 1280 }; 1281 1282 1283 this.reset = function () { 1284 1285 state = STATE.NONE; 1286 1287 this.target.copy( this.target0 ); 1288 this.object.position.copy( this.position0 ); 1289 this.object.zoom = this.zoom0; 1290 1291 this.object.updateProjectionMatrix(); 1292 this.dispatchEvent( changeEvent ); 1293 1294 this.update(); 1295 1296 }; 1297 1298 this.getPolarAngle = function () { 1299 1300 return phi; 1301 1302 }; 1303 1304 this.getAzimuthalAngle = function () { 1305 1306 return theta 1307 1308 }; 1309 1310 function getAutoRotationAngle() { 1311 1312 return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 1313 1314 } 1315 1316 function getZoomScale() { 1317 1318 return Math.pow( 0.95, scope.zoomSpeed ); 1319 1320 } 1321 1322 function onMouseDown( event ) { 1323 1324 momentumOn = false; 1325 1326 momentumLeft = momentumUp = 0; 1327 1328 if ( scope.enabled === false ) return; 1329 event.preventDefault(); 1330 1331 if ( event.button === scope.mouseButtons.ORBIT ) { 1332 if ( scope.noRotate === true ) return; 1333 1334 state = STATE.ROTATE; 1335 1336 rotateStart.set( event.clientX, event.clientY ); 1337 1338 } else if ( event.button === scope.mouseButtons.ZOOM ) { 1339 if ( scope.noZoom === true ) return; 1340 1341 state = STATE.DOLLY; 1342 1343 dollyStart.set( event.clientX, event.clientY ); 1344 1345 } else if ( event.button === scope.mouseButtons.PAN ) { 1346 if ( scope.noPan === true ) return; 1347 1348 state = STATE.PAN; 1349 1350 panStart.set( event.clientX, event.clientY ); 1351 1352 } 1353 1354 if ( state !== STATE.NONE ) { 1355 document.addEventListener( 'mousemove', onMouseMove, false ); 1356 document.addEventListener( 'mouseup', onMouseUp, false ); 1357 scope.dispatchEvent( startEvent ); 1358 } 1359 1360 scope.update(); 1361 1362 } 1363 1364 function onMouseMove( event ) { 1365 1366 if ( scope.enabled === false ) return; 1367 1368 event.preventDefault(); 1369 1370 var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 1371 1372 if ( state === STATE.ROTATE ) { 1373 1374 if ( scope.noRotate === true ) return; 1375 1376 rotateEnd.set( event.clientX, event.clientY ); 1377 rotateDelta.subVectors( rotateEnd, rotateStart ); 1378 1379 // rotating across whole screen goes 360 degrees around 1380 scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 1381 1382 // rotating up and down along whole screen attempts to go 360, but limited to 180 1383 scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 1384 1385 rotateStart.copy( rotateEnd ); 1386 1387 if( eventPrevious ){ 1388 momentumLeft = event.clientX - eventPrevious.clientX; 1389 momentumUp = event.clientY - eventPrevious.clientY; 1390 } 1391 1392 eventPrevious = event; 1393 1394 } else if ( state === STATE.DOLLY ) { 1395 1396 if ( scope.noZoom === true ) return; 1397 1398 dollyEnd.set( event.clientX, event.clientY ); 1399 dollyDelta.subVectors( dollyEnd, dollyStart ); 1400 1401 if ( dollyDelta.y > 0 ) { 1402 1403 scope.dollyIn(); 1404 1405 } else if ( dollyDelta.y < 0 ) { 1406 1407 scope.dollyOut(); 1408 1409 } 1410 1411 dollyStart.copy( dollyEnd ); 1412 1413 } else if ( state === STATE.PAN ) { 1414 1415 if ( scope.noPan === true ) return; 1416 1417 panEnd.set( event.clientX, event.clientY ); 1418 panDelta.subVectors( panEnd, panStart ); 1419 1420 scope.pan( panDelta.x, panDelta.y ); 1421 1422 panStart.copy( panEnd ); 1423 1424 } 1425 1426 if ( state !== STATE.NONE ) scope.update(); 1427 1428 } 1429 1430 function onMouseUp( /* event */ ) { 1431 1432 momentumOn = true; 1433 1434 eventPrevious = undefined; 1435 1436 if ( scope.enabled === false ) return; 1437 1438 document.removeEventListener( 'mousemove', onMouseMove, false ); 1439 document.removeEventListener( 'mouseup', onMouseUp, false ); 1440 scope.dispatchEvent( endEvent ); 1441 state = STATE.NONE; 1442 1443 } 1444 1445 function onMouseWheel( event ) { 1446 1447 if ( scope.enabled === false || scope.noZoom === true || state !== STATE.NONE ) return; 1448 1449 event.preventDefault(); 1450 event.stopPropagation(); 1451 1452 var delta = 0; 1453 1454 if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9 1455 1456 delta = event.wheelDelta; 1457 1458 } else if ( event.detail !== undefined ) { // Firefox 1459 1460 delta = - event.detail; 1461 1462 } 1463 1464 if ( delta > 0 ) { 1465 1466 //scope.dollyOut(); 1467 scope.object.fov = ( scope.object.fov < scope.maxFov ) 1468 ? scope.object.fov + 1 1469 : scope.maxFov; 1470 scope.object.updateProjectionMatrix(); 1471 1472 } else if ( delta < 0 ) { 1473 1474 //scope.dollyIn(); 1475 scope.object.fov = ( scope.object.fov > scope.minFov ) 1476 ? scope.object.fov - 1 1477 : scope.minFov; 1478 scope.object.updateProjectionMatrix(); 1479 1480 } 1481 1482 scope.update(); 1483 scope.dispatchEvent( changeEvent ); 1484 scope.dispatchEvent( startEvent ); 1485 scope.dispatchEvent( endEvent ); 1486 1487 } 1488 1489 function onKeyUp ( event ) { 1490 1491 switch ( event.keyCode ) { 1492 1493 case scope.keys.UP: 1494 keyUp = false; 1495 break; 1496 1497 case scope.keys.BOTTOM: 1498 keyBottom = false; 1499 break; 1500 1501 case scope.keys.LEFT: 1502 keyLeft = false; 1503 break; 1504 1505 case scope.keys.RIGHT: 1506 keyRight = false; 1507 break; 1508 1509 } 1510 1511 } 1512 1513 function onKeyDown( event ) { 1514 1515 if ( scope.enabled === false || scope.noKeys === true || scope.noRotate === true ) return; 1516 1517 switch ( event.keyCode ) { 1518 1519 case scope.keys.UP: 1520 keyUp = true; 1521 break; 1522 1523 case scope.keys.BOTTOM: 1524 keyBottom = true; 1525 break; 1526 1527 case scope.keys.LEFT: 1528 keyLeft = true; 1529 break; 1530 1531 case scope.keys.RIGHT: 1532 keyRight = true; 1533 break; 1534 1535 } 1536 1537 if (keyUp || keyBottom || keyLeft || keyRight) { 1538 1539 momentumOn = true; 1540 1541 if (keyUp) momentumUp = - scope.rotateSpeed * scope.momentumKeydownFactor; 1542 if (keyBottom) momentumUp = scope.rotateSpeed * scope.momentumKeydownFactor; 1543 if (keyLeft) momentumLeft = - scope.rotateSpeed * scope.momentumKeydownFactor; 1544 if (keyRight) momentumLeft = scope.rotateSpeed * scope.momentumKeydownFactor; 1545 1546 } 1547 1548 } 1549 1550 function touchstart( event ) { 1551 1552 momentumOn = false; 1553 1554 momentumLeft = momentumUp = 0; 1555 1556 if ( scope.enabled === false ) return; 1557 1558 switch ( event.touches.length ) { 1559 1560 case 1: // one-fingered touch: rotate 1561 1562 if ( scope.noRotate === true ) return; 1563 1564 state = STATE.TOUCH_ROTATE; 1565 1566 rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 1567 break; 1568 1569 case 2: // two-fingered touch: dolly 1570 1571 if ( scope.noZoom === true ) return; 1572 1573 state = STATE.TOUCH_DOLLY; 1574 1575 var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 1576 var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 1577 var distance = Math.sqrt( dx * dx + dy * dy ); 1578 1579 break; 1580 1581 case 3: // three-fingered touch: pan 1582 1583 if ( scope.noPan === true ) return; 1584 1585 state = STATE.TOUCH_PAN; 1586 1587 panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 1588 break; 1589 1590 default: 1591 1592 state = STATE.NONE; 1593 1594 } 1595 1596 if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent ); 1597 1598 } 1599 1600 function touchmove( event ) { 1601 1602 if ( scope.enabled === false ) return; 1603 1604 event.preventDefault(); 1605 event.stopPropagation(); 1606 1607 var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 1608 1609 switch ( event.touches.length ) { 1610 1611 case 1: // one-fingered touch: rotate 1612 1613 if ( scope.noRotate === true ) return; 1614 if ( state !== STATE.TOUCH_ROTATE ) return; 1615 1616 rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 1617 rotateDelta.subVectors( rotateEnd, rotateStart ); 1618 1619 // rotating across whole screen goes 360 degrees around 1620 scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 1621 // rotating up and down along whole screen attempts to go 360, but limited to 180 1622 scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 1623 1624 rotateStart.copy( rotateEnd ); 1625 1626 if( eventPrevious ){ 1627 momentumLeft = event.touches[ 0 ].pageX - eventPrevious.pageX; 1628 momentumUp = event.touches[ 0 ].pageY - eventPrevious.pageY; 1629 } 1630 1631 eventPrevious = { 1632 pageX: event.touches[ 0 ].pageX, 1633 pageY: event.touches[ 0 ].pageY, 1634 }; 1635 1636 scope.update(); 1637 break; 1638 1639 case 2: // two-fingered touch: dolly 1640 1641 if ( scope.noZoom === true ) return; 1642 if ( state !== STATE.TOUCH_DOLLY ) return; 1643 1644 var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 1645 var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 1646 var distance = Math.sqrt( dx * dx + dy * dy ); 1647 1648 if ( event.scale < 1 ) { 1649 1650 scope.object.fov = ( scope.object.fov < scope.maxFov ) 1651 ? scope.object.fov + 1 1652 : scope.maxFov; 1653 scope.object.updateProjectionMatrix(); 1654 1655 } else if ( event.scale > 1 ) { 1656 1657 scope.object.fov = ( scope.object.fov > scope.minFov ) 1658 ? scope.object.fov - 1 1659 : scope.minFov; 1660 scope.object.updateProjectionMatrix(); 1661 1662 } 1663 1664 scope.update(); 1665 scope.dispatchEvent( changeEvent ); 1666 break; 1667 1668 case 3: // three-fingered touch: pan 1669 1670 if ( scope.noPan === true ) return; 1671 if ( state !== STATE.TOUCH_PAN ) return; 1672 1673 panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 1674 panDelta.subVectors( panEnd, panStart ); 1675 1676 scope.pan( panDelta.x, panDelta.y ); 1677 1678 panStart.copy( panEnd ); 1679 1680 scope.update(); 1681 break; 1682 1683 default: 1684 1685 state = STATE.NONE; 1686 1687 } 1688 1689 } 1690 1691 function touchend( /* event */ ) { 1692 1693 momentumOn = true; 1694 1695 eventPrevious = undefined; 1696 1697 if ( scope.enabled === false ) return; 1698 1699 scope.dispatchEvent( endEvent ); 1700 state = STATE.NONE; 1701 1702 } 1703 1704 //this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 1705 this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 1706 this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 1707 this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 1708 1709 this.domElement.addEventListener( 'touchstart', touchstart, false ); 1710 this.domElement.addEventListener( 'touchend', touchend, false ); 1711 this.domElement.addEventListener( 'touchmove', touchmove, false ); 1712 1713 window.addEventListener( 'keyup', onKeyUp, false ); 1714 window.addEventListener( 'keydown', onKeyDown, false ); 1715 1716 // force an update at start 1717 this.update(); 1718 1719}; 1720 1721THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 1722THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;;/** 1723 * @author richt / http://richt.me 1724 * @author WestLangley / http://github.com/WestLangley 1725 * 1726 * W3C Device Orientation control (http://w3c.github.io/deviceorientation/spec-source-orientation.html) 1727 */ 1728 1729THREE.DeviceOrientationControls = function( camera, domElement ) { 1730 1731 var scope = this; 1732 var changeEvent = { type: 'change' }; 1733 1734 var rotY = 0; 1735 var rotX = 0; 1736 var tempX = 0; 1737 var tempY = 0; 1738 1739 this.camera = camera; 1740 this.camera.rotation.reorder( "YXZ" ); 1741 this.domElement = ( domElement !== undefined ) ? domElement : document; 1742 1743 this.enabled = true; 1744 1745 this.deviceOrientation = {}; 1746 this.screenOrientation = 0; 1747 1748 this.alpha = 0; 1749 this.alphaOffsetAngle = 0; 1750 1751 1752 var onDeviceOrientationChangeEvent = function( event ) { 1753 1754 scope.deviceOrientation = event; 1755 1756 }; 1757 1758 var onScreenOrientationChangeEvent = function() { 1759 1760 scope.screenOrientation = window.orientation || 0; 1761 1762 }; 1763 1764 var onTouchStartEvent = function (event) { 1765 1766 event.preventDefault(); 1767 event.stopPropagation(); 1768 1769 tempX = event.touches[ 0 ].pageX; 1770 tempY = event.touches[ 0 ].pageY; 1771 1772 }; 1773 1774 var onTouchMoveEvent = function (event) { 1775 1776 event.preventDefault(); 1777 event.stopPropagation(); 1778
1779 rotY += THREE.Math.degToRad( ( event.touches[ 0 ].pageX - tempX ) / 4 ); 1780 rotX += THREE.Math.degToRad( ( tempY - event.touches[ 0 ].pageY ) / 4 ); 1781 1782 scope.updateAlphaOffsetAngle( rotY ); 1783 1784 tempX = event.touches[ 0 ].pageX; 1785 tempY = event.touches[ 0 ].pageY; 1786 1787 }; 1788 1789 // The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y'' 1790 1791 var setCameraQuaternion = function( quaternion, alpha, beta, gamma, orient ) { 1792 1793 var zee = new THREE.Vector3( 0, 0, 1 ); 1794 1795 var euler = new THREE.Euler(); 1796 1797 var q0 = new THREE.Quaternion(); 1798 1799 var q1 = new THREE.Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis 1800 1801 var vectorFingerY; 1802 var fingerQY = new THREE.Quaternion(); 1803 var fingerQX = new THREE.Quaternion(); 1804 1805 if ( scope.screenOrientation == 0 ) { 1806 1807 vectorFingerY = new THREE.Vector3( 1, 0, 0 ); 1808 fingerQY.setFromAxisAngle( vectorFingerY, -rotX ); 1809 1810 } else if ( scope.screenOrientation == 180 ) { 1811 1812 vectorFingerY = new THREE.Vector3( 1, 0, 0 ); 1813 fingerQY.setFromAxisAngle( vectorFingerY, rotX ); 1814 1815 } else if ( scope.screenOrientation == 90 ) { 1816 1817 vectorFingerY = new THREE.Vector3( 0, 1, 0 ); 1818 fingerQY.setFromAxisAngle( vectorFingerY, rotX ); 1819 1820 } else if ( scope.screenOrientation == - 90) { 1821 1822 vectorFingerY = new THREE.Vector3( 0, 1, 0 ); 1823 fingerQY.setFromAxisAngle( vectorFingerY, -rotX ); 1824 1825 } 1826 1827 q1.multiply( fingerQY ); 1828 q1.multiply( fingerQX ); 1829 1830 euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us 1831 1832 quaternion.setFromEuler( euler ); // orient the device 1833 1834 quaternion.multiply( q1 ); // camera looks out the back of the device, not the top 1835 1836 quaternion.multiply( q0.setFromAxisAngle( zee, - orient ) ); // adjust for screen orientation 1837 1838 }; 1839 1840 this.connect = function() { 1841 1842 onScreenOrientationChangeEvent(); // run once on load 1843 1844 window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent, false ); 1845 window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false ); 1846 window.addEventListener( 'deviceorientation', this.update.bind( this ), false ); 1847 1848 scope.domElement.addEventListener( "touchstart", onTouchStartEvent, false ); 1849 scope.domElement.addEventListener( "touchmove", onTouchMoveEvent, false ); 1850 1851 scope.enabled = true; 1852 1853 }; 1854 1855 this.disconnect = function() { 1856 1857 window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent, false ); 1858 window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false ); 1859 window.removeEventListener( 'deviceorientation', this.update.bind( this ), false ); 1860 1861 scope.domElement.removeEventListener( "touchstart", onTouchStartEvent, false ); 1862 scope.domElement.removeEventListener( "touchmove", onTouchMoveEvent, false ); 1863 1864 scope.enabled = false; 1865 1866 }; 1867 1868 this.update = function( ignoreUpdate ) { 1869 1870 if ( scope.enabled === false ) return; 1871 1872 var alpha = scope.deviceOrientation.alpha ? THREE.Math.degToRad( scope.deviceOrientation.alpha ) + this.alphaOffsetAngle : 0; // Z 1873 var beta = scope.deviceOrientation.beta ? THREE.Math.degToRad( scope.deviceOrientation.beta ) : 0; // X' 1874 var gamma = scope.deviceOrientation.gamma ? THREE.Math.degToRad( scope.deviceOrientation.gamma ) : 0; // Y'' 1875 var orient = scope.screenOrientation ? THREE.Math.degToRad( scope.screenOrientation ) : 0; // O 1876 1877 setCameraQuaternion( scope.camera.quaternion, alpha, beta, gamma, orient ); 1878 this.alpha = alpha; 1879 1880 ignoreUpdate !== true && this.dispatchEvent( changeEvent ); 1881 1882 }; 1883 1884 this.updateAlphaOffsetAngle = function( angle ) { 1885 1886 this.alphaOffsetAngle = angle; 1887 this.update(); 1888 1889 }; 1890 1891 this.dispose = function() { 1892 1893 this.disconnect(); 1894 1895 }; 1896 1897 this.connect(); 1898 1899}; 1900 1901THREE.DeviceOrientationControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 1902THREE.DeviceOrientationControls.prototype.constructor = THREE.DeviceOrientationControls;; /** The Bend modifier lets you bend the current selection up to 90 degrees about a single axis, 1903 * producing a uniform bend in an object's geometry. 1904 * You can control the angle and direction of the bend on any of three axes. 1905 * The geometry has to have rather large number of polygons! 1906 * options: 1907 * direction - deformation direction (in local coordinates!). 1908 * axis - deformation axis (in local coordinates!). Vector of direction and axis are perpendicular. 1909 * angle - deformation angle. 1910 * @author Vildanov Almaz / [email protected] 1911 * The algorithm of a bend is based on the chain line cosh: y = 1/b * cosh(b*x) - 1/b. It can be used only in three.js. 1912 */ 1913 1914THREE.BendModifier = function () { 1915 1916}; 1917 1918THREE.BendModifier.prototype = { 1919 1920 constructor: THREE.BendModifier, 1921 1922 set: function ( direction, axis, angle ) { 1923 this.direction = new THREE.Vector3(); this.direction.copy( direction ); 1924 this.axis = new THREE.Vector3(); this.axis.copy( axis ); 1925 this.angle = angle; 1926 return this 1927 }, 1928 1929 _sign: function (a) { 1930 return 0 > a ? -1 : 0 < a ? 1 : 0 1931 }, 1932 1933 _cosh: function( x ) { 1934 return ( Math.exp( x ) + Math.exp( -x ) ) / 2; 1935 }, 1936 1937 _sinhInverse: function( x ) { 1938 return Math.log( Math.abs( x ) + Math.sqrt( x * x + 1 ) ); 1939 }, 1940 1941 modify: function ( geometry ) { 1942 1943 var thirdAxis = new THREE.Vector3(); thirdAxis.crossVectors( this.direction, this.axis ); 1944 1945 // P - matrices of the change-of-coordinates 1946 var P = new THREE.Matrix4(); 1947 P.set ( thirdAxis.x, thirdAxis.y, thirdAxis.z, 0, 1948 this.direction.x, this.direction.y, this.direction.z, 0, 1949 this.axis.x, this.axis.y, this.axis.z, 0, 1950 0, 0, 0, 1 ).transpose(); 1951 1952 var InverseP = new THREE.Matrix4().getInverse( P ); 1953 var newVertices = []; var oldVertices = []; var anglesBetweenOldandNewVertices = []; 1954 1955 var meshGeometryBoundingBoxMaxx = 0; var meshGeometryBoundingBoxMinx = 0; 1956 var meshGeometryBoundingBoxMaxy = 0; var meshGeometryBoundingBoxMiny = 0; 1957 1958 for (var i = 0; i < geometry.vertices.length; i++) { 1959 1960 newVertices[i] = new THREE.Vector3(); newVertices[i].copy( geometry.vertices[i] ).applyMatrix4( InverseP ); 1961 if ( newVertices[i].x > meshGeometryBoundingBoxMaxx ) { meshGeometryBoundingBoxMaxx = newVertices[i].x; } 1962 if ( newVertices[i].x < meshGeometryBoundingBoxMinx ) { meshGeometryBoundingBoxMinx = newVertices[i].x; } 1963 if ( newVertices[i].y > meshGeometryBoundingBoxMaxy ) { meshGeometryBoundingBoxMaxy = newVertices[i].y; } 1964 if ( newVertices[i].y < meshGeometryBoundingBoxMiny ) { meshGeometryBoundingBoxMiny = newVertices[i].y; } 1965 1966 } 1967 1968 var meshWidthold = meshGeometryBoundingBoxMaxx - meshGeometryBoundingBoxMinx; 1969 var meshDepth = meshGeometryBoundingBoxMaxy - meshGeometryBoundingBoxMiny; 1970 var ParamB = 2 * this._sinhInverse( Math.tan( this.angle ) ) / meshWidthold; 1971 var oldMiddlex = (meshGeometryBoundingBoxMaxx + meshGeometryBoundingBoxMinx) / 2; 1972 var oldMiddley = (meshGeometryBoundingBoxMaxy + meshGeometryBoundingBoxMiny) / 2; 1973 1974 for (var i = 0; i < geometry.vertices.length; i++ ) { 1975 1976 oldVertices[i] = new THREE.Vector3(); oldVertices[i].copy( newVertices[i] ); 1977 newVertices[i].x = this._sign( newVertices[i].x - oldMiddlex ) * 1 / ParamB * this._sinhInverse( ( newVertices[i].x - oldMiddlex ) * ParamB ); 1978 1979 } 1980 1981 var meshWidth = 2 / ParamB * this._sinhInverse( meshWidthold / 2 * ParamB ); 1982 1983 var NewParamB = 2 * this._sinhInverse( Math.tan( this.angle ) ) / meshWidth; 1984 1985 var rightEdgePos = new THREE.Vector3( meshWidth / 2, -meshDepth / 2, 0 ); 1986 rightEdgePos.y = 1 / NewParamB * this._cosh( NewParamB * rightEdgePos.x ) - 1 / NewParamB - meshDepth / 2; 1987 1988 var bendCenter = new THREE.Vector3( 0, rightEdgePos.y + rightEdgePos.x / Math.tan( this.angle ), 0 ); 1989 1990 for ( var i = 0; i < geometry.vertices.length; i++ ) { 1991 1992 var x0 = this._sign( oldVertices[i].x - oldMiddlex ) * 1 / ParamB * this._sinhInverse( ( oldVertices[i].x - oldMiddlex ) * ParamB ); 1993 var y0 = 1 / NewParamB * this._cosh( NewParamB * x0 ) - 1 / NewParamB; 1994 1995 var k = new THREE.Vector3( bendCenter.x - x0, bendCenter.y - ( y0 - meshDepth / 2 ), bendCenter.z ).normalize(); 1996 1997 var Q = new THREE.Vector3(); 1998 Q.addVectors( new THREE.Vector3( x0, y0 - meshDepth / 2, oldVertices[i].z ), k.multiplyScalar( oldVertices[i].y + meshDepth / 2 ) ); 1999 newVertices[i].x = Q.x; newVertices[i].y = Q.y; 2000 2001 } 2002 2003 var middle = oldMiddlex * meshWidth / meshWidthold; 2004 2005 for ( var i = 0; i < geometry.vertices.length; i++ ) { 2006 2007 var O = new THREE.Vector3( oldMiddlex, oldMiddley, oldVertices[i].z ); 2008 var p = new THREE.Vector3(); p.subVectors( oldVertices[i], O ); 2009 var q = new THREE.Vector3(); q.subVectors( newVertices[i], O ); 2010 2011 anglesBetweenOldandNewVertices[i] = Math.acos( 1 / this._cosh( ParamB * newVertices[i].x ) ) * this._sign( newVertices[i].x ); 2012 2013 newVertices[i].x = newVertices[i].x + middle; 2014 geometry.vertices[i].copy( newVertices[i].applyMatrix4( P ) ); 2015 2016 } 2017 2018 geometry.computeFaceNormals(); 2019 geometry.verticesNeedUpdate = true; 2020 geometry.normalsNeedUpdate = true; 2021 2022 // compute Vertex Normals 2023 var fvNames = [ 'a', 'b', 'c', 'd' ]; 2024 2025 for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) { 2026 2027 var face = geometry.faces[ f ]; 2028 if ( face.vertexNormals === undefined ) { 2029 continue; 2030 } 2031 for ( var v = 0, vl = face.vertexNormals.length; v < vl; v ++ ) { 2032 2033 var angle = anglesBetweenOldandNewVertices[ face[ fvNames[ v ] ] ]; 2034 var x = this.axis.x, 2035 y = this.axis.y, 2036 z = this.axis.z; 2037 2038 var rotateMatrix = new THREE.Matrix3(); 2039 rotateMatrix.set ( Math.cos(angle) + (1-Math.cos(angle))*x*x, (1-Math.cos(angle))*x*y - Math.sin(angle)*z, (1-Math.cos(angle))*x*z + Math.sin(angle)*y, 2040 (1-Math.cos(angle))*y*x + Math.sin(angle)*z, Math.cos(angle) + (1-Math.cos(angle))*y*y, (1-Math.cos(angle))*y*z - Math.sin(angle)*x, 2041 (1-Math.cos(angle))*z*x - Math.sin(angle)*y, (1-Math.cos(angle))*z*y + Math.sin(angle)*x, Math.cos(angle) + (1-Math.cos(angle))*z*z ); 2042 2043 face.vertexNormals[ v ].applyMatrix3( rotateMatrix ); 2044 2045 } 2046 2047 } 2048 // end compute Vertex Normals 2049 2050 return this 2051 } 2052};/** 2053 * @author mrdoob / http://mrdoob.com/ 2054 */ 2055 2056THREE.CardboardEffect = function ( renderer ) { 2057
2058 var _camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); 2059 2060 var _scene = new THREE.Scene(); 2061 2062 var _stereo = new THREE.StereoCamera(); 2063 _stereo.aspect = 0.5; 2064 2065 var _params = { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat }; 2066 2067 var _renderTarget = new THREE.WebGLRenderTarget( 512, 512, _params ); 2068 _renderTarget.scissorTest = true; 2069 _renderTarget.texture.generateMipmaps = false; 2070 2071 // Distortion Mesh ported from: 2072 // https://github.com/borismus/webvr-boilerplate/blob/master/src/distortion/barrel-distortion-fragment.js 2073 2074 var distortion = new THREE.Vector2( 0.441, 0.156 ); 2075 2076 var geometry = new THREE.PlaneBufferGeometry( 1, 1, 10, 20 ).removeAttribute( 'normal' ).toNonIndexed(); 2077 2078 var positions = geometry.attributes.position.array; 2079 var uvs = geometry.attributes.uv.array; 2080 2081 // duplicate 2082 geometry.attributes.position.count *= 2; 2083 geometry.attributes.uv.count *= 2; 2084 2085 var positions2 = new Float32Array( positions.length * 2 ); 2086 positions2.set( positions ); 2087 positions2.set( positions, positions.length ); 2088 2089 var uvs2 = new Float32Array( uvs.length * 2 ); 2090 uvs2.set( uvs ); 2091 uvs2.set( uvs, uvs.length ); 2092 2093 var vector = new THREE.Vector2(); 2094 var length = positions.length / 3; 2095 2096 for ( var i = 0, l = positions2.length / 3; i < l; i ++ ) { 2097 2098 vector.x = positions2[ i * 3 + 0 ]; 2099 vector.y = positions2[ i * 3 + 1 ]; 2100 2101 var dot = vector.dot( vector ); 2102 var scalar = 1.5 + ( distortion.x + distortion.y * dot ) * dot; 2103 2104 var offset = i < length ? 0 : 1; 2105 2106 positions2[ i * 3 + 0 ] = ( vector.x / scalar ) * 1.5 - 0.5 + offset; 2107 positions2[ i * 3 + 1 ] = ( vector.y / scalar ) * 3.0; 2108 2109 uvs2[ i * 2 ] = ( uvs2[ i * 2 ] + offset ) * 0.5; 2110 2111 } 2112 2113 geometry.attributes.position.array = positions2; 2114 geometry.attributes.uv.array = uvs2; 2115 2116 // 2117 2118 // var material = new THREE.MeshBasicMaterial( { wireframe: true } ); 2119 var material = new THREE.MeshBasicMaterial( { map: _renderTarget.texture } ); 2120 var mesh = new THREE.Mesh( geometry, material ); 2121 _scene.add( mesh ); 2122 2123 // 2124 2125 this.setSize = function ( width, height ) { 2126 2127 renderer.setSize( width, height ); 2128 2129 var pixelRatio = renderer.getPixelRatio(); 2130 2131 _renderTarget.setSize( width * pixelRatio, height * pixelRatio ); 2132 2133 }; 2134 2135 this.render = function ( scene, camera ) { 2136 2137 scene.updateMatrixWorld(); 2138 2139 if ( camera.parent === null ) camera.updateMatrixWorld(); 2140 2141 _stereo.update( camera ); 2142 2143 var width = _renderTarget.width / 2; 2144 var height = _renderTarget.height; 2145 2146 _renderTarget.scissor.set( 0, 0, width, height ); 2147 _renderTarget.viewport.set( 0, 0, width, height ); 2148 renderer.render( scene, _stereo.cameraL, _renderTarget ); 2149 2150 _renderTarget.scissor.set( width, 0, width, height ); 2151 _renderTarget.viewport.set( width, 0, width, height ); 2152 renderer.render( scene, _stereo.cameraR, _renderTarget ); 2153 2154 renderer.render( _scene, _camera ); 2155 2156 }; 2157 2158};;/** 2159 * @author alteredq / http://alteredqualia.com/ 2160 * @authod mrdoob / http://mrdoob.com/ 2161 * @authod arodic / http://aleksandarrodic.com/ 2162 * @authod fonserbc / http://fonserbc.github.io/ 2163*/ 2164 2165THREE.StereoEffect = function ( renderer ) { 2166 2167 var _stereo = new THREE.StereoCamera(); 2168 _stereo.aspect = 0.5; 2169 2170 this.setEyeSeparation = function ( eyeSep ) { 2171 2172 _stereo.eyeSep = eyeSep; 2173 2174 }; 2175 2176 this.setSize = function ( width, height ) { 2177 2178 renderer.setSize( width, height ); 2179 2180 }; 2181 2182 this.render = function ( scene, camera ) { 2183 2184 scene.updateMatrixWorld(); 2185 2186 if ( camera.parent === null ) camera.updateMatrixWorld(); 2187 2188 _stereo.update( camera ); 2189 2190 var size = renderer.getSize(); 2191 2192 if ( renderer.autoClear ) renderer.clear(); 2193 renderer.setScissorTest( true ); 2194 2195 renderer.setScissor( 0, 0, size.width / 2, size.height ); 2196 renderer.setViewport( 0, 0, size.width / 2, size.height ); 2197 renderer.render( scene, _stereo.cameraL ); 2198 2199 renderer.setScissor( size.width / 2, 0, size.width / 2, size.height ); 2200 renderer.setViewport( size.width / 2, 0, size.width / 2, size.height ); 2201 renderer.render( scene, _stereo.cameraR ); 2202 2203 renderer.setScissorTest( false ); 2204 2205 }; 2206 2207}; 2208;var GSVPANO = GSVPANO || {}; 2209GSVPANO.PanoLoader = function (parameters) { 2210 2211 'use strict'; 2212 2213 var _parameters = parameters || {}, 2214 _location, 2215 _zoom, 2216 _panoId, 2217 _panoClient = new google.maps.StreetViewService(), 2218 _count = 0, 2219 _total = 0, 2220 _canvas = [], 2221 _ctx = [], 2222 _wc = 0, 2223 _hc = 0, 2224 result = null, 2225 rotation = 0, 2226 copyright = '', 2227 onSizeChange = null, 2228 onPanoramaLoad = null; 2229 2230 var levelsW = [ 1, 2, 4, 7, 13, 26 ], 2231 levelsH = [ 1, 1, 2, 4, 7, 13 ]; 2232 2233 var widths = [ 416, 832, 1664, 3328, 6656, 13312 ], 2234 heights = [ 416, 416, 832, 1664, 3328, 6656 ]; 2235 2236 var gl = null; 2237 try{ 2238 var canvas = document.createElement( 'canvas' ); 2239 gl = canvas.getContext('experimental-webgl'); 2240 if(gl == null){ 2241 gl = canvas.getContext('webgl'); 2242 } 2243 } 2244 catch(error){} 2245 2246 var maxW = 1024, 2247 maxH = 1024; 2248 2249 if( gl ) { 2250 var maxTexSize = Math.max( gl.getParameter(gl.MAX_TEXTURE_SIZE), 6656 ); 2251 //alert( 'MAX_TEXTURE_SIZE ' + maxTexSize ); 2252 maxW = maxH = maxTexSize; 2253 } 2254 2255 this.setProgress = function (loaded, total) { 2256 2257 if (this.onProgress) { 2258 this.onProgress({loaded: loaded, total: total}); 2259 } 2260 2261 }; 2262 2263 this.throwError = function (message) { 2264 2265 if (this.onError) { 2266 this.onError(message); 2267 } else { 2268 console.error(message); 2269 } 2270 2271 }; 2272 2273 this.adaptTextureToZoom = function () { 2274 2275 var w = levelsW[ _zoom ] * 416, 2276 h = levelsH[ _zoom ] * 416; 2277 2278 w = widths [ _zoom ]; 2279 h = heights[ _zoom ]; 2280 2281 _wc = Math.ceil( w / maxW ); 2282 _hc = Math.ceil( h / maxH ); 2283 2284 _canvas = []; _ctx = []; 2285 2286 var ptr = 0; 2287 for( var y = 0; y < _hc; y++ ) { 2288 for( var x = 0; x < _wc; x++ ) { 2289 var c = document.createElement('canvas'); 2290 if( x < ( _wc - 1 ) ) c.width = maxW; else c.width = w - ( maxW * x ); 2291 if( y < ( _hc - 1 ) ) c.height = maxH; else c.height = h - ( maxH * y ); 2292 _canvas.push( c ); 2293 _ctx.push( c.getContext('2d') ); 2294 ptr++; 2295 } 2296 } 2297 2298 }; 2299 2300 this.composeFromTile = function (x, y, texture) { 2301 2302 x *= 512; 2303 y *= 512; 2304 var px = Math.floor( x / maxW ), py = Math.floor( y / maxH ); 2305 2306 x -= px * maxW; 2307 y -= py * maxH; 2308 2309 _ctx[ py * _wc + px ].drawImage(texture, 0, 0, texture.width, texture.height, x, y, 512, 512 ); 2310 this.progress(); 2311 2312 }; 2313 2314 this.progress = function() { 2315 2316 _count++; 2317 2318 var p = Math.round(_count * 100 / _total); 2319 this.setProgress(_count, _total); 2320 2321 if (_count === _total) { 2322 this.canvas = _canvas; 2323 this.panoId = _panoId; 2324 this.zoom = _zoom; 2325 if (this.onPanoramaLoad) { 2326 this.onPanoramaLoad(_canvas[0]); 2327 } 2328 } 2329 } 2330 2331 this.loadFromId = function( id ) { 2332 2333 _panoId = id; 2334 this.composePanorama(); 2335 2336 }; 2337 2338 this.composePanorama = function () { 2339 2340 this.setProgress(0, 1); 2341 2342 var w = levelsW[ _zoom ], 2343 h = levelsH[ _zoom ], 2344 self = this, 2345 url, 2346 x, 2347 y; 2348 2349 _count = 0; 2350 _total = w * h; 2351 2352 var self = this; 2353 for( var y = 0; y < h; y++ ) { 2354 for( var x = 0; x < w; x++ ) { 2355 var url = 'https://geo0.ggpht.com/cbk?cb_client=maps_sv.tactile&authuser=0&hl=en&output=tile&zoom=' + _zoom + '&x=' + x + '&y=' + y + '&panoid=' + _panoId + '&nbt&fover=2'; 2356 ( function( x, y ) { 2357 if( _parameters.useWebGL ) { 2358 var texture = THREE.ImageUtils.loadTexture( url, null, function() { 2359 self.composeFromTile( x, y, texture ); 2360 } ); 2361 } else { 2362 var img = new Image(); 2363 img.addEventListener( 'load', function() { 2364 self.composeFromTile( x, y, this ); 2365 } ); 2366 img.crossOrigin = ''; 2367 img.src = url; 2368 } 2369 } )( x, y ); 2370 } 2371 } 2372 2373 }; 2374 2375 this.load = function ( panoid ) { 2376 2377 this.loadPano( panoid ); 2378 2379 }; 2380 2381 this.loadPano = function( id ) { 2382 2383 var self = this; 2384 _panoClient.getPanoramaById( id, function (result, status) { 2385 if (status === google.maps.StreetViewStatus.OK) { 2386 self.result = result; 2387 if( self.onPanoramaData ) self.onPanoramaData( result ); 2388 //var h = google.maps.geometry.spherical.computeHeading(location, result.location.latLng); 2389 //rotation = (result.tiles.centerHeading - h) * Math.PI / 180.0; 2390 copyright = result.copyright; 2391 self.copyright = result.copyright; 2392 _panoId = result.location.pano; 2393 self.location = location; 2394 self.composePanorama(); 2395 } else { 2396 if( self.onNoPanoramaData ) self.onNoPanoramaData( status ); 2397 self.throwError('Could not retrieve panorama for the following reason: ' + status); 2398 } 2399 }); 2400 2401 }; 2402 2403 this.setZoom = function( z ) { 2404 _zoom = z; 2405 this.adaptTextureToZoom(); 2406 }; 2407 2408 this.setZoom( _parameters.zoom || 1 ); 2409 2410};;(function(){ 2411 2412 'use strict'; 2413 2414 /** 2415 * Data Image Source 2416 * @type {String} 2417 */ 2418 PANOLENS.DataImageSource = 'https://pchen66.github.io/Panolens/asset/icon/'; 2419 2420 /** 2421 * Data Image 2422 * @memberOf PANOLENS 2423 * @enum {string} 2424 */ 2425 PANOLENS.DataImage = { 2426 Info: PANOLENS.DataImageSource + 'information.png', 2427 Arrow: PANOLENS.DataImageSource + 'arrow-up.png', 2428 FullscreenEnter: PANOLENS.DataImageSource + 'fullscreen.png', 2429 FullscreenLeave: PANOLENS.DataImageSource + 'fullscreen-exit.png', 2430 VideoPlay: PANOLENS.DataImageSource + 'video-play.png', 2431 VideoPause: PANOLENS.DataImageSource + 'pause.png',
2432 WhiteTile: PANOLENS.DataImageSource + 'tiles.png', 2433 ReticleIdle: PANOLENS.DataImageSource + 'reticle-idle.png', 2434 Setting: PANOLENS.DataImageSource + 'setting.png', 2435 ChevronRight: PANOLENS.DataImageSource + 'chevron-right.png', 2436 Check: PANOLENS.DataImageSource + 'check.png', 2437 ViewIndicator: PANOLENS.DataImageSource + 'view-indicator.svg', 2438 ReticleDwell: PANOLENS.DataImageSource + 'reticle-animation.png' 2439 }; 2440 2441})();;(function(){ 2442 2443 'use strict'; 2444 2445 /** 2446 * Control Index Enum 2447 * @memberOf PANOLENS 2448 * @enum {number} 2449 */ 2450 2451 PANOLENS.Controls = { 2452 2453 ORBIT: 0, 2454 2455 DEVICEORIENTATION: 1 2456 2457 }; 2458 2459 /** 2460 * Effect Mode Enum 2461 * @memberOf PANOLENS 2462 * @enum {number} 2463 */ 2464 PANOLENS.Modes = { 2465 2466 /** Unknown */ 2467 UNKNOWN: 0, 2468 2469 /** Normal */ 2470 NORMAL: 1, 2471 2472 /** Google Cardboard*/ 2473 CARDBOARD: 2, 2474 2475 /** Stereoscopic **/ 2476 STEREO: 3 2477 2478 }; 2479 2480})();;(function(){ 2481 2482 'use strict'; 2483 2484 /** 2485 * Utility 2486 * @namespace PANOLENS.Utils 2487 * @memberOf PANOLENS 2488 * @type {object} 2489 */ 2490 PANOLENS.Utils = {}; 2491 2492 PANOLENS.Utils.checkTouchSupported = function () { 2493 2494 return window ? 'ontouchstart' in window || window.navigator.msMaxTouchPoints : false; 2495 2496 }; 2497 2498})();;(function(){ 2499 2500 'use strict'; 2501 2502 /** 2503 * Image loader with progress based on {@link https://github.com/mrdoob/three.js/blob/master/src/loaders/ImageLoader.js} 2504 * @memberOf PANOLENS.Utils 2505 * @namespace 2506 */ 2507 PANOLENS.Utils.ImageLoader = {}; 2508 2509 /** 2510 * Load an image with XMLHttpRequest to provide progress checking 2511 * @param {string} url - An image url 2512 * @param {function} onLoad - On load callback 2513 * @param {function} onProgress - In progress callback 2514 * @param {function} onError - On error callback 2515 * @return {HTMLImageElement} - DOM image element 2516 */ 2517 2518 PANOLENS.Utils.ImageLoader.load = function ( url, onLoad, onProgress, onError ) { 2519 2520 var cached, request, arrayBufferView, blob, urlCreator, image, reference; 2521 2522 // Reference key 2523 for ( var iconName in PANOLENS.DataImage ) { 2524 2525 if ( PANOLENS.DataImage.hasOwnProperty( iconName ) 2526 && url === PANOLENS.DataImage[ iconName ] ) { 2527 2528 reference = iconName; 2529 2530 } 2531 2532 } 2533 2534 // Cached 2535 cached = THREE.Cache.get( reference ? reference : url ); 2536 2537 if ( cached !== undefined ) { 2538 2539 if ( onLoad ) { 2540 2541 setTimeout( function () { 2542 2543 if ( onProgress ) { 2544 2545 onProgress( { loaded: 1, total: 1 } ); 2546 2547 } 2548 2549 onLoad( cached ); 2550 2551 }, 0 ); 2552 2553 } 2554 2555 return cached; 2556 2557 } 2558 2559 // Construct a new XMLHttpRequest 2560 urlCreator = window.URL || window.webkitURL; 2561 image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); 2562 2563 // Add to cache 2564 THREE.Cache.add( reference ? reference : url, image ); 2565 2566 function onImageLoaded () { 2567 2568 urlCreator.revokeObjectURL( image.src ); 2569 onLoad && onLoad( image ); 2570 2571 } 2572 2573 if ( url.indexOf( 'data:' ) === 0 ) { 2574 2575 image.addEventListener( 'load', onImageLoaded, false ); 2576 image.src = url; 2577 return image; 2578 } 2579 2580 image.crossOrigin = this.crossOrigin !== undefined ? this.crossOrigin : ''; 2581 2582 request = new XMLHttpRequest(); 2583 request.open( 'GET', url, true ); 2584 request.responseType = 'arraybuffer'; 2585 request.onprogress = function ( event ) { 2586 2587 if ( event.lengthComputable ) { 2588 2589 onProgress && onProgress( { loaded: event.loaded, total: event.total } ); 2590 2591 } 2592 2593 }; 2594 request.onloadend = function( event ) { 2595 2596 arrayBufferView = new Uint8Array( this.response ); 2597 blob = new Blob( [ arrayBufferView ] ); 2598 2599 image.addEventListener( 'load', onImageLoaded, false ); 2600 image.src = urlCreator.createObjectURL( blob ); 2601 2602 }; 2603 2604 request.send(null); 2605 2606 }; 2607 2608 // Enable cache 2609 THREE.Cache.enabled = true; 2610 2611})();;(function(){ 2612 2613 'use strict'; 2614 2615 /** 2616 * Texture loader based on {@link https://github.com/mrdoob/three.js/blob/master/src/loaders/TextureLoader.js} 2617 * @memberOf PANOLENS.Utils 2618 * @namespace 2619 */ 2620 PANOLENS.Utils.TextureLoader = {}; 2621 2622 /** 2623 * Load image texture 2624 * @param {string} url - An image url 2625 * @param {function} onLoad - On load callback 2626 * @param {function} onProgress - In progress callback 2627 * @param {function} onError - On error callback 2628 * @return {THREE.Texture} - Image texture 2629 */ 2630 PANOLENS.Utils.TextureLoader.load = function ( url, onLoad, onProgress, onError ) { 2631 2632 var texture = new THREE.Texture(); 2633 2634 PANOLENS.Utils.ImageLoader.load( url, function ( image ) { 2635 2636 texture.image = image; 2637 texture.needsUpdate = true; 2638 2639 onLoad && onLoad( texture ); 2640 2641 }, onProgress, onError ); 2642 2643 return texture; 2644 2645 }; 2646 2647})();;(function(){ 2648 2649 'use strict'; 2650 2651 /**
2652 * Cube Texture Loader based on {@link https://github.com/mrdoob/three.js/blob/master/src/loaders/CubeTextureLoader.js} 2653 * @memberOf PANOLENS.Utils 2654 * @namespace 2655 */ 2656 PANOLENS.Utils.CubeTextureLoader = {}; 2657 2658 /** 2659 * Load 6 images as a cube texture 2660 * @param {array} urls - Array with 6 image urls 2661 * @param {function} onLoad - On load callback 2662 * @param {function} onProgress - In progress callback 2663 * @param {function} onError - On error callback 2664 * @return {THREE.CubeTexture} - Cube texture 2665 */ 2666 PANOLENS.Utils.CubeTextureLoader.load = function ( urls, onLoad, onProgress, onError ) { 2667 2668 var texture, loaded, progress, all, loadings; 2669 2670 texture = new THREE.CubeTexture( [] ); 2671 2672 loaded = 0; 2673 progress = {}; 2674 all = {}; 2675 2676 urls.map( function ( url, index ) { 2677 2678 PANOLENS.Utils.ImageLoader.load( url, function ( image ) { 2679 2680 texture.images[ index ] = image; 2681 2682 loaded++; 2683 2684 if ( loaded === 6 ) { 2685 2686 texture.needsUpdate = true; 2687 2688 onLoad && onLoad( texture ); 2689 2690 } 2691 2692 }, function ( event ) { 2693 2694 progress[ index ] = { loaded: event.loaded, total: event.total }; 2695 2696 all.loaded = 0; 2697 all.total = 0; 2698 loadings = 0; 2699 2700 for ( var i in progress ) { 2701 2702 loadings++; 2703 all.loaded += progress[ i ].loaded; 2704 all.total += progress[ i ].total; 2705 2706 } 2707 2708 if ( loadings < 6 ) { 2709 2710 all.total = all.total / loadings * 6; 2711 2712 } 2713 2714 onProgress && onProgress( all ); 2715 2716 }, onError ); 2717 2718 } ); 2719 2720 return texture; 2721 2722 }; 2723 2724})();;/** 2725 * Stereographic projection shader 2726 * based on http://notlion.github.io/streetview-stereographic 2727 * @author pchen66 2728 */ 2729 2730PANOLENS.StereographicShader = { 2731 2732 uniforms: { 2733 2734 "tDiffuse": { value: new THREE.Texture() }, 2735 "resolution": { value: 1.0 }, 2736 "transform": { value: new THREE.Matrix4() }, 2737 "zoom": { value: 1.0 } 2738 2739 }, 2740 2741 vertexShader: [ 2742 2743 "varying vec2 vUv;", 2744 2745 "void main() {", 2746 2747 "vUv = uv;", 2748 "gl_Position = vec4( position, 1.0 );", 2749 2750 "}" 2751 2752 ].join( "\n" ), 2753 2754 fragmentShader: [ 2755 2756 "uniform sampler2D tDiffuse;", 2757 "uniform float resolution;", 2758 "uniform mat4 transform;", 2759 "uniform float zoom;", 2760 2761 "varying vec2 vUv;", 2762 2763 "const float PI = 3.141592653589793;", 2764 2765 "void main(){", 2766 2767 "vec2 position = -1.0 + 2.0 * vUv;", 2768 2769 "position *= vec2( zoom * resolution, zoom * 0.5 );", 2770 2771 "float x2y2 = position.x * position.x + position.y * position.y;", 2772 "vec3 sphere_pnt = vec3( 2. * position, x2y2 - 1. ) / ( x2y2 + 1. );", 2773 2774 "sphere_pnt = vec3( transform * vec4( sphere_pnt, 1.0 ) );", 2775 2776 "vec2 sampleUV = vec2(", 2777 "(atan(sphere_pnt.y, sphere_pnt.x) / PI + 1.0) * 0.5,", 2778 "(asin(sphere_pnt.z) / PI + 0.5)", 2779 ");", 2780 2781 "gl_FragColor = texture2D( tDiffuse, sampleUV );", 2782 2783 "}" 2784 2785 ].join( "\n" ) 2786 2787};;( function () { 2788 2789 'use strict'; 2790 2791 /** 2792 * Skeleton panorama derived from THREE.Mesh 2793 * @constructor 2794 * @param {THREE.Geometry} geometry - The geometry for this panorama 2795 * @param {THREE.Material} material - The material for this panorama 2796 */ 2797 PANOLENS.Panorama = function ( geometry, material ) { 2798 2799 THREE.Mesh.call( this ); 2800 2801 this.type = 'panorama'; 2802 2803 this.ImageQualityLow = 1; 2804 this.ImageQualityFair = 2; 2805 this.ImageQualityMedium = 3; 2806 this.ImageQualityHigh = 4; 2807 this.ImageQualitySuperHigh = 5; 2808 2809 this.animationDuration = 1000; 2810 2811 this.defaultInfospotSize = 350; 2812 2813 this.container = undefined; 2814 2815 this.loaded = false; 2816 2817 this.linkedSpots = []; 2818 2819 this.isInfospotVisible = false; 2820 2821 this.linkingImageURL = undefined; 2822 this.linkingImageScale = undefined; 2823 2824 this.geometry = geometry; 2825 2826 this.material = material; 2827 this.material.side = THREE.DoubleSide; 2828 this.material.visible = false; 2829 2830 this.scale.x *= -1; 2831 2832 this.infospotAnimation = new TWEEN.Tween( this ).to( {}, this.animationDuration / 2 ); 2833 2834 this.addEventListener( 'load', this.fadeIn.bind( this ) ); 2835 this.addEventListener( 'panolens-container', this.setContainer.bind( this ) ); 2836 this.addEventListener( 'click', this.onClick.bind( this ) ); 2837 2838 this.setupTransitions(); 2839 2840 } 2841 2842 PANOLENS.Panorama.prototype = Object.create( THREE.Mesh.prototype ); 2843 2844 PANOLENS.Panorama.prototype.constructor = PANOLENS.Panorama; 2845 2846 /** 2847 * Adding an object 2848 * To counter the scale.x = -1, it will automatically add an 2849 * empty object with inverted scale on x 2850 * @param {THREE.Object3D} object - The object to be added 2851 */ 2852 PANOLENS.Panorama.prototype.add = function ( object ) { 2853 2854 var scope, invertedObject; 2855 2856 scope = this; 2857 2858 if ( arguments.length > 1 ) { 2859 2860 for ( var i = 0; i < arguments.length; i ++ ) { 2861 2862 this.add( arguments[ i ] ); 2863 2864 } 2865 2866 return this; 2867 2868 } 2869 2870 // In case of infospots 2871 if ( object instanceof PANOLENS.Infospot ) { 2872 2873 invertedObject = object; 2874 2875 if ( object.dispatchEvent ) { 2876 2877 this.container && object.dispatchEvent( { type: 'panolens-container', container: this.container } ); 2878 2879 object.dispatchEvent( { type: 'panolens-infospot-focus', method: function ( vector, duration, easing ) { 2880 2881 /** 2882 * Infospot focus handler event 2883 * @type {object} 2884 * @event PANOLENS.Panorama#panolens-viewer-handler 2885 * @property {string} method - Viewer function name 2886 * @property {*} data - The argument to be passed into the method 2887 */ 2888 scope.dispatchEvent( { type : 'panolens-viewer-handler', method: 'tweenControlCenter', data: [ vector, duration, easing ] } ); 2889 2890 2891 } } ); 2892 } 2893 2894 } else { 2895 2896 // Counter scale.x = -1 effect 2897 invertedObject = new THREE.Object3D();
2898 invertedObject.scale.x = -1; 2899 invertedObject.scalePlaceHolder = true; 2900 invertedObject.add( object ); 2901 2902 } 2903 2904 THREE.Object3D.prototype.add.call( this, invertedObject ); 2905 2906 }; 2907 2908 PANOLENS.Panorama.prototype.load = function () { 2909 2910 this.onLoad(); 2911 2912 }; 2913 2914 /** 2915 * Click event handler 2916 * @param {object} event - Click event 2917 * @fires PANOLENS.Infospot#dismiss 2918 */ 2919 PANOLENS.Panorama.prototype.onClick = function ( event ) { 2920 2921 if ( event.intersects && event.intersects.length === 0 ) { 2922 2923 this.traverse( function ( object ) { 2924 2925 /** 2926 * Dimiss event 2927 * @type {object} 2928 * @event PANOLENS.Infospot#dismiss 2929 */ 2930 object.dispatchEvent( { type: 'dismiss' } ); 2931 2932 } ); 2933 2934 } 2935 2936 }; 2937 2938 /** 2939 * Set container of this panorama 2940 * @param {HTMLElement|object} data - Data with container information 2941 * @fires PANOLENS.Infospot#panolens-container 2942 */ 2943 PANOLENS.Panorama.prototype.setContainer = function ( data ) { 2944 2945 var container; 2946 2947 if ( data instanceof HTMLElement ) { 2948 2949 container = data; 2950 2951 } else if ( data && data.container ) { 2952 2953 container = data.container; 2954 2955 } 2956 2957 if ( container ) { 2958 2959 this.children.forEach( function ( child ) { 2960 2961 if ( child instanceof PANOLENS.Infospot && child.dispatchEvent ) { 2962 2963 /** 2964 * Set container event 2965 * @type {object} 2966 * @event PANOLENS.Infospot#panolens-container 2967 * @property {HTMLElement} container - The container of this panorama 2968 */ 2969 child.dispatchEvent( { type: 'panolens-container', container: container } ); 2970 2971 } 2972 2973 } ); 2974 2975 this.container = container; 2976 2977 } 2978 2979 2980 }; 2981 2982 /** 2983 * This will be called when panorama is loaded 2984 * @fires PANOLENS.Panorama#load 2985 */ 2986 PANOLENS.Panorama.prototype.onLoad = function () { 2987 2988 this.loaded = true; 2989 2990 /** 2991 * Load panorama event 2992 * @type {object} 2993 * @event PANOLENS.Panorama#load 2994 */ 2995 this.dispatchEvent( { type: 'load' } ); 2996 2997 }; 2998 2999 /** 3000 * This will be called when panorama is in progress 3001 * @fires PANOLENS.Panorama#progress 3002 */ 3003 PANOLENS.Panorama.prototype.onProgress = function ( progress ) { 3004 3005 /** 3006 * Loading panorama progress event 3007 * @type {object} 3008 * @event PANOLENS.Panorama#progress 3009 * @property {object} progress - The progress object containing loaded and total amount 3010 */ 3011 this.dispatchEvent( { type: 'progress', progress: progress } ); 3012 3013 }; 3014 3015 /** 3016 * This will be called when panorama loading has error 3017 * @fires PANOLENS.Panorama#error 3018 */ 3019 PANOLENS.Panorama.prototype.onError = function () { 3020 3021 /** 3022 * Loading panorama error event 3023 * @type {object} 3024 * @event PANOLENS.Panorama#error 3025 */ 3026 this.dispatchEvent( { type: 'error' } ); 3027 3028 }; 3029 3030 /** 3031 * Get zoom level based on window width 3032 * @return {number} zoom level indicating image quality 3033 */ 3034 PANOLENS.Panorama.prototype.getZoomLevel = function () { 3035 3036 var zoomLevel; 3037 3038 if ( window.innerWidth <= 800 ) { 3039 3040 zoomLevel = this.ImageQualityFair; 3041 3042 } else if ( window.innerWidth > 800 && window.innerWidth <= 1280 ) { 3043 3044 zoomLevel = this.ImageQualityMedium; 3045 3046 } else if ( window.innerWidth > 1280 && window.innerWidth <= 1920 ) { 3047 3048 zoomLevel = this.ImageQualityHigh; 3049 3050 } else if ( window.innerWidth > 1920 ) { 3051 3052 zoomLevel = this.ImageQualitySuperHigh; 3053 3054 } else { 3055 3056 zoomLevel = this.ImageQualityLow; 3057 3058 } 3059 3060 return zoomLevel; 3061 3062 }; 3063 3064 /** 3065 * Update texture of a panorama 3066 * @param {THREE.Texture} texture - Texture to be updated 3067 */ 3068 PANOLENS.Panorama.prototype.updateTexture = function ( texture ) { 3069 3070 this.material.map = texture; 3071 3072 this.material.needsUpdate = true; 3073 3074 }; 3075 3076 /** 3077 * Toggle visibility of infospots in this panorama 3078 * @param {boolean} isVisible - Visibility of infospots 3079 * @param {number} delay - Delay in milliseconds to change visibility 3080 * @fires PANOLENS.Panorama#infospot-animation-complete 3081 */ 3082 PANOLENS.Panorama.prototype.toggleInfospotVisibility = function ( isVisible, delay ) { 3083 3084 delay = ( delay !== undefined ) ? delay : 0; 3085 3086 var scope, visible; 3087 3088 scope = this; 3089 visible = ( isVisible !== undefined ) ? isVisible : ( this.isInfospotVisible ? false : true ); 3090 3091 this.traverse( function ( object ) { 3092 3093 if ( object instanceof PANOLENS.Infospot ) { 3094 3095 visible ? object.show( delay ) : object.hide( delay ); 3096 3097 } 3098 3099 } ); 3100 3101 this.isInfospotVisible = visible; 3102 3103 // Animation complete event 3104 this.infospotAnimation.onComplete( function () { 3105 3106 /** 3107 * Complete toggling infospot visibility 3108 * @event PANOLENS.Panorama#infospot-animation-complete 3109 * @type {object} 3110 */ 3111 scope.dispatchEvent( { type : 'infospot-animation-complete', visible: visible } ); 3112 3113 } ).delay( delay ).start(); 3114 3115 }; 3116 3117 /** 3118 * Set image of this panorama's linking infospot 3119 * @param {string} url - Url to the image asset 3120 * @param {number} scale - Scale factor of the infospot 3121 */ 3122 PANOLENS.Panorama.prototype.setLinkingImage = function ( url, scale ) { 3123 3124 this.linkingImageURL = url; 3125 this.linkingImageScale = scale; 3126 3127 }; 3128 3129 /** 3130 * Link one-way panorama 3131 * @param {PANOLENS.Panorama} pano - The panorama to be linked to 3132 * @param {THREE.Vector3} position - The position of infospot which navigates to the pano 3133 * @param {number} [imageScale=300] - Image scale of linked infospot 3134 * @param {string} [imageSrc=PANOLENS.DataImage.Arrow] - The image source of linked infospot 3135 */ 3136 PANOLENS.Panorama.prototype.link = function ( pano, position, imageScale, imageSrc ) { 3137 3138 var scope = this, spot, scale, img; 3139 3140 this.visible = true; 3141 3142 if ( !position ) { 3143 3144 console.warn( 'Please specify infospot position for linking' ); 3145 3146 return; 3147 3148 } 3149 3150 // Infospot scale 3151 if ( imageScale !== undefined ) { 3152 3153 scale = imageScale; 3154 3155 } else if ( pano.linkingImageScale !== undefined ) { 3156 3157 scale = pano.linkingImageScale; 3158 3159 } else { 3160 3161 scale = 300; 3162 3163 } 3164 3165 3166 // Infospot image 3167 if ( imageSrc ) { 3168 3169 img = imageSrc 3170 3171 } else if ( pano.linkingImageURL ) { 3172 3173 img = pano.linkingImageURL; 3174 3175 } else { 3176 3177 img = PANOLENS.DataImage.Arrow; 3178 3179 } 3180 3181 // Creates a new infospot 3182 spot = new PANOLENS.Infospot( scale, img ); 3183 spot.position.copy( position ); 3184 spot.toPanorama = pano; 3185 spot.addEventListener( 'click', function () { 3186 3187 /** 3188 * Viewer handler event 3189 * @type {object} 3190 * @event PANOLENS.Panorama#panolens-viewer-handler 3191 * @property {string} method - Viewer function name 3192 * @property {*} data - The argument to be passed into the method 3193 */ 3194 scope.dispatchEvent( { type : 'panolens-viewer-handler', method: 'setPanorama', data: pano } ); 3195 3196 } ); 3197 3198 this.linkedSpots.push( spot ); 3199 3200 this.add( spot ); 3201 3202 this.visible = false; 3203 3204 }; 3205 3206 PANOLENS.Panorama.prototype.reset = function () { 3207 3208 this.children.length = 0; 3209 3210 }; 3211 3212 PANOLENS.Panorama.prototype.setupTransitions = function () { 3213 3214 this.fadeInAnimation = new TWEEN.Tween( this.material ) 3215 .easing( TWEEN.Easing.Quartic.Out ) 3216 .onStart( function () { 3217 3218 this.visible = true; 3219 this.material.visible = true; 3220 3221 /** 3222 * Enter panorama fade in start event 3223 * @event PANOLENS.Panorama#enter-fade-start 3224 * @type {object} 3225 */ 3226 this.dispatchEvent( { type: 'enter-fade-start' } ); 3227 3228 }.bind( this ) ); 3229 3230 this.fadeOutAnimation = new TWEEN.Tween( this.material ) 3231 .easing( TWEEN.Easing.Quartic.Out ) 3232 .onComplete( function () { 3233 3234 this.visible = false; 3235 this.material.visible = true; 3236 3237 /** 3238 * Leave panorama complete event 3239 * @event PANOLENS.Panorama#leave-complete 3240 * @type {object} 3241 */ 3242 this.dispatchEvent( { type: 'leave-complete' } ); 3243 3244 }.bind( this ) ); 3245 3246 this.enterTransition = new TWEEN.Tween( this ) 3247 .easing( TWEEN.Easing.Quartic.Out ) 3248 .onComplete( function () { 3249 3250 /** 3251 * Enter panorama and animation complete event 3252 * @event PANOLENS.Panorama#enter-animation-complete 3253 * @type {object} 3254 */ 3255 this.dispatchEvent( { type: 'enter-animation-complete' } ); 3256 3257 }.bind ( this ) ) 3258 .start(); 3259 3260 this.leaveTransition = new TWEEN.Tween( this ) 3261 .easing( TWEEN.Easing.Quartic.Out ); 3262 3263 }; 3264 3265 /** 3266 * Start fading in animation 3267 * @fires PANOLENS.Panorama#enter-fade-complete 3268 */ 3269 PANOLENS.Panorama.prototype.fadeIn = function ( duration ) { 3270 3271 duration = duration >= 0 ? duration : this.animationDuration; 3272 3273 this.fadeOutAnimation.stop(); 3274 this.fadeInAnimation 3275 .to( { opacity: 1 }, duration ) 3276 .onComplete( function () { 3277 3278 this.toggleInfospotVisibility( true, duration / 2 ); 3279 3280 /** 3281 * Enter panorama fade complete event 3282 * @event PANOLENS.Panorama#enter-fade-complete 3283 * @type {object} 3284 */ 3285 this.dispatchEvent( { type: 'enter-fade-complete' } ); 3286 3287 }.bind( this ) ) 3288 .start(); 3289 3290 }; 3291 3292 /** 3293 * Start fading out animation 3294 */ 3295 PANOLENS.Panorama.prototype.fadeOut = function ( duration ) { 3296 3297 duration = duration >= 0 ? duration : this.animationDuration; 3298 3299 this.fadeInAnimation.stop(); 3300 this.fadeOutAnimation.to( { opacity: 0 }, duration ).start(); 3301 3302 }; 3303 3304 /** 3305 * This will be called when entering a panorama 3306 * @fires PANOLENS.Panorama#enter 3307 * @fires PANOLENS.Panorama#enter-animation-start 3308 */ 3309 PANOLENS.Panorama.prototype.onEnter = function () { 3310 3311 var duration = this.animationDuration; 3312 3313 this.leaveTransition.stop(); 3314 this.enterTransition 3315 .to( {}, duration ) 3316 .onStart( function () { 3317 3318 /** 3319 * Enter panorama and animation starting event 3320 * @event PANOLENS.Panorama#enter-animation-start 3321 * @type {object} 3322 */ 3323 this.dispatchEvent( { type: 'enter-animation-start' } ); 3324 3325 if ( this.loaded ) { 3326 3327 this.fadeIn( duration ); 3328 3329 } else { 3330 3331 this.load(); 3332 3333 } 3334 3335 }.bind( this ) ) 3336 .start(); 3337 3338 /** 3339 * Enter panorama event 3340 * @event PANOLENS.Panorama#enter 3341 * @type {object} 3342 */ 3343 this.dispatchEvent( { type: 'enter' } ); 3344 3345 }; 3346 3347 /** 3348 * This will be called when leaving a panorama 3349 * @fires PANOLENS.Panorama#leave 3350 */ 3351 PANOLENS.Panorama.prototype.onLeave = function () { 3352 3353 var duration = this.animationDuration; 3354 3355 this.enterTransition.stop(); 3356 this.leaveTransition 3357 .to( {}, duration ) 3358 .onStart( function () { 3359 3360 /** 3361 * Leave panorama and animation starting event 3362 * @event PANOLENS.Panorama#leave-animation-start 3363 * @type {object} 3364 */ 3365 this.dispatchEvent( { type: 'leave-animation-start' } ); 3366 3367 this.fadeOut( duration ); 3368 this.toggleInfospotVisibility( false ); 3369 3370 }.bind( this ) ) 3371 .start(); 3372 3373 /** 3374 * Leave panorama event 3375 * @event PANOLENS.Panorama#leave 3376 * @type {object} 3377 */ 3378 this.dispatchEvent( { type: 'leave' } ); 3379 3380 }; 3381 3382 /** 3383 * Dispose panorama 3384 */ 3385 PANOLENS.Panorama.prototype.dispose = function () { 3386 3387 /** 3388 * On panorama dispose handler 3389 * @type {object} 3390 * @event PANOLENS.Panorama#panolens-viewer-handler 3391 * @property {string} method - Viewer function name 3392 * @property {*} data - The argument to be passed into the method 3393 */ 3394 this.dispatchEvent( { type : 'panolens-viewer-handler', method: 'onPanoramaDispose', data: this } ); 3395 3396 // recursive disposal on 3d objects 3397 function recursiveDispose ( object ) { 3398 3399 for ( var i = object.children.length - 1; i >= 0; i-- ) { 3400 3401 recursiveDispose( object.children[i] ); 3402 object.remove( object.children[i] ); 3403 3404 } 3405 3406 if ( object instanceof PANOLENS.Infospot ) { 3407 3408 object.dispose(); 3409 3410 } 3411 3412 object.geometry && object.geometry.dispose(); 3413 object.material && object.material.dispose(); 3414 } 3415 3416 recursiveDispose( this ); 3417 3418 if ( this.parent ) { 3419 3420 this.parent.remove( this ); 3421 3422 } 3423 3424 }; 3425 3426} )();;(function(){ 3427 3428 'use strict'; 3429 3430 /** 3431 * Equirectangular based image panorama 3432 * @constructor 3433 * @param {string} image - Image url or HTMLImageElement 3434 * @param {number} [radius=5000] - Radius of panorama 3435 */ 3436 PANOLENS.ImagePanorama = function ( image, radius ) { 3437
3438 radius = radius || 5000; 3439 3440 var geometry = new THREE.SphereGeometry( radius, 60, 40 ), 3441 material = new THREE.MeshBasicMaterial( { opacity: 0, transparent: true } ); 3442 3443 PANOLENS.Panorama.call( this, geometry, material ); 3444 3445 this.src = image; 3446 3447 } 3448 3449 PANOLENS.ImagePanorama.prototype = Object.create( PANOLENS.Panorama.prototype ); 3450 3451 PANOLENS.ImagePanorama.prototype.constructor = PANOLENS.ImagePanorama; 3452 3453 /** 3454 * Load image asset 3455 * @param {*} src - Url or image element 3456 */ 3457 PANOLENS.ImagePanorama.prototype.load = function ( src ) { 3458 3459 src = src || this.src; 3460 3461 if ( !src ) { 3462 3463 console.warn( 'Image source undefined' ); 3464 3465 return; 3466 3467 } else if ( typeof src === 'string' ) { 3468 3469 PANOLENS.Utils.TextureLoader.load( src, this.onLoad.bind( this ), this.onProgress.bind( this ), this.onError.bind( this ) ); 3470 3471 } else if ( src instanceof HTMLImageElement ) { 3472 3473 this.onLoad( new THREE.Texture( src ) ); 3474 3475 } 3476 3477 }; 3478 3479 /** 3480 * This will be called when image is loaded 3481 * @param {THREE.Texture} texture - Texture to be updated 3482 */ 3483 PANOLENS.ImagePanorama.prototype.onLoad = function ( texture ) { 3484 3485 texture.minFilter = texture.magFilter = THREE.LinearFilter; 3486 3487 texture.needsUpdate = true; 3488 3489 this.updateTexture( texture ); 3490 3491 // Call onLoad after second frame being painted 3492 window.requestAnimationFrame(function(){ 3493 3494 window.requestAnimationFrame(function(){ 3495 3496 PANOLENS.Panorama.prototype.onLoad.call( this ); 3497 3498 3499 }.bind(this)); 3500 3501 }.bind(this)); 3502 3503 3504 3505 }; 3506 3507 PANOLENS.ImagePanorama.prototype.reset = function () { 3508 3509 PANOLENS.Panorama.prototype.reset.call( this ); 3510 3511 }; 3512 3513})();;(function(){ 3514 3515 'use strict'; 3516 3517 /** 3518 * Google streetview panorama 3519 * 3520 * [How to get Panorama ID]{@link http://stackoverflow.com/questions/29916149/google-maps-streetview-how-to-get-panorama-id} 3521 * @constructor 3522 * @param {string} panoId - Panorama id from Google Streetview 3523 * @param {number} [radius=5000] - The minimum radius for this panoram 3524 */ 3525 PANOLENS.GoogleStreetviewPanorama = function ( panoId, radius ) { 3526 3527 PANOLENS.ImagePanorama.call( this, undefined, radius ); 3528 3529 this.panoId = panoId; 3530 3531 this.gsvLoader = undefined; 3532 3533 this.loadRequested = false; 3534 3535 this.setupGoogleMapAPI(); 3536 3537 } 3538 3539 PANOLENS.GoogleStreetviewPanorama.prototype = Object.create( PANOLENS.ImagePanorama.prototype ); 3540 3541 PANOLENS.GoogleStreetviewPanorama.constructor = PANOLENS.GoogleStreetviewPanorama; 3542 3543 /** 3544 * Load Google Street View by panorama id 3545 * @param {string} panoId - Gogogle Street View panorama id 3546 */ 3547 PANOLENS.GoogleStreetviewPanorama.prototype.load = function ( panoId ) { 3548 3549 this.loadRequested = true; 3550 3551 panoId = ( panoId || this.panoId ) || {}; 3552 3553 if ( panoId && this.gsvLoader ) { 3554 3555 this.loadGSVLoader( panoId ); 3556 3557 } else { 3558 3559 this.gsvLoader = {}; 3560 3561 } 3562 3563 }; 3564 3565 /** 3566 * Setup Google Map API 3567 */ 3568 PANOLENS.GoogleStreetviewPanorama.prototype.setupGoogleMapAPI = function () { 3569 3570 var script = document.createElement( 'script' ); 3571 script.src = 'https://maps.googleapis.com/maps/api/js'; 3572 script.onreadystatechange = this.setGSVLoader.bind( this ); 3573 script.onload = this.setGSVLoader.bind( this ); 3574 3575 document.getElementsByTagName('head')[0].appendChild( script ); 3576 3577 }; 3578 3579 /** 3580 * Set GSV Loader 3581 */ 3582 PANOLENS.GoogleStreetviewPanorama.prototype.setGSVLoader = function () { 3583 3584 this.gsvLoader = new GSVPANO.PanoLoader(); 3585 3586 if ( this.gsvLoader === {} || this.loadRequested ) { 3587 3588 this.load(); 3589 3590 } 3591 3592 }; 3593 3594 /** 3595 * Get GSV Loader 3596 * @return {object} GSV Loader instance 3597 */ 3598 PANOLENS.GoogleStreetviewPanorama.prototype.getGSVLoader = function () { 3599 3600 return this.gsvLoader; 3601 3602 }; 3603 3604 /** 3605 * Load GSV Loader 3606 * @param {string} panoId - Gogogle Street View panorama id 3607 */ 3608 PANOLENS.GoogleStreetviewPanorama.prototype.loadGSVLoader = function ( panoId ) { 3609 3610 this.loadRequested = false; 3611 3612 this.gsvLoader.onProgress = this.onProgress.bind( this ); 3613 3614 this.gsvLoader.onPanoramaLoad = this.onLoad.bind( this ); 3615 3616 this.gsvLoader.setZoom( this.getZoomLevel() ); 3617 3618 this.gsvLoader.load( panoId ); 3619 3620 this.gsvLoader.loaded = true; 3621 }; 3622 3623 /** 3624 * This will be called when panorama is loaded 3625 * @param {HTMLCanvasElement} canvas - Canvas where the tiles have been drawn 3626 */ 3627 PANOLENS.GoogleStreetviewPanorama.prototype.onLoad = function ( canvas ) { 3628 3629 if ( !this.gsvLoader ) { return; } 3630 3631 PANOLENS.ImagePanorama.prototype.onLoad.call( this, new THREE.Texture( canvas ) ); 3632 3633 }; 3634 3635 PANOLENS.GoogleStreetviewPanorama.prototype.reset = function () { 3636 3637 this.gsvLoader = undefined; 3638 3639 PANOLENS.ImagePanorama.prototype.reset.call( this ); 3640 3641 }; 3642 3643})();;(function(){ 3644 3645 'use strict'; 3646 3647 /** 3648 * Cubemap-based panorama 3649 * @constructor 3650 * @param {array} images - An array of cubetexture containing six images 3651 * @param {number} [edgeLength=10000] - The length of cube's edge 3652 */ 3653 PANOLENS.CubePanorama = function ( images, edgeLength ){ 3654 3655 var shader, geometry, material; 3656 3657 this.images = images || []; 3658 3659 edgeLength = edgeLength || 10000; 3660 shader = JSON.parse( JSON.stringify( THREE.ShaderLib[ 'cube' ] ) ); 3661 3662 geometry = new THREE.BoxGeometry( edgeLength, edgeLength, edgeLength ); 3663 material = new THREE.ShaderMaterial( { 3664 3665 fragmentShader: shader.fragmentShader, 3666 vertexShader: shader.vertexShader, 3667 uniforms: shader.uniforms, 3668 side: THREE.BackSide 3669 3670 } ); 3671 3672 PANOLENS.Panorama.call( this, geometry, material ); 3673 3674 } 3675 3676 PANOLENS.CubePanorama.prototype = Object.create( PANOLENS.Panorama.prototype ); 3677 3678 PANOLENS.CubePanorama.prototype.constructor = PANOLENS.CubePanorama; 3679 3680 /** 3681 * Load 6 images and bind listeners 3682 */ 3683 PANOLENS.CubePanorama.prototype.load = function () { 3684 3685 PANOLENS.Utils.CubeTextureLoader.load( 3686 3687 this.images, 3688 3689 this.onLoad.bind( this ), 3690 this.onProgress.bind( this ), 3691 this.onError.bind( this ) 3692 3693 ); 3694 3695 }; 3696 3697 /** 3698 * This will be called when 6 textures are ready 3699 * @param {THREE.CubeTexture} texture - Cube texture 3700 */ 3701 PANOLENS.CubePanorama.prototype.onLoad = function ( texture ) { 3702 3703 this.material.uniforms[ 'tCube' ].value = texture; 3704 3705 PANOLENS.Panorama.prototype.onLoad.call( this ); 3706 3707 }; 3708 3709})();;(function(){ 3710 3711 'use strict'; 3712 3713 /** 3714 * Basic panorama with 6 faces tile images 3715 * @constructor 3716 * @param {number} [edgeLength=10000] - The length of cube's edge 3717 */ 3718 PANOLENS.BasicPanorama = function ( edgeLength ) { 3719 3720 var tile = PANOLENS.DataImage.WhiteTile; 3721 3722 PANOLENS.CubePanorama.call( this, [ tile, tile, tile, tile, tile, tile ], edgeLength ); 3723 3724 } 3725 3726 PANOLENS.BasicPanorama.prototype = Object.create( PANOLENS.CubePanorama.prototype ); 3727 3728 PANOLENS.BasicPanorama.prototype.constructor = PANOLENS.BasicPanorama; 3729 3730})();;(function(){ 3731 3732 'use strict'; 3733 3734 /** 3735 * Video Panorama 3736 * @constructor 3737 * @param {string} src - Equirectangular video url 3738 * @param {object} [options] - Option for video settings 3739 * @param {HTMLElement} [options.videoElement] - HTML5 video element contains the video 3740 * @param {boolean} [options.loop=true] - Specify if the video should loop in the end 3741 * @param {boolean} [options.muted=false] - Mute the video or not 3742 * @param {boolean} [options.autoplay=false] - Specify if the video should auto play 3743 * @param {boolean} [options.playsinline=true] - Specify if video should play inline for iOS. If you want it to auto play inline, set both autoplay and muted options to true 3744 * @param {string} [options.crossOrigin="anonymous"] - Sets the cross-origin attribute for the video, which allows for cross-origin videos in some browsers (Firefox, Chrome). Set to either "anonymous" or "use-credentials". 3745 * @param {number} [radius=5000] - The minimum radius for this panoram 3746 */ 3747 PANOLENS.VideoPanorama = function ( src, options, radius ) { 3748
3749 radius = radius || 5000; 3750 3751 var geometry = new THREE.SphereGeometry( radius, 60, 40 ), 3752 material = new THREE.MeshBasicMaterial( { opacity: 0, transparent: true } ); 3753 3754 PANOLENS.Panorama.call( this, geometry, material ); 3755 3756 this.src = src; 3757 this.options = options || {}; 3758 this.options.playsinline = this.options.playsinline !== false ? true : false; 3759 3760 this.videoElement = undefined; 3761 this.videoRenderObject = undefined; 3762 this.videoProgress = 0; 3763 3764 this.isIOS = /iPhone|iPad|iPod/i.test( navigator.userAgent ); 3765 this.isMobile = this.isIOS || /Android|BlackBerry|Opera Mini|IEMobile/i.test( navigator.userAgent ); 3766 3767 this.addEventListener( 'leave', this.pauseVideo.bind( this ) ); 3768 this.addEventListener( 'enter-fade-start', this.resumeVideoProgress.bind( this ) ); 3769 this.addEventListener( 'video-toggle', this.toggleVideo.bind( this ) ); 3770 this.addEventListener( 'video-time', this.setVideoCurrentTime.bind( this ) ); 3771 3772 }; 3773 3774 PANOLENS.VideoPanorama.prototype = Object.create( PANOLENS.Panorama.prototype ); 3775 3776 PANOLENS.VideoPanorama.constructor = PANOLENS.VideoPanorama; 3777 3778 /** 3779 * Load video panorama 3780 * @param {string} src - The video url 3781 * @param {object} options - Option object containing videoElement 3782 * @fires PANOLENS.Panorama#panolens-viewer-handler 3783 */ 3784 PANOLENS.VideoPanorama.prototype.load = function ( src, options ) { 3785 3786 var scope = this; 3787 3788 src = ( src || this.src ) || ''; 3789 options = ( options || this.options ) || {}; 3790 3791 this.videoElement = options.videoElement || document.createElement( 'video' ); 3792 3793 this.videoElement.muted = options.muted || false; 3794 this.videoElement.loop = ( options.loop !== undefined ) ? options.loop : true; 3795 this.videoElement.autoplay = ( options.autoplay !== undefined ) ? options.autoplay : false; 3796 this.videoElement.crossOrigin = ( options.crossOrigin !== undefined ) ? options.crossOrigin : "anonymous"; 3797 3798 // iphone inline player 3799 if (options.playsinline) { 3800 this.videoElement.setAttribute( "playsinline", "" ); 3801 this.videoElement.setAttribute( "webkit-playsinline", "" ); 3802 } 3803 3804 var onloadeddata = function(){ 3805 3806 scope.onProgress( { loaded: 1, total: 1 } ); 3807 scope.setVideoTexture( scope.videoElement ); 3808 3809 if ( scope.videoElement.autoplay ) { 3810 3811 /** 3812 * Viewer handler event 3813 * @type {object} 3814 * @property {string} method - 'updateVideoPlayButton' 3815 * @property {boolean} data - Pause video or not 3816 */ 3817 scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'updateVideoPlayButton', data: false } ); 3818 3819 } 3820 3821 // For mobile silent autoplay 3822 if ( scope.isMobile ) { 3823 3824 if ( scope.videoElement.autoplay && scope.videoElement.muted ) { 3825 3826 /** 3827 * Viewer handler event 3828 * @type {object} 3829 * @property {string} method - 'updateVideoPlayButton' 3830 * @property {boolean} data - Pause video or not 3831 */ 3832 scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'updateVideoPlayButton', data: false } ); 3833 3834 } else { 3835 3836 /** 3837 * Viewer handler event 3838 * @type {object} 3839 * @property {string} method - 'updateVideoPlayButton' 3840 * @property {boolean} data - Pause video or not 3841 */ 3842 scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'updateVideoPlayButton', data: true } ); 3843 3844 } 3845 3846 } 3847 3848 scope.onLoad(); 3849 }; 3850 3851 /** 3852 * Ready state of the audio/video element 3853 * 0 = HAVE_NOTHING - no information whether or not the audio/video is ready 3854 * 1 = HAVE_METADATA - metadata for the audio/video is ready 3855 * 2 = HAVE_CURRENT_DATA - data for the current playback position is available, but not enough data to play next frame/millisecond 3856 * 3 = HAVE_FUTURE_DATA - data for the current and at least the next frame is available 3857 * 4 = HAVE_ENOUGH_DATA - enough data available to start playing 3858 */ 3859 if ( this.videoElement.readyState > 2 ) { 3860 3861 onloadeddata(); 3862 3863 } else { 3864 3865 if ( !this.videoElement.querySelectorAll('source').length || !this.videoElement.src ) { 3866 3867 this.videoElement.src = src; 3868 3869 } 3870 3871 this.videoElement.load(); 3872 } 3873 3874 this.videoElement.onloadeddata = onloadeddata; 3875 3876 3877 this.videoElement.ontimeupdate = function ( event ) { 3878 3879 scope.videoProgress = this.duration >= 0 ? this.currentTime / this.duration : 0; 3880 3881 /** 3882 * Viewer handler event 3883 * @type {object} 3884 * @property {string} method - 'onVideoUpdate' 3885 * @property {number} data - The percentage of video progress. Range from 0.0 to 1.0 3886 */ 3887 scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'onVideoUpdate', data: scope.videoProgress } ); 3888 3889 }; 3890 3891 this.videoElement.addEventListener( 'ended', function () { 3892 3893 if ( !scope.options.loop ) { 3894 3895 scope.resetVideo(); 3896 scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'updateVideoPlayButton', data: true } ); 3897 3898 } 3899 3900 }, false ); 3901 3902 }; 3903 3904 /** 3905 * Set video texture 3906 * @param {HTMLVideoElement} video - The html5 video element 3907 * @fires PANOLENS.Panorama#panolens-viewer-handler 3908 */ 3909 PANOLENS.VideoPanorama.prototype.setVideoTexture = function ( video ) { 3910 3911 var videoTexture, videoRenderObject, scene; 3912 3913 if ( !video ) return; 3914 3915 videoTexture = new THREE.VideoTexture( video ); 3916 videoTexture.minFilter = THREE.LinearFilter; 3917 videoTexture.magFilter = THREE.LinearFilter; 3918 videoTexture.format = THREE.RGBFormat; 3919 3920 videoRenderObject = { 3921 3922 video : video, 3923 videoTexture: videoTexture 3924 3925 }; 3926 3927 if ( this.isIOS ){ 3928 3929 enableInlineVideo( video ); 3930 3931 } 3932 3933 this.updateTexture( videoTexture ); 3934 3935 this.videoRenderObject = videoRenderObject; 3936 3937 }; 3938 3939 PANOLENS.VideoPanorama.prototype.reset = function () { 3940 3941 this.videoElement = undefined; 3942 3943 PANOLENS.Panorama.prototype.reset.call( this ); 3944 3945 }; 3946 3947 /** 3948 * Check if video is paused 3949 * @return {boolean} - is video paused or not 3950 */ 3951 PANOLENS.VideoPanorama.prototype.isVideoPaused = function () { 3952 3953 return this.videoRenderObject.video.paused; 3954 3955 }; 3956 3957 /** 3958 * Toggle video to play or pause 3959 */ 3960 PANOLENS.VideoPanorama.prototype.toggleVideo = function () { 3961 3962 if ( this.videoRenderObject && this.videoRenderObject.video ) { 3963 3964 if ( this.isVideoPaused() ) { 3965 3966 this.videoRenderObject.video.play(); 3967 3968 3969 } else { 3970 3971 this.videoRenderObject.video.pause(); 3972 3973 } 3974 3975 } 3976 3977 }; 3978 3979 /** 3980 * Set video currentTime 3981 * @param {object} event - Event contains percentage. Range from 0.0 to 1.0 3982 */ 3983 PANOLENS.VideoPanorama.prototype.setVideoCurrentTime = function ( event ) { 3984 3985 if ( this.videoRenderObject && this.videoRenderObject.video && !Number.isNaN(event.percentage) && event.percentage !== 1 ) { 3986 3987 this.videoRenderObject.video.currentTime = this.videoRenderObject.video.duration * event.percentage; 3988 3989 this.dispatchEvent( { type: 'panolens-viewer-handler', method: 'onVideoUpdate', data: event.percentage } ); 3990 3991 } 3992 3993 }; 3994 3995 /** 3996 * Play video 3997 */ 3998 PANOLENS.VideoPanorama.prototype.playVideo = function () { 3999 4000 if ( this.videoRenderObject && this.videoRenderObject.video && this.isVideoPaused() ) { 4001 4002 this.videoRenderObject.video.play(); 4003 4004 } 4005 4006 /** 4007 * Play event 4008 * @type {object} 4009 * @event 'play' 4010 * */ 4011 this.dispatchEvent( { type: 'play' } ); 4012 4013 }; 4014 4015 /** 4016 * Pause video 4017 */ 4018 PANOLENS.VideoPanorama.prototype.pauseVideo = function () { 4019 4020 if ( this.videoRenderObject && this.videoRenderObject.video && !this.isVideoPaused() ) { 4021 4022 this.videoRenderObject.video.pause(); 4023 4024 } 4025 4026 /** 4027 * Pause event 4028 * @type {object} 4029 * @event 'pause' 4030 * */ 4031 this.dispatchEvent( { type: 'pause' } ); 4032 4033 }; 4034 4035 /** 4036 * Resume video 4037 */ 4038 PANOLENS.VideoPanorama.prototype.resumeVideoProgress = function () { 4039 4040 if ( this.videoElement.autoplay && !this.isMobile ) { 4041 4042 this.playVideo(); 4043 4044 /** 4045 * Viewer handler event 4046 * @type {object} 4047 * @property {string} method - 'updateVideoPlayButton' 4048 * @property {boolean} data - Pause video or not 4049 */ 4050 this.dispatchEvent( { type: 'panolens-viewer-handler', method: 'updateVideoPlayButton', data: false } ); 4051 4052 } else { 4053 4054 this.pauseVideo(); 4055 4056 /** 4057 * Viewer handler event 4058 * @type {object} 4059 * @property {string} method - 'updateVideoPlayButton' 4060 * @property {boolean} data - Pause video or not 4061 */ 4062 this.dispatchEvent( { type: 'panolens-viewer-handler', method: 'updateVideoPlayButton', data: true } ); 4063 4064 } 4065 4066 this.setVideoCurrentTime( { percentage: this.videoProgress } ); 4067 4068 }; 4069 4070 /** 4071 * Reset video at stating point 4072 */ 4073 PANOLENS.VideoPanorama.prototype.resetVideo = function () { 4074 4075 if ( this.videoRenderObject && this.videoRenderObject.video ) { 4076 4077 this.setVideoCurrentTime( { percentage: 0 } ); 4078 4079 } 4080 4081 }; 4082 4083 /** 4084 * Check if video is muted 4085 * @return {boolean} - is video muted or not 4086 */ 4087 PANOLENS.VideoPanorama.prototype.isVideoMuted = function () { 4088 4089 return this.videoRenderObject.video.muted; 4090 4091 }; 4092 4093 /** 4094 * Mute video 4095 */ 4096 PANOLENS.VideoPanorama.prototype.muteVideo = function () { 4097 4098 if ( this.videoRenderObject && this.videoRenderObject.video && !this.isVideoMuted() ) { 4099 4100 this.videoRenderObject.video.muted = true; 4101 4102 } 4103 4104 this.dispatchEvent( { type: 'volumechange' } ); 4105 4106 }; 4107 4108 /** 4109 * Unmute video 4110 */ 4111 PANOLENS.VideoPanorama.prototype.unmuteVideo = function () { 4112 4113 if ( this.videoRenderObject && this.videoRenderObject.video && this.isVideoMuted() ) { 4114 4115 this.videoRenderObject.video.muted = false; 4116 4117 } 4118 4119 this.dispatchEvent( { type: 'volumechange' } ); 4120 4121 }; 4122 4123 /** 4124 * Returns the video element 4125 */ 4126 PANOLENS.VideoPanorama.prototype.getVideoElement = function () { 4127 4128 return this.videoRenderObject.video; 4129 4130 }; 4131 4132 /** 4133 * Dispose video panorama 4134 */ 4135 PANOLENS.VideoPanorama.prototype.dispose = function () { 4136 4137 this.resetVideo(); 4138 this.pauseVideo(); 4139 4140 this.removeEventListener( 'leave', this.pauseVideo.bind( this ) ); 4141 this.removeEventListener( 'enter-fade-start', this.resumeVideoProgress.bind( this ) ); 4142 this.removeEventListener( 'video-toggle', this.toggleVideo.bind( this ) ); 4143 this.removeEventListener( 'video-time', this.setVideoCurrentTime.bind( this ) ); 4144 4145 PANOLENS.Panorama.prototype.dispose.call( this ); 4146 4147 }; 4148 4149})();;(function(){ 4150 4151 'use strict'; 4152 4153 /** 4154 * Empty panorama 4155 * @constructor 4156 * @param {number} [radius=5000] - Radius of panorama 4157 */ 4158 PANOLENS.EmptyPanorama = function ( radius ) { 4159
4160 radius = radius || 5000; 4161 4162 var geometry = new THREE.Geometry(), 4163 material = new THREE.MeshBasicMaterial( { 4164 color: 0x000000, opacity: 1, transparent: true 4165 } ); 4166 4167 PANOLENS.Panorama.call( this, geometry, material ); 4168 4169 } 4170 4171 PANOLENS.EmptyPanorama.prototype = Object.create( PANOLENS.Panorama.prototype ); 4172 4173 PANOLENS.EmptyPanorama.prototype.constructor = PANOLENS.EmptyPanorama; 4174 4175})();;( function () { 4176 4177 /** 4178 * Little Planet 4179 * @constructor 4180 * @param {string} type - Type of little planet basic class 4181 * @param {string} source - URL for the image source 4182 * @param {number} [size=10000] - Size of plane geometry 4183 * @param {number} [ratio=0.5] - Ratio of plane geometry's height against width 4184 */ 4185 PANOLENS.LittlePlanet = function ( type, source, size, ratio ) { 4186 4187 type = type || 'image'; 4188 4189 type === 'image' && PANOLENS.ImagePanorama.call( this, source, size ); 4190 4191 this.size = size || 10000; 4192 this.ratio = ratio || 0.5; 4193 this.EPS = 0.000001; 4194 this.frameId; 4195 4196 this.geometry = this.createGeometry(); 4197 this.material = this.createMaterial( this.size ); 4198 4199 this.dragging = false; 4200 this.userMouse = new THREE.Vector2(); 4201 4202 this.quatA = new THREE.Quaternion(); 4203 this.quatB = new THREE.Quaternion(); 4204 this.quatCur = new THREE.Quaternion(); 4205 this.quatSlerp = new THREE.Quaternion(); 4206 4207 this.vectorX = new THREE.Vector3( 1, 0, 0 ); 4208 this.vectorY = new THREE.Vector3( 0, 1, 0 ); 4209 4210 this.addEventListener( 'window-resize', this.onWindowResize ); 4211 4212 }; 4213 4214 PANOLENS.LittlePlanet.prototype = Object.create( PANOLENS.ImagePanorama.prototype ); 4215 4216 PANOLENS.LittlePlanet.prototype.constructor = PANOLENS.LittlePlanet; 4217 4218 PANOLENS.LittlePlanet.prototype.createGeometry = function () { 4219 4220 return new THREE.PlaneGeometry( this.size, this.size * this.ratio ); 4221 4222 }; 4223 4224 PANOLENS.LittlePlanet.prototype.createMaterial = function ( size ) { 4225 4226 var uniforms = PANOLENS.StereographicShader.uniforms; 4227 uniforms.zoom.value = size; 4228 4229 return new THREE.ShaderMaterial( { 4230 4231 uniforms: uniforms, 4232 vertexShader: PANOLENS.StereographicShader.vertexShader, 4233 fragmentShader: PANOLENS.StereographicShader.fragmentShader 4234 4235 } ); 4236 4237 }; 4238 4239 PANOLENS.LittlePlanet.prototype.registerMouseEvents = function () { 4240 4241 this.container.addEventListener( 'mousedown', this.onMouseDown.bind( this ), false ); 4242 this.container.addEventListener( 'mousemove', this.onMouseMove.bind( this ), false ); 4243 this.container.addEventListener( 'mouseup', this.onMouseUp.bind( this ), false ); 4244 this.container.addEventListener( 'touchstart', this.onMouseDown.bind( this ), false ); 4245 this.container.addEventListener( 'touchmove', this.onMouseMove.bind( this ), false ); 4246 this.container.addEventListener( 'touchend', this.onMouseUp.bind( this ), false ); 4247 this.container.addEventListener( 'mousewheel', this.onMouseWheel.bind( this ), false ); 4248 this.container.addEventListener( 'DOMMouseScroll', this.onMouseWheel.bind( this ), false ); 4249 this.container.addEventListener( 'contextmenu', this.onContextMenu.bind( this ), false ); 4250 4251 }; 4252 4253 PANOLENS.LittlePlanet.prototype.unregisterMouseEvents = function () { 4254 4255 this.container.removeEventListener( 'mousedown', this.onMouseDown.bind( this ), false ); 4256 this.container.removeEventListener( 'mousemove', this.onMouseMove.bind( this ), false ); 4257 this.container.removeEventListener( 'mouseup', this.onMouseUp.bind( this ), false ); 4258 this.container.removeEventListener( 'touchstart', this.onMouseDown.bind( this ), false ); 4259 this.container.removeEventListener( 'touchmove', this.onMouseMove.bind( this ), false ); 4260 this.container.removeEventListener( 'touchend', this.onMouseUp.bind( this ), false ); 4261 this.container.removeEventListener( 'mousewheel', this.onMouseWheel.bind( this ), false ); 4262 this.container.removeEventListener( 'DOMMouseScroll', this.onMouseWheel.bind( this ), false ); 4263 this.container.removeEventListener( 'contextmenu', this.onContextMenu.bind( this ), false ); 4264 4265 }; 4266 4267 PANOLENS.LittlePlanet.prototype.onMouseDown = function ( event ) { 4268 4269 var x = ( event.clientX >= 0 ) ? event.clientX : event.touches[ 0 ].clientX; 4270 var y = ( event.clientY >= 0 ) ? event.clientY : event.touches[ 0 ].clientY; 4271 4272 var inputCount = ( event.touches && event.touches.length ) || 1 ; 4273 4274 switch ( inputCount ) { 4275 4276 case 1: 4277 4278 this.dragging = true; 4279 this.userMouse.set( x, y ); 4280 4281 break; 4282 4283 case 2: 4284
4285 var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 4286 var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 4287 var distance = Math.sqrt( dx * dx + dy * dy ); 4288 this.userMouse.pinchDistance = distance; 4289 4290 break; 4291 4292 default: 4293 4294 break; 4295 4296 } 4297 4298 this.onUpdateCallback(); 4299 4300 }; 4301 4302 PANOLENS.LittlePlanet.prototype.onMouseMove = function ( event ) { 4303 4304 var x = ( event.clientX >= 0 ) ? event.clientX : event.touches[ 0 ].clientX; 4305 var y = ( event.clientY >= 0 ) ? event.clientY : event.touches[ 0 ].clientY; 4306 4307 var inputCount = ( event.touches && event.touches.length ) || 1 ; 4308 4309 switch ( inputCount ) { 4310 4311 case 1: 4312 4313 var angleX = THREE.Math.degToRad( x - this.userMouse.x ) * 0.4; 4314 var angleY = THREE.Math.degToRad( y - this.userMouse.y ) * 0.4; 4315 4316 if ( this.dragging ) { 4317 this.quatA.setFromAxisAngle( this.vectorY, angleX ); 4318 this.quatB.setFromAxisAngle( this.vectorX, angleY ); 4319 this.quatCur.multiply( this.quatA ).multiply( this.quatB ); 4320 this.userMouse.set( x, y ); 4321 } 4322 4323 break; 4324 4325 case 2: 4326 4327 var uniforms = this.material.uniforms; 4328 var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 4329 var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 4330 var distance = Math.sqrt( dx * dx + dy * dy ); 4331 4332 this.addZoomDelta( this.userMouse.pinchDistance - distance ); 4333 4334 break; 4335 4336 default: 4337 4338 break; 4339 4340 } 4341 4342 }; 4343 4344 PANOLENS.LittlePlanet.prototype.onMouseUp = function ( event ) { 4345 4346 this.dragging = false; 4347 4348 }; 4349 4350 PANOLENS.LittlePlanet.prototype.onMouseWheel = function ( event ) { 4351 4352 event.preventDefault(); 4353 event.stopPropagation(); 4354 4355 var delta = 0; 4356 4357 if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9 4358 4359 delta = event.wheelDelta; 4360 4361 } else if ( event.detail !== undefined ) { // Firefox 4362 4363 delta = - event.detail; 4364 4365 } 4366 4367 this.addZoomDelta( delta ); 4368 this.onUpdateCallback(); 4369 4370 }; 4371 4372 PANOLENS.LittlePlanet.prototype.addZoomDelta = function ( delta ) { 4373 4374 var uniforms = this.material.uniforms; 4375 var lowerBound = this.size * 0.1; 4376 var upperBound = this.size * 10; 4377 4378 uniforms.zoom.value += delta; 4379 4380 if ( uniforms.zoom.value <= lowerBound ) { 4381 4382 uniforms.zoom.value = lowerBound; 4383 4384 } else if ( uniforms.zoom.value >= upperBound ) { 4385 4386 uniforms.zoom.value = upperBound; 4387 4388 } 4389 4390 }; 4391 4392 PANOLENS.LittlePlanet.prototype.onUpdateCallback = function () { 4393 4394 this.frameId = window.requestAnimationFrame( this.onUpdateCallback.bind( this ) ); 4395 4396 this.quatSlerp.slerp( this.quatCur, 0.1 ); 4397 this.material.uniforms.transform.value.makeRotationFromQuaternion( this.quatSlerp ); 4398 4399 if ( !this.dragging && 1.0 - this.quatSlerp.clone().dot( this.quatCur ) < this.EPS ) { 4400 4401 window.cancelAnimationFrame( this.frameId ); 4402 4403 } 4404 4405 }; 4406 4407 PANOLENS.LittlePlanet.prototype.reset = function () { 4408 4409 this.quatCur.set( 0, 0, 0, 1 ); 4410 this.quatSlerp.set( 0, 0, 0, 1 ); 4411 this.onUpdateCallback(); 4412 4413 }; 4414 4415 PANOLENS.LittlePlanet.prototype.onLoad = function () { 4416 4417 this.material.uniforms.resolution.value = this.container.clientWidth / this.container.clientHeight; 4418 4419 this.registerMouseEvents(); 4420 this.onUpdateCallback(); 4421 4422 this.dispatchEvent( { type: 'panolens-viewer-handler', method: 'disableControl' } ); 4423 4424 }; 4425 4426 PANOLENS.LittlePlanet.prototype.onLeave = function () { 4427 4428 this.unregisterMouseEvents(); 4429 4430 this.dispatchEvent( { type: 'panolens-viewer-handler', method: 'enableControl', data: PANOLENS.Controls.ORBIT } ); 4431 4432 window.cancelAnimationFrame( this.frameId ); 4433 4434 PANOLENS.Panorama.prototype.onLeave.call( this ); 4435 4436 }; 4437 4438 PANOLENS.LittlePlanet.prototype.onWindowResize = function () { 4439 4440 this.material.uniforms.resolution.value = this.container.clientWidth / this.container.clientHeight; 4441 4442 }; 4443 4444 PANOLENS.LittlePlanet.prototype.onContextMenu = function () { 4445 4446 this.dragging = false; 4447 4448 }; 4449 4450})();;( function () { 4451 4452 /** 4453 * Image Little Planet 4454 * @constructor 4455 * @param {string} source - URL for the image source 4456 * @param {number} [size=10000] - Size of plane geometry 4457 * @param {number} [ratio=0.5] - Ratio of plane geometry's height against width 4458 */ 4459 PANOLENS.ImageLittlePlanet = function ( source, size, ratio ) { 4460 4461 PANOLENS.LittlePlanet.call( this, 'image', source, size, ratio ); 4462 4463 }; 4464 4465 PANOLENS.ImageLittlePlanet.prototype = Object.create( PANOLENS.LittlePlanet.prototype ); 4466 4467 PANOLENS.ImageLittlePlanet.prototype.constructor = PANOLENS.ImageLittlePlanet; 4468 4469 PANOLENS.ImageLittlePlanet.prototype.onLoad = function ( texture ) { 4470 4471 this.updateTexture( texture ); 4472 4473 PANOLENS.ImagePanorama.prototype.onLoad.call( this, texture ); 4474 PANOLENS.LittlePlanet.prototype.onLoad.call( this ); 4475 4476 }; 4477 4478 PANOLENS.ImageLittlePlanet.prototype.updateTexture = function ( texture ) { 4479 4480 texture.minFilter = texture.magFilter = THREE.LinearFilter; 4481 4482 this.material.uniforms[ "tDiffuse" ].value = texture; 4483 4484 }; 4485 4486} )();;(function(){ 4487 4488 /** 4489 * Reticle 3D Sprite 4490 * @constructor 4491 * @param {THREE.Color} [color=0xfffff] - Color of the reticle sprite 4492 * @param {boolean} [autoSelect=true] - Auto selection 4493 * @param {string} [idleImageUrl=PANOLENS.DataImage.ReticleIdle] - Image asset url 4494 * @param {string} [dwellImageUrl=PANOLENS.DataImage.ReticleDwell] - Image asset url 4495 * @param {number} [dwellTime=1500] - Duration for dwelling sequence to complete 4496 * @param {number}
4496 [dwellSpriteAmount=45] - Number of dwelling sprite sequence 4497 */ 4498 PANOLENS.Reticle = function ( color, autoSelect, idleImageUrl, dwellImageUrl, dwellTime, dwellSpriteAmount ) { 4499 4500 color = color || 0xffffff; 4501 4502 this.autoSelect = autoSelect != undefined ? autoSelect : true; 4503 4504 this.dwellTime = dwellTime || 1500; 4505 this.dwellSpriteAmount = dwellSpriteAmount || 45; 4506 this.dwellInterval = this.dwellTime / this.dwellSpriteAmount; 4507 4508 this.IDLE = 0; 4509 this.DWELLING = 1; 4510 this.status; 4511 4512 this.scaleIdle = new THREE.Vector3( 0.2, 0.2, 1 ); 4513 this.scaleDwell = new THREE.Vector3( 1, 0.8, 1 ); 4514 4515 this.textureLoaded = false; 4516 this.idleImageUrl = idleImageUrl || PANOLENS.DataImage.ReticleIdle; 4517 this.dwellImageUrl = dwellImageUrl || PANOLENS.DataImage.ReticleDwell; 4518 this.idleTexture = new THREE.Texture(); 4519 this.dwellTexture = new THREE.Texture(); 4520 4521 THREE.Sprite.call( this, new THREE.SpriteMaterial( { color: color, depthTest: false } ) ); 4522 4523 this.currentTile = 0; 4524 this.startTime = 0; 4525 4526 this.visible = false; 4527 this.renderOrder = 10; 4528 this.timerId; 4529 4530 // initial update 4531 this.updateStatus( this.IDLE ); 4532 4533 }; 4534 4535 PANOLENS.Reticle.prototype = Object.create( THREE.Sprite.prototype ); 4536 4537 PANOLENS.Reticle.prototype.constructor = PANOLENS.Reticle; 4538 4539 /** 4540 * Make reticle visible 4541 */ 4542 PANOLENS.Reticle.prototype.show = function () { 4543 4544 this.visible = true; 4545 4546 }; 4547 4548 /** 4549 * Make reticle invisible 4550 */ 4551 PANOLENS.Reticle.prototype.hide = function () { 4552 4553 this.visible = false; 4554 4555 }; 4556 4557 /** 4558 * Load reticle textures 4559 */ 4560 PANOLENS.Reticle.prototype.loadTextures = function () { 4561 4562 this.idleTexture = PANOLENS.Utils.TextureLoader.load( this.idleImageUrl ); 4563 this.dwellTexture = PANOLENS.Utils.TextureLoader.load( this.dwellImageUrl ); 4564 4565 this.material.map = this.idleTexture; 4566 this.setupDwellSprite( this.dwellTexture ); 4567 this.textureLoaded = true; 4568 4569 }; 4570 4571 /** 4572 * Start reticle timer selection 4573 * @param {function} completeCallback - Callback after dwell completes 4574 */ 4575 PANOLENS.Reticle.prototype.select = function ( completeCallback ) { 4576 4577 if ( performance.now() - this.startTime >= this.dwellTime ) { 4578 4579 this.completeDwelling(); 4580 completeCallback(); 4581 4582 } else if ( this.autoSelect ){ 4583 4584 this.updateDwelling( performance.now() ); 4585 this.timerId = window.requestAnimationFrame( this.select.bind( this, completeCallback ) ); 4586 4587 } 4588 4589 }; 4590 4591 /** 4592 * Clear and reset reticle timer 4593 */ 4594 PANOLENS.Reticle.prototype.clearTimer = function () { 4595 4596 window.cancelAnimationFrame( this.timerId ); 4597 this.timerId = null; 4598 4599 }; 4600 4601 /** 4602 * Setup dwell sprite animation 4603 */ 4604 PANOLENS.Reticle.prototype.setupDwellSprite = function ( texture ) { 4605 4606 texture.wrapS = THREE.RepeatWrapping; 4607 texture.repeat.set( 1 / this.dwellSpriteAmount, 1 ); 4608 4609 } 4610 4611 /** 4612 * Update reticle status 4613 * @param {number} status - Reticle status 4614 */ 4615 PANOLENS.Reticle.prototype.updateStatus = function ( status ) { 4616 4617 this.status = status; 4618 4619 if ( status === this.IDLE ) { 4620 this.scale.copy( this.scaleIdle ); 4621 this.material.map = this.idleTexture; 4622 } else if ( status === this.DWELLING ) { 4623 this.scale.copy( this.scaleDwell ); 4624 this.material.map = this.dwellTexture; 4625 } 4626 4627 this.currentTile = 0; 4628 this.material.map.offset.x = 0; 4629 4630 }; 4631 4632 /** 4633 * Start dwelling sequence 4634 */ 4635 PANOLENS.Reticle.prototype.startDwelling = function ( completeCallback ) { 4636 4637 if ( !this.autoSelect ) { 4638 4639 return; 4640 4641 } 4642 4643 this.startTime = performance.now(); 4644 this.updateStatus( this.DWELLING ); 4645 this.select( completeCallback ); 4646 4647 }; 4648 4649 /** 4650 * Update dwelling sequence 4651 * @param {number} time - Timestamp for elasped time 4652 */ 4653 PANOLENS.Reticle.prototype.updateDwelling = function ( time ) { 4654 4655 var elasped = time - this.startTime; 4656 4657 if ( this.currentTile <= this.dwellSpriteAmount ) { 4658 this.currentTile = Math.floor( elasped / this.dwellTime * this.dwellSpriteAmount ); 4659 this.material.map.offset.x = this.currentTile / this.dwellSpriteAmount; 4660 } else { 4661 this.updateStatus( this.IDLE ); 4662 } 4663 4664 }; 4665 4666 /** 4667 * Cancel dwelling 4668 */ 4669 PANOLENS.Reticle.prototype.cancelDwelling = function () { 4670 this.clearTimer(); 4671 this.updateStatus( this.IDLE ); 4672 4673 }; 4674 4675 /** 4676 * Complete dwelling 4677 */ 4678 PANOLENS.Reticle.prototype.completeDwelling = function () { 4679 this.clearTimer(); 4680 this.updateStatus( this.IDLE ); 4681 }; 4682 4683})();;( function () { 4684 4685 /** 4686 * Creates a tile with bent capability 4687 * @constructor 4688 * @param {number} [width=10] - Width along the X axis 4689 * @param {number} [height=5] - Height along the Y axis 4690 * @param {number} [widthSegments=20] - Width segments 4691 * @param {number} [heightSegments=20] - Height segments 4692 * @param {THREE.Vector3} [forceDirection=THREE.Vector3( 0, 0, 1 )] - Force direction 4693 * @param {THREE.Vector3} [forceAxis=THREE.Vector3( 0, 1, 0 )] - Along this axis 4694 * @param {number} [forceAngle=Math.PI/12] - Angle to bend in radians 4695 */ 4696 PANOLENS.Tile = function ( width, height, widthSegments, heightSegments, forceDirection, forceAxis, forceAngle ) { 4697 4698 var scope = this; 4699 4700 this.parameters = { 4701 width: width, 4702 height: height, 4703 widthSegments: widthSegments, 4704 heightSegments: heightSegments, 4705 forceDirection: forceDirection, 4706 forceAxis: forceAxis, 4707 forceAngle: forceAngle 4708 }; 4709 4710 width = width || 10; 4711 height = height || 5; 4712 widthSegments = widthSegments || 1; 4713 heightSegments = heightSegments || 1; 4714 forceDirection = forceDirection || new THREE.Vector3( 0, 0, 1 ); 4715 forceAxis = forceAxis || new THREE.Vector3( 0, 1, 0 ); 4716 forceAngle = forceAngle !== undefined ? forceAngle : 0; 4717 4718 THREE.Mesh.call( this, 4719 new THREE.PlaneGeometry( width, height, widthSegments, heightSegments ), 4720 new THREE.MeshBasicMaterial( { color: 0xffffff, transparent: true } ) 4721 ); 4722 4723 this.bendModifier = new THREE.BendModifier(); 4724 4725 this.entity = undefined; 4726 4727 this.animationDuration = 500; 4728 this.animationFadeOut = undefined; 4729 this.animationFadeIn = undefined; 4730 this.animationTranslation = undefined; 4731 this.tweens = {}; 4732 4733 if ( forceAngle !== 0 ) { 4734 4735 this.bend( forceDirection, forceAxis, forceAngle ); 4736 4737 } 4738 4739 this.originalGeometry = this.geometry.clone(); 4740 } 4741 4742 PANOLENS.Tile.prototype = Object.create( THREE.Mesh.prototype ); 4743 4744 PANOLENS.Tile.prototype.constructor = PANOLENS.Tile; 4745 4746 PANOLENS.Tile.prototype.clone = function (){ 4747 4748 var parameters = this.parameters, tile; 4749 4750 tile = new PANOLENS.Tile( 4751 parameters.width, 4752 parameters.height, 4753 parameters.widthSegments, 4754 parameters.heightSegments, 4755 parameters.forceDirection, 4756 parameters.forceAxis, 4757 parameters.forceAngle 4758 ); 4759 4760 tile.setEntity( this.entity ); 4761 tile.material = this.material.clone(); 4762 4763 return tile; 4764 4765 }; 4766 4767 /** 4768 * Bend panel with direction, axis, and angle 4769 * @param {THREE.Vector3} direction - Force direction 4770 * @param {THREE.Vector3} axis - Along this axis 4771 * @param {number} angle - Angle to bend in radians 4772 */ 4773 PANOLENS.Tile.prototype.bend = function ( direction, axis, angle ) { 4774 4775 this.bendModifier.set( direction, axis, angle ).modify( this.geometry ); 4776 4777 }; 4778 4779 /** 4780 * Restore geometry back to initial state 4781 */ 4782 PANOLENS.Tile.prototype.unbend = function () { 4783 4784 var geometry = this.geometry; 4785 4786 this.geometry = this.originalGeometry; 4787 this.originalGeometry = this.geometry.clone(); 4788 4789 geometry.dispose(); 4790 geometry = null; 4791 4792 }; 4793 4794 /** 4795 * Create a tween object for animation 4796 * based on - {@link https://github.com/tweenjs/tween.js} 4797 * @param {string} name - Name of the tween animation 4798 * @param {object} object - Object to be tweened 4799 * @param {object} toState - Final state of the object's properties 4800 * @param {number} duration - Tweening duration 4801 * @param {TWEEN.Easing} easing - Easing function 4802 * @param {number} delay - Animation delay time 4803 * @param {Function} onStart - On start function 4804 * @param {Function} onUpdate - On update function 4805 * @param {Function} onComplete - On complete function 4806 * @return {TWEEN.Tween} - Tween object 4807 */ 4808 PANOLENS.Tile.prototype.tween = function ( name, object, toState, duration, easing, delay, onStart, onUpdate, onComplete ) { 4809 4810 object = object || this; 4811 toState = toState || {}; 4812 duration = duration || this.animationDuration; 4813 easing = easing || TWEEN.Easing.Exponential.Out;
4814 delay = delay !== undefined ? delay : 0; 4815 onStart = onStart ? onStart : null; 4816 onUpdate = onUpdate ? onUpdate : null; 4817 onComplete = onComplete ? onComplete : null; 4818 4819 if ( !this.tweens[name] ) { 4820 this.tweens[name] = new TWEEN.Tween( object ) 4821 .to( toState, duration ) 4822 .easing( easing ) 4823 .delay( delay ) 4824 .onStart( onStart ) 4825 .onUpdate( onUpdate ) 4826 .onComplete( onComplete ); 4827 } 4828 4829 return this.tweens[name]; 4830 4831 }; 4832 4833 /** 4834 * Short-hand for displaying a single ripple effect 4835 * by duplicating itself and fadeout 4836 * @param {number} scale - The duplicated self fadeout scale 4837 * @param {number} duration - Effect duration 4838 * @param {TWEEN.Easing} easing - Easing function 4839 */ 4840 PANOLENS.Tile.prototype.ripple = function ( scale, duration, easing ) { 4841 4842 scale = scale || 2; 4843 duration = duration || 200; 4844 easing = easing || TWEEN.Easing.Cubic.Out; 4845 4846 var scope = this, ripple = this.clone(); 4847 4848 new TWEEN.Tween( ripple.scale ) 4849 .to({x: scale, y: scale}, duration ) 4850 .easing( easing ) 4851 .start(); 4852 4853 new TWEEN.Tween( ripple.material ) 4854 .to({opacity: 0}, duration ) 4855 .easing( easing ) 4856 .onComplete(function(){ 4857 scope.remove( ripple ); 4858 ripple.geometry.dispose(); 4859 ripple.material.dispose(); 4860 }) 4861 .start(); 4862 4863 this.add( ripple ); 4864 4865 }; 4866 4867 /** 4868 * Set entity if multiple objects are considered as one entity 4869 * @param {object} entity - Entity represents whole group structure 4870 */ 4871 PANOLENS.Tile.prototype.setEntity = function ( entity ) { 4872 4873 this.entity = entity; 4874 4875 }; 4876 4877} )();;(function(){ 4878 4879 'use strict'; 4880 4881 /** 4882 * Group consists of tile array 4883 * @constructor 4884 * @param {array} tileArray - Tile array of PANOLENS.Tile 4885 * @param {number} verticalGap - Vertical gap between each tile 4886 * @param {number} depthGap - Depth gap between each tile 4887 * @param {number} animationDuration - Animation duration 4888 * @param {number} offset - Offset index 4889 */ 4890 PANOLENS.TileGroup = function ( tileArray, verticalGap, depthGap, animationDuration, offset ) { 4891 4892 var scope = this, textureLoader; 4893 4894 THREE.Object3D.call( this ); 4895 4896 this.tileArray = tileArray || []; 4897 this.offset = offset !== undefined ? offset : 0; 4898 this.v_gap = verticalGap !== undefined ? verticalGap : 6; 4899 this.d_gap = depthGap !== undefined ? depthGap : 2; 4900 this.animationDuration = animationDuration !== undefined ? animationDuration : 200; 4901 this.animationEasing = TWEEN.Easing.Exponential.Out; 4902 this.visibleDelta = 2; 4903 4904 this.tileArray.map( function ( tile, index ) { 4905 4906 if ( tile.image ) { 4907 4908 PANOLENS.Utils.TextureLoader.load( tile.image, scope.setTexture.bind( tile ) ); 4909 4910 } 4911 4912 tile.position.set( 0, index * -scope.v_gap, index * -scope.d_gap ); 4913 tile.originalPosition = tile.position.clone(); 4914 tile.setEntity( scope ); 4915 scope.add( tile ); 4916 4917 } ); 4918 4919 this.updateVisbility(); 4920 4921 } 4922 4923 PANOLENS.TileGroup.prototype = Object.create( THREE.Object3D.prototype ); 4924 4925 PANOLENS.TileGroup.prototype.constructor = PANOLENS.TileGroup; 4926 4927 /** 4928 * Update corresponding tile textures 4929 * @param {array} imageArray - Image array with index to index image update 4930 */ 4931 PANOLENS.TileGroup.prototype.updateTexture = function ( imageArray ) { 4932 4933 var scope = this; 4934 4935 imageArray = imageArray || []; 4936 this.children.map( function ( child, index ) { 4937 if ( child instanceof PANOLENS.Tile && imageArray[index] ) { 4938 PANOLENS.Utils.TextureLoader.load( imageArray[index], scope.setTexture.bind( child ) ); 4939 if ( child.outline ) { 4940 child.outline.material.visible = true; 4941 } 4942 } 4943 } ); 4944 4945 }; 4946 4947 /** 4948 * Update all tile textures and hide the remaining ones 4949 * @param {array} imageArray - Image array with index to index image update 4950 */ 4951 PANOLENS.TileGroup.prototype.updateAllTexture = function ( imageArray ) { 4952 4953 this.updateTexture( imageArray ); 4954 4955 if ( imageArray.length < this.children.length ) { 4956 for ( var i = imageArray.length; i < this.children.length; i++ ) { 4957 if ( this.children[i] instanceof PANOLENS.Tile ) { 4958 this.children[i].material.visible = false; 4959 if ( this.children[i].outline ) { 4960 this.children[i].outline.material.visible = false; 4961 } 4962 } 4963 } 4964 } 4965 4966 } 4967 4968 /** 4969 * Set individual texture 4970 * @param {THREE.Texture} texture - Texture to be updated 4971 */ 4972 PANOLENS.TileGroup.prototype.setTexture = function ( texture ) { 4973 4974 texture.minFilter = THREE.LinearFilter; 4975 texture.magFilter = THREE.LinearFilter; 4976 this.material.visible = true; 4977 this.material.map = texture; 4978 this.material.needsUpdate = true; 4979 4980 }; 4981 4982 /** 4983 * Update visibility 4984 */ 4985 PANOLENS.TileGroup.prototype.updateVisbility = function () { 4986 4987 this.children[this.offset].visible = true; 4988 new TWEEN.Tween( this.children[this.offset].material ) 4989 .to( { opacity: 1 }, this.animationDuration ) 4990 .easing( this.animationEasing ) 4991 .start(); 4992 4993 if ( this.children[this.offset].outline ) { 4994 4995 this.children[this.offset].outline.visible = true; 4996 4997 } 4998 4999 // Backward 5000 for ( var i = this.offset - 1; i >= 0 ; i-- ) { 5001 5002 if ( this.tileArray.indexOf(this.children[i]) === -1 ) { continue; } 5003 5004 if ( this.offset - i <= this.visibleDelta ) { 5005 5006 this.children[i].visible = true; 5007 new TWEEN.Tween( this.children[i].material ) 5008 .to( { opacity: 1 / ( this.offset - i ) * 0.5 }, this.animationDuration ) 5009 .easing( this.animationEasing ) 5010 .start(); 5011 5012 } else { 5013 5014 this.children[i].visible = false; 5015 5016 } 5017 5018 this.children[i].outline && (this.children[i].outline.visible = false); 5019 5020 } 5021 5022 // Forward 5023 for ( var i = this.offset + 1; i < this.children.length ; i++ ) { 5024 5025 if ( this.tileArray.indexOf(this.children[i]) === -1 ) { continue; } 5026 5027 if ( i - this.offset <= this.visibleDelta ) { 5028 5029 this.children[i].visible = true; 5030 new TWEEN.Tween( this.children[i].material ) 5031 .to( { opacity: 1 / ( i - this.offset ) * 0.5 }, this.animationDuration ) 5032 .easing( this.animationEasing ) 5033 .start(); 5034 5035 } else { 5036 5037 this.children[i].visible = false; 5038 5039 } 5040 5041 this.children[i].outline && (this.children[i].outline.visible = false); 5042 5043 } 5044 5045 }; 5046 5047 /** 5048 * Scroll up 5049 * @param {number} duration - Scroll up duration 5050 */ 5051 PANOLENS.TileGroup.prototype.scrollUp = function ( duration ) { 5052 5053 var tiles = this.tileArray, offset; 5054 5055 duration = duration !== undefined ? duration : this.animationDuration; 5056 5057 offset = this.offset + 1; 5058 5059 if ( this.offset < tiles.length - 1 && tiles[ this.offset + 1 ].material.visible ) { 5060 5061 for ( var i = tiles.length - 1; i >= 0; i-- ) { 5062 5063 new TWEEN.Tween( tiles[i].position ) 5064 .to({ y: ( i - offset ) * -this.v_gap, 5065 z: Math.abs( i - offset ) * -this.d_gap }, duration ) 5066 .easing( this.animationEasing ) 5067 .start(); 5068 5069 } 5070 5071 this.offset ++; 5072 this.updateVisbility(); 5073 this.dispatchEvent( { type: 'scroll', direction: 'up' } ); 5074 5075 } 5076 5077 }; 5078 5079 /** 5080 * Scroll down 5081 * @param {number} duration - Scroll up duration 5082 */ 5083 PANOLENS.TileGroup.prototype.scrollDown = function ( duration ) { 5084 5085 var tiles = this.tileArray, offset; 5086 5087 duration = duration !== undefined ? duration : this.animationDuration; 5088 5089 offset = this.offset - 1; 5090 5091 if ( this.offset > 0 && tiles[ this.offset - 1 ].material.visible ) { 5092 5093 for ( var i = 0; i < tiles.length; i++ ) { 5094 5095 new TWEEN.Tween( tiles[i].position ) 5096 .to({ y: ( i - offset ) * -this.v_gap, 5097 z: Math.abs( i - offset ) * -this.d_gap }, duration ) 5098 .easing( this.animationEasing ) 5099 .start(); 5100 5101 } 5102 5103 this.offset --; 5104 this.updateVisbility(); 5105 this.dispatchEvent( { type: 'scroll', direction: 'down' } ); 5106 5107 } 5108 5109 }; 5110 5111 PANOLENS.TileGroup.prototype.reset = function () { 5112 5113 this.tileArray.map( function ( child, index ) { 5114 5115 child.position.copy( child.originalPosition ); 5116 5117 } ); 5118 5119 this.offset = 0; 5120 this.updateVisbility(); 5121 5122 }; 5123 5124 /** 5125 * Get current index 5126 * @return {number} Index of the group. Range from 0 to this.tileArray.length 5127 */ 5128 PANOLENS.TileGroup.prototype.getIndex = function () { 5129 5130 return this.offset; 5131 5132 }; 5133 5134 /** 5135 * Get visible tile counts 5136 * @return {number} Number of visible tiles 5137 */ 5138 PANOLENS.TileGroup.prototype.getTileCount = function () { 5139 5140 var count = 0; 5141 5142 this.tileArray.map( function ( tile ) { 5143 5144 if ( tile.material.visible ) { 5145 5146 count ++; 5147 5148 } 5149 5150 } ); 5151 5152 return count; 5153 5154 }; 5155 5156})();;(function(){ 5157 5158 'use strict'; 5159 5160 var sharedFont, sharedTexture; 5161 var pendingQueue = []; 5162 5163 /** 5164 * Sprite text based on {@link https://github.com/Jam3/three-bmfont-text} 5165 * @constructor 5166 * @param {string} text - Text to be displayed 5167 * @param {number} maxWidth - Max width 5168 * @param {number} color - Color in hexadecimal 5169 * @param {number} opacity - Text opacity 5170 * @param {object} options - Options to create text geometry 5171 */ 5172 PANOLENS.SpriteText = function ( text, maxWidth, color, opacity, options ) { 5173 5174 THREE.Object3D.call( this ); 5175 5176 this.text = text || ''; 5177 this.maxWidth = maxWidth || 2000; 5178 this.color = color || 0xffffff; 5179 this.opacity = opacity !== undefined ? opacity : 1; 5180 this.options = options || {}; 5181 5182 this.animationDuration = 500; 5183 this.animationFadeOut = undefined; 5184 this.animationFadeIn = undefined; 5185 this.tweens = {}; 5186 5187 this.addText( text ); 5188 5189 } 5190 5191 PANOLENS.SpriteText.prototype = Object.create( THREE.Object3D.prototype ); 5192 5193 PANOLENS.SpriteText.prototype.constructor = PANOLENS.SpriteText; 5194 5195 // Reference function will be overwritten by Bmfont.js 5196 PANOLENS.SpriteText.prototype.generateTextGeometry = function () {}; 5197 PANOLENS.SpriteText.prototype.generateSDFShader = function () {}; 5198 5199 /** 5200 * Set BMFont 5201 * @param {Function} callback - Callback after font is loaded 5202 * @param {object} font - The font to be loaded 5203 * @param {THREE.Texture} texture - Font texture 5204 */ 5205 PANOLENS.SpriteText.prototype.setBMFont = function ( callback, font, texture ) { 5206 5207 texture.needsUpdate = true; 5208 texture.minFilter = THREE.LinearMipMapLinearFilter; 5209 texture.magFilter = THREE.LinearFilter; 5210 texture.generateMipmaps = true; 5211 texture.anisotropy = 8; 5212 5213 sharedFont = font; 5214 sharedTexture = texture; 5215 5216 for ( var i = pendingQueue.length - 1; i >= 0; i-- ) { 5217 pendingQueue[ i ].target.addText( pendingQueue[ i ].text ); 5218 } 5219 5220 while ( pendingQueue.length > 0 ) { 5221 pendingQueue.pop();
5222 } 5223 5224 callback && callback(); 5225 5226 }; 5227 5228 /** 5229 * Add text mesh 5230 * @param {string} text - Text to be displayed 5231 */ 5232 PANOLENS.SpriteText.prototype.addText = function ( text ) { 5233 5234 if ( !sharedFont || !sharedTexture ) { 5235 pendingQueue.push( { target: this, text: text } ); 5236 return; 5237 } 5238 5239 var textAnchor = new THREE.Object3D(); 5240 5241 this.options.text = text; 5242 this.options.font = sharedFont; 5243 this.options.width = this.maxWidth; 5244 5245 var geometry = this.generateTextGeometry( this.options ); 5246 geometry.computeBoundingBox(); 5247 geometry.computeBoundingSphere(); 5248 5249 var material = new THREE.RawShaderMaterial(this.generateSDFShader({ 5250 map: sharedTexture, 5251 side: THREE.DoubleSide, 5252 transparent: true, 5253 color: this.color, 5254 opacity: this.opacity 5255 })); 5256 5257 var layout = geometry.layout; 5258 var textMesh = new THREE.Mesh( geometry, material ); 5259 5260 textMesh.entity = this; 5261 textMesh.position.x = -layout.width / 2; 5262 textMesh.position.y = layout.height * 1.035; 5263 5264 textAnchor.scale.x = textAnchor.scale.y = -0.05; 5265 textAnchor.add( textMesh ); 5266 5267 this.mesh = textMesh; 5268 this.add( textAnchor ); 5269 5270 }; 5271 5272 /** 5273 * Update text geometry 5274 * @param {object} options - Geometry options based on 5275 * https://github.com/Jam3/three-bmfont-text#geometry--createtextopt 5276 */ 5277 PANOLENS.SpriteText.prototype.update = function ( options ) { 5278 5279 var mesh; 5280 5281 options = options || {}; 5282 5283 mesh = this.mesh; 5284 5285 mesh.geometry.update( options ); 5286 mesh.position.x = -mesh.geometry.layout.width / 2; 5287 mesh.position.y = mesh.geometry.layout.height * 1.035; 5288 5289 }; 5290 5291 /** 5292 * Create a tween object for animation 5293 * based on - {@link https://github.com/tweenjs/tween.js} 5294 * @param {string} name - Name of the tween animation 5295 * @param {object} object - Object to be tweened 5296 * @param {object} toState - Final state of the object's properties 5297 * @param {number} duration - Tweening duration 5298 * @param {TWEEN.Easing} easing - Easing function 5299 * @param {number} delay - Animation delay time 5300 * @param {Function} onStart - On start function 5301 * @param {Function} onUpdate - On update function 5302 * @param {Function} onComplete - On complete function 5303 * @return {TWEEN.Tween} - Tween object 5304 */ 5305 PANOLENS.SpriteText.prototype.tween = function ( name, object, toState, duration, easing, delay, onStart, onUpdate, onComplete ) { 5306 5307 object = object || this; 5308 toState = toState || {}; 5309 duration = duration || this.animationDuration; 5310 easing = easing || TWEEN.Easing.Exponential.Out; 5311 delay = delay !== undefined ? delay : 0; 5312 onStart = onStart ? onStart : null; 5313 onUpdate = onUpdate ? onUpdate : null; 5314 onComplete = onComplete ? onComplete : null; 5315 5316 if ( !this.tweens[name] ) { 5317 this.tweens[name] = new TWEEN.Tween( object ) 5318 .to( toState, duration ) 5319 .easing( easing ) 5320 .delay( delay ) 5321 .onStart( onStart ) 5322 .onUpdate( onUpdate ) 5323 .onComplete( onComplete ); 5324 } 5325 5326 return this.tweens[name]; 5327 5328 }; 5329 5330 /** 5331 * Get geometry layout 5332 * @return {object} Text geometry layout 5333 */ 5334 PANOLENS.SpriteText.prototype.getLayout = function () { 5335 5336 return this.mesh && this.mesh.geometry && this.mesh.geometry.layout || {}; 5337 5338 }; 5339 5340 /** 5341 * Set entity if multiple objects are considered as one entity 5342 * @param {object} entity - Entity represents whole group structure 5343 */ 5344 PANOLENS.SpriteText.prototype.setEntity = function ( entity ) { 5345 5346 this.entity = entity; 5347 5348 }; 5349 5350})();;(function () { 5351 5352 /** 5353 * Widget for controls 5354 * @constructor 5355 * @param {HTMLElement} container - A domElement where default control widget will be attached to 5356 */ 5357 PANOLENS.Widget = function ( container ) { 5358 5359 THREE.EventDispatcher.call( this ); 5360 5361 this.DEFAULT_TRANSITION = 'all 0.27s ease'; 5362 this.TOUCH_ENABLED = PANOLENS.Utils.checkTouchSupported(); 5363 this.PREVENT_EVENT_HANDLER = function ( event ) { 5364 event.preventDefault(); 5365 event.stopPropagation(); 5366 }; 5367 5368 this.container = container; 5369 5370 this.barElement; 5371 this.fullscreenElement; 5372 this.videoElement; 5373 this.settingElement; 5374 5375 this.mainMenu; 5376 5377 this.activeMainItem; 5378 this.activeSubMenu; 5379 this.mask; 5380 5381 } 5382 5383 PANOLENS.Widget.prototype = Object.create( THREE.EventDispatcher.prototype ); 5384 5385 PANOLENS.Widget.prototype.constructor = PANOLENS.Widget; 5386 5387 /** 5388 * Add control bar 5389 */ 5390 PANOLENS.Widget.prototype.addControlBar = function () { 5391 5392 if ( !this.container ) { 5393 5394 console.warn( 'Widget container not set' ); 5395 return; 5396 } 5397 5398 var scope = this, bar, styleTranslate, styleOpacity, gradientStyle; 5399 5400 gradientStyle = 'linear-gradient(bottom, rgba(0,0,0,0.2), rgba(0,0,0,0))'; 5401 5402 bar = document.createElement( 'div' ); 5403 bar.style.width = '100%'; 5404 bar.style.height = '44px'; 5405 bar.style.float = 'left'; 5406 bar.style.transform = bar.style.webkitTransform = bar.style.msTransform = 'translateY(-100%)'; 5407 bar.style.background = '-webkit-' + gradientStyle; 5408 bar.style.background = '-moz-' + gradientStyle; 5409 bar.style.background = '-o-' + gradientStyle; 5410 bar.style.background = '-ms-' + gradientStyle; 5411 bar.style.background = gradientStyle; 5412 bar.style.transition = this.DEFAULT_TRANSITION; 5413 bar.style.pointerEvents = 'none'; 5414 bar.isHidden = false; 5415 bar.toggle = function () { 5416 bar.isHidden = !bar.isHidden; 5417 styleTranslate = bar.isHidden ? 'translateY(0)' : 'translateY(-100%)'; 5418 styleOpacity = bar.isHidden ? 0 : 1; 5419 bar.style.transform = bar.style.webkitTransform = bar.style.msTransform = styleTranslate; 5420 bar.style.opacity = styleOpacity; 5421 }; 5422 5423 // Menu 5424 var menu = this.createDefaultMenu(); 5425 this.mainMenu = this.createMainMenu( menu ); 5426 bar.appendChild( this.mainMenu ); 5427 5428 // Mask 5429 var mask = this.createMask(); 5430 this.mask = mask; 5431 this.container.appendChild( mask ); 5432 5433 // Dispose 5434 bar.dispose = function () { 5435 5436 if ( scope.fullscreenElement ) { 5437 5438 bar.removeChild( scope.fullscreenElement ); 5439 scope.fullscreenElement.dispose(); 5440 scope.fullscreenElement = null; 5441 5442 } 5443 5444 if ( scope.settingElement ) { 5445 5446 bar.removeChild( scope.settingElement ); 5447 scope.settingElement.dispose(); 5448 scope.settingElement = null; 5449 5450 } 5451 5452 if ( scope.videoElement ) { 5453 5454 bar.removeChild( scope.videoElement ); 5455 scope.videoElement.dispose(); 5456 scope.videoElement = null; 5457 5458 } 5459 5460 }; 5461 5462 this.container.appendChild( bar ); 5463 5464 // Mask events 5465 this.mask.addEventListener( 'mousemove', this.PREVENT_EVENT_HANDLER, true ); 5466 this.mask.addEventListener( 'mouseup', this.PREVENT_EVENT_HANDLER, true ); 5467 this.mask.addEventListener( 'mousedown', this.PREVENT_EVENT_HANDLER, true ); 5468 this.mask.addEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'click', function ( event ) { 5469 5470 event.preventDefault(); 5471 event.stopPropagation(); 5472 5473 scope.mask.hide(); 5474 scope.settingElement.deactivate(); 5475 5476 }, false ); 5477 5478 // Event listener 5479 this.addEventListener( 'control-bar-toggle', bar.toggle ); 5480 5481 this.barElement = bar; 5482 5483 }; 5484 5485 /** 5486 * Create default menu 5487 */ 5488 PANOLENS.Widget.prototype.createDefaultMenu = function () { 5489 5490 var scope = this, handler; 5491 5492 handler = function ( method, data ) { 5493 5494 return function () { 5495 5496 scope.dispatchEvent( { 5497 5498 type: 'panolens-viewer-handler', 5499 method: method, 5500 data: data 5501 5502 } ); 5503 5504 } 5505 5506 }; 5507 5508 return [ 5509 5510 { 5511 title: 'Control', 5512 subMenu: [ 5513 { 5514 title: this.TOUCH_ENABLED ? 'Touch' : 'Mouse', 5515 handler: handler( 'enableControl', PANOLENS.Controls.ORBIT ) 5516 }, 5517 { 5518 title: 'Sensor', 5519 handler: handler( 'enableControl', PANOLENS.Controls.DEVICEORIENTATION ) 5520 } 5521 ] 5522 }, 5523 5524 { 5525 title: 'Mode', 5526 subMenu: [ 5527 { 5528 title: 'Normal', 5529 handler: handler( 'disableEffect' ) 5530 }, 5531 { 5532 title: 'Cardboard', 5533 handler: handler( 'enableEffect', PANOLENS.Modes.CARDBOARD ) 5534 }, 5535 { 5536 title: 'Stereoscopic', 5537 handler: handler( 'enableEffect', PANOLENS.Modes.STEREO ) 5538 } 5539 ] 5540 } 5541 5542 ]; 5543 5544 }; 5545 5546 /** 5547 * Add buttons on top of control bar 5548 * @param {string} name - The control button name to be created 5549 */ 5550 PANOLENS.Widget.prototype.addControlButton = function ( name ) { 5551 5552 var element; 5553 5554 switch( name ) { 5555 5556 case 'fullscreen': 5557 5558 element = this.createFullscreenButton(); 5559 this.fullscreenElement = element; 5560 5561 break; 5562 5563 case 'setting': 5564 5565 element = this.createSettingButton(); 5566 this.settingElement = element; 5567 5568 break; 5569 5570 case 'video': 5571 5572 element = this.createVideoControl(); 5573 this.videoElement = element; 5574 5575 break; 5576 5577 default: 5578 5579 return; 5580 5581 } 5582 5583 if ( !element ) { 5584 5585 return; 5586 5587 } 5588 5589 this.barElement.appendChild( element ); 5590 5591 }; 5592 5593 PANOLENS.Widget.prototype.createMask = function () { 5594 5595 var element = document.createElement( 'div' ); 5596 element.style.position = 'absolute'; 5597 element.style.top = 0; 5598 element.style.left = 0; 5599 element.style.width = '100%'; 5600 element.style.height = '100%'; 5601 element.style.background = 'transparent'; 5602 element.style.display = 'none'; 5603 5604 element.show = function () { 5605 5606 this.style.display = 'block'; 5607 5608 }; 5609 5610 element.hide = function () { 5611 5612 this.style.display = 'none'; 5613 5614 }; 5615 5616 return element; 5617 5618 }; 5619 5620 /** 5621 * Create Setting button to toggle menu 5622 */ 5623 PANOLENS.Widget.prototype.createSettingButton = function () { 5624 5625 var scope = this, item; 5626 5627 function onTap ( event ) { 5628 5629 event.preventDefault(); 5630 event.stopPropagation(); 5631
5632 scope.mainMenu.toggle(); 5633 5634 if ( this.activated ) { 5635 5636 this.deactivate(); 5637 5638 } else { 5639 5640 this.activate(); 5641 5642 } 5643 5644 } 5645 5646 item = this.createCustomItem( { 5647 5648 style : { 5649 5650 backgroundImage : 'url("' + PANOLENS.DataImage.Setting + '")', 5651 webkitTransition : this.DEFAULT_TRANSITION, 5652 transition : this.DEFAULT_TRANSITION 5653 5654 }, 5655 5656 onTap: onTap 5657 5658 } ); 5659 5660 item.activate = function () { 5661 5662 this.style.transform = 'rotate3d(0,0,1,90deg)'; 5663 this.activated = true; 5664 scope.mask.show(); 5665 5666 }; 5667 5668 item.deactivate = function () { 5669 5670 this.style.transform = 'rotate3d(0,0,0,0)'; 5671 this.activated = false; 5672 scope.mask.hide(); 5673 5674 if ( scope.mainMenu && scope.mainMenu.visible ) { 5675 5676 scope.mainMenu.hide(); 5677 5678 } 5679 5680 if ( scope.activeSubMenu && scope.activeSubMenu.visible ) { 5681 5682 scope.activeSubMenu.hide(); 5683 5684 } 5685 5686 if ( scope.mainMenu && scope.mainMenu._width ) { 5687 5688 scope.mainMenu.changeSize( scope.mainMenu._width ); 5689 scope.mainMenu.unslideAll(); 5690 5691 } 5692 5693 }; 5694 5695 item.activated = false; 5696 5697 return item; 5698 5699 }; 5700 5701 /** 5702 * Create Fullscreen button 5703 * @return {HTMLSpanElement} - The dom element icon for fullscreen 5704 * @fires PANOLENS.Widget#panolens-viewer-handler 5705 */ 5706 PANOLENS.Widget.prototype.createFullscreenButton = function () { 5707 5708 var scope = this, item, isFullscreen = false, tapSkipped = true, stylesheetId; 5709 5710 stylesheetId = 'panolens-style-addon'; 5711 5712 // Don't create button if no support 5713 if ( !document.fullscreenEnabled && 5714 !document.webkitFullscreenEnabled && 5715 !document.mozFullScreenEnabled && 5716 !document.msFullscreenEnabled ) { 5717 return; 5718 } 5719 5720 function onTap ( event ) { 5721 5722 event.preventDefault(); 5723 event.stopPropagation(); 5724 5725 tapSkipped = false; 5726 5727 if ( !isFullscreen ) { 5728 scope.container.requestFullscreen && scope.container.requestFullscreen(); 5729 scope.container.msRequestFullscreen && scope.container.msRequestFullscreen(); 5730 scope.container.mozRequestFullScreen && scope.container.mozRequestFullScreen(); 5731 scope.container.webkitRequestFullscreen && scope.container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); 5732 isFullscreen = true; 5733 } else { 5734 document.exitFullscreen && document.exitFullscreen(); 5735 document.msExitFullscreen && document.msExitFullscreen(); 5736 document.mozCancelFullScreen && document.mozCancelFullScreen(); 5737 document.webkitExitFullscreen && document.webkitExitFullscreen(); 5738 isFullscreen = false; 5739 } 5740 5741 this.style.backgroundImage = ( isFullscreen ) 5742 ? 'url("' + PANOLENS.DataImage.FullscreenLeave + '")' 5743 : 'url("' + PANOLENS.DataImage.FullscreenEnter + '")'; 5744 5745 } 5746 5747 function onFullScreenChange (e) { 5748 5749 if ( tapSkipped ) { 5750 5751 isFullscreen = !isFullscreen; 5752 5753 item.style.backgroundImage = ( isFullscreen ) 5754 ? 'url("' + PANOLENS.DataImage.FullscreenLeave + '")' 5755 : 'url("' + PANOLENS.DataImage.FullscreenEnter + '")'; 5756 5757 } 5758 5759 /** 5760 * Viewer handler event 5761 * @type {object} 5762 * @property {string} method - 'onWindowResize' function call on PANOLENS.Viewer 5763 */ 5764 scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'onWindowResize', data: false } ); 5765 5766 tapSkipped = true; 5767 5768 } 5769 5770 document.addEventListener( 'fullscreenchange', onFullScreenChange, false ); 5771 document.addEventListener( 'webkitfullscreenchange', onFullScreenChange, false ); 5772 document.addEventListener( 'mozfullscreenchange', onFullScreenChange, false ); 5773 document.addEventListener( 'MSFullscreenChange', onFullScreenChange, false ); 5774 5775 item = this.createCustomItem( { 5776 5777 style : { 5778 5779 backgroundImage : 'url("' + PANOLENS.DataImage.FullscreenEnter + '")' 5780 5781 }, 5782 5783 onTap : onTap 5784 5785 } ); 5786 5787 // Add fullscreen stlye if not exists 5788 if ( !document.querySelector( stylesheetId ) ) { 5789 var sheet = document.createElement( 'style' ); 5790 sheet.id = stylesheetId; 5791 sheet.innerHTML = ':-webkit-full-screen { width: 100% !important; height: 100% !important }'; 5792 document.body.appendChild( sheet ); 5793 } 5794 5795 return item; 5796 5797 }; 5798 5799 /** 5800 * Create video control container 5801 * @return {HTMLSpanElement} - The dom element icon for video control 5802 */ 5803 PANOLENS.Widget.prototype.createVideoControl = function () { 5804 5805 var item; 5806 5807 item = document.createElement( 'span' ); 5808 item.style.display = 'none'; 5809 item.show = function () { 5810 5811 item.style.display = ''; 5812 5813 }; 5814 5815 item.hide = function () { 5816 5817 item.style.display = 'none'; 5818 item.controlButton.paused = true; 5819 item.controlButton.update(); 5820 5821 }; 5822 5823 item.controlButton = this.createVideoControlButton(); 5824 item.seekBar = this.createVideoControlSeekbar(); 5825 5826 item.appendChild( item.controlButton ); 5827 item.appendChild( item.seekBar ); 5828 5829 item.dispose = function () { 5830 5831 item.removeChild( item.controlButton ); 5832 item.removeChild( item.seekBar ); 5833 5834 item.controlButton.dispose(); 5835 item.controlButton = null; 5836 5837 item.seekBar.dispose(); 5838 item.seekBar = null; 5839 5840 }; 5841 5842 this.addEventListener( 'video-control-show', item.show ); 5843 this.addEventListener( 'video-control-hide', item.hide ); 5844 5845 return item; 5846 5847 }; 5848 5849 /** 5850 * Create video control button 5851 * @return {HTMLSpanElement} - The dom element icon for video control 5852 * @fires PANOLENS.Widget#panolens-viewer-handler 5853 */ 5854 PANOLENS.Widget.prototype.createVideoControlButton = function () { 5855 5856 var scope = this, item; 5857 5858 function onTap ( event ) { 5859 5860 event.preventDefault(); 5861 event.stopPropagation(); 5862 5863 /** 5864 * Viewer handler event 5865 * @type {object} 5866 * @property {string} method - 'toggleVideoPlay' function call on PANOLENS.Viewer 5867 */ 5868 scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'toggleVideoPlay', data: !this.paused } ); 5869 5870 this.paused = !this.paused; 5871
5872 item.update(); 5873 5874 }; 5875 5876 item = this.createCustomItem( { 5877 5878 style : { 5879 5880 float : 'left', 5881 backgroundImage : 'url("' + PANOLENS.DataImage.VideoPlay + '")' 5882 5883 }, 5884 5885 onTap : onTap 5886 5887 } ); 5888 5889 item.paused = true; 5890 5891 item.update = function ( paused ) { 5892 5893 this.paused = paused !== undefined ? paused : this.paused; 5894 5895 this.style.backgroundImage = 'url("' + ( this.paused 5896 ? PANOLENS.DataImage.VideoPlay 5897 : PANOLENS.DataImage.VideoPause ) + '")'; 5898 5899 }; 5900 5901 return item; 5902 5903 }; 5904 5905 /** 5906 * Create video seekbar 5907 * @return {HTMLSpanElement} - The dom element icon for video seekbar 5908 * @fires PANOLENS.Widget#panolens-viewer-handler 5909 */ 5910 PANOLENS.Widget.prototype.createVideoControlSeekbar = function () { 5911 5912 var scope = this, item, progressElement, progressElementControl, 5913 isDragging = false, mouseX, percentageNow, percentageNext; 5914 5915 progressElement = document.createElement( 'div' ); 5916 progressElement.style.width = '0%'; 5917 progressElement.style.height = '100%'; 5918 progressElement.style.backgroundColor = '#fff'; 5919 5920 progressElementControl = document.createElement( 'div' ); 5921 progressElementControl.style.float = 'right'; 5922 progressElementControl.style.width = '14px'; 5923 progressElementControl.style.height = '14px'; 5924 progressElementControl.style.transform = 'translate(7px, -5px)'; 5925 progressElementControl.style.borderRadius = '50%'; 5926 progressElementControl.style.backgroundColor = '#ddd'; 5927 5928 progressElementControl.addEventListener( 'mousedown', onMouseDown, false ); 5929 progressElementControl.addEventListener( 'touchstart', onMouseDown, false ); 5930 5931 function onMouseDown ( event ) { 5932 5933 event.stopPropagation(); 5934 5935 isDragging = true; 5936 5937 mouseX = event.clientX || ( event.changedTouches && event.changedTouches[0].clientX ); 5938 5939 percentageNow = parseInt( progressElement.style.width ) / 100; 5940 5941 addControlListeners(); 5942 } 5943 5944 function onVideoControlDrag ( event ) { 5945 5946 var clientX; 5947 5948 if( isDragging ){ 5949 5950 clientX = event.clientX || ( event.changedTouches && event.changedTouches[0].clientX ); 5951 5952 percentageNext = ( clientX - mouseX ) / item.clientWidth; 5953 5954 percentageNext = percentageNow + percentageNext; 5955 5956 percentageNext = percentageNext > 1 ? 1 : ( ( percentageNext < 0 ) ? 0 : percentageNext ); 5957 5958 item.setProgress ( percentageNext ); 5959 5960 /** 5961 * Viewer handler event 5962 * @type {object} 5963 * @property {string} method - 'setVideoCurrentTime' function call on PANOLENS.Viewer 5964 * @property {number} data - Percentage of current video. Range from 0.0 to 1.0 5965 */ 5966 scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'setVideoCurrentTime', data: percentageNext } ); 5967 5968 } 5969 5970 } 5971 5972 function onVideoControlStop ( event ) { 5973 5974 event.stopPropagation(); 5975 5976 isDragging = false; 5977 5978 removeControlListeners(); 5979 5980 } 5981 5982 function addControlListeners () { 5983 5984 scope.container.addEventListener( 'mousemove', onVideoControlDrag, false ); 5985 scope.container.addEventListener( 'mouseup', onVideoControlStop, false ); 5986 scope.container.addEventListener( 'touchmove', onVideoControlDrag, false ); 5987 scope.container.addEventListener( 'touchend', onVideoControlStop, false ); 5988 5989 5990 } 5991 5992 function removeControlListeners () { 5993 5994 scope.container.removeEventListener( 'mousemove', onVideoControlDrag, false ); 5995 scope.container.removeEventListener( 'mouseup', onVideoControlStop, false ); 5996 scope.container.removeEventListener( 'touchmove', onVideoControlDrag, false ); 5997 scope.container.removeEventListener( 'touchend', onVideoControlStop, false ); 5998 5999 } 6000 6001 function onTap ( event ) { 6002 6003 event.preventDefault(); 6004 event.stopPropagation(); 6005 6006 var percentage; 6007 6008 if ( event.target === progressElementControl ) { return; } 6009 6010 percentage = ( event.changedTouches && event.changedTouches.length > 0 ) 6011 ? ( event.changedTouches[0].pageX - event.target.getBoundingClientRect().left ) / this.clientWidth 6012 : event.offsetX / this.clientWidth; 6013 6014 /** 6015 * Viewer handler event 6016 * @type {object} 6017 * @property {string} method - 'setVideoCurrentTime' function call on PANOLENS.Viewer 6018 * @property {number} data - Percentage of current video. Range from 0.0 to 1.0 6019 */ 6020 scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'setVideoCurrentTime', data: percentage } ); 6021 6022 item.setProgress( event.offsetX / this.clientWidth ); 6023 6024 }; 6025 6026 function onDispose () { 6027
6028 removeControlListeners(); 6029 progressElement = null; 6030 progressElementControl = null; 6031 6032 } 6033 6034 progressElement.appendChild( progressElementControl ); 6035 6036 item = this.createCustomItem( { 6037 6038 style : { 6039 6040 float : 'left', 6041 width : '30%', 6042 height : '4px', 6043 marginTop : '20px', 6044 backgroundColor : 'rgba(188,188,188,0.8)' 6045 6046 }, 6047 6048 onTap : onTap, 6049 onDispose: onDispose 6050 6051 } ); 6052 6053 item.appendChild( progressElement ); 6054 6055 item.setProgress = function( percentage ) { 6056 6057 progressElement.style.width = percentage * 100 + '%'; 6058 6059 }; 6060 6061 this.addEventListener( 'video-update', function ( event ) { 6062 6063 item.setProgress( event.percentage ); 6064 6065 } ); 6066 6067 return item; 6068 6069 }; 6070 6071 /** 6072 * Create menu item 6073 * @param {string} title - Title to display 6074 * @return {HTMLDomElement} - An anchor tag element 6075 */ 6076 PANOLENS.Widget.prototype.createMenuItem = function ( title ) { 6077 6078 var scope = this, item = document.createElement( 'a' ); 6079 item.textContent = title; 6080 item.style.display = 'block'; 6081 item.style.padding = '10px'; 6082 item.style.textDecoration = 'none'; 6083 item.style.cursor = 'pointer'; 6084 item.style.pointerEvents = 'auto'; 6085 item.style.transition = this.DEFAULT_TRANSITION; 6086 6087 item.slide = function ( right ) { 6088 6089 this.style.transform = 'translateX(' + ( right ? '' : '-' ) + '100%)'; 6090 6091 }; 6092 6093 item.unslide = function () { 6094 6095 this.style.transform = 'translateX(0)'; 6096 6097 }; 6098 6099 item.setIcon = function ( url ) { 6100 6101 if ( this.icon ) { 6102 6103 this.icon.style.backgroundImage = 'url(' + url + ')'; 6104 6105 } 6106 6107 }; 6108 6109 item.setSelectionTitle = function ( title ) { 6110 6111 if ( this.selection ) { 6112 6113 this.selection.textContent = title; 6114 6115 } 6116 6117 }; 6118 6119 item.addSelection = function ( name ) { 6120 6121 var selection = document.createElement( 'span' ); 6122 selection.style.fontSize = '13px'; 6123 selection.style.fontWeight = '300'; 6124 selection.style.float = 'right'; 6125 6126 this.selection = selection; 6127 this.setSelectionTitle( name ); 6128 this.appendChild( selection ); 6129 6130 return this; 6131 6132 }; 6133 6134 item.addIcon = function ( url, left, flip ) { 6135 6136 url = url || PANOLENS.DataImage.ChevronRight; 6137 left = left || false; 6138 flip = flip || false; 6139 6140 var element = document.createElement( 'span' ); 6141 element.style.float = left ? 'left' : 'right'; 6142 element.style.width = '17px'; 6143 element.style.height = '17px'; 6144 element.style[ 'margin' + ( left ? 'Right' : 'Left' ) ] = '12px'; 6145 element.style.backgroundSize = 'cover'; 6146 6147 if ( flip ) { 6148 6149 element.style.transform = 'rotateZ(180deg)'; 6150 6151 } 6152 6153 this.icon = element; 6154 this.setIcon( url ); 6155 this.appendChild( element ); 6156 6157 return this; 6158 6159 }; 6160 6161 item.addSubMenu = function ( title, items ) { 6162 6163 this.subMenu = scope.createSubMenu( title, items ); 6164 6165 return this; 6166 6167 }; 6168 6169 item.addEventListener( 'mouseenter', function () { 6170 6171 this.style.backgroundColor = '#e0e0e0'; 6172 6173 }, false ); 6174 6175 item.addEventListener( 'mouseleave', function () { 6176 6177 this.style.backgroundColor = '#fafafa'; 6178 6179 }, false ); 6180 6181 return item; 6182 6183 }; 6184 6185 /** 6186 * Create menu item header 6187 * @param {string} title - Title to display 6188 * @return {HTMLDomElement} - An anchor tag element 6189 */ 6190 PANOLENS.Widget.prototype.createMenuItemHeader = function ( title ) { 6191 6192 var header = this.createMenuItem( title ); 6193 6194 header.style.borderBottom = '1px solid #333'; 6195 header.style.paddingBottom = '15px'; 6196 6197 return header; 6198 6199 }; 6200 6201 /** 6202 * Create main menu 6203 * @param {array} menus - Menu array list 6204 * @return {HTMLDomElement} - A span element 6205 */ 6206 PANOLENS.Widget.prototype.createMainMenu = function ( menus ) { 6207 6208 var scope = this, menu = this.createMenu(), subMenu; 6209 6210 menu._width = 200; 6211 menu.changeSize( menu._width ); 6212 6213 function onTap ( event ) { 6214 6215 event.preventDefault(); 6216 event.stopPropagation(); 6217 6218 var mainMenu = scope.mainMenu, subMenu = this.subMenu; 6219 6220 function onNextTick () { 6221 6222 mainMenu.changeSize( subMenu.clientWidth ); 6223 subMenu.show();
6224 subMenu.unslideAll(); 6225 6226 } 6227 6228 mainMenu.hide(); 6229 mainMenu.slideAll(); 6230 mainMenu.parentElement.appendChild( subMenu ); 6231 6232 scope.activeMainItem = this; 6233 scope.activeSubMenu = subMenu; 6234 6235 window.requestAnimationFrame( onNextTick ); 6236 6237 }; 6238 6239 for ( var i = 0; i < menus.length; i++ ) { 6240 6241 var item = menu.addItem( menus[ i ].title ); 6242 6243 item.style.paddingLeft = '20px'; 6244 6245 item.addIcon() 6246 .addEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'click', onTap, false ); 6247 6248 if ( menus[ i ].subMenu && menus[ i ].subMenu.length > 0 ) { 6249 6250 var title = menus[ i ].subMenu[ 0 ].title; 6251 6252 item.addSelection( title ) 6253 .addSubMenu( menus[ i ].title, menus[ i ].subMenu ); 6254 6255 } 6256 6257 } 6258 6259 return menu; 6260 6261 }; 6262 6263 /** 6264 * Create sub menu 6265 * @param {string} title - Sub menu title 6266 * @param {array} items - Item array list 6267 * @return {HTMLDomElement} - A span element 6268 */ 6269 PANOLENS.Widget.prototype.createSubMenu = function ( title, items ) { 6270 6271 var scope = this, menu, subMenu = this.createMenu(); 6272 6273 subMenu.items = items; 6274 subMenu.activeItem; 6275 6276 function onTap ( event ) { 6277 6278 event.preventDefault(); 6279 event.stopPropagation(); 6280 6281 menu = scope.mainMenu; 6282 menu.changeSize( menu._width ); 6283 menu.unslideAll(); 6284 menu.show(); 6285 subMenu.slideAll( true ); 6286 subMenu.hide(); 6287 6288 if ( this.type !== 'header' ) { 6289 6290 subMenu.setActiveItem( this ); 6291 scope.activeMainItem.setSelectionTitle( this.textContent ); 6292 6293 this.handler && this.handler(); 6294 6295 } 6296 6297 } 6298 6299 subMenu.addHeader( title ).addIcon( undefined, true, true ).addEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'click', onTap, false ); 6300 6301 for ( var i = 0; i < items.length; i++ ) { 6302 6303 var item = subMenu.addItem( items[ i ].title ); 6304 6305 item.style.fontWeight = 300; 6306 item.handler = items[ i ].handler; 6307 item.addIcon( ' ', true ); 6308 item.addEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'click', onTap, false ); 6309 6310 if ( !subMenu.activeItem ) { 6311 6312 subMenu.setActiveItem( item ); 6313 6314 } 6315 6316 } 6317 6318 subMenu.slideAll( true ); 6319 6320 return subMenu; 6321 6322 }; 6323 6324 /** 6325 * Create general menu 6326 * @return {HTMLDomElement} - A span element 6327 */ 6328 PANOLENS.Widget.prototype.createMenu = function () { 6329 6330 var scope = this, menu = document.createElement( 'span' ), style; 6331 6332 style = menu.style; 6333 6334 style.padding = '5px 0'; 6335 style.position = 'fixed'; 6336 style.bottom = '100%'; 6337 style.right = '14px'; 6338 style.backgroundColor = '#fafafa'; 6339 style.fontFamily = 'Helvetica Neue'; 6340 style.fontSize = '14px'; 6341 style.visibility = 'hidden'; 6342 style.opacity = 0; 6343 style.boxShadow = '0 0 12pt rgba(0,0,0,0.25)'; 6344 style.borderRadius = '2px'; 6345 style.overflow = 'hidden'; 6346 style.willChange = 'width, height, opacity'; 6347 style.pointerEvents = 'auto'; 6348 style.transition = this.DEFAULT_TRANSITION; 6349 6350 menu.visible = false; 6351 6352 menu.changeSize = function ( width, height ) { 6353 6354 if ( width ) { 6355 6356 this.style.width = width + 'px'; 6357 6358 } 6359 6360 if ( height ) { 6361 6362 this.style.height = height + 'px'; 6363 6364 } 6365 6366 }; 6367 6368 menu.show = function () { 6369 6370 this.style.opacity = 1; 6371 this.style.visibility = 'visible'; 6372 this.visible = true; 6373 6374 }; 6375 6376 menu.hide = function () { 6377 6378 this.style.opacity = 0; 6379 this.style.visibility = 'hidden'; 6380 this.visible = false; 6381 6382 }; 6383 6384 menu.toggle = function () { 6385 6386 if ( this.visible ) { 6387 6388 this.hide(); 6389 6390 } else { 6391 6392 this.show(); 6393 6394 } 6395 6396 }; 6397 6398 menu.slideAll = function ( right ) { 6399 6400 for ( var i = 0; i < menu.children.length; i++ ){ 6401 6402 if ( menu.children[ i ].slide ) { 6403 6404 menu.children[ i ].slide( right ); 6405 6406 } 6407 6408 } 6409 6410 }; 6411 6412 menu.unslideAll = function () { 6413 6414 for ( var i = 0; i < menu.children.length; i++ ){ 6415 6416 if ( menu.children[ i ].unslide ) { 6417 6418 menu.children[ i ].unslide(); 6419 6420 } 6421 6422 } 6423 6424 }; 6425 6426 menu.addHeader = function ( title ) { 6427 6428 var header = scope.createMenuItemHeader( title ); 6429 header.type = 'header'; 6430 6431 this.appendChild( header ); 6432 6433 return header; 6434 6435 }; 6436 6437 menu.addItem = function ( title ) { 6438 6439 var item = scope.createMenuItem( title ); 6440 item.type = 'item'; 6441 6442 this.appendChild( item ); 6443 6444 return item; 6445 6446 }; 6447 6448 menu.setActiveItem = function ( item ) { 6449 6450 if ( this.activeItem ) { 6451 6452 this.activeItem.setIcon( ' ' ); 6453 6454 } 6455 6456 item.setIcon( PANOLENS.DataImage.Check ); 6457 6458 this.activeItem = item; 6459 6460 }; 6461 6462 menu.addEventListener( 'mousemove', this.PREVENT_EVENT_HANDLER, true ); 6463 menu.addEventListener( 'mouseup', this.PREVENT_EVENT_HANDLER, true ); 6464 menu.addEventListener( 'mousedown', this.PREVENT_EVENT_HANDLER, true ); 6465 6466 return menu; 6467 6468 }; 6469 6470 /** 6471 * Create custom item element 6472 * @return {HTMLSpanElement} - The dom element icon 6473 */ 6474 PANOLENS.Widget.prototype.createCustomItem = function ( options ) { 6475 6476 options = options || {}; 6477 6478 var scope = this, 6479 item = options.element || document.createElement( 'span' ); 6480 6481 item.style.cursor = 'pointer'; 6482 item.style.float = 'right'; 6483 item.style.width = '44px'; 6484 item.style.height = '100%'; 6485 item.style.backgroundSize = '60%'; 6486 item.style.backgroundRepeat = 'no-repeat'; 6487 item.style.backgroundPosition = 'center'; 6488 item.style.webkitUserSelect = 6489 item.style.MozUserSelect = 6490 item.style.userSelect = 'none'; 6491 item.style.position = 'relative'; 6492 item.style.pointerEvents = 'auto'; 6493 6494 // White glow on icon 6495 item.addEventListener( scope.TOUCH_ENABLED ? 'touchstart' : 'mouseenter', function() { 6496 item.style.filter = 6497 item.style.webkitFilter = 'drop-shadow(0 0 5px rgba(255,255,255,1))'; 6498 }); 6499 item.addEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'mouseleave', function() { 6500 item.style.filter = 6501 item.style.webkitFilter = ''; 6502 }); 6503 6504 item = this.mergeStyleOptions( item, options.style ); 6505 6506 if ( options.onTap ) { 6507 6508 item.addEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'click', options.onTap, false ); 6509 6510 } 6511 6512 item.dispose = function () { 6513 6514 item.removeEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'click', options.onTap, false ); 6515 6516 options.onDispose && options.onDispose(); 6517 6518 }; 6519 6520 return item; 6521 6522 }; 6523 6524 /** 6525 * Merge item css style 6526 * @param {HTMLDOMElement} element - The element to be merged with style 6527 * @param {object} options - The style options 6528 * @return {HTMLDOMElement} - The same element with merged styles 6529 */ 6530 PANOLENS.Widget.prototype.mergeStyleOptions = function ( element, options ) { 6531 6532 options = options || {}; 6533 6534 for ( var property in options ){ 6535 6536 if ( options.hasOwnProperty( property ) ) { 6537 6538 element.style[ property ] = options[ property ]; 6539 6540 } 6541 6542 } 6543 6544 return element; 6545 6546 }; 6547 6548 /** 6549 * Dispose widgets by detaching dom elements from container 6550 */ 6551 PANOLENS.Widget.prototype.dispose = function () { 6552 6553 if ( this.barElement ) { 6554 this.container.removeChild( this.barElement ); 6555 this.barElement.dispose(); 6556 this.barElement = null; 6557 6558 } 6559 6560 }; 6561 6562})();;( function () { 6563 6564 /** 6565 * Information spot attached to panorama 6566 * @constructor 6567 * @param {number} [scale=300] - Infospot scale 6568 * @param {imageSrc} [imageSrc=PANOLENS.DataImage.Info] - Image overlay info 6569 * @param {boolean} [animated=true] - Enable default hover animation 6570 */ 6571 PANOLENS.Infospot = function ( scale, imageSrc, animated ) { 6572 6573 var scope = this, ratio, startScale, endScale, duration; 6574 6575 scale = scale || 300; 6576 imageSrc = imageSrc || PANOLENS.DataImage.Info; 6577 duration = 500; 6578 6579 THREE.Sprite.call( this ); 6580 6581 this.type = 'infospot'; 6582 6583 this.animated = animated !== undefined ? animated : true; 6584 this.isHovering = false; 6585 this.visible = false; 6586 6587 this.element; 6588 this.toPanorama; 6589 this.cursorStyle; 6590 6591 this.mode = PANOLENS.Modes.UNKNOWN; 6592 6593 this.scale.set( scale, scale, 1 ); 6594 this.rotation.y = Math.PI; 6595 this.scaleFactor = 1.3; 6596 6597 this.container; 6598 6599 // Event Handler 6600 this.HANDLER_FOCUS; 6601 6602 PANOLENS.Utils.TextureLoader.load( imageSrc, postLoad ); 6603 6604 function postLoad ( texture ) { 6605 6606 texture.wrapS = THREE.RepeatWrapping; 6607 texture.repeat.x = - 1; 6608
6609 texture.image.width = texture.image.naturalWidth || 64; 6610 texture.image.height = texture.image.naturalHeight || 64; 6611 6612 ratio = texture.image.width / texture.image.height; 6613 scope.scale.set( ratio * scale, scale, 1 ); 6614 6615 startScale = scope.scale.clone(); 6616 6617 scope.scaleUpAnimation = new TWEEN.Tween( scope.scale ) 6618 .to( { x: startScale.x * scope.scaleFactor, y: startScale.y * scope.scaleFactor }, duration ) 6619 .easing( TWEEN.Easing.Elastic.Out ); 6620 6621 scope.scaleDownAnimation = new TWEEN.Tween( scope.scale ) 6622 .to( { x: startScale.x, y: startScale.y }, duration ) 6623 .easing( TWEEN.Easing.Elastic.Out ); 6624 6625 scope.material.side = THREE.DoubleSide; 6626 scope.material.map = texture; 6627 scope.material.depthTest = false; 6628 scope.material.needsUpdate = true; 6629 6630 } 6631 6632 function show () { 6633 6634 this.visible = true; 6635 6636 } 6637 6638 function hide () { 6639 6640 this.visible = false; 6641 6642 } 6643 6644 // Add show and hide animations 6645 this.showAnimation = new TWEEN.Tween( this.material ) 6646 .to( { opacity: 1 }, duration ) 6647 .onStart( show.bind( this ) ) 6648 .easing( TWEEN.Easing.Quartic.Out ); 6649 6650 this.hideAnimation = new TWEEN.Tween( this.material ) 6651 .to( { opacity: 0 }, duration ) 6652 .onComplete( hide.bind( this ) ) 6653 .easing( TWEEN.Easing.Quartic.Out ); 6654 6655 // Attach event listeners 6656 this.addEventListener( 'click', this.onClick ); 6657 this.addEventListener( 'hover', this.onHover ); 6658 this.addEventListener( 'hoverenter', this.onHoverStart ); 6659 this.addEventListener( 'hoverleave', this.onHoverEnd ); 6660 this.addEventListener( 'panolens-dual-eye-effect', this.onDualEyeEffect ); 6661 this.addEventListener( 'panolens-container', this.setContainer.bind( this ) ); 6662 this.addEventListener( 'dismiss', this.onDismiss ); 6663 this.addEventListener( 'panolens-infospot-focus', this.setFocusMethod ); 6664 6665 }; 6666 6667 PANOLENS.Infospot.prototype = Object.create( THREE.Sprite.prototype ); 6668 6669 /** 6670 * Set infospot container 6671 * @param {HTMLElement|object} data - Data with container information 6672 */ 6673 PANOLENS.Infospot.prototype.setContainer = function ( data ) { 6674 6675 var container; 6676 6677 if ( data instanceof HTMLElement ) { 6678 6679 container = data; 6680 6681 } else if ( data && data.container ) { 6682 6683 container = data.container; 6684 6685 } 6686 6687 // Append element if exists 6688 if ( container && this.element ) { 6689 6690 container.appendChild( this.element ); 6691 6692 } 6693 6694 this.container = container; 6695 6696 }; 6697 6698 /** 6699 * Get container 6700 * @return {HTMLElement} - The container of this infospot 6701 */ 6702 PANOLENS.Infospot.prototype.getContainer = function () { 6703 6704 return this.container; 6705 6706 }; 6707 6708 /** 6709 * This will be called by a click event 6710 * Translate and lock the hovering element if any 6711 * @param {object} event - Event containing mouseEvent with clientX and clientY 6712 */ 6713 PANOLENS.Infospot.prototype.onClick = function ( event ) { 6714 6715 if ( this.element && this.getContainer() ) { 6716 6717 this.onHoverStart( event ); 6718 6719 // Lock element 6720 this.lockHoverElement(); 6721 6722 } 6723 6724 }; 6725 6726 /** 6727 * Dismiss current element if any 6728 * @param {object} event - Dismiss event 6729 */ 6730 PANOLENS.Infospot.prototype.onDismiss = function ( event ) { 6731 6732 if ( this.element ) { 6733 6734 this.unlockHoverElement(); 6735 this.onHoverEnd(); 6736 6737 } 6738 6739 }; 6740 6741 /** 6742 * This will be called by a mouse hover event 6743 * Translate the hovering element if any 6744 * @param {object} event - Event containing mouseEvent with clientX and clientY 6745 */ 6746 PANOLENS.Infospot.prototype.onHover = function ( event ) { 6747 6748 }; 6749 6750 /** 6751 * This will be called on a mouse hover start 6752 * Sets cursor style to 'pointer', display the element and scale up the infospot 6753 */ 6754 PANOLENS.Infospot.prototype.onHoverStart = function ( event ) { 6755 6756 if ( !this.getContainer() ) { return; } 6757 6758 var cursorStyle = this.cursorStyle || ( this.mode === PANOLENS.Modes.NORMAL ? 'pointer' : 'default' ); 6759 6760 this.isHovering = true; 6761 this.container.style.cursor = cursorStyle; 6762 6763 if ( this.animated ) { 6764 6765 this.scaleDownAnimation && this.scaleDownAnimation.stop(); 6766 this.scaleUpAnimation && this.scaleUpAnimation.start(); 6767 6768 } 6769 6770 if ( this.element && event.mouseEvent.clientX >= 0 && event.mouseEvent.clientY >= 0 ) { 6771 6772 if ( this.mode === PANOLENS.Modes.CARDBOARD || this.mode === PANOLENS.Modes.STEREO ) { 6773 6774 this.element.style.display = 'none'; 6775 this.element.left && ( this.element.left.style.display = 'block' ); 6776 this.element.right && ( this.element.right.style.display = 'block' ); 6777 6778 // Store element width for reference 6779 this.element._width = this.element.left.clientWidth; 6780 this.element._height = this.element.left.clientHeight; 6781 6782 } else { 6783 6784 this.element.style.display = 'block'; 6785 this.element.left && ( this.element.left.style.display = 'none' ); 6786 this.element.right && ( this.element.right.style.display = 'none' ); 6787 6788 // Store element width for reference 6789 this.element._width = this.element.clientWidth; 6790 this.element._height = this.element.clientHeight; 6791 6792 } 6793 6794 } 6795 6796 }; 6797 6798 /** 6799 * This will be called on a mouse hover end 6800 * Sets cursor style to 'default', hide the element and scale down the infospot 6801 */ 6802 PANOLENS.Infospot.prototype.onHoverEnd = function () { 6803 6804 if ( !this.getContainer() ) { return; } 6805 6806 this.isHovering = false; 6807 this.container.style.cursor = 'default'; 6808 6809 if ( this.animated ) { 6810 6811 this.scaleUpAnimation && this.scaleUpAnimation.stop(); 6812 this.scaleDownAnimation && this.scaleDownAnimation.start(); 6813 6814 } 6815 6816 if ( this.element && !this.element.locked ) { 6817 6818 this.element.style.display = 'none'; 6819 this.element.left && ( this.element.left.style.display = 'none' ); 6820 this.element.right && ( this.element.right.style.display = 'none' ); 6821 6822 this.unlockHoverElement(); 6823 6824 } 6825 6826 }; 6827 6828 /** 6829 * On dual eye effect handler 6830 * Creates duplicate left and right element 6831 * @param {object} event - panolens-dual-eye-effect event 6832 */ 6833 PANOLENS.Infospot.prototype.onDualEyeEffect = function ( event ) { 6834 6835 if ( !this.getContainer() ) { return; } 6836 6837 var element, halfWidth, halfHeight; 6838 6839 this.mode = event.mode; 6840 6841 element = this.element; 6842 6843 halfWidth = this.container.clientWidth / 2; 6844 halfHeight = this.container.clientHeight / 2; 6845 6846 if ( !element ) { 6847 6848 return; 6849 6850 } 6851 6852 if ( !element.left || !element.right ) { 6853 6854 element.left = element.cloneNode( true ); 6855 element.right = element.cloneNode( true ); 6856 6857 } 6858 6859 if ( this.mode === PANOLENS.Modes.CARDBOARD || this.mode === PANOLENS.Modes.STEREO ) { 6860 6861 element.left.style.display = element.style.display; 6862 element.right.style.display = element.style.display; 6863 element.style.display = 'none'; 6864 6865 } else { 6866 6867 element.style.display = element.left.style.display; 6868 element.left.style.display = 'none'; 6869 element.right.style.display = 'none'; 6870 6871 } 6872 6873 // Update elements translation 6874 this.translateElement( halfWidth, halfHeight ); 6875 6876 this.container.appendChild( element.left ); 6877 this.container.appendChild( element.right ); 6878 6879 }; 6880 6881 /** 6882 * Translate the hovering element by css transform 6883 * @param {number} x - X position on the window screen 6884 * @param {number} y - Y position on the window screen 6885 */ 6886 PANOLENS.Infospot.prototype.translateElement = function ( x, y ) { 6887 6888 if ( !this.element._width || !this.element._height || !this.getContainer() ) { 6889 6890 return; 6891 6892 } 6893 6894 var left, top, element, width, height, delta, container; 6895 6896 container = this.container; 6897 element = this.element; 6898 width = element._width / 2; 6899 height = element._height / 2; 6900 delta = element.verticalDelta !== undefined ? element.verticalDelta : 40; 6901 6902 left = x - width; 6903 top = y - height - delta; 6904 6905 if ( ( this.mode === PANOLENS.Modes.CARDBOARD || this.mode === PANOLENS.Modes.STEREO ) 6906 && element.left && element.right 6907 && !( x === container.clientWidth / 2 && y === container.clientHeight / 2 ) ) { 6908 6909 left = container.clientWidth / 4 - width + ( x - container.clientWidth / 2 ); 6910 top = container.clientHeight / 2 - height - delta + ( y - container.clientHeight / 2 ); 6911 6912 this.setElementStyle( 'transform', element.left, 'translate(' + left + 'px, ' + top + 'px)' ); 6913 6914 left += container.clientWidth / 2; 6915 6916 this.setElementStyle( 'transform', element.right, 'translate(' + left + 'px, ' + top + 'px)' ); 6917 6918 } else { 6919 6920 this.setElementStyle( 'transform', element, 'translate(' + left + 'px, ' + top + 'px)' ); 6921 6922 } 6923 6924 }; 6925 6926 /** 6927 * Set vendor specific css 6928 * @param {string} type - CSS style name 6929 * @param {HTMLElement} element - The element to be modified 6930 * @param {string} value - Style value 6931 */ 6932 PANOLENS.Infospot.prototype.setElementStyle = function ( type, element, value ) { 6933 6934 var style = element.style; 6935 6936 if ( type === 'transform' ) { 6937 6938 style.webkitTransform = style.msTransform = style.transform = value; 6939 6940 } 6941 6942 }; 6943 6944 /** 6945 * Set hovering text content 6946 * @param {string} text - Text to be displayed 6947 */ 6948 PANOLENS.Infospot.prototype.setText = function ( text ) { 6949 6950 if ( this.element ) { 6951 6952 this.element.textContent = text; 6953 6954 } 6955 6956 }; 6957 6958 /** 6959 * Set cursor css style on hover 6960 */ 6961 PANOLENS.Infospot.prototype.setCursorHoverStyle = function ( style ) { 6962 6963 this.cursorStyle = style; 6964 6965 }; 6966 6967 /** 6968 * Add hovering text element 6969 * @param {string} text - Text to be displayed 6970 * @param {number} [delta=40] - Vertical delta to the infospot 6971 */ 6972 PANOLENS.Infospot.prototype.addHoverText = function ( text, delta ) { 6973 6974 if ( !this.element ) { 6975 6976 this.element = document.createElement( 'div' ); 6977 this.element.style.display = 'none'; 6978 this.element.style.color = '#fff'; 6979 this.element.style.top = 0; 6980 this.element.style.maxWidth = '50%'; 6981 this.element.style.maxHeight = '50%'; 6982 this.element.style.textShadow = '0 0 3px #000000'; 6983 this.element.style.fontFamily = '"Trebuchet MS", Helvetica, sans-serif'; 6984 this.element.style.position = 'absolute'; 6985 this.element.classList.add( 'panolens-infospot' ); 6986 this.element.verticalDelta = delta !== undefined ? delta : 40; 6987 6988 } 6989 6990 this.setText( text ); 6991 6992 }; 6993 6994 /** 6995 * Add hovering element by cloning an element 6996 * @param {HTMLDOMElement} el - Element to be cloned and displayed 6997 * @param {number} [delta=40] - Vertical delta to the infospot 6998 */
6999 PANOLENS.Infospot.prototype.addHoverElement = function ( el, delta ) { 7000 7001 if ( !this.element ) { 7002 7003 this.element = el.cloneNode( true ); 7004 this.element.style.display = 'none'; 7005 this.element.style.top = 0; 7006 this.element.style.position = 'absolute'; 7007 this.element.classList.add( 'panolens-infospot' ); 7008 this.element.verticalDelta = delta !== undefined ? delta : 40; 7009 7010 } 7011 7012 }; 7013 7014 /** 7015 * Remove hovering element 7016 */ 7017 PANOLENS.Infospot.prototype.removeHoverElement = function () { 7018 7019 if ( this.element ) { 7020 7021 if ( this.element.left ) { 7022 7023 this.container.removeChild( this.element.left ); 7024 this.element.left = null; 7025 7026 } 7027 7028 if ( this.element.right ) { 7029 7030 this.container.removeChild( this.element.right ); 7031 this.element.right = null; 7032 7033 } 7034 7035 this.container.removeChild( this.element ); 7036 this.element = null; 7037 7038 } 7039 7040 }; 7041 7042 /** 7043 * Lock hovering element 7044 */ 7045 PANOLENS.Infospot.prototype.lockHoverElement = function () { 7046 7047 if ( this.element ) { 7048 7049 this.element.locked = true; 7050 7051 } 7052 7053 }; 7054 7055 /** 7056 * Unlock hovering element 7057 */ 7058 PANOLENS.Infospot.prototype.unlockHoverElement = function () { 7059 7060 if ( this.element ) { 7061 7062 this.element.locked = false; 7063 7064 } 7065 7066 }; 7067 7068 /** 7069 * Show infospot 7070 * @param {number} [delay=0] - Delay time to show 7071 */ 7072 PANOLENS.Infospot.prototype.show = function ( delay ) { 7073 7074 delay = delay || 0; 7075 7076 if ( this.animated ) { 7077 7078 this.hideAnimation && this.hideAnimation.stop(); 7079 this.showAnimation && this.showAnimation.delay( delay ).start(); 7080 7081 } 7082 7083 }; 7084 7085 /** 7086 * Hide infospot 7087 * @param {number} [delay=0] - Delay time to hide 7088 */ 7089 PANOLENS.Infospot.prototype.hide = function ( delay ) { 7090 7091 delay = delay || 0; 7092 7093 if ( this.animated ) { 7094 7095 this.showAnimation && this.showAnimation.stop(); 7096 this.hideAnimation && this.hideAnimation.delay( delay ).start(); 7097 7098 } 7099 7100 7101 }; 7102 7103 /** 7104 * Set focus event handler 7105 */ 7106 PANOLENS.Infospot.prototype.setFocusMethod = function ( event ) { 7107 7108 if ( event ) { 7109 7110 this.HANDLER_FOCUS = event.method; 7111 7112 } 7113 7114 }; 7115 7116 /** 7117 * Focus camera center to this infospot 7118 * @param {number} [duration=1000] - Duration to tween 7119 * @param {function} [easing=TWEEN.Easing.Exponential.Out] - Easing function 7120 */ 7121 PANOLENS.Infospot.prototype.focus = function ( duration, easing ) { 7122 7123 if ( this.HANDLER_FOCUS ) { 7124 7125 this.HANDLER_FOCUS( this.position, duration, easing ); 7126 this.onDismiss(); 7127 7128 } 7129 7130 }; 7131 7132 /** 7133 * Dispose infospot 7134 */ 7135 PANOLENS.Infospot.prototype.dispose = function () { 7136 7137 this.removeHoverElement(); 7138 this.material.dispose(); 7139 7140 if ( this.parent ) { 7141 7142 this.parent.remove( this ); 7143 7144 } 7145 7146 }; 7147 7148} )();;( function () { 7149 7150 'use strict'; 7151 7152 /** 7153 * Viewer contains pre-defined scene, camera and renderer 7154 * @constructor 7155 * @param {object} [options] - Use custom or default config options 7156 * @param {HTMLElement} [options.container] - A HTMLElement to host the canvas 7157 * @param {THREE.Scene} [options.scene=THREE.Scene] - A THREE.Scene which contains panorama and 3D objects 7158 * @param {THREE.Camera} [options.camera=THREE.PerspectiveCamera] - A THREE.Camera to view the scene 7159 * @param {THREE.WebGLRenderer} [options.renderer=THREE.WebGLRenderer] - A THREE.WebGLRenderer to render canvas 7160 * @param {boolean} [options.controlBar=true] - Show/hide control bar on the bottom of the container 7161 * @param {array} [options.controlButtons=[]] - Button names to mount on controlBar if controlBar exists, Defaults to ['fullscreen', 'setting', 'video'] 7162 * @param {boolean} [options.autoHideControlBar=false] - Auto hide control bar when click on non-active area 7163 * @param {boolean} [options.autoHideInfospot=true] - Auto hide infospots when click on non-active area 7164 * @param {boolean} [options.horizontalView=false] - Allow only horizontal camera control 7165 * @param {number} [options.clickTolerance=10] - Distance tolerance to tigger click / tap event 7166 * @param {number} [options.cameraFov=60] - Camera field of view value 7167 * @param {boolean} [options.reverseDragging=false] - Reverse dragging direction 7168 * @param {boolean} [options.enableReticle=false] - Enable reticle for mouseless interaction other than VR mode 7169 * @param {number} [options.dwellTime=1500] - Dwell time for reticle selection 7170 * @param {boolean} [options.autoReticleSelect=true] - Auto select a clickable target after dwellTime 7171 * @param {boolean} [options.viewIndicator=false] - Adds an angle view indicator in upper left corner 7172 * @param {number} [options.indicatorSize=30] - Size of View Indicator 7173 * @param {string} [options.output='none'] - Whether and where to output raycast position. Could be 'console' or 'overlay' 7174 */ 7175 PANOLENS.Viewer = function ( options ) { 7176 7177 THREE.EventDispatcher.call( this ); 7178 7179 if ( !THREE ) { 7180 7181 console.error('Three.JS not found'); 7182 7183 return; 7184 } 7185 7186 var container; 7187 7188 options = options || {}; 7189 options.controlBar = options.controlBar !== undefined ? options.controlBar : true; 7190 options.controlButtons = options.controlButtons || [ 'fullscreen', 'setting', 'video' ]; 7191 options.autoHideControlBar = options.autoHideControlBar !== undefined ? options.autoHideControlBar : false; 7192 options.autoHideInfospot = options.autoHideInfospot !== undefined ? options.autoHideInfospot : true; 7193 options.horizontalView = options.horizontalView !== undefined ? options.horizontalView : false; 7194 options.clickTolerance = options.clickTolerance || 10; 7195 options.cameraFov = options.cameraFov || 60; 7196 options.reverseDragging = options.reverseDragging || false; 7197 options.enableReticle = options.enableReticle || false; 7198 options.dwellTime = options.dwellTime || 1500; 7199 options.autoReticleSelect = options.autoReticleSelect !== undefined ? options.autoReticleSelect : true; 7200 options.viewIndicator = options.viewIndicator !== undefined ? options.viewIndicator : false; 7201 options.indicatorSize = options.indicatorSize || 30; 7202 options.output = options.output ? options.output : 'none'; 7203 7204 this.options = options; 7205 7206 // Container 7207 if ( options.container ) { 7208 7209 container = options.container; 7210 container._width = container.clientWidth;
7211 container._height = container.clientHeight; 7212 7213 } else { 7214 7215 container = document.createElement( 'div' ); 7216 container.classList.add( 'panolens-container' ); 7217 container.style.width = '100%'; 7218 container.style.height = '100%'; 7219 container._width = window.innerWidth; 7220 container._height = window.innerHeight; 7221 document.body.appendChild( container ); 7222 7223 } 7224 7225 this.container = container; 7226 7227 this.camera = options.camera || new THREE.PerspectiveCamera( this.options.cameraFov, this.container.clientWidth / this.container.clientHeight, 1, 10000 ); 7228 this.scene = options.scene || new THREE.Scene(); 7229 this.renderer = options.renderer || new THREE.WebGLRenderer( { alpha: true, antialias: false } ); 7230 7231 this.viewIndicatorSize = options.indicatorSize; 7232 7233 this.reticle = {}; 7234 this.tempEnableReticle = this.options.enableReticle; 7235 7236 this.mode = PANOLENS.Modes.NORMAL; 7237 7238 this.OrbitControls; 7239 this.DeviceOrientationControls; 7240 7241 this.CardboardEffect; 7242 this.StereoEffect; 7243 7244 this.controls; 7245 this.effect; 7246 this.panorama; 7247 this.widget; 7248 7249 this.hoverObject; 7250 this.infospot; 7251 this.pressEntityObject; 7252 this.pressObject; 7253 7254 this.raycaster = new THREE.Raycaster(); 7255 this.raycasterPoint = new THREE.Vector2(); 7256 this.userMouse = new THREE.Vector2(); 7257 this.updateCallbacks = []; 7258 this.requestAnimationId; 7259 7260 this.cameraFrustum = new THREE.Frustum(); 7261 this.cameraViewProjectionMatrix = new THREE.Matrix4(); 7262 7263 this.outputDivElement; 7264 7265 // Handler references 7266 this.HANDLER_MOUSE_DOWN = this.onMouseDown.bind( this ); 7267 this.HANDLER_MOUSE_UP = this.onMouseUp.bind( this ); 7268 this.HANDLER_MOUSE_MOVE = this.onMouseMove.bind( this ); 7269 this.HANDLER_WINDOW_RESIZE = this.onWindowResize.bind( this ); 7270 this.HANDLER_KEY_DOWN = this.onKeyDown.bind( this ); 7271 this.HANDLER_KEY_UP = this.onKeyUp.bind( this ); 7272 this.HANDLER_TAP = this.onTap.bind( this, { 7273 clientX: this.container.clientWidth / 2, 7274 clientY: this.container.clientHeight / 2 7275 } ); 7276 7277 // Flag for infospot output 7278 this.OUTPUT_INFOSPOT = false; 7279 7280 // Animations 7281 this.tweenLeftAnimation = new TWEEN.Tween(); 7282 this.tweenUpAnimation = new TWEEN.Tween(); 7283 7284 // Renderer 7285 this.renderer.setPixelRatio( window.devicePixelRatio ); 7286 this.renderer.setSize( this.container.clientWidth, this.container.clientHeight ); 7287 this.renderer.setClearColor( 0x000000, 1 ); 7288 this.renderer.sortObjects = false; 7289 7290 // Append Renderer Element to container 7291 this.renderer.domElement.classList.add( 'panolens-canvas' ); 7292 this.renderer.domElement.style.display = 'block'; 7293 this.container.style.backgroundColor = '#000'; 7294 this.container.appendChild( this.renderer.domElement ); 7295 7296 // Camera Controls 7297 this.OrbitControls = new THREE.OrbitControls( this.camera, this.container ); 7298 this.OrbitControls.name = 'orbit'; 7299 this.OrbitControls.minDistance = 1; 7300 this.OrbitControls.noPan = true; 7301 this.DeviceOrientationControls = new THREE.DeviceOrientationControls( this.camera, this.container ); 7302 this.DeviceOrientationControls.name = 'device-orientation'; 7303 this.DeviceOrientationControls.enabled = false; 7304 this.camera.position.z = 1; 7305 7306 // Register change event if passiveRenering 7307 if ( this.options.passiveRendering ) { 7308 7309 console.warn( 'passiveRendering is now deprecated' ); 7310 7311 } 7312 7313 // Controls 7314 this.controls = [ this.OrbitControls, this.DeviceOrientationControls ]; 7315 this.control = this.OrbitControls; 7316 7317 // Cardboard effect 7318 this.CardboardEffect = new THREE.CardboardEffect( this.renderer ); 7319 this.CardboardEffect.setSize( this.container.clientWidth, this.container.clientHeight ); 7320 7321 // Stereo effect 7322 this.StereoEffect = new THREE.StereoEffect( this.renderer ); 7323 this.StereoEffect.setSize( this.container.clientWidth, this.container.clientHeight ); 7324 7325 this.effect = this.CardboardEffect; 7326 7327 // Add default hidden reticle 7328 this.addReticle(); 7329 7330 // Lock horizontal view 7331 if ( this.options.horizontalView ) { 7332 this.OrbitControls.minPolarAngle = Math.PI / 2; 7333 this.OrbitControls.maxPolarAngle = Math.PI / 2; 7334 } 7335 7336 // Add Control UI 7337 if ( this.options.controlBar !== false ) { 7338 this.addDefaultControlBar( this.options.controlButtons ); 7339 } 7340 7341 // Add View Indicator 7342 if ( this.options.viewIndicator ) { 7343 this.addViewIndicator(); 7344 } 7345 7346 // Reverse dragging direction 7347 if ( this.options.reverseDragging ) { 7348 this.reverseDraggingDirection(); 7349 } 7350 7351 // Register event if reticle is enabled, otherwise defaults to mouse 7352 if ( this.options.enableReticle ) { 7353 this.enableReticleControl(); 7354 } else { 7355 this.registerMouseAndTouchEvents(); 7356 } 7357 7358 if ( this.options.output === 'overlay' ) {
7359 this.addOutputElement(); 7360 } 7361 7362 // Register dom event listeners 7363 this.registerEventListeners(); 7364 7365 // Animate 7366 this.animate.call( this ); 7367 7368 }; 7369 7370 PANOLENS.Viewer.prototype = Object.create( THREE.EventDispatcher.prototype ); 7371 7372 PANOLENS.Viewer.prototype.constructor = PANOLENS.Viewer; 7373 7374 /** 7375 * Add an object to the scene 7376 * Automatically hookup with panolens-viewer-handler listener 7377 * to communicate with viewer method 7378 * @param {THREE.Object3D} object - The object to be added 7379 */ 7380 PANOLENS.Viewer.prototype.add = function ( object ) { 7381 7382 if ( arguments.length > 1 ) { 7383 7384 for ( var i = 0; i < arguments.length; i ++ ) { 7385 7386 this.add( arguments[ i ] ); 7387 7388 } 7389 7390 return this; 7391 7392 } 7393 7394 this.scene.add( object ); 7395 7396 // All object added to scene has 'panolens-viewer-handler' event to handle viewer communication 7397 if ( object.addEventListener ) { 7398 7399 object.addEventListener( 'panolens-viewer-handler', this.eventHandler.bind( this ) ); 7400 7401 } 7402 7403 // All object added to scene being passed with container 7404 if ( object instanceof PANOLENS.Panorama && object.dispatchEvent ) { 7405 7406 object.dispatchEvent( { type: 'panolens-container', container: this.container } ); 7407 7408 } 7409 7410 // Hookup default panorama event listeners 7411 if ( object.type === 'panorama' ) { 7412 7413 this.addPanoramaEventListener( object ); 7414 7415 if ( !this.panorama ) { 7416 7417 this.setPanorama( object ); 7418 7419 } 7420 7421 } 7422 7423 }; 7424 7425 /** 7426 * Remove an object from the scene 7427 * @param {THREE.Object3D} object - Object to be removed 7428 */ 7429 PANOLENS.Viewer.prototype.remove = function ( object ) { 7430 7431 if ( object.removeEventListener ) { 7432 7433 object.removeEventListener( 'panolens-viewer-handler', this.eventHandler.bind( this ) ); 7434 7435 } 7436 7437 this.scene.remove( object ); 7438 7439 }; 7440 7441 /** 7442 * Add default control bar 7443 * @param {array} array - The control buttons array 7444 */ 7445 PANOLENS.Viewer.prototype.addDefaultControlBar = function ( array ) { 7446 7447 var scope = this; 7448 7449 if ( this.widget ) { 7450 7451 console.warn( 'Default control bar exists' ); 7452 return; 7453 7454 } 7455 7456 this.widget = new PANOLENS.Widget( this.container ); 7457 this.widget.addEventListener( 'panolens-viewer-handler', this.eventHandler.bind( this ) ); 7458 this.widget.addControlBar(); 7459 array.forEach( function( buttonName ){ 7460 7461 scope.widget.addControlButton( buttonName ); 7462 7463 } ); 7464 7465 }; 7466 7467 /** 7468 * Set a panorama to be the current one 7469 * @param {PANOLENS.Panorama} pano - Panorama to be set 7470 */ 7471 PANOLENS.Viewer.prototype.setPanorama = function ( pano ) { 7472 7473 var scope = this, leavingPanorama = this.panorama; 7474 7475 if ( pano.type === 'panorama' && leavingPanorama !== pano ) { 7476 7477 // Clear exisiting infospot 7478 this.hideInfospot(); 7479 7480 var afterEnterComplete = function () { 7481 7482 leavingPanorama && leavingPanorama.onLeave(); 7483 pano.removeEventListener( 'enter-fade-start', afterEnterComplete ); 7484 7485 }; 7486 7487 pano.addEventListener( 'enter-fade-start', afterEnterComplete ); 7488 7489 // Assign and enter panorama 7490 (this.panorama = pano).onEnter(); 7491 7492 } 7493 7494 }; 7495 7496 /** 7497 * Event handler to execute commands from child objects 7498 * @param {object} event - The dispatched event with method as function name and data as an argument 7499 */ 7500 PANOLENS.Viewer.prototype.eventHandler = function ( event ) { 7501 7502 if ( event.method && this[ event.method ] ) { 7503 7504 this[ event.method ]( event.data ); 7505 7506 } 7507 7508 }; 7509 7510 /** 7511 * Dispatch event to all descendants 7512 * @param {object} event - Event to be passed along 7513 */ 7514 PANOLENS.Viewer.prototype.dispatchEventToChildren = function ( event ) { 7515 7516 this.scene.traverse( function ( object ) { 7517 7518 if ( object.dispatchEvent ) { 7519 7520 object.dispatchEvent( event ); 7521 7522 } 7523 7524 }); 7525 7526 }; 7527 7528 /** 7529 * Set widget content 7530 * @param {integer} controlIndex - Control index 7531 * @param {PANOLENS.Modes} mode - Modes for effects 7532 */ 7533 PANOLENS.Viewer.prototype.activateWidgetItem = function ( controlIndex, mode ) { 7534 7535 var mainMenu = this.widget.mainMenu; 7536 var ControlMenuItem = mainMenu.children[ 0 ]; 7537 var ModeMenuItem = mainMenu.children[ 1 ]; 7538 7539 var item; 7540 7541 if ( controlIndex !== undefined ) { 7542 7543 switch ( controlIndex ) { 7544 7545 case 0: 7546 7547 item = ControlMenuItem.subMenu.children[ 1 ]; 7548 7549 break; 7550 7551 case 1: 7552 7553 item = ControlMenuItem.subMenu.children[ 2 ]; 7554 7555 break; 7556 7557 default: 7558 7559 item = ControlMenuItem.subMenu.children[ 1 ]; 7560 7561 break; 7562 7563 } 7564 7565 ControlMenuItem.subMenu.setActiveItem( item ) 7566 ControlMenuItem.setSelectionTitle( item.textContent ); 7567 7568 } 7569 7570 if ( mode !== undefined ) { 7571 7572 switch( mode ) { 7573 7574 case PANOLENS.Modes.CARDBOARD: 7575 7576 item = ModeMenuItem.subMenu.children[ 2 ]; 7577 7578 break; 7579 7580 case PANOLENS.Modes.STEREO: 7581 7582 item = ModeMenuItem.subMenu.children[ 3 ]; 7583 7584 break; 7585 7586 default: 7587 7588 item = ModeMenuItem.subMenu.children[ 1 ]; 7589 7590 break; 7591 } 7592 7593 ModeMenuItem.subMenu.setActiveItem( item ) 7594 ModeMenuItem.setSelectionTitle( item.textContent ); 7595 7596 } 7597 7598 }; 7599 7600 /** 7601 * Enable rendering effect 7602 * @param {PANOLENS.Modes} mode - Modes for effects 7603 */ 7604 PANOLENS.Viewer.prototype.enableEffect = function ( mode ) { 7605 7606 if ( this.mode === mode ) { return; } 7607 if ( mode === PANOLENS.Modes.NORMAL ) { this.disableEffect(); return; } 7608 else { this.mode = mode; } 7609 7610 var fov = this.camera.fov; 7611 7612 switch( mode ) { 7613 7614 case PANOLENS.Modes.CARDBOARD: 7615 7616 this.effect = this.CardboardEffect; 7617 this.enableReticleControl(); 7618 7619 break; 7620 7621 case PANOLENS.Modes.STEREO: 7622 7623 this.effect = this.StereoEffect; 7624 this.enableReticleControl(); 7625 7626 break; 7627 7628 default: 7629 7630 this.effect = null; 7631 this.disableReticleControl(); 7632 7633 break; 7634 7635 } 7636 7637 this.activateWidgetItem( undefined, this.mode ); 7638 7639 /** 7640 * Dual eye effect event 7641 * @type {object} 7642 * @event PANOLENS.Viewer#panolens-dual-eye-effect 7643 * @event PANOLENS.Infospot#panolens-dual-eye-effect 7644 * @property {PANOLENS.Modes} mode - Current display mode 7645 */ 7646 this.dispatchEventToChildren( { type: 'panolens-dual-eye-effect', mode: this.mode } ); 7647 7648 // Force effect stereo camera to update by refreshing fov 7649 this.camera.fov = fov + 10e-3; 7650 this.effect.setSize( this.container.clientWidth, this.container.clientHeight ); 7651 this.render(); 7652 this.camera.fov = fov; 7653 7654 }; 7655 7656 /** 7657 * Disable additional rendering effect 7658 */ 7659 PANOLENS.Viewer.prototype.disableEffect = function () { 7660 7661 if ( this.mode === PANOLENS.Modes.NORMAL ) { return; } 7662 7663 this.mode = PANOLENS.Modes.NORMAL; 7664 this.disableReticleControl(); 7665 7666 this.activateWidgetItem( undefined, this.mode ); 7667 7668 /** 7669 * Dual eye effect event 7670 * @type {object} 7671 * @event PANOLENS.Viewer#panolens-dual-eye-effect 7672 * @event PANOLENS.Infospot#panolens-dual-eye-effect 7673 * @property {PANOLENS.Modes} mode - Current display mode 7674 */ 7675 this.dispatchEventToChildren( { type: 'panolens-dual-eye-effect', mode: this.mode } ); 7676 7677 this.renderer.setSize( this.container.clientWidth, this.container.clientHeight ); 7678 this.render(); 7679 }; 7680 7681 /** 7682 * Enable reticle control 7683 */ 7684 PANOLENS.Viewer.prototype.enableReticleControl = function () { 7685 7686 if ( this.reticle.visible ) { return; } 7687 if ( !this.reticle.textureLoaded ) { this.reticle.loadTextures(); } 7688 7689 this.tempEnableReticle = true; 7690 7691 // Register reticle event and unregister mouse event 7692 this.unregisterMouseAndTouchEvents(); 7693 this.reticle.show(); 7694 this.registerReticleEvent(); 7695 this.updateReticleEvent(); 7696 7697 }; 7698 7699 /** 7700 * Disable reticle control 7701 */ 7702 PANOLENS.Viewer.prototype.disableReticleControl = function () { 7703 7704 this.tempEnableReticle = false; 7705 7706 // Register mouse event and unregister reticle event 7707 if ( !this.options.enableReticle ) { 7708 7709 this.reticle.hide(); 7710 this.unregisterReticleEvent(); 7711 this.registerMouseAndTouchEvents(); 7712 7713 } else { 7714 7715 this.updateReticleEvent(); 7716 7717 } 7718 7719 }; 7720 7721 /** 7722 * Toggle video play or stop 7723 * @fires PANOLENS.Viewer#video-toggle 7724 */ 7725 PANOLENS.Viewer.prototype.toggleVideoPlay = function ( pause ) { 7726 7727 if ( this.panorama instanceof PANOLENS.VideoPanorama ) { 7728 7729 /** 7730 * Toggle video event 7731 * @type {object} 7732 * @event PANOLENS.Viewer#video-toggle 7733 */ 7734 this.panorama.dispatchEvent( { type: 'video-toggle', pause: pause } ); 7735 7736 } 7737 7738 }; 7739 7740 /** 7741 * Set currentTime in a video 7742 * @param {number} percentage - Percentage of a video. Range from 0.0 to 1.0 7743 * @fires PANOLENS.Viewer#video-time 7744 */ 7745 PANOLENS.Viewer.prototype.setVideoCurrentTime = function ( percentage ) { 7746 7747 if ( this.panorama instanceof PANOLENS.VideoPanorama ) { 7748 7749 /** 7750 * Setting video time event 7751 * @type {object} 7752 * @event PANOLENS.Viewer#video-time 7753 * @property {number} percentage - Percentage of a video. Range from 0.0 to 1.0 7754 */ 7755 this.panorama.dispatchEvent( { type: 'video-time', percentage: percentage } ); 7756 7757 } 7758 7759 }; 7760 7761 /** 7762 * This will be called when video updates if an widget is present 7763 * @param {number} percentage - Percentage of a video. Range from 0.0 to 1.0 7764 * @fires PANOLENS.Viewer#video-update 7765 */ 7766 PANOLENS.Viewer.prototype.onVideoUpdate = function ( percentage ) { 7767 7768 /** 7769 * Video update event 7770 * @type {object} 7771 * @event PANOLENS.Viewer#video-update 7772 * @property {number} percentage - Percentage of a video. Range from 0.0 to 1.0 7773 */
7774 this.widget && this.widget.dispatchEvent( { type: 'video-update', percentage: percentage } ); 7775 7776 }; 7777 7778 /** 7779 * Add update callback to be called every animation frame 7780 */ 7781 PANOLENS.Viewer.prototype.addUpdateCallback = function ( fn ) { 7782 7783 if ( fn ) { 7784 7785 this.updateCallbacks.push( fn ); 7786 7787 } 7788 7789 }; 7790 7791 /** 7792 * Remove update callback 7793 * @param {Function} fn - The function to be removed 7794 */ 7795 PANOLENS.Viewer.prototype.removeUpdateCallback = function ( fn ) { 7796 7797 var index = this.updateCallbacks.indexOf( fn ); 7798 7799 if ( fn && index >= 0 ) { 7800 7801 this.updateCallbacks.splice( index, 1 ); 7802 7803 } 7804 7805 }; 7806 7807 /** 7808 * Show video widget 7809 */ 7810 PANOLENS.Viewer.prototype.showVideoWidget = function () { 7811 7812 /** 7813 * Show video widget event 7814 * @type {object} 7815 * @event PANOLENS.Viewer#video-control-show 7816 */ 7817 this.widget && this.widget.dispatchEvent( { type: 'video-control-show' } ); 7818 7819 }; 7820 7821 /** 7822 * Hide video widget 7823 */ 7824 PANOLENS.Viewer.prototype.hideVideoWidget = function () { 7825 7826 /** 7827 * Hide video widget 7828 * @type {object} 7829 * @event PANOLENS.Viewer#video-control-hide 7830 */
7831 this.widget && this.widget.dispatchEvent( { type: 'video-control-hide' } ); 7832 7833 }; 7834 7835 PANOLENS.Viewer.prototype.updateVideoPlayButton = function ( paused ) { 7836 7837 if ( this.widget && 7838 this.widget.videoElement && 7839 this.widget.videoElement.controlButton ) { 7840 7841 this.widget.videoElement.controlButton.update( paused ); 7842 7843 } 7844 7845 }; 7846 7847 /** 7848 * Add default panorama event listeners 7849 * @param {PANOLENS.Panorama} pano - The panorama to be added with event listener 7850 */ 7851 PANOLENS.Viewer.prototype.addPanoramaEventListener = function ( pano ) { 7852 7853 var scope = this; 7854 7855 // Set camera control on every panorama 7856 pano.addEventListener( 'enter-fade-start', this.setCameraControl.bind( this ) ); 7857 7858 // Show and hide widget event only when it's PANOLENS.VideoPanorama 7859 if ( pano instanceof PANOLENS.VideoPanorama ) { 7860 7861 pano.addEventListener( 'enter-fade-start', this.showVideoWidget.bind( this ) ); 7862 pano.addEventListener( 'leave', function () { 7863 7864 if ( !(this.panorama instanceof PANOLENS.VideoPanorama) ) { 7865 7866 this.hideVideoWidget.call( this ); 7867 7868 } 7869 7870 }.bind( this ) ); 7871 7872 } 7873 7874 }; 7875 7876 /** 7877 * Set camera control 7878 */ 7879 PANOLENS.Viewer.prototype.setCameraControl = function () { 7880 7881 this.OrbitControls.target.copy( this.panorama.position ); 7882 7883 }; 7884 7885 /** 7886 * Get current camera control 7887 * @return {object} - Current navigation control. THREE.OrbitControls or THREE.DeviceOrientationControls 7888 */ 7889 PANOLENS.Viewer.prototype.getControl = function () { 7890 7891 return this.control; 7892 7893 }, 7894 7895 /** 7896 * Get scene 7897 * @return {THREE.Scene} - Current scene which the viewer is built on 7898 */ 7899 PANOLENS.Viewer.prototype.getScene = function () { 7900 7901 return this.scene; 7902 7903 }; 7904 7905 /** 7906 * Get camera 7907 * @return {THREE.Camera} - The scene camera 7908 */ 7909 PANOLENS.Viewer.prototype.getCamera = function () { 7910 7911 return this.camera; 7912 7913 }, 7914 7915 /** 7916 * Get renderer 7917 * @return {THREE.WebGLRenderer} - The renderer using webgl 7918 */ 7919 PANOLENS.Viewer.prototype.getRenderer = function () { 7920 7921 return this.renderer; 7922 7923 }; 7924 7925 /** 7926 * Get container 7927 * @return {HTMLDOMElement} - The container holds rendererd canvas 7928 */ 7929 PANOLENS.Viewer.prototype.getContainer = function () { 7930 7931 return this.container; 7932 7933 }; 7934 7935 /** 7936 * Get control name 7937 * @return {string} - Control name. 'orbit' or 'device-orientation' 7938 */ 7939 PANOLENS.Viewer.prototype.getControlName = function () { 7940 7941 return this.control.name; 7942 7943 }; 7944 7945 /** 7946 * Get next navigation control name 7947 * @return {string} - Next control name 7948 */ 7949 PANOLENS.Viewer.prototype.getNextControlName = function () { 7950 7951 return this.controls[ this.getNextControlIndex() ].name; 7952 7953 }; 7954 7955 /** 7956 * Get next navigation control index 7957 * @return {number} - Next control index 7958 */ 7959 PANOLENS.Viewer.prototype.getNextControlIndex = function () { 7960 7961 var controls, control, nextIndex; 7962 7963 controls = this.controls; 7964 control = this.control; 7965 nextIndex = controls.indexOf( control ) + 1; 7966 7967 return ( nextIndex >= controls.length ) ? 0 : nextIndex; 7968 7969 }; 7970 7971 /** 7972 * Set field of view of camera 7973 */ 7974 PANOLENS.Viewer.prototype.setCameraFov = function ( fov ) { 7975 7976 this.camera.fov = fov; 7977 this.camera.updateProjectionMatrix(); 7978 7979 }; 7980 7981 /** 7982 * Enable control by index 7983 * @param {PANOLENS.Controls} index - Index of camera control 7984 */ 7985 PANOLENS.Viewer.prototype.enableControl = function ( index ) { 7986 7987 index = ( index >= 0 && index < this.controls.length ) ? index : 0; 7988 7989 this.control.enabled = false; 7990 7991 this.control = this.controls[ index ]; 7992 7993 this.control.enabled = true; 7994 7995 switch ( index ) { 7996 7997 case PANOLENS.Controls.ORBIT: 7998 7999 this.camera.position.copy( this.panorama.position ); 8000 this.camera.position.z += 1; 8001 8002 break; 8003 8004 case PANOLENS.Controls.DEVICEORIENTATION: 8005 8006 this.camera.position.copy( this.panorama.position ); 8007 8008 break; 8009 8010 default: 8011 8012 break; 8013 } 8014 8015 this.control.update(); 8016 8017 this.activateWidgetItem( index, undefined ); 8018 8019 }; 8020 8021 /** 8022 * Disable current control 8023 */ 8024 PANOLENS.Viewer.prototype.disableControl = function () { 8025 8026 this.control.enabled = false; 8027 8028 }; 8029 8030 /** 8031 * Toggle next control 8032 */ 8033 PANOLENS.Viewer.prototype.toggleNextControl = function () { 8034 8035 this.enableControl( this.getNextControlIndex() ); 8036 8037 }; 8038 8039 /** 8040 * Screen Space Projection 8041 */ 8042 PANOLENS.Viewer.prototype.getScreenVector = function ( worldVector ) { 8043 8044 var vector = worldVector.clone(); 8045 var widthHalf = ( this.container.clientWidth ) / 2; 8046 var heightHalf = this.container.clientHeight / 2; 8047 8048 vector.project( this.camera ); 8049
8050 vector.x = ( vector.x * widthHalf ) + widthHalf; 8051 vector.y = - ( vector.y * heightHalf ) + heightHalf; 8052 vector.z = 0; 8053 8054 return vector; 8055 8056 }; 8057 8058 /** 8059 * Check Sprite in Viewport 8060 */ 8061 PANOLENS.Viewer.prototype.checkSpriteInViewport = function ( sprite ) { 8062 8063 this.camera.matrixWorldInverse.getInverse( this.camera.matrixWorld ); 8064 this.cameraViewProjectionMatrix.multiplyMatrices( this.camera.projectionMatrix, this.camera.matrixWorldInverse ); 8065 this.cameraFrustum.setFromMatrix( this.cameraViewProjectionMatrix ); 8066 8067 return sprite.visible && this.cameraFrustum.intersectsSprite( sprite ); 8068 8069 }; 8070 8071 /** 8072 * Reverse dragging direction 8073 */ 8074 PANOLENS.Viewer.prototype.reverseDraggingDirection = function () { 8075 8076 this.OrbitControls.rotateSpeed *= -1; 8077 this.OrbitControls.momentumScalingFactor *= -1; 8078 8079 }; 8080 8081 /** 8082 * Add reticle 8083 */ 8084 PANOLENS.Viewer.prototype.addReticle = function () { 8085 8086 this.reticle = new PANOLENS.Reticle( 0xffffff, 8087 this.options.autoReticleSelect, 8088 PANOLENS.DataImage.ReticleIdle, 8089 PANOLENS.DataImage.ReticleDwell, 8090 this.options.dwellTime, 8091 45 ); 8092 this.reticle.position.z = -10; 8093 this.camera.add( this.reticle ); 8094 this.scene.add( this.camera ); 8095 8096 }; 8097 8098 /** 8099 * Tween control looking center 8100 * @param {THREE.Vector3} vector - Vector to be looked at the center 8101 * @param {number} [duration=1000] - Duration to tween 8102 * @param {function} [easing=TWEEN.Easing.Exponential.Out] - Easing function 8103 */ 8104 PANOLENS.Viewer.prototype.tweenControlCenter = function ( vector, duration, easing ) { 8105 8106 if ( this.control !== this.OrbitControls ) { 8107 8108 return; 8109 8110 } 8111 8112 // Pass in arguments as array 8113 if ( vector instanceof Array ) { 8114 8115 duration = vector[ 1 ]; 8116 easing = vector[ 2 ]; 8117 vector = vector[ 0 ]; 8118 8119 } 8120 8121 duration = duration !== undefined ? duration : 1000; 8122 easing = easing || TWEEN.Easing.Exponential.Out; 8123 8124 var scope, ha, va, chv, cvv, hv, vv, vptc, ov, nv; 8125 8126 scope = this; 8127 8128 chv = this.camera.getWorldDirection(); 8129 cvv = chv.clone(); 8130 8131 vptc = this.panorama.getWorldPosition().sub( this.camera.getWorldPosition() ); 8132 8133 hv = vector.clone(); 8134 // Scale effect 8135 hv.x *= -1; 8136 hv.add( vptc ).normalize(); 8137 vv = hv.clone(); 8138 8139 chv.y = 0; 8140 hv.y = 0; 8141 8142 ha = Math.atan2( hv.z, hv.x ) - Math.atan2( chv.z, chv.x ); 8143 ha = ha > Math.PI ? ha - 2 * Math.PI : ha; 8144 ha = ha < -Math.PI ? ha + 2 * Math.PI : ha; 8145 va = Math.abs( cvv.angleTo( chv ) + ( cvv.y * vv.y <= 0 ? vv.angleTo( hv ) : -vv.angleTo( hv ) ) ); 8146 va *= vv.y < cvv.y ? 1 : -1; 8147 8148 ov = { left: 0, up: 0 }; 8149 nv = { left: 0, up: 0 }; 8150 8151 this.tweenLeftAnimation.stop(); 8152 this.tweenUpAnimation.stop(); 8153 8154 this.tweenLeftAnimation = new TWEEN.Tween( ov ) 8155 .to( { left: ha }, duration ) 8156 .easing( easing ) 8157 .onUpdate(function(){ 8158 scope.control.rotateLeft( this.left - nv.left ); 8159 nv.left = this.left; 8160 }) 8161 .start(); 8162 8163 this.tweenUpAnimation = new TWEEN.Tween( ov ) 8164 .to( { up: va }, duration ) 8165 .easing( easing ) 8166 .onUpdate(function(){ 8167 scope.control.rotateUp( this.up - nv.up ); 8168 nv.up = this.up; 8169 }) 8170 .start(); 8171 8172 }; 8173 8174 /** 8175 * Tween control looking center by object 8176 * @param {THREE.Object3D} object - Object to be looked at the center 8177 * @param {number} [duration=1000] - Duration to tween 8178 * @param {function} [easing=TWEEN.Easing.Exponential.Out] - Easing function 8179 */ 8180 PANOLENS.Viewer.prototype.tweenControlCenterByObject = function ( object, duration, easing ) { 8181 8182 var isUnderScalePlaceHolder = false; 8183 8184 object.traverseAncestors( function ( ancestor ) { 8185 8186 if ( ancestor.scalePlaceHolder ) { 8187 8188 isUnderScalePlaceHolder = true; 8189 8190 } 8191 } ); 8192 8193 if ( isUnderScalePlaceHolder ) { 8194 8195 var invertXVector = new THREE.Vector3( -1, 1, 1 ); 8196 8197 this.tweenControlCenter( object.getWorldPosition().multiply( invertXVector ), duration, easing ); 8198 8199 } else { 8200 8201 this.tweenControlCenter( object.getWorldPosition(), duration, easing ); 8202 8203 } 8204 8205 }; 8206 8207 /** 8208 * This is called when window size is changed 8209 * @fires PANOLENS.Viewer#window-resize 8210 * @param {number} [windowWidth] - Specify if custom element has changed width 8211 * @param {number} [windowHeight] - Specify if custom element has changed height 8212 */ 8213 PANOLENS.Viewer.prototype.onWindowResize = function ( windowWidth, windowHeight ) { 8214 8215 var width, height, expand; 8216 8217 expand = this.container.classList.contains( 'panolens-container' ) || this.container.isFullscreen;
8218 8219 if ( windowWidth !== undefined && windowHeight !== undefined ) { 8220 8221 width = windowWidth; 8222 height = windowHeight; 8223 this.container._width = windowWidth; 8224 this.container._height = windowHeight; 8225 8226 } else { 8227 8228 width = expand ? Math.max(document.documentElement.clientWidth, window.innerWidth || 0) : this.container.clientWidth; 8229 height = expand ? Math.max(document.documentElement.clientHeight, window.innerHeight || 0) : this.container.clientHeight; 8230 this.container._width = width; 8231 this.container._height = height; 8232 8233 } 8234 8235 this.camera.aspect = width / height; 8236 this.camera.updateProjectionMatrix(); 8237 8238 this.renderer.setSize( width, height ); 8239 8240 // Update reticle 8241 if ( this.options.enableReticle || this.tempEnableReticle ) { 8242 8243 this.updateReticleEvent(); 8244 8245 } 8246 8247 /** 8248 * Window resizing event 8249 * @type {object} 8250 * @event PANOLENS.Viewer#window-resize 8251 * @property {number} width - Width of the window 8252 * @property {number} height - Height of the window 8253 */ 8254 this.dispatchEvent( { type: 'window-resize', width: width, height: height }); 8255 this.scene.traverse( function ( object ) { 8256 8257 if ( object.dispatchEvent ) { 8258 8259 object.dispatchEvent( { type: 'window-resize', width: width, height: height }); 8260 8261 } 8262 8263 } ); 8264 8265 }; 8266 8267 PANOLENS.Viewer.prototype.addOutputElement = function () { 8268 8269 var element = document.createElement( 'div' ); 8270 element.style.position = 'absolute'; 8271 element.style.right = '10px'; 8272 element.style.top = '10px'; 8273 element.style.color = "#fff"; 8274 this.container.appendChild( element ); 8275 this.outputDivElement = element; 8276 8277 }; 8278 8279 /** 8280 * Output infospot attach position in developer console by holding down Ctrl button 8281 */ 8282 PANOLENS.Viewer.prototype.outputInfospotPosition = function () { 8283 8284 var intersects, point, panoramaWorldPosition, outputPosition; 8285 8286 intersects = this.raycaster.intersectObject( this.panorama, true ); 8287 8288 if ( intersects.length > 0 ) { 8289 8290 point = intersects[0].point; 8291 panoramaWorldPosition = this.panorama.getWorldPosition(); 8292 8293 // Panorama is scaled -1 on X axis 8294 outputPosition = new THREE.Vector3( 8295 -(point.x - panoramaWorldPosition.x).toFixed(2), 8296 (point.y - panoramaWorldPosition.y).toFixed(2), 8297 (point.z - panoramaWorldPosition.z).toFixed(2) 8298 ); 8299 8300 switch ( this.options.output ) { 8301 8302 case 'console': 8303 console.info( outputPosition.x + ', ' + outputPosition.y + ', ' + outputPosition.z ); 8304 break; 8305 8306 case 'overlay': 8307 this.outputDivElement.textContent = outputPosition.x + ', ' + outputPosition.y + ', ' + outputPosition.z; 8308 break; 8309 8310 default: 8311 break; 8312 8313 } 8314 8315 } 8316 8317 }; 8318 8319 PANOLENS.Viewer.prototype.onMouseDown = function ( event ) { 8320 8321 event.preventDefault(); 8322 8323 this.userMouse.x = ( event.clientX >= 0 ) ? event.clientX : event.touches[0].clientX; 8324 this.userMouse.y = ( event.clientY >= 0 ) ? event.clientY : event.touches[0].clientY; 8325 this.userMouse.type = 'mousedown'; 8326 this.onTap( event ); 8327 8328 }; 8329 8330 PANOLENS.Viewer.prototype.onMouseMove = function ( event ) { 8331 8332 event.preventDefault(); 8333 this.userMouse.type = 'mousemove'; 8334 this.onTap( event ); 8335 8336 }; 8337 8338 PANOLENS.Viewer.prototype.onMouseUp = function ( event ) { 8339 8340 var onTarget = false, type; 8341 8342 this.userMouse.type = 'mouseup'; 8343 8344 type = ( this.userMouse.x >= event.clientX - this.options.clickTolerance 8345 && this.userMouse.x <= event.clientX + this.options.clickTolerance 8346 && this.userMouse.y >= event.clientY - this.options.clickTolerance 8347 && this.userMouse.y <= event.clientY + this.options.clickTolerance ) 8348 || ( event.changedTouches 8349 && this.userMouse.x >= event.changedTouches[0].clientX - this.options.clickTolerance 8350 && this.userMouse.x <= event.changedTouches[0].clientX + this.options.clickTolerance 8351 && this.userMouse.y >= event.changedTouches[0].clientY - this.options.clickTolerance 8352 && this.userMouse.y <= event.changedTouches[0].clientY + this.options.clickTolerance ) 8353 ? 'click' : undefined; 8354 8355 // Event should happen on canvas 8356 if ( event && event.target && !event.target.classList.contains( 'panolens-canvas' ) ) { return; } 8357 8358 event.preventDefault(); 8359 8360 if ( event.changedTouches && event.changedTouches.length === 1 ) { 8361 8362 onTarget = this.onTap( { clientX : event.changedTouches[0].clientX, clientY : event.changedTouches[0].clientY }, type ); 8363 8364 } else { 8365 8366 onTarget = this.onTap( event, type ); 8367 8368 } 8369 8370 this.userMouse.type = 'none'; 8371 8372 if ( onTarget ) { 8373 8374 return; 8375 8376 } 8377 8378 if ( type === 'click' ) { 8379 8380 this.options.autoHideInfospot && this.panorama && this.panorama.toggleInfospotVisibility(); 8381 this.options.autoHideControlBar && this.toggleControlBar(); 8382 8383 } 8384 8385 }; 8386 8387 PANOLENS.Viewer.prototype.onTap = function ( event, type ) { 8388 8389 var intersects, intersect_entity, intersect; 8390 8391 this.raycasterPoint.x = ( ( event.clientX - this.container.offsetLeft ) / this.container.clientWidth ) * 2 - 1; 8392 this.raycasterPoint.y = - ( ( event.clientY - this.container.offsetTop ) / this.container.clientHeight ) * 2 + 1; 8393 8394 this.raycaster.setFromCamera( this.raycasterPoint, this.camera ); 8395 8396 // Return if no panorama 8397 if ( !this.panorama ) { 8398 8399 return; 8400 8401 } 8402 8403 // output infospot information 8404 if ( event.type !== 'mousedown' && PANOLENS.Utils.checkTouchSupported() || this.OUTPUT_INFOSPOT ) { 8405 8406 this.outputInfospotPosition(); 8407 8408 } 8409 8410 intersects = this.raycaster.intersectObjects( this.panorama.children, true ); 8411 8412 intersect_entity = this.getConvertedIntersect( intersects ); 8413 8414 intersect = ( intersects.length > 0 ) ? intersects[0].object : intersect; 8415 8416 if ( this.userMouse.type === 'mouseup' ) { 8417 8418 if ( intersect_entity && this.pressEntityObject === intersect_entity && this.pressEntityObject.dispatchEvent ) { 8419 8420 this.pressEntityObject.dispatchEvent( { type: 'pressstop-entity', mouseEvent: event } ); 8421 8422 } 8423 8424 this.pressEntityObject = undefined; 8425 8426 } 8427 8428 if ( this.userMouse.type === 'mouseup' ) { 8429 8430 if ( intersect && this.pressObject === intersect && this.pressObject.dispatchEvent ) { 8431 8432 this.pressObject.dispatchEvent( { type: 'pressstop', mouseEvent: event } ); 8433 8434 } 8435 8436 this.pressObject = undefined; 8437 8438 } 8439 8440 if ( type === 'click' ) { 8441 8442 this.panorama.dispatchEvent( { type: 'click', intersects: intersects, mouseEvent: event } ); 8443 8444 if ( intersect_entity && intersect_entity.dispatchEvent ) { 8445 8446 intersect_entity.dispatchEvent( { type: 'click-entity', mouseEvent: event } ); 8447 8448 } 8449 8450 if ( intersect && intersect.dispatchEvent ) { 8451 8452 intersect.dispatchEvent( { type: 'click', mouseEvent: event } ); 8453 8454 } 8455 8456 } else { 8457 8458 this.panorama.dispatchEvent( { type: 'hover', intersects: intersects, mouseEvent: event } ); 8459 8460 if ( ( this.hoverObject && intersects.length > 0 && this.hoverObject !== intersect_entity ) 8461 || ( this.hoverObject && intersects.length === 0 ) ){ 8462 8463 if ( this.hoverObject.dispatchEvent ) { 8464 8465 this.hoverObject.dispatchEvent( { type: 'hoverleave', mouseEvent: event } ); 8466 8467 // Cancel dwelling 8468 this.reticle.cancelDwelling(); 8469 8470 } 8471 8472 this.hoverObject = undefined; 8473 8474 } 8475 8476 if ( intersect_entity && intersects.length > 0 ) { 8477 8478 if ( this.hoverObject !== intersect_entity ) { 8479 8480 this.hoverObject = intersect_entity; 8481 8482 if ( this.hoverObject.dispatchEvent ) { 8483 8484 this.hoverObject.dispatchEvent( { type: 'hoverenter', mouseEvent: event } ); 8485 8486 // Start reticle timer 8487 if ( this.options.autoReticleSelect && this.options.enableReticle || this.tempEnableReticle ) { 8488 this.reticle.startDwelling( this.onTap.bind( this, event, 'click' ) ); 8489 } 8490 8491 } 8492 8493 } 8494 8495 if ( this.userMouse.type === 'mousedown' && this.pressEntityObject != intersect_entity ) { 8496 8497 this.pressEntityObject = intersect_entity; 8498 8499 if ( this.pressEntityObject.dispatchEvent ) { 8500 8501 this.pressEntityObject.dispatchEvent( { type: 'pressstart-entity', mouseEvent: event } ); 8502 8503 } 8504 8505 } 8506 8507 if ( this.userMouse.type === 'mousedown' && this.pressObject != intersect ) { 8508 8509 this.pressObject = intersect; 8510 8511 if ( this.pressObject.dispatchEvent ) { 8512
8513 this.pressObject.dispatchEvent( { type: 'pressstart', mouseEvent: event } ); 8514 8515 } 8516 8517 } 8518 8519 if ( this.userMouse.type === 'mousemove' || this.options.enableReticle ) { 8520 8521 if ( intersect && intersect.dispatchEvent ) { 8522 8523 intersect.dispatchEvent( { type: 'hover', mouseEvent: event } ); 8524 8525 } 8526 8527 if ( this.pressEntityObject && this.pressEntityObject.dispatchEvent ) { 8528 8529 this.pressEntityObject.dispatchEvent( { type: 'pressmove-entity', mouseEvent: event } ); 8530 8531 } 8532 8533 if ( this.pressObject && this.pressObject.dispatchEvent ) { 8534 8535 this.pressObject.dispatchEvent( { type: 'pressmove', mouseEvent: event } ); 8536 8537 } 8538 8539 } 8540 8541 } 8542 8543 if ( !intersect_entity && this.pressEntityObject && this.pressEntityObject.dispatchEvent ) { 8544 8545 this.pressEntityObject.dispatchEvent( { type: 'pressstop-entity', mouseEvent: event } ); 8546 8547 this.pressEntityObject = undefined; 8548 8549 } 8550 8551 if ( !intersect && this.pressObject && this.pressObject.dispatchEvent ) { 8552 8553 this.pressObject.dispatchEvent( { type: 'pressstop', mouseEvent: event } ); 8554 8555 this.pressObject = undefined; 8556 8557 } 8558 8559 } 8560 8561 // Infospot handler 8562 if ( intersect && intersect instanceof PANOLENS.Infospot ) { 8563 8564 this.infospot = intersect; 8565 8566 if ( type === 'click' ) { 8567 8568 return true; 8569 8570 } 8571 8572 8573 } else if ( this.infospot ) { 8574 8575 this.hideInfospot(); 8576 8577 } 8578 8579 }; 8580 8581 PANOLENS.Viewer.prototype.getConvertedIntersect = function ( intersects ) { 8582 8583 var intersect; 8584 8585 for ( var i = 0; i < intersects.length; i++ ) { 8586 8587 if ( intersects[i].distance >= 0 && intersects[i].object && !intersects[i].object.passThrough ) { 8588 8589 if ( intersects[i].object.entity && intersects[i].object.entity.passThrough ) { 8590 continue; 8591 } else if ( intersects[i].object.entity && !intersects[i].object.entity.passThrough ) { 8592 intersect = intersects[i].object.entity; 8593 break; 8594 } else { 8595 intersect = intersects[i].object; 8596 break; 8597 } 8598 8599 } 8600 8601 } 8602 8603 return intersect; 8604 8605 }; 8606 8607 PANOLENS.Viewer.prototype.hideInfospot = function ( intersects ) { 8608 8609 if ( this.infospot ) { 8610 8611 this.infospot.onHoverEnd(); 8612 8613 this.infospot = undefined; 8614 8615 } 8616 8617 }; 8618 8619 /** 8620 * Toggle control bar 8621 * @fires [PANOLENS.Viewer#control-bar-toggle] 8622 */ 8623 PANOLENS.Viewer.prototype.toggleControlBar = function () { 8624 8625 /** 8626 * Toggle control bar event 8627 * @type {object} 8628 * @event PANOLENS.Viewer#control-bar-toggle 8629 */
8630 this.widget && this.widget.dispatchEvent( { type: 'control-bar-toggle' } ); 8631 8632 }; 8633 8634 PANOLENS.Viewer.prototype.onKeyDown = function ( event ) { 8635 8636 if ( this.options.output && this.options.output !== 'none' && event.key === 'Control' ) { 8637 8638 this.OUTPUT_INFOSPOT = true; 8639 8640 } 8641 8642 }; 8643 8644 PANOLENS.Viewer.prototype.onKeyUp = function ( event ) { 8645 8646 this.OUTPUT_INFOSPOT = false; 8647 8648 }; 8649 8650 /** 8651 * Update control and callbacks 8652 */ 8653 PANOLENS.Viewer.prototype.update = function () { 8654 8655 TWEEN.update(); 8656 8657 this.updateCallbacks.forEach( function( callback ){ callback(); } ); 8658 8659 this.control.update(); 8660 8661 this.scene.traverse( function( child ){ 8662 if ( child instanceof PANOLENS.Infospot 8663 && child.element 8664 && ( this.hoverObject === child 8665 || child.element.style.display !== 'none' 8666 || (child.element.left && child.element.left.style.display !== 'none') 8667 || (child.element.right && child.element.right.style.display !== 'none') ) ) { 8668 if ( this.checkSpriteInViewport( child ) ) { 8669 var vector = this.getScreenVector( child.getWorldPosition() ); 8670 child.translateElement( vector.x, vector.y ); 8671 } else { 8672 child.onDismiss(); 8673 } 8674 8675 } 8676 }.bind(this) ); 8677 8678 }; 8679 8680 /** 8681 * Rendering function to be called on every animation frame 8682 */ 8683 PANOLENS.Viewer.prototype.render = function () { 8684 8685 if ( this.mode === PANOLENS.Modes.CARDBOARD || this.mode === PANOLENS.Modes.STEREO ) { 8686 8687 this.effect.render( this.scene, this.camera ); 8688 8689 } else { 8690 8691 this.renderer.render( this.scene, this.camera ); 8692 8693 } 8694 8695 }; 8696 8697 PANOLENS.Viewer.prototype.animate = function () { 8698 8699 this.requestAnimationId = window.requestAnimationFrame( this.animate.bind( this ) ); 8700 8701 this.onChange(); 8702 8703 }; 8704 8705 PANOLENS.Viewer.prototype.onChange = function () { 8706 8707 this.update(); 8708 this.render(); 8709 8710 }; 8711 8712 /** 8713 * Register mouse and touch event on container 8714 */ 8715 PANOLENS.Viewer.prototype.registerMouseAndTouchEvents = function () { 8716 8717 this.container.addEventListener( 'mousedown' , this.HANDLER_MOUSE_DOWN, false ); 8718 this.container.addEventListener( 'mousemove' , this.HANDLER_MOUSE_MOVE, false ); 8719 this.container.addEventListener( 'mouseup' , this.HANDLER_MOUSE_UP , false ); 8720 this.container.addEventListener( 'touchstart', this.HANDLER_MOUSE_DOWN, false ); 8721 this.container.addEventListener( 'touchend' , this.HANDLER_MOUSE_UP , false ); 8722 8723 }; 8724 8725 /** 8726 * Unregister mouse and touch event on container 8727 */ 8728 PANOLENS.Viewer.prototype.unregisterMouseAndTouchEvents = function () { 8729 8730 this.container.removeEventListener( 'mousedown' , this.HANDLER_MOUSE_DOWN, false ); 8731 this.container.removeEventListener( 'mousemove' , this.HANDLER_MOUSE_MOVE, false ); 8732 this.container.removeEventListener( 'mouseup' , this.HANDLER_MOUSE_UP , false ); 8733 this.container.removeEventListener( 'touchstart', this.HANDLER_MOUSE_DOWN, false ); 8734 this.container.removeEventListener( 'touchend' , this.HANDLER_MOUSE_UP , false ); 8735 }; 8736 8737 /** 8738 * Register reticle event 8739 */ 8740 PANOLENS.Viewer.prototype.registerReticleEvent = function () { 8741 8742 this.addUpdateCallback( this.HANDLER_TAP ); 8743 8744 }; 8745 8746 /** 8747 * Unregister reticle event 8748 */ 8749 PANOLENS.Viewer.prototype.unregisterReticleEvent = function () { 8750 8751 this.removeUpdateCallback( this.HANDLER_TAP ); 8752 8753 }; 8754 8755 /** 8756 * Update reticle event 8757 */ 8758 PANOLENS.Viewer.prototype.updateReticleEvent = function () { 8759 8760 var centerX, centerY; 8761 8762 centerX = this.container.clientWidth / 2 + this.container.offsetLeft; 8763 centerY = this.container.clientHeight / 2; 8764 8765 this.removeUpdateCallback( this.HANDLER_TAP ); 8766 this.HANDLER_TAP = this.onTap.bind( this, { clientX: centerX, clientY: centerY } ); 8767 this.addUpdateCallback( this.HANDLER_TAP ); 8768 8769 }; 8770 8771 /** 8772 * Register container and window listeners 8773 */ 8774 PANOLENS.Viewer.prototype.registerEventListeners = function () { 8775 8776 // Resize Event 8777 window.addEventListener( 'resize' , this.HANDLER_WINDOW_RESIZE, true ); 8778 8779 // Keyboard Event 8780 window.addEventListener( 'keydown', this.HANDLER_KEY_DOWN, true ); 8781 window.addEventListener( 'keyup' , this.HANDLER_KEY_UP , true ); 8782 8783 }; 8784 8785 /** 8786 * Unregister container and window listeners 8787 */ 8788 PANOLENS.Viewer.prototype.unregisterEventListeners = function () { 8789 8790 // Resize Event 8791 window.removeEventListener( 'resize' , this.HANDLER_WINDOW_RESIZE, true ); 8792 8793 // Keyboard Event 8794 window.removeEventListener( 'keydown', this.HANDLER_KEY_DOWN, true ); 8795 window.removeEventListener( 'keyup' , this.HANDLER_KEY_UP , true ); 8796 8797 }; 8798 8799 /** 8800 * Dispose all scene objects and clear cache 8801 */ 8802 PANOLENS.Viewer.prototype.dispose = function () { 8803 8804 // Unregister dom event listeners 8805 this.unregisterEventListeners(); 8806 8807 // recursive disposal on 3d objects 8808 function recursiveDispose ( object ) { 8809 8810 for ( var i = object.children.length - 1; i >= 0; i-- ) { 8811 8812 recursiveDispose( object.children[i] ); 8813 object.remove( object.children[i] ); 8814 8815 } 8816 8817 if ( object instanceof PANOLENS.Infospot ) { 8818 8819 object.dispose(); 8820 8821 } 8822 8823 object.geometry && object.geometry.dispose(); 8824 object.material && object.material.dispose(); 8825 } 8826 8827 recursiveDispose( this.scene ); 8828 8829 // dispose widget 8830 if ( this.widget ) { 8831
8832 this.widget.dispose(); 8833 this.widget = null; 8834 8835 } 8836 8837 // clear cache 8838 if ( THREE.Cache && THREE.Cache.enabled ) { 8839 8840 THREE.Cache.clear(); 8841 8842 } 8843 8844 }; 8845 8846 /** 8847 * Destory viewer by disposing and stopping requestAnimationFrame 8848 */ 8849 PANOLENS.Viewer.prototype.destory = function () { 8850 8851 this.dispose(); 8852 this.render(); 8853 window.cancelAnimationFrame( this.requestAnimationId ); 8854 8855 }; 8856 8857 /** 8858 * On panorama dispose 8859 */ 8860 PANOLENS.Viewer.prototype.onPanoramaDispose = function ( panorama ) { 8861 8862 if ( panorama instanceof PANOLENS.VideoPanorama ) { 8863 8864 this.hideVideoWidget(); 8865 8866 } 8867 8868 if ( panorama === this.panorama ) { 8869 8870 this.panorama = null; 8871 8872 } 8873 8874 }; 8875 8876 /** 8877 * Load ajax call 8878 * @param {string} url - URL to be requested 8879 * @param {function} [callback] - Callback after request completes 8880 */ 8881 PANOLENS.Viewer.prototype.loadAsyncRequest = function ( url, callback ) { 8882 8883 var request = new XMLHttpRequest(); 8884 request.onloadend = function ( event ) { 8885 callback && callback( event ); 8886 }; 8887 request.open( "GET", url, true ); 8888 request.send( null ); 8889 8890 }; 8891 8892 /** 8893 * View indicator in upper left 8894 * */ 8895 PANOLENS.Viewer.prototype.addViewIndicator = function () { 8896 8897 var scope = this; 8898 8899 function loadViewIndicator ( asyncEvent ) { 8900 8901 if ( asyncEvent.loaded === 0 ) return; 8902 8903 var viewIndicatorDiv = asyncEvent.target.responseXML.documentElement; 8904 viewIndicatorDiv.style.width = scope.viewIndicatorSize + "px"; 8905 viewIndicatorDiv.style.height = scope.viewIndicatorSize + "px"; 8906 viewIndicatorDiv.style.position = "absolute"; 8907 viewIndicatorDiv.style.top = "10px"; 8908 viewIndicatorDiv.style.left = "10px"; 8909 viewIndicatorDiv.style.opacity = "0.5"; 8910 viewIndicatorDiv.style.cursor = "pointer"; 8911 viewIndicatorDiv.id = "panolens-view-indicator-container"; 8912 8913 scope.container.appendChild( viewIndicatorDiv ); 8914 8915 var indicator = viewIndicatorDiv.querySelector( "#indicator" ); 8916 var setIndicatorD = function () { 8917 8918 scope.radius = scope.viewIndicatorSize * 0.225; 8919 scope.currentPanoAngle = scope.camera.rotation.y - THREE.Math.degToRad( 90 ); 8920 scope.fovAngle = THREE.Math.degToRad( scope.camera.fov ) ; 8921 scope.leftAngle = -scope.currentPanoAngle - scope.fovAngle / 2; 8922 scope.rightAngle = -scope.currentPanoAngle + scope.fovAngle / 2; 8923 scope.leftX = scope.radius * Math.cos( scope.leftAngle ); 8924 scope.leftY = scope.radius * Math.sin( scope.leftAngle ); 8925 scope.rightX = scope.radius * Math.cos( scope.rightAngle ); 8926 scope.rightY = scope.radius * Math.sin( scope.rightAngle ); 8927 scope.indicatorD = "M " + scope.leftX + " " + scope.leftY + " A " + scope.radius + " " + scope.radius + " 0 0 1 " + scope.rightX + " " + scope.rightY; 8928 8929 if ( scope.leftX && scope.leftY && scope.rightX && scope.rightY && scope.radius ) { 8930 8931 indicator.setAttribute( "d", scope.indicatorD ); 8932 8933 } 8934 8935 }; 8936 8937 scope.addUpdateCallback( setIndicatorD ); 8938 8939 var indicatorOnMouseEnter = function () { 8940 8941 this.style.opacity = "1"; 8942 8943 }; 8944 8945 var indicatorOnMouseLeave = function () { 8946 8947 this.style.opacity = "0.5"; 8948 8949 }; 8950 8951 viewIndicatorDiv.addEventListener( "mouseenter", indicatorOnMouseEnter ); 8952 viewIndicatorDiv.addEventListener( "mouseleave", indicatorOnMouseLeave ); 8953 } 8954 8955 this.loadAsyncRequest( PANOLENS.DataImage.ViewIndicator, loadViewIndicator ); 8956 8957 }; 8958 8959 /** 8960 * Append custom control item to existing control bar 8961 * @param {object} [option={}] - Style object to overwirte default element style. It takes 'style', 'onTap' and 'group' properties. 8962 */ 8963 PANOLENS.Viewer.prototype.appendControlItem = function ( option ) { 8964 8965 var item = this.widget.createCustomItem( option ); 8966 8967 if ( option.group === 'video' ) { 8968 8969 this.widget.videoElement.appendChild( item ); 8970 8971 } else { 8972 8973 this.widget.barElement.appendChild( item ); 8974 8975 } 8976 8977 return item; 8978 8979 }; 8980 8981} )(); 8982;(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 8983 var createLayout = require('layout-bmfont-text') 8984 var inherits = require('inherits') 8985 var createIndices = require('quad-indices') 8986 var buffer = require('three-buffer-vertex-data') 8987 var assign = require('object-assign') 8988 8989 var vertices = require('./lib/vertices') 8990 var utils = require('./lib/utils') 8991 8992 var Base = THREE.BufferGeometry 8993 8994 module.exports = function createTextGeometry (opt) { 8995 return new TextGeometry(opt) 8996 } 8997 8998 function TextGeometry (opt) { 8999 Base.call(this) 9000 9001 if (typeof opt === 'string') { 9002 opt = { text: opt } 9003 } 9004 9005 // use these as default values for any subsequent
9006 // calls to update() 9007 this._opt = assign({}, opt) 9008 9009 // also do an initial setup... 9010 if (opt) this.update(opt) 9011 } 9012 9013 inherits(TextGeometry, Base) 9014 9015 TextGeometry.prototype.update = function (opt) { 9016 if (typeof opt === 'string') { 9017 opt = { text: opt } 9018 } 9019 9020 // use constructor defaults 9021 opt = assign({}, this._opt, opt) 9022 9023 if (!opt.font) { 9024 throw new TypeError('must specify a { font } in options') 9025 } 9026 9027 this.layout = createLayout(opt) 9028 9029 // get vec2 texcoords 9030 var flipY = opt.flipY !== false 9031 9032 // the desired BMFont data 9033 var font = opt.font 9034 9035 // determine texture size from font file 9036 var texWidth = font.common.scaleW 9037 var texHeight = font.common.scaleH 9038 9039 // get visible glyphs 9040 var glyphs = this.layout.glyphs.filter(function (glyph) { 9041 var bitmap = glyph.data 9042 return bitmap.width * bitmap.height > 0 9043 }) 9044 9045 // provide visible glyphs for convenience 9046 this.visibleGlyphs = glyphs 9047 9048 // get common vertex data 9049 var positions = vertices.positions(glyphs) 9050 var uvs = vertices.uvs(glyphs, texWidth, texHeight, flipY) 9051 var indices = createIndices({ 9052 clockwise: true, 9053 type: 'uint16', 9054 count: glyphs.length 9055 }) 9056 9057 // update vertex data 9058 buffer.index(this, indices, 1, 'uint16') 9059 buffer.attr(this, 'position', positions, 2) 9060 buffer.attr(this, 'uv', uvs, 2) 9061 9062 // update multipage data 9063 if (!opt.multipage && 'page' in this.attributes) { 9064 // disable multipage rendering 9065 this.removeAttribute('page') 9066 } else if (opt.multipage) { 9067 var pages = vertices.pages(glyphs) 9068 // enable multipage rendering 9069 buffer.attr(this, 'page', pages, 1) 9070 } 9071 } 9072 9073 TextGeometry.prototype.computeBoundingSphere = function () { 9074 if (this.boundingSphere === null) { 9075 this.boundingSphere = new THREE.Sphere() 9076 } 9077 9078 var positions = this.attributes.position.array 9079 var itemSize = this.attributes.position.itemSize 9080 if (!positions || !itemSize || positions.length < 2) { 9081 this.boundingSphere.radius = 0 9082 this.boundingSphere.center.set(0, 0, 0) 9083 return 9084 } 9085 utils.computeSphere(positions, this.boundingSphere) 9086 if (isNaN(this.boundingSphere.radius)) { 9087 console.error('THREE.BufferGeometry.computeBoundingSphere(): ' + 9088 'Computed radius is NaN. The ' + 9089 '"position" attribute is likely to have NaN values.') 9090 } 9091 } 9092 9093 TextGeometry.prototype.computeBoundingBox = function () { 9094 if (this.boundingBox === null) { 9095 this.boundingBox = new THREE.Box3() 9096 } 9097 9098 var bbox = this.boundingBox 9099 var positions = this.attributes.position.array 9100 var itemSize = this.attributes.position.itemSize 9101 if (!positions || !itemSize || positions.length < 2) { 9102 bbox.makeEmpty() 9103 return 9104 } 9105 utils.computeBox(positions, bbox) 9106 } 9107 9108 },{"./lib/utils":2,"./lib/vertices":3,"inherits":4,"layout-bmfont-text":5,"object-assign":26,"quad-indices":27,"three-buffer-vertex-data":31}],2:[function(require,module,exports){ 9109 var itemSize = 2 9110 var box = { min: [0, 0], max: [0, 0] } 9111 9112 function bounds (positions) { 9113 var count = positions.length / itemSize 9114 box.min[0] = positions[0] 9115 box.min[1] = positions[1] 9116 box.max[0] = positions[0] 9117 box.max[1] = positions[1] 9118 9119 for (var i = 0; i < count; i++) { 9120 var x = positions[i * itemSize + 0] 9121 var y = positions[i * itemSize + 1] 9122 box.min[0] = Math.min(x, box.min[0]) 9123 box.min[1] = Math.min(y, box.min[1]) 9124 box.max[0] = Math.max(x, box.max[0]) 9125 box.max[1] = Math.max(y, box.max[1]) 9126 } 9127 } 9128 9129 module.exports.computeBox = function (positions, output) { 9130 bounds(positions) 9131 output.min.set(box.min[0], box.min[1], 0) 9132 output.max.set(box.max[0], box.max[1], 0) 9133 } 9134 9135 module.exports.computeSphere = function (positions, output) { 9136 bounds(positions) 9137 var minX = box.min[0] 9138 var minY = box.min[1] 9139 var maxX = box.max[0] 9140 var maxY = box.max[1] 9141 var width = maxX - minX 9142 var height = maxY - minY 9143 var length = Math.sqrt(width * width + height * height) 9144 output.center.set(minX + width / 2, minY + height / 2, 0) 9145 output.radius = length / 2 9146 } 9147 9148 },{}],3:[function(require,module,exports){ 9149 module.exports.pages = function pages (glyphs) { 9150 var pages = new Float32Array(glyphs.length * 4 * 1) 9151 var i = 0 9152 glyphs.forEach(function (glyph) { 9153 var id = glyph.data.page || 0 9154 pages[i++] = id 9155 pages[i++] = id 9156 pages[i++] = id 9157 pages[i++] = id 9158 }) 9159 return pages 9160 } 9161 9162 module.exports.uvs = function uvs (glyphs, texWidth, texHeight, flipY) { 9163 var uvs = new Float32Array(glyphs.length * 4 * 2) 9164 var i = 0 9165 glyphs.forEach(function (glyph) { 9166 var bitmap = glyph.data
9167 var bw = (bitmap.x + bitmap.width) 9168 var bh = (bitmap.y + bitmap.height) 9169 9170 // top left position 9171 var u0 = bitmap.x / texWidth 9172 var v1 = bitmap.y / texHeight 9173 var u1 = bw / texWidth 9174 var v0 = bh / texHeight 9175 9176 if (flipY) { 9177 v1 = (texHeight - bitmap.y) / texHeight 9178 v0 = (texHeight - bh) / texHeight 9179 } 9180 9181 // BL 9182 uvs[i++] = u0 9183 uvs[i++] = v1 9184 // TL 9185 uvs[i++] = u0 9186 uvs[i++] = v0 9187 // TR 9188 uvs[i++] = u1 9189 uvs[i++] = v0 9190 // BR 9191 uvs[i++] = u1 9192 uvs[i++] = v1 9193 }) 9194 return uvs 9195 } 9196 9197 module.exports.positions = function positions (glyphs) { 9198 var positions = new Float32Array(glyphs.length * 4 * 2) 9199 var i = 0 9200 glyphs.forEach(function (glyph) { 9201 var bitmap = glyph.data 9202 9203 // bottom left position 9204 var x = glyph.position[0] + bitmap.xoffset 9205 var y = glyph.position[1] + bitmap.yoffset 9206 9207 // quad size 9208 var w = bitmap.width 9209 var h = bitmap.height 9210 9211 // BL 9212 positions[i++] = x 9213 positions[i++] = y 9214 // TL 9215 positions[i++] = x 9216 positions[i++] = y + h 9217 // TR 9218 positions[i++] = x + w 9219 positions[i++] = y + h 9220 // BR 9221 positions[i++] = x + w 9222 positions[i++] = y 9223 }) 9224 return positions 9225 } 9226 9227 },{}],4:[function(require,module,exports){ 9228 if (typeof Object.create === 'function') { 9229 // implementation from standard node.js 'util' module 9230 module.exports = function inherits(ctor, superCtor) { 9231 ctor.super_ = superCtor 9232 ctor.prototype = Object.create(superCtor.prototype, { 9233 constructor: { 9234 value: ctor, 9235 enumerable: false, 9236 writable: true, 9237 configurable: true 9238 } 9239 }); 9240 }; 9241 } else { 9242 // old school shim for old browsers 9243 module.exports = function inherits(ctor, superCtor) { 9244 ctor.super_ = superCtor 9245 var TempCtor = function () {} 9246 TempCtor.prototype = superCtor.prototype 9247 ctor.prototype = new TempCtor() 9248 ctor.prototype.constructor = ctor 9249 } 9250 } 9251 9252 },{}],5:[function(require,module,exports){ 9253 var wordWrap = require('word-wrapper') 9254 var xtend = require('xtend') 9255 var findChar = require('indexof-property')('id') 9256 var number = require('as-number') 9257 9258 var X_HEIGHTS = ['x', 'e', 'a', 'o', 'n', 's', 'r', 'c', 'u', 'm', 'v', 'w', 'z'] 9259 var M_WIDTHS = ['m', 'w'] 9260 var CAP_HEIGHTS = ['H', 'I', 'N', 'E', 'F', 'K', 'L', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] 9261 9262 9263 var TAB_ID = '\t'.charCodeAt(0) 9264 var SPACE_ID = ' '.charCodeAt(0) 9265 var ALIGN_LEFT = 0, 9266 ALIGN_CENTER = 1, 9267 ALIGN_RIGHT = 2 9268 9269 module.exports = function createLayout(opt) { 9270 return new TextLayout(opt) 9271 } 9272 9273 function TextLayout(opt) { 9274 this.glyphs = [] 9275 this._measure = this.computeMetrics.bind(this) 9276 this.update(opt) 9277 } 9278 9279 TextLayout.prototype.update = function(opt) { 9280 opt = xtend({ 9281 measure: this._measure 9282 }, opt) 9283 this._opt = opt 9284 this._opt.tabSize = number(this._opt.tabSize, 4) 9285 9286 if (!opt.font) 9287 throw new Error('must provide a valid bitmap font') 9288 9289 var glyphs = this.glyphs 9290 var text = opt.text||'' 9291 var font = opt.font 9292 this._setupSpaceGlyphs(font) 9293 9294 var lines = wordWrap.lines(text, opt) 9295 var minWidth = opt.width || 0 9296 9297 //clear glyphs 9298 glyphs.length = 0 9299 9300 //get max line width 9301 var maxLineWidth = lines.reduce(function(prev, line) { 9302 return Math.max(prev, line.width, minWidth) 9303 }, 0) 9304 9305 //the pen position 9306 var x = 0 9307 var y = 0 9308 var lineHeight = number(opt.lineHeight, font.common.lineHeight) 9309 var baseline = font.common.base 9310 var descender = lineHeight-baseline 9311 var letterSpacing = opt.letterSpacing || 0 9312 var height = lineHeight * lines.length - descender 9313 var align = getAlignType(this._opt.align) 9314 9315 //draw text along baseline 9316 y -= height 9317 9318 //the metrics for this text layout 9319 this._width = maxLineWidth 9320 this._height = height 9321 this._descender = lineHeight - baseline 9322 this._baseline = baseline 9323 this._xHeight = getXHeight(font) 9324 this._capHeight = getCapHeight(font) 9325 this._lineHeight = lineHeight
9326 this._ascender = lineHeight - descender - this._xHeight 9327 9328 //layout each glyph 9329 var self = this 9330 lines.forEach(function(line, lineIndex) { 9331 var start = line.start 9332 var end = line.end 9333 var lineWidth = line.width 9334 var lastGlyph 9335 9336 //for each glyph in that line... 9337 for (var i=start; i<end; i++) { 9338 var id = text.charCodeAt(i) 9339 var glyph = self.getGlyph(font, id) 9340 if (glyph) { 9341 if (lastGlyph) 9342 x += getKerning(font, lastGlyph.id, glyph.id) 9343 9344 var tx = x 9345 if (align === ALIGN_CENTER) 9346 tx += (maxLineWidth-lineWidth)/2 9347 else if (align === ALIGN_RIGHT) 9348 tx += (maxLineWidth-lineWidth) 9349 9350 glyphs.push({ 9351 position: [tx, y], 9352 data: glyph, 9353 index: i, 9354 line: lineIndex 9355 }) 9356 9357 //move pen forward 9358 x += glyph.xadvance + letterSpacing 9359 lastGlyph = glyph 9360 } 9361 } 9362 9363 //next line down 9364 y += lineHeight 9365 x = 0 9366 }) 9367 this._linesTotal = lines.length; 9368 } 9369 9370 TextLayout.prototype._setupSpaceGlyphs = function(font) { 9371 //These are fallbacks, when the font doesn't include 9372 //' ' or '\t' glyphs 9373 this._fallbackSpaceGlyph = null 9374 this._fallbackTabGlyph = null 9375 9376 if (!font.chars || font.chars.length === 0) 9377 return 9378 9379 //try to get space glyph 9380 //then fall back to the 'm' or 'w' glyphs 9381 //then fall back to the first glyph available 9382 var space = getGlyphById(font, SPACE_ID) 9383 || getMGlyph(font) 9384 || font.chars[0] 9385 9386 //and create a fallback for tab 9387 var tabWidth = this._opt.tabSize * space.xadvance 9388 this._fallbackSpaceGlyph = space 9389 this._fallbackTabGlyph = xtend(space, { 9390 x: 0, y: 0, xadvance: tabWidth, id: TAB_ID, 9391 xoffset: 0, yoffset: 0, width: 0, height: 0 9392 }) 9393 } 9394 9395 TextLayout.prototype.getGlyph = function(font, id) { 9396 var glyph = getGlyphById(font, id) 9397 if (glyph) 9398 return glyph 9399 else if (id === TAB_ID) 9400 return this._fallbackTabGlyph 9401 else if (id === SPACE_ID) 9402 return this._fallbackSpaceGlyph 9403 return null 9404 } 9405 9406 TextLayout.prototype.computeMetrics = function(text, start, end, width) { 9407 var letterSpacing = this._opt.letterSpacing || 0 9408 var font = this._opt.font 9409 var curPen = 0 9410 var curWidth = 0 9411 var count = 0 9412 var glyph 9413 var lastGlyph 9414 9415 if (!font.chars || font.chars.length === 0) { 9416 return { 9417 start: start, 9418 end: start, 9419 width: 0 9420 } 9421 } 9422 9423 end = Math.min(text.length, end) 9424 for (var i=start; i < end; i++) { 9425 var id = text.charCodeAt(i) 9426 var glyph = this.getGlyph(font, id) 9427 9428 if (glyph) { 9429 //move pen forward 9430 var xoff = glyph.xoffset 9431 var kern = lastGlyph ? getKerning(font, lastGlyph.id, glyph.id) : 0 9432 curPen += kern 9433 9434 var nextPen = curPen + glyph.xadvance + letterSpacing 9435 var nextWidth = curPen + glyph.width 9436 9437 //we've hit our limit; we can't move onto the next glyph 9438 if (nextWidth >= width || nextPen >= width) 9439 break 9440 9441 //otherwise continue along our line 9442 curPen = nextPen 9443 curWidth = nextWidth 9444 lastGlyph = glyph 9445 } 9446 count++ 9447 } 9448 9449 //make sure rightmost edge lines up with rendered glyphs 9450 if (lastGlyph) 9451 curWidth += lastGlyph.xoffset 9452 9453 return { 9454 start: start, 9455 end: start + count, 9456 width: curWidth 9457 } 9458 } 9459 9460 //getters for the private vars 9461 ;['width', 'height', 9462 'descender', 'ascender', 9463 'xHeight', 'baseline', 9464 'capHeight', 9465 'lineHeight' ].forEach(addGetter) 9466 9467 function addGetter(name) { 9468 Object.defineProperty(TextLayout.prototype, name, { 9469 get: wrapper(name), 9470 configurable: true 9471 }) 9472 } 9473 9474 //create lookups for private vars 9475 function wrapper(name) { 9476 return (new Function([ 9477 'return function '+name+'() {', 9478 ' return this._'+name, 9479 '}' 9480 ].join('\n')))() 9481 } 9482 9483 function getGlyphById(font, id) { 9484 if (!font.chars || font.chars.length === 0) 9485 return null 9486 9487 var glyphIdx = findChar(font.chars, id) 9488 if (glyphIdx >= 0) 9489 return font.chars[glyphIdx] 9490 return null 9491 } 9492 9493 function getXHeight(font) { 9494 for (var i=0; i<X_HEIGHTS.length; i++) {
9495 var id = X_HEIGHTS[i].charCodeAt(0) 9496 var idx = findChar(font.chars, id) 9497 if (idx >= 0) 9498 return font.chars[idx].height 9499 } 9500 return 0 9501 } 9502 9503 function getMGlyph(font) { 9504 for (var i=0; i<M_WIDTHS.length; i++) { 9505 var id = M_WIDTHS[i].charCodeAt(0) 9506 var idx = findChar(font.chars, id) 9507 if (idx >= 0) 9508 return font.chars[idx] 9509 } 9510 return 0 9511 } 9512 9513 function getCapHeight(font) { 9514 for (var i=0; i<CAP_HEIGHTS.length; i++) { 9515 var id = CAP_HEIGHTS[i].charCodeAt(0) 9516 var idx = findChar(font.chars, id) 9517 if (idx >= 0) 9518 return font.chars[idx].height 9519 } 9520 return 0 9521 } 9522 9523 function getKerning(font, left, right) { 9524 if (!font.kernings || font.kernings.length === 0) 9525 return 0 9526 9527 var table = font.kernings 9528 for (var i=0; i<table.length; i++) { 9529 var kern = table[i] 9530 if (kern.first === left && kern.second === right) 9531 return kern.amount 9532 } 9533 return 0 9534 } 9535 9536 function getAlignType(align) { 9537 if (align === 'center') 9538 return ALIGN_CENTER 9539 else if (align === 'right') 9540 return ALIGN_RIGHT 9541 return ALIGN_LEFT 9542 } 9543 },{"as-number":6,"indexof-property":7,"word-wrapper":8,"xtend":9}],6:[function(require,module,exports){ 9544 module.exports = function numtype(num, def) { 9545 return typeof num === 'number' 9546 ? num 9547 : (typeof def === 'number' ? def : 0) 9548 } 9549 },{}],7:[function(require,module,exports){ 9550 module.exports = function compile(property) { 9551 if (!property || typeof property !== 'string') 9552 throw new Error('must specify property for indexof search') 9553 9554 return new Function('array', 'value', 'start', [ 9555 'start = start || 0', 9556 'for (var i=start; i<array.length; i++)', 9557 ' if (array[i]["' + property +'"] === value)', 9558 ' return i', 9559 'return -1' 9560 ].join('\n')) 9561 } 9562 },{}],8:[function(require,module,exports){ 9563 var newline = /\n/ 9564 var newlineChar = '\n' 9565 var whitespace = /\s/ 9566 9567 module.exports = function(text, opt) { 9568 var lines = module.exports.lines(text, opt) 9569 return lines.map(function(line) { 9570 return text.substring(line.start, line.end) 9571 }).join('\n') 9572 } 9573 9574 module.exports.lines = function wordwrap(text, opt) { 9575 opt = opt||{} 9576 9577 //zero width results in nothing visible 9578 if (opt.width === 0 && opt.mode !== 'nowrap') 9579 return [] 9580 9581 text = text||'' 9582 var width = typeof opt.width === 'number' ? opt.width : Number.MAX_VALUE 9583 var start = Math.max(0, opt.start||0) 9584 var end = typeof opt.end === 'number' ? opt.end : text.length 9585 var mode = opt.mode 9586 9587 var measure = opt.measure || monospace 9588 if (mode === 'pre') 9589 return pre(measure, text, start, end, width) 9590 else 9591 return greedy(measure, text, start, end, width, mode) 9592 } 9593 9594 function idxOf(text, chr, start, end) { 9595 var idx = text.indexOf(chr, start) 9596 if (idx === -1 || idx > end) 9597 return end 9598 return idx 9599 } 9600 9601 function isWhitespace(chr) { 9602 return whitespace.test(chr) 9603 } 9604 9605 function pre(measure, text, start, end, width) { 9606 var lines = [] 9607 var lineStart = start 9608 for (var i=start; i<end && i<text.length; i++) { 9609 var chr = text.charAt(i) 9610 var isNewline = newline.test(chr) 9611 9612 //If we've reached a newline, then step down a line 9613 //Or if we've reached the EOF 9614 if (isNewline || i===end-1) { 9615 var lineEnd = isNewline ? i : i+1 9616 var measured = measure(text, lineStart, lineEnd, width) 9617 lines.push(measured) 9618 9619 lineStart = i+1 9620 } 9621 } 9622 return lines 9623 } 9624 9625 function greedy(measure, text, start, end, width, mode) { 9626 //A greedy word wrapper based on LibGDX algorithm 9627 //https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/BitmapFontCache.java 9628 var lines = [] 9629 9630 var testWidth = width 9631 //if 'nowrap' is specified, we only wrap on newline chars 9632 if (mode === 'nowrap') 9633 testWidth = Number.MAX_VALUE 9634 9635 while (start < end && start < text.length) { 9636 //get next newline position 9637 var newLine = idxOf(text, newlineChar, start, end) 9638 9639 //eat whitespace at start of line 9640 while (start < newLine) { 9641 if (!isWhitespace( text.charAt(start) )) 9642 break 9643 start++ 9644 } 9645 9646 //determine visible # of glyphs for the available width 9647 var measured = measure(text, start, newLine, testWidth) 9648 9649 var lineEnd = start + (measured.end-measured.start) 9650 var nextStart = lineEnd + newlineChar.length 9651 9652 //if we had to cut the line before the next newline... 9653 if (lineEnd < newLine) { 9654 //find char to break on 9655 while (lineEnd > start) { 9656 if (isWhitespace(text.charAt(lineEnd))) 9657 break 9658 lineEnd-- 9659 } 9660 if (lineEnd === start) { 9661 if (nextStart > start + newlineChar.length) nextStart-- 9662 lineEnd = nextStart // If no characters to break, show all. 9663 } else { 9664 nextStart = lineEnd 9665 //eat whitespace at end of line 9666 while (lineEnd > start) { 9667 if (!isWhitespace(text.charAt(lineEnd - newlineChar.length))) 9668 break 9669 lineEnd-- 9670 } 9671 } 9672 } 9673 if (lineEnd >= start) { 9674 var result = measure(text, start, lineEnd, testWidth) 9675 lines.push(result) 9676 } 9677 start = nextStart 9678 } 9679 return lines 9680 } 9681 9682 //determines the visible number of glyphs within a given width 9683 function monospace(text, start, end, width) { 9684 var glyphs = Math.min(width, end-start) 9685 return { 9686 start: start, 9687 end: start+glyphs 9688 } 9689 } 9690 },{}],9:[function(require,module,exports){ 9691 module.exports = extend 9692 9693 var hasOwnProperty = Object.prototype.hasOwnProperty; 9694 9695 function extend() { 9696 var target = {} 9697 9698 for (var i = 0; i < arguments.length; i++) { 9699 var source = arguments[i] 9700 9701 for (var key in source) { 9702 if (hasOwnProperty.call(source, key)) { 9703 target[key] = source[key] 9704 } 9705 } 9706 } 9707 9708 return target 9709 } 9710 9711 },{}],10:[function(require,module,exports){ 9712 (function (Buffer){ 9713 var xhr = require('xhr') 9714 var noop = function(){} 9715 var parseASCII = require('parse-bmfont-ascii') 9716 var parseXML = require('parse-bmfont-xml') 9717 var readBinary = require('parse-bmfont-binary') 9718 var isBinaryFormat = require('./lib/is-binary') 9719 var xtend = require('xtend') 9720 9721 var xml2 = (function hasXML2() { 9722 return window.XMLHttpRequest && "withCredentials" in new XMLHttpRequest 9723 })() 9724 9725 module.exports = function(opt, cb) { 9726 cb = typeof cb === 'function' ? cb : noop 9727 9728 if (typeof opt === 'string') 9729 opt = { uri: opt } 9730 else if (!opt) 9731 opt = {} 9732 9733 var expectBinary = opt.binary 9734 if (expectBinary) 9735 opt = getBinaryOpts(opt) 9736 9737 xhr(opt, function(err, res, body) { 9738 if (err) 9739 return cb(err) 9740 if (!/^2/.test(res.statusCode)) 9741 return cb(new Error('http status code: '+res.statusCode)) 9742 if (!body) 9743 return cb(new Error('no body result')) 9744 9745 var binary = false 9746 9747 //if the response type is an array buffer, 9748 //we need to convert it into a regular Buffer object 9749 if (isArrayBuffer(body)) { 9750 var array = new Uint8Array(body) 9751 body = new Buffer(array, 'binary') 9752 } 9753 9754 //now check the string/Buffer response 9755 //and see if it has a binary BMF header 9756 if (isBinaryFormat(body)) { 9757 binary = true 9758 //if we have a string, turn it into a Buffer 9759 if (typeof body === 'string') 9760 body = new Buffer(body, 'binary') 9761 } 9762 9763 //we are not parsing a binary format, just ASCII/XML/etc 9764 if (!binary) { 9765 //might still be a buffer if responseType is 'arraybuffer' 9766 if (Buffer.isBuffer(body)) 9767 body = body.toString(opt.encoding) 9768 body = body.trim() 9769 } 9770 9771 var result 9772 try { 9773 var type = res.headers['content-type'] 9774 if (binary) 9775 result = readBinary(body) 9776 else if (/json/.test(type) || body.charAt(0) === '{') 9777 result = JSON.parse(body) 9778 else if (/xml/.test(type) || body.charAt(0) === '<') 9779 result = parseXML(body) 9780 else 9781 result = parseASCII(body) 9782 } catch (e) { 9783 cb(new Error('error parsing font '+e.message)) 9784 cb = noop 9785 } 9786 cb(null, result) 9787 }) 9788 } 9789 9790 function isArrayBuffer(arr) { 9791 var str = Object.prototype.toString 9792 return str.call(arr) === '[object ArrayBuffer]' 9793 } 9794 9795 function getBinaryOpts(opt) { 9796 //IE10+ and other modern browsers support array buffers 9797 if (xml2) 9798 return xtend(opt, { responseType: 'arraybuffer' }) 9799 9800 if (typeof window.XMLHttpRequest === 'undefined') 9801 throw new Error('your browser does not support XHR loading') 9802 9803 //IE9 and XML1 browsers could still use an override 9804 var req = new window.XMLHttpRequest() 9805 req.overrideMimeType('text/plain;
9805 charset=x-user-defined') 9806 return xtend({ 9807 xhr: req 9808 }, opt) 9809 } 9810 }).call(this,require("buffer").Buffer) 9811 },{"./lib/is-binary":11,"buffer":37,"parse-bmfont-ascii":13,"parse-bmfont-binary":14,"parse-bmfont-xml":15,"xhr":18,"xtend":25}],11:[function(require,module,exports){ 9812 (function (Buffer){ 9813 var equal = require('buffer-equal') 9814 var HEADER = new Buffer([66, 77, 70, 3]) 9815 9816 module.exports = function(buf) { 9817 if (typeof buf === 'string') 9818 return buf.substring(0, 3) === 'BMF' 9819 return buf.length > 4 && equal(buf.slice(0, 4), HEADER) 9820 } 9821 }).call(this,require("buffer").Buffer) 9822 },{"buffer":37,"buffer-equal":12}],12:[function(require,module,exports){ 9823 var Buffer = require('buffer').Buffer; // for use with browserify 9824 9825 module.exports = function (a, b) { 9826 if (!Buffer.isBuffer(a)) return undefined; 9827 if (!Buffer.isBuffer(b)) return undefined; 9828 if (typeof a.equals === 'function') return a.equals(b); 9829 if (a.length !== b.length) return false; 9830 9831 for (var i = 0; i < a.length; i++) { 9832 if (a[i] !== b[i]) return false; 9833 } 9834 9835 return true; 9836 }; 9837 9838 },{"buffer":37}],13:[function(require,module,exports){ 9839 module.exports = function parseBMFontAscii(data) { 9840 if (!data) 9841 throw new Error('no data provided') 9842 data = data.toString().trim() 9843 9844 var output = { 9845 pages: [], 9846 chars: [], 9847 kernings: [] 9848 } 9849 9850 var lines = data.split(/\r\n?|\n/g) 9851 9852 if (lines.length === 0) 9853 throw new Error('no data in BMFont file') 9854 9855 for (var i = 0; i < lines.length; i++) { 9856 var lineData = splitLine(lines[i], i) 9857 if (!lineData) //skip empty lines 9858 continue 9859 9860 if (lineData.key === 'page') { 9861 if (typeof lineData.data.id !== 'number') 9862 throw new Error('malformed file at line ' + i + ' -- needs page id=N') 9863 if (typeof lineData.data.file !== 'string') 9864 throw new Error('malformed file at line ' + i + ' -- needs page file="path"') 9865 output.pages[lineData.data.id] = lineData.data.file 9866 } else if (lineData.key === 'chars' || lineData.key === 'kernings') { 9867 //... do nothing for these two ... 9868 } else if (lineData.key === 'char') { 9869 output.chars.push(lineData.data) 9870 } else if (lineData.key === 'kerning') { 9871 output.kernings.push(lineData.data) 9872 } else { 9873 output[lineData.key] = lineData.data 9874 } 9875 } 9876 9877 return output 9878 } 9879 9880 function splitLine(line, idx) { 9881 line = line.replace(/\t+/g, ' ').trim() 9882 if (!line) 9883 return null 9884 9885 var space = line.indexOf(' ') 9886 if (space === -1) 9887 throw new Error("no named row at line " + idx) 9888 9889 var key = line.substring(0, space) 9890 9891 line = line.substring(space + 1) 9892 //clear "letter" field as it is non-standard and 9893 //requires additional complexity to parse " / = symbols 9894 line = line.replace(/letter=[\'\"]\S+[\'\"]/gi, '') 9895 line = line.split("=") 9896 line = line.map(function(str) { 9897 return str.trim().match((/(".*?"|[^"\s]+)+(?=\s*|\s*$)/g)) 9898 }) 9899 9900 var data = [] 9901 for (var i = 0; i < line.length; i++) { 9902 var dt = line[i] 9903 if (i === 0) { 9904 data.push({ 9905 key: dt[0], 9906 data: "" 9907 }) 9908 } else if (i === line.length - 1) { 9909 data[data.length - 1].data = parseData(dt[0]) 9910 } else { 9911 data[data.length - 1].data = parseData(dt[0]) 9912 data.push({ 9913 key: dt[1], 9914 data: "" 9915 }) 9916 } 9917 } 9918 9919 var out = { 9920 key: key, 9921 data: {} 9922 } 9923 9924 data.forEach(function(v) { 9925 out.data[v.key] = v.data; 9926 }) 9927 9928 return out 9929 } 9930 9931 function parseData(data) { 9932 if (!data || data.length === 0) 9933 return "" 9934 9935 if (data.indexOf('"') === 0 || data.indexOf("'") === 0) 9936 return data.substring(1, data.length - 1) 9937 if (data.indexOf(',') !== -1) 9938 return parseIntList(data) 9939 return parseInt(data, 10) 9940 } 9941 9942 function parseIntList(data) { 9943 return data.split(',').map(function(val) { 9944 return parseInt(val, 10) 9945 }) 9946 } 9947 },{}],14:[function(require,module,exports){ 9948 var HEADER = [66, 77, 70] 9949 9950 module.exports = function readBMFontBinary(buf) { 9951 if (buf.length < 6) 9952 throw new Error('invalid buffer length for BMFont') 9953 9954 var header = HEADER.every(function(byte, i) { 9955 return buf.readUInt8(i) === byte 9956 }) 9957 9958 if (!header) 9959 throw new Error('BMFont missing BMF byte header') 9960 9961 var i = 3 9962 var vers = buf.readUInt8(i++) 9963 if (vers > 3) 9964 throw new Error('Only supports BMFont Binary v3 (BMFont App v1.10)') 9965 9966 var target = { kernings: [], chars: [] } 9967 for (var b=0; b<5; b++) 9968 i += readBlock(target, buf, i) 9969 return target 9970 } 9971 9972 function readBlock(target, buf, i) { 9973 if (i > buf.length-1) 9974 return 0 9975 9976 var blockID = buf.readUInt8(i++) 9977 var blockSize = buf.readInt32LE(i) 9978 i += 4
9979 9980 switch(blockID) { 9981 case 1: 9982 target.info = readInfo(buf, i) 9983 break 9984 case 2: 9985 target.common = readCommon(buf, i) 9986 break 9987 case 3: 9988 target.pages = readPages(buf, i, blockSize) 9989 break 9990 case 4: 9991 target.chars = readChars(buf, i, blockSize) 9992 break 9993 case 5: 9994 target.kernings = readKernings(buf, i, blockSize) 9995 break 9996 } 9997 return 5 + blockSize 9998 } 9999 10000 function readInfo(buf, i) { 10001 var info = {} 10002 info.size = buf.readInt16LE(i) 10003 10004 var bitField = buf.readUInt8(i+2) 10005 info.smooth = (bitField >> 7) & 1 10006 info.unicode = (bitField >> 6) & 1 10007 info.italic = (bitField >> 5) & 1 10008 info.bold = (bitField >> 4) & 1 10009 10010 //fixedHeight is only mentioned in binary spec 10011 if ((bitField >> 3) & 1) 10012 info.fixedHeight = 1 10013 10014 info.charset = buf.readUInt8(i+3) || '' 10015 info.stretchH = buf.readUInt16LE(i+4) 10016 info.aa = buf.readUInt8(i+6) 10017 info.padding = [ 10018 buf.readInt8(i+7), 10019 buf.readInt8(i+8), 10020 buf.readInt8(i+9), 10021 buf.readInt8(i+10) 10022 ] 10023 info.spacing = [ 10024 buf.readInt8(i+11), 10025 buf.readInt8(i+12) 10026 ] 10027 info.outline = buf.readUInt8(i+13) 10028 info.face = readStringNT(buf, i+14) 10029 return info 10030 } 10031 10032 function readCommon(buf, i) { 10033 var common = {} 10034 common.lineHeight = buf.readUInt16LE(i) 10035 common.base = buf.readUInt16LE(i+2) 10036 common.scaleW = buf.readUInt16LE(i+4) 10037 common.scaleH = buf.readUInt16LE(i+6) 10038 common.pages = buf.readUInt16LE(i+8) 10039 var bitField = buf.readUInt8(i+10) 10040 common.packed = 0 10041 common.alphaChnl = buf.readUInt8(i+11) 10042 common.redChnl = buf.readUInt8(i+12) 10043 common.greenChnl = buf.readUInt8(i+13) 10044 common.blueChnl = buf.readUInt8(i+14) 10045 return common 10046 } 10047 10048 function readPages(buf, i, size) { 10049 var pages = [] 10050 var text = readNameNT(buf, i) 10051 var len = text.length+1 10052 var count = size / len 10053 for (var c=0; c<count; c++) { 10054 pages[c] = buf.slice(i, i+text.length).toString('utf8') 10055 i += len 10056 } 10057 return pages 10058 } 10059 10060 function readChars(buf, i, blockSize) { 10061 var chars = [] 10062 10063 var count = blockSize / 20 10064 for (var c=0; c<count; c++) { 10065 var char = {} 10066 var off = c*20 10067 char.id = buf.readUInt32LE(i + 0 + off) 10068 char.x = buf.readUInt16LE(i + 4 + off) 10069 char.y = buf.readUInt16LE(i + 6 + off) 10070 char.width = buf.readUInt16LE(i + 8 + off) 10071 char.height = buf.readUInt16LE(i + 10 + off) 10072 char.xoffset = buf.readInt16LE(i + 12 + off) 10073 char.yoffset = buf.readInt16LE(i + 14 + off) 10074 char.xadvance = buf.readInt16LE(i + 16 + off) 10075 char.page = buf.readUInt8(i + 18 + off) 10076 char.chnl = buf.readUInt8(i + 19 + off) 10077 chars[c] = char 10078 } 10079 return chars 10080 } 10081 10082 function readKernings(buf, i, blockSize) { 10083 var kernings = [] 10084 var count = blockSize / 10 10085 for (var c=0; c<count; c++) { 10086 var kern = {} 10087 var off = c*10 10088 kern.first = buf.readUInt32LE(i + 0 + off) 10089 kern.second = buf.readUInt32LE(i + 4 + off) 10090 kern.amount = buf.readInt16LE(i + 8 + off) 10091 kernings[c] = kern 10092 } 10093 return kernings 10094 } 10095 10096 function readNameNT(buf, offset) { 10097 var pos=offset 10098 for (; pos<buf.length; pos++) { 10099 if (buf[pos] === 0x00) 10100 break 10101 } 10102 return buf.slice(offset, pos) 10103 } 10104 10105 function readStringNT(buf, offset) { 10106 return readNameNT(buf, offset).toString('utf8') 10107 } 10108 },{}],15:[function(require,module,exports){ 10109 var parseAttributes = require('./parse-attribs') 10110 var parseFromString = require('xml-parse-from-string') 10111 10112 //In some cases element.attribute.nodeName can return 10113 //all lowercase values.. so we need to map them to the correct 10114 //case 10115 var NAME_MAP = { 10116 scaleh: 'scaleH', 10117 scalew: 'scaleW', 10118 stretchh: 'stretchH', 10119 lineheight: 'lineHeight', 10120 alphachnl: 'alphaChnl', 10121 redchnl: 'redChnl', 10122 greenchnl: 'greenChnl', 10123 bluechnl: 'blueChnl' 10124 } 10125 10126 module.exports = function parse(data) { 10127 data = data.toString() 10128 10129 var xmlRoot = parseFromString(data) 10130 var output = { 10131 pages: [], 10132 chars: [], 10133 kernings: [] 10134 } 10135 10136 //get config settings 10137 ;['info', 'common'].forEach(function(key) { 10138 var element = xmlRoot.getElementsByTagName(key)[0] 10139 if (element) 10140 output[key] = parseAttributes(getAttribs(element)) 10141 }) 10142 10143 //get page info 10144 var pageRoot = xmlRoot.getElementsByTagName('pages')[0] 10145 if (!pageRoot) 10146 throw new Error('malformed file -- no <pages> element') 10147 var pages = pageRoot.getElementsByTagName('page') 10148 for (var i=0; i<pages.length; i++) { 10149 var p = pages[i] 10150 var id = parseInt(p.getAttribute('id'), 10) 10151 var file = p.getAttribute('file') 10152 if (isNaN(id)) 10153 throw new Error('malformed file -- page "id" attribute is NaN') 10154 if (!file) 10155 throw new Error('malformed file -- needs page "file" attribute') 10156 output.pages[parseInt(id, 10)] = file 10157 } 10158 10159 //get kernings / chars 10160 ;['chars', 'kernings'].forEach(function(key) { 10161 var element = xmlRoot.getElementsByTagName(key)[0] 10162 if (!element) 10163 return
10164 var childTag = key.substring(0, key.length-1) 10165 var children = element.getElementsByTagName(childTag) 10166 for (var i=0; i<children.length; i++) { 10167 var child = children[i] 10168 output[key].push(parseAttributes(getAttribs(child))) 10169 } 10170 }) 10171 return output 10172 } 10173 10174 function getAttribs(element) { 10175 var attribs = getAttribList(element) 10176 return attribs.reduce(function(dict, attrib) { 10177 var key = mapName(attrib.nodeName) 10178 dict[key] = attrib.nodeValue 10179 return dict 10180 }, {}) 10181 } 10182 10183 function getAttribList(element) { 10184 //IE8+ and modern browsers 10185 var attribs = [] 10186 for (var i=0; i<element.attributes.length; i++) 10187 attribs.push(element.attributes[i]) 10188 return attribs 10189 } 10190 10191 function mapName(nodeName) { 10192 return NAME_MAP[nodeName.toLowerCase()] || nodeName 10193 } 10194 },{"./parse-attribs":16,"xml-parse-from-string":17}],16:[function(require,module,exports){ 10195 //Some versions of GlyphDesigner have a typo 10196 //that causes some bugs with parsing. 10197 //Need to confirm with recent version of the software 10198 //to see whether this is still an issue or not. 10199 var GLYPH_DESIGNER_ERROR = 'chasrset' 10200 10201 module.exports = function parseAttributes(obj) { 10202 if (GLYPH_DESIGNER_ERROR in obj) { 10203 obj['charset'] = obj[GLYPH_DESIGNER_ERROR] 10204 delete obj[GLYPH_DESIGNER_ERROR] 10205 } 10206 10207 for (var k in obj) { 10208 if (k === 'face' || k === 'charset') 10209 continue 10210 else if (k === 'padding' || k === 'spacing') 10211 obj[k] = parseIntList(obj[k]) 10212 else 10213 obj[k] = parseInt(obj[k], 10) 10214 } 10215 return obj 10216 } 10217 10218 function parseIntList(data) { 10219 return data.split(',').map(function(val) { 10220 return parseInt(val, 10) 10221 }) 10222 } 10223 },{}],17:[function(require,module,exports){ 10224 module.exports = (function xmlparser() { 10225 //common browsers 10226 if (typeof window.DOMParser !== 'undefined') { 10227 return function(str) { 10228 var parser = new window.DOMParser() 10229 return parser.parseFromString(str, 'application/xml') 10230 } 10231 } 10232 10233 //IE8 fallback 10234 if (typeof window.ActiveXObject !== 'undefined' 10235 && new window.ActiveXObject('Microsoft.XMLDOM')) { 10236 return function(str) { 10237 var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM") 10238 xmlDoc.async = "false" 10239 xmlDoc.loadXML(str) 10240 return xmlDoc 10241 } 10242 } 10243 10244 //last resort fallback 10245 return function(str) { 10246 var div = document.createElement('div') 10247 div.innerHTML = str 10248 return div 10249 } 10250 })() 10251 },{}],18:[function(require,module,exports){ 10252 "use strict"; 10253 var window = require("global/window") 10254 var once = require("once") 10255 var isFunction = require("is-function") 10256 var parseHeaders = require("parse-headers") 10257 var xtend = require("xtend") 10258 10259 module.exports = createXHR 10260 createXHR.XMLHttpRequest = window.XMLHttpRequest || noop 10261 createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window.XDomainRequest 10262 10263 forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) { 10264 createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) { 10265 options = initParams(uri, options, callback) 10266 options.method = method.toUpperCase() 10267 return _createXHR(options) 10268 } 10269 }) 10270 10271 function forEachArray(array, iterator) { 10272 for (var i = 0; i < array.length; i++) { 10273 iterator(array[i]) 10274 } 10275 } 10276 10277 function isEmpty(obj){ 10278 for(var i in obj){ 10279 if(obj.hasOwnProperty(i)) return false 10280 } 10281 return true 10282 } 10283 10284 function initParams(uri, options, callback) { 10285 var params = uri 10286 10287 if (isFunction(options)) { 10288 callback = options 10289 if (typeof uri === "string") { 10290 params = {uri:uri} 10291 } 10292 } else { 10293 params = xtend(options, {uri: uri}) 10294 } 10295 10296 params.callback = callback 10297 return params 10298 } 10299 10300 function createXHR(uri, options, callback) { 10301 options = initParams(uri, options, callback) 10302 return _createXHR(options) 10303 } 10304 10305 function _createXHR(options) { 10306 var callback = options.callback 10307 if(typeof callback === "undefined"){ 10308 throw new Error("callback argument missing") 10309 } 10310 callback = once(callback) 10311 10312 function readystatechange() { 10313 if (xhr.readyState === 4) { 10314 loadFunc() 10315 } 10316 } 10317 10318 function getBody() { 10319 // Chrome with requestType=blob throws errors arround when even testing access to responseText 10320 var body = undefined 10321 10322 if (xhr.response) { 10323 body = xhr.response 10324 } else if (xhr.responseType === "text" || !xhr.responseType) { 10325 body = xhr.responseText || xhr.responseXML 10326 } 10327 10328 if (isJson) { 10329 try { 10330 body = JSON.parse(body) 10331 } catch (e) {} 10332 } 10333 10334 return body 10335 } 10336 10337 var failureResponse = { 10338 body: undefined, 10339 headers: {}, 10340 statusCode: 0, 10341 method: method,
vendor: 4,097 bytes, lines 10342-10456
10342 url: uri, 10343 rawRequest: xhr 10344 } 10345 10346 function errorFunc(evt) { 10347 clearTimeout(timeoutTimer) 10348 if(!(evt instanceof Error)){ 10349 evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") ) 10350 } 10351 evt.statusCode = 0 10352 callback(evt, failureResponse) 10353 } 10354 10355 // will load the data & process the response in a special response object 10356 function loadFunc() { 10357 if (aborted) return 10358 var status 10359 clearTimeout(timeoutTimer) 10360 if(options.useXDR && xhr.status===undefined) { 10361 //IE8 CORS GET successful response doesn't have a status field, but body is fine 10362 status = 200 10363 } else { 10364 status = (xhr.status === 1223 ? 204 : xhr.status) 10365 } 10366 var response = failureResponse 10367 var err = null 10368 10369 if (status !== 0){ 10370 response = { 10371 body: getBody(), 10372 statusCode: status, 10373 method: method, 10374 headers: {}, 10375 url: uri, 10376 rawRequest: xhr 10377 } 10378 if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE 10379 response.headers = parseHeaders(xhr.getAllResponseHeaders()) 10380 } 10381 } else { 10382 err = new Error("Internal XMLHttpRequest Error") 10383 } 10384 callback(err, response, response.body) 10385 10386 } 10387 10388 var xhr = options.xhr || null 10389 10390 if (!xhr) { 10391 if (options.cors || options.useXDR) { 10392 xhr = new createXHR.XDomainRequest() 10393 }else{ 10394 xhr = new createXHR.XMLHttpRequest() 10395 } 10396 } 10397 10398 var key 10399 var aborted 10400 var uri = xhr.url = options.uri || options.url 10401 var method = xhr.method = options.method || "GET" 10402 var body = options.body || options.data || null 10403 var headers = xhr.headers = options.headers || {} 10404 var sync = !!options.sync 10405 var isJson = false 10406 var timeoutTimer 10407 10408 if ("json" in options) { 10409 isJson = true 10410 headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json") //Don't override existing accept header declared by user 10411 if (method !== "GET" && method !== "HEAD") { 10412 headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json") //Don't override existing accept header declared by user 10413 body = JSON.stringify(options.json) 10414 } 10415 } 10416 10417 xhr.onreadystatechange = readystatechange 10418 xhr.onload = loadFunc 10419 xhr.onerror = errorFunc 10420 // IE9 must have onprogress be set to a unique function. 10421 xhr.onprogress = function () { 10422 // IE must die 10423 } 10424 xhr.ontimeout = errorFunc 10425 xhr.open(method, uri, !sync, options.username, options.password) 10426 //has to be after open 10427 if(!sync) { 10428 xhr.withCredentials = !!options.withCredentials 10429 } 10430 // Cannot set timeout with sync request 10431 // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly 10432 // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent 10433 if (!sync && options.timeout > 0 ) { 10434 timeoutTimer = setTimeout(function(){ 10435 aborted=true//IE9 may still call readystatechange 10436 xhr.abort("timeout") 10437 var e = new Error("XMLHttpRequest timeout") 10438 e.code = "ETIMEDOUT" 10439 errorFunc(e) 10440 }, options.timeout ) 10441 } 10442 10443 if (xhr.setRequestHeader) { 10444 for(key in headers){ 10445 if(headers.hasOwnProperty(key)){ 10446 xhr.setRequestHeader(key, headers[key]) 10447 } 10448 } 10449 } else if (options.headers && !isEmpty(options.headers)) { 10450 throw new Error("Headers cannot be set on an XDomainRequest object") 10451 } 10452 10453 if ("responseType" in options) { 10454 xhr.responseType = options.responseType 10455 } 10456
10457 if ("beforeSend" in options && 10458 typeof options.beforeSend === "function" 10459 ) { 10460 options.beforeSend(xhr) 10461 } 10462 10463 xhr.send(body) 10464 10465 return xhr 10466 10467 10468 } 10469 10470 function noop() {} 10471 10472 },{"global/window":19,"is-function":20,"once":21,"parse-headers":24,"xtend":25}],19:[function(require,module,exports){ 10473 (function (global){ 10474 if (typeof window !== "undefined") { 10475 module.exports = window; 10476 } else if (typeof global !== "undefined") { 10477 module.exports = global; 10478 } else if (typeof self !== "undefined"){ 10479 module.exports = self; 10480 } else { 10481 module.exports = {}; 10482 } 10483 10484 }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 10485 },{}],20:[function(require,module,exports){ 10486 module.exports = isFunction 10487 10488 var toString = Object.prototype.toString 10489 10490 function isFunction (fn) { 10491 var string = toString.call(fn) 10492 return string === '[object Function]' || 10493 (typeof fn === 'function' && string !== '[object RegExp]') || 10494 (typeof window !== 'undefined' && 10495 // IE8 and below 10496 (fn === window.setTimeout || 10497 fn === window.alert || 10498 fn === window.confirm || 10499 fn === window.prompt)) 10500 }; 10501 10502 },{}],21:[function(require,module,exports){ 10503 module.exports = once 10504 10505 once.proto = once(function () { 10506 Object.defineProperty(Function.prototype, 'once', { 10507 value: function () { 10508 return once(this) 10509 }, 10510 configurable: true 10511 }) 10512 }) 10513 10514 function once (fn) { 10515 var called = false 10516 return function () { 10517 if (called) return 10518 called = true 10519 return fn.apply(this, arguments) 10520 } 10521 } 10522 10523 },{}],22:[function(require,module,exports){ 10524 var isFunction = require('is-function') 10525 10526 module.exports = forEach 10527 10528 var toString = Object.prototype.toString 10529 var hasOwnProperty = Object.prototype.hasOwnProperty 10530 10531 function forEach(list, iterator, context) { 10532 if (!isFunction(iterator)) { 10533 throw new TypeError('iterator must be a function') 10534 } 10535 10536 if (arguments.length < 3) { 10537 context = this 10538 } 10539 10540 if (toString.call(list) === '[object Array]') 10541 forEachArray(list, iterator, context) 10542 else if (typeof list === 'string') 10543 forEachString(list, iterator, context) 10544 else 10545 forEachObject(list, iterator, context) 10546 } 10547 10548 function forEachArray(array, iterator, context) { 10549 for (var i = 0, len = array.length; i < len; i++) { 10550 if (hasOwnProperty.call(array, i)) { 10551 iterator.call(context, array[i], i, array) 10552 } 10553 } 10554 } 10555 10556 function forEachString(string, iterator, context) { 10557 for (var i = 0, len = string.length; i < len; i++) { 10558 // no such thing as a sparse string. 10559 iterator.call(context, string.charAt(i), i, string) 10560 } 10561 } 10562 10563 function forEachObject(object, iterator, context) { 10564 for (var k in object) { 10565 if (hasOwnProperty.call(object, k)) { 10566 iterator.call(context, object[k], k, object) 10567 } 10568 } 10569 } 10570 10571 },{"is-function":20}],23:[function(require,module,exports){ 10572 10573 exports = module.exports = trim; 10574 10575 function trim(str){ 10576 return str.replace(/^\s*|\s*$/g, ''); 10577 } 10578 10579 exports.left = function(str){ 10580 return str.replace(/^\s*/, ''); 10581 }; 10582 10583 exports.right = function(str){ 10584 return str.replace(/\s*$/, ''); 10585 }; 10586 10587 },{}],24:[function(require,module,exports){ 10588 var trim = require('trim') 10589 , forEach = require('for-each') 10590 , isArray = function(arg) { 10591 return Object.prototype.toString.call(arg) === '[object Array]'; 10592 } 10593 10594 module.exports = function (headers) { 10595 if (!headers) 10596 return {} 10597 10598 var result = {} 10599
10600 forEach( 10601 trim(headers).split('\n') 10602 , function (row) { 10603 var index = row.indexOf(':') 10604 , key = trim(row.slice(0, index)).toLowerCase() 10605 , value = trim(row.slice(index + 1)) 10606 10607 if (typeof(result[key]) === 'undefined') { 10608 result[key] = value 10609 } else if (isArray(result[key])) { 10610 result[key].push(value) 10611 } else { 10612 result[key] = [ result[key], value ] 10613 } 10614 } 10615 ) 10616 10617 return result 10618 } 10619 },{"for-each":22,"trim":23}],25:[function(require,module,exports){ 10620 arguments[4][9][0].apply(exports,arguments) 10621 },{"dup":9}],26:[function(require,module,exports){ 10622 /* eslint-disable no-unused-vars */ 10623 'use strict'; 10624 var hasOwnProperty = Object.prototype.hasOwnProperty; 10625 var propIsEnumerable = Object.prototype.propertyIsEnumerable; 10626 10627 function toObject(val) { 10628 if (val === null || val === undefined) { 10629 throw new TypeError('Object.assign cannot be called with null or undefined'); 10630 } 10631 10632 return Object(val); 10633 } 10634 10635 module.exports = Object.assign || function (target, source) { 10636 var from; 10637 var to = toObject(target); 10638 var symbols; 10639 10640 for (var s = 1; s < arguments.length; s++) { 10641 from = Object(arguments[s]); 10642 10643 for (var key in from) { 10644 if (hasOwnProperty.call(from, key)) { 10645 to[key] = from[key]; 10646 } 10647 } 10648 10649 if (Object.getOwnPropertySymbols) { 10650 symbols = Object.getOwnPropertySymbols(from); 10651 for (var i = 0; i < symbols.length; i++) { 10652 if (propIsEnumerable.call(from, symbols[i])) { 10653 to[symbols[i]] = from[symbols[i]]; 10654 } 10655 } 10656 } 10657 } 10658 10659 return to; 10660 }; 10661 10662 },{}],27:[function(require,module,exports){ 10663 var dtype = require('dtype') 10664 var anArray = require('an-array') 10665 var isBuffer = require('is-buffer') 10666 10667 var CW = [0, 2, 3] 10668 var CCW = [2, 1, 3] 10669 10670 module.exports = function createQuadElements(array, opt) { 10671 //if user didn't specify an output array 10672 if (!array || !(anArray(array) || isBuffer(array))) { 10673 opt = array || {} 10674 array = null 10675 } 10676 10677 if (typeof opt === 'number') //backwards-compatible 10678 opt = { count: opt } 10679 else 10680 opt = opt || {} 10681 10682 var type = typeof opt.type === 'string' ? opt.type : 'uint16' 10683 var count = typeof opt.count === 'number' ? opt.count : 1 10684 var start = (opt.start || 0) 10685 10686 var dir = opt.clockwise !== false ? CW : CCW, 10687 a = dir[0], 10688 b = dir[1], 10689 c = dir[2] 10690 10691 var numIndices = count * 6 10692 10693 var indices = array || new (dtype(type))(numIndices) 10694 for (var i = 0, j = 0; i < numIndices; i += 6, j += 4) { 10695 var x = i + start 10696 indices[x + 0] = j + 0 10697 indices[x + 1] = j + 1 10698 indices[x + 2] = j + 2 10699 indices[x + 3] = j + a 10700 indices[x + 4] = j + b 10701 indices[x + 5] = j + c 10702 } 10703 return indices 10704 } 10705 },{"an-array":28,"dtype":29,"is-buffer":30}],28:[function(require,module,exports){ 10706 var str = Object.prototype.toString 10707 10708 module.exports = anArray 10709 10710 function anArray(arr) { 10711 return ( 10712 arr.BYTES_PER_ELEMENT 10713 && str.call(arr.buffer) === '[object ArrayBuffer]' 10714 || Array.isArray(arr) 10715 ) 10716 } 10717 10718 },{}],29:[function(require,module,exports){ 10719 module.exports = function(dtype) { 10720 switch (dtype) { 10721 case 'int8': 10722 return Int8Array 10723 case 'int16': 10724 return Int16Array 10725 case 'int32': 10726 return Int32Array 10727 case 'uint8': 10728 return Uint8Array 10729 case 'uint16': 10730 return Uint16Array 10731 case 'uint32': 10732 return Uint32Array 10733 case 'float32': 10734 return Float32Array 10735 case 'float64': 10736 return Float64Array 10737 case 'array': 10738 return Array 10739 case 'uint8_clamped': 10740 return Uint8ClampedArray 10741 } 10742 } 10743 10744 },{}],30:[function(require,module,exports){ 10745 /** 10746 * Determine if an object is Buffer 10747 * 10748 * Author: Feross Aboukhadijeh <[email protected]> <http://feross.org> 10749 * License: MIT 10750 * 10751 * `npm install is-buffer` 10752 */ 10753 10754 module.exports = function (obj) { 10755 return !!(obj != null && 10756 (obj._isBuffer || // For Safari 5-7 (missing Object.prototype.constructor)
10757 (obj.constructor && 10758 typeof obj.constructor.isBuffer === 'function' && 10759 obj.constructor.isBuffer(obj)) 10760 )) 10761 } 10762 10763 },{}],31:[function(require,module,exports){ 10764 var flatten = require('flatten-vertex-data') 10765 10766 module.exports.attr = setAttribute 10767 module.exports.index = setIndex 10768 10769 function setIndex (geometry, data, itemSize, dtype) { 10770 if (typeof itemSize !== 'number') itemSize = 1 10771 if (typeof dtype !== 'number') dtype = 'uint16' 10772 10773 var isR69 = !geometry.index && typeof geometry.setIndex !== 'function' 10774 var attrib = isR69 ? geometry.getAttribute('index') : geometry.index 10775 var newAttrib = updateAttribute(attrib, data, itemSize, dtype) 10776 if (newAttrib) { 10777 if (isR69) geometry.addAttribute('index', newAttrib) 10778 else geometry.index = newAttrib 10779 } 10780 } 10781 10782 function setAttribute (geometry, key, data, itemSize, dtype) { 10783 if (typeof itemSize !== 'number') itemSize = 3 10784 if (typeof dtype !== 'number') dtype = 'float32' 10785 if (Array.isArray(data) && 10786 Array.isArray(data[0]) && 10787 data[0].length !== itemSize) { 10788 throw new Error('Nested vertex array has unexpected size; expected ' + 10789 itemSize + ' but found ' + data[0].length) 10790 } 10791 10792 var attrib = geometry.getAttribute(key) 10793 var newAttrib = updateAttribute(attrib, data, itemSize, dtype) 10794 if (newAttrib) { 10795 geometry.addAttribute(key, newAttrib) 10796 } 10797 } 10798 10799 function updateAttribute (attrib, data, itemSize, dtype) { 10800 data = data || [] 10801 if (!attrib || rebuildAttribute(attrib, data, itemSize)) { 10802 // create a new array with desired type 10803 data = flatten(data, dtype) 10804 attrib = new THREE.BufferAttribute(data, itemSize) 10805 attrib.needsUpdate = true 10806 return attrib 10807 } else { 10808 // copy data into the existing array 10809 flatten(data, attrib.array) 10810 attrib.needsUpdate = true 10811 return null 10812 } 10813 } 10814 10815 // Test whether the attribute needs to be re-created, 10816 // returns false if we can re-use it as-is. 10817 function rebuildAttribute (attrib, data, itemSize) { 10818 if (attrib.itemSize !== itemSize) return true 10819 if (!attrib.array) return true 10820 var attribLength = attrib.array.length 10821 if (Array.isArray(data) && Array.isArray(data[0])) { 10822 // [ [ x, y, z ] ] 10823 return attribLength !== data.length * itemSize 10824 } else { 10825 // [ x, y, z ] 10826 return attribLength !== data.length 10827 } 10828 } 10829 10830 },{"flatten-vertex-data":32}],32:[function(require,module,exports){ 10831 /*eslint new-cap:0*/ 10832 var dtype = require('dtype') 10833 module.exports = flattenVertexData 10834 function flattenVertexData (data, output, offset) { 10835 if (!data) throw new TypeError('must specify data as first parameter') 10836 offset = +(offset || 0) | 0 10837 10838 if (Array.isArray(data) && Array.isArray(data[0])) { 10839 var dim = data[0].length 10840 var length = data.length * dim 10841 10842 // no output specified, create a new typed array 10843 if (!output || typeof output === 'string') { 10844 output = new (dtype(output || 'float32'))(length + offset) 10845 } 10846 10847 var dstLength = output.length - offset 10848 if (length !== dstLength) { 10849 throw new Error('source length ' + length + ' (' + dim + 'x' + data.length + ')' + 10850 ' does not match destination length ' + dstLength) 10851 } 10852 10853 for (var i = 0, k = offset; i < data.length; i++) { 10854 for (var j = 0; j < dim; j++) { 10855 output[k++] = data[i][j] 10856 } 10857 } 10858 } else { 10859 if (!output || typeof output === 'string') { 10860 // no output, create a new one 10861 var Ctor = dtype(output || 'float32') 10862 if (offset === 0) { 10863 output = new Ctor(data) 10864 } else { 10865 output = new Ctor(data.length + offset) 10866 output.set(data, offset) 10867 } 10868 } else { 10869 // store output in existing array 10870 output.set(data, offset) 10871 } 10872 } 10873 10874 return output 10875 } 10876 10877 },{"dtype":33}],33:[function(require,module,exports){ 10878 arguments[4][29][0].apply(exports,arguments) 10879 },{"dup":29}],34:[function(require,module,exports){ 10880 var assign = require('object-assign') 10881 10882 module.exports = function createSDFShader (opt) { 10883 opt = opt || {} 10884 var opacity = typeof opt.opacity === 'number' ? opt.opacity : 1 10885 var alphaTest = typeof opt.alphaTest === 'number' ? opt.alphaTest : 0.0001 10886 var precision = opt.precision || 'highp' 10887 var color = opt.color 10888 var map = opt.map 10889 10890 // remove to satisfy r73 10891 delete opt.map 10892 delete opt.color 10893 delete opt.precision 10894 delete opt.opacity 10895 10896 return assign({ 10897 uniforms: { 10898 opacity: { type: 'f', value: opacity }, 10899 map: { type: 't', value: map || new THREE.Texture() }, 10900 color: { type: 'c', value: new THREE.Color(color) } 10901 }, 10902 vertexShader: [ 10903 'attribute vec2 uv;', 10904 'attribute vec4 position;', 10905 'uniform mat4 projectionMatrix;', 10906 'uniform mat4 modelViewMatrix;', 10907 'varying vec2 vUv;', 10908 'void main() {', 10909 'vUv = uv;',
10910 'gl_Position = projectionMatrix * modelViewMatrix * position;', 10911 '}' 10912 ].join('\n'), 10913 fragmentShader: [ 10914 '#ifdef GL_OES_standard_derivatives', 10915 '#extension GL_OES_standard_derivatives : enable', 10916 '#endif', 10917 'precision ' + precision + ' float;', 10918 'uniform float opacity;', 10919 'uniform vec3 color;', 10920 'uniform sampler2D map;', 10921 'varying vec2 vUv;', 10922 10923 'float aastep(float value) {', 10924 ' #ifdef GL_OES_standard_derivatives', 10925 ' float afwidth = length(vec2(dFdx(value), dFdy(value))) * 0.70710678118654757;', 10926 ' #else', 10927 ' float afwidth = (1.0 / 32.0) * (1.4142135623730951 / (2.0 * gl_FragCoord.w));', 10928 ' #endif', 10929 ' return smoothstep(0.5 - afwidth, 0.5 + afwidth, value);', 10930 '}', 10931 10932 'void main() {', 10933 ' vec4 texColor = texture2D(map, vUv);', 10934 ' float alpha = aastep(texColor.a);', 10935 ' gl_FragColor = vec4(color, opacity * alpha);', 10936 alphaTest === 0 10937 ? '' 10938 : ' if (gl_FragColor.a < ' + alphaTest + ') discard;', 10939 '}' 10940 ].join('\n') 10941 }, opt) 10942 } 10943 10944 },{"object-assign":26}],35:[function(require,module,exports){ 10945 var loadFont = require('load-bmfont') 10946 //global.THREE = require('three') 10947 10948 // A utility to load a font, then a texture 10949 module.exports = function (opt, cb) { 10950 loadFont(opt.font, function (err, font) { 10951 if (err) throw err 10952 PANOLENS.Utils.TextureLoader.load( opt.image, function (tex) { 10953 cb(font, tex) 10954 } ); 10955 }) 10956 } 10957 10958 },{"load-bmfont":10}],36:[function(require,module,exports){ 10959 10960 var createText = require('../') 10961 var SDFShader = require('../shaders/sdf') 10962 10963 if ( PANOLENS && PANOLENS.Utils && PANOLENS.SpriteText ) { 10964 PANOLENS.Utils.loadBMFont = function(fontObject, callback){ 10965 require('./load')(fontObject, PANOLENS.SpriteText.prototype.setBMFont.bind(PANOLENS.SpriteText.prototype, callback)); 10966 }; 10967 PANOLENS.SpriteText.prototype.generateTextGeometry = createText; 10968 PANOLENS.SpriteText.prototype.generateSDFShader = SDFShader; 10969 } 10970 10971 },{"../":1,"../shaders/sdf":34,"./load":35}],37:[function(require,module,exports){ 10972 (function (global){ 10973 /*! 10974 * The buffer module from node.js, for the browser. 10975 * 10976 * @author Feross Aboukhadijeh <[email protected]> <http://feross.org> 10977 * @license MIT 10978 */ 10979 /* eslint-disable no-proto */ 10980 10981 'use strict' 10982 10983 var base64 = require('base64-js') 10984 var ieee754 = require('ieee754') 10985 var isArray = require('isarray') 10986 10987 exports.Buffer = Buffer 10988 exports.SlowBuffer = SlowBuffer 10989 exports.INSPECT_MAX_BYTES = 50 10990 Buffer.poolSize = 8192 // not used by this implementation 10991 10992 var rootParent = {} 10993 10994 /** 10995 * If `Buffer.TYPED_ARRAY_SUPPORT`: 10996 * === true Use Uint8Array implementation (fastest) 10997 * === false Use Object implementation (most compatible, even IE6) 10998 * 10999 * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, 11000 * Opera 11.6+, iOS 4.2+. 11001 * 11002 * Due to various browser bugs, sometimes the Object implementation will be used even 11003 * when the browser supports typed arrays. 11004 * 11005 * Note: 11006 * 11007 * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, 11008 * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. 11009 * 11010 * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. 11011 * 11012 * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of 11013 * incorrect length in some situations. 11014 11015 * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they 11016 * get the Object implementation, which is slower but behaves correctly. 11017 */ 11018 Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined 11019 ? global.TYPED_ARRAY_SUPPORT 11020 : typedArraySupport() 11021 11022 function typedArraySupport () { 11023 try { 11024 var arr = new Uint8Array(1) 11025 arr.foo = function () { return 42 } 11026 return arr.foo() === 42 && // typed array instances can be augmented 11027 typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` 11028 arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` 11029 } catch (e) { 11030 return false 11031 } 11032 } 11033 11034 function kMaxLength () { 11035 return Buffer.TYPED_ARRAY_SUPPORT 11036 ? 0x7fffffff 11037 : 0x3fffffff 11038 } 11039 11040 /** 11041 * Class: Buffer 11042 * ============= 11043 * 11044 * The Buffer constructor returns instances of `Uint8Array` that are augmented 11045 * with function properties for all the node `Buffer` API functions. We use 11046 * `Uint8Array` so that square bracket notation works as expected -- it returns 11047 * a single octet. 11048 * 11049 * By augmenting the instances, we can avoid modifying the `Uint8Array` 11050 * prototype. 11051 */ 11052 function Buffer (arg) { 11053 if (!(this instanceof Buffer)) { 11054 // Avoid going through an ArgumentsAdaptorTrampoline in the common case. 11055 if (arguments.length > 1) return new Buffer(arg, arguments[1]) 11056 return new Buffer(arg) 11057 } 11058 11059 if (!Buffer.TYPED_ARRAY_SUPPORT) { 11060 this.length = 0 11061 this.parent = undefined 11062 } 11063 11064 // Common case. 11065 if (typeof arg === 'number') { 11066 return fromNumber(this, arg) 11067 } 11068 11069 // Slightly less common case. 11070 if (typeof arg === 'string') { 11071 return fromString(this, arg, arguments.length > 1 ? arguments[1] : 'utf8') 11072 } 11073 11074 // Unusual. 11075 return fromObject(this, arg) 11076 } 11077 11078 function fromNumber (that, length) { 11079 that = allocate(that, length < 0 ? 0 : checked(length) | 0) 11080 if (!Buffer.TYPED_ARRAY_SUPPORT) { 11081 for (var i = 0; i < length; i++) { 11082 that[i] = 0 11083 } 11084 } 11085 return that 11086 } 11087 11088 function fromString (that, string, encoding) { 11089 if (typeof encoding !== 'string' || encoding === '') encoding = 'utf8' 11090 11091 // Assumption: byteLength() return value is always < kMaxLength. 11092 var length = byteLength(string, encoding) | 0 11093 that = allocate(that, length) 11094 11095 that.write(string, encoding) 11096 return that 11097 } 11098 11099 function fromObject (that, object) { 11100 if (Buffer.isBuffer(object)) return fromBuffer(that, object) 11101 11102 if (isArray(object)) return fromArray(that, object) 11103 11104 if (object == null) { 11105 throw new TypeError('must start with number, buffer, array or string') 11106 } 11107 11108 if (typeof ArrayBuffer !== 'undefined') { 11109 if (object.buffer instanceof ArrayBuffer) { 11110 return fromTypedArray(that, object) 11111 } 11112 if (object instanceof ArrayBuffer) { 11113 return fromArrayBuffer(that, object) 11114 } 11115 } 11116 11117 if (object.length) return fromArrayLike(that, object) 11118 11119 return fromJsonObject(that, object) 11120 } 11121 11122 function fromBuffer (that, buffer) { 11123 var length = checked(buffer.length) | 0 11124 that = allocate(that, length) 11125 buffer.copy(that, 0, 0, length) 11126 return that 11127 } 11128 11129 function fromArray (that, array) { 11130 var length = checked(array.length) | 0 11131 that = allocate(that, length) 11132 for (var i = 0; i < length; i += 1) { 11133 that[i] = array[i] & 255 11134 } 11135 return that 11136 } 11137 11138 // Duplicate of fromArray() to keep fromArray() monomorphic. 11139 function fromTypedArray (that, array) { 11140 var length = checked(array.length) | 0 11141 that = allocate(that, length) 11142 // Truncating the elements is probably not what people expect from typed 11143 // arrays with BYTES_PER_ELEMENT > 1 but it's compatible with the behavior 11144 // of the old Buffer constructor. 11145 for (var i = 0; i < length; i += 1) { 11146 that[i] = array[i] & 255 11147 } 11148 return that 11149 } 11150 11151 function fromArrayBuffer (that, array) { 11152 //array.byteLength // this throws if `array` is not a valid ArrayBuffer 11153 11154 if (Buffer.TYPED_ARRAY_SUPPORT) { 11155 // Return an augmented `Uint8Array` instance, for best performance 11156 that = new Uint8Array(array) 11157 that.__proto__ = Buffer.prototype 11158 } else { 11159 // Fallback: Return an object instance of the Buffer class 11160 that = fromTypedArray(that, new Uint8Array(array)) 11161 } 11162 return that 11163 } 11164 11165 function fromArrayLike (that, array) { 11166 var length = checked(array.length) | 0 11167 that = allocate(that, length) 11168 for (var i = 0; i < length; i += 1) { 11169 that[i] = array[i] & 255 11170 } 11171 return that 11172 } 11173 11174 // Deserialize { type: 'Buffer', data: [1,2,3,...] } into a Buffer object. 11175 // Returns a zero-length buffer for inputs that don't conform to the spec. 11176 function fromJsonObject (that, object) { 11177 var array 11178 var length = 0 11179 11180 if (object.type === 'Buffer' && isArray(object.data)) { 11181 array = object.data 11182 length = checked(array.length) | 0 11183 } 11184 that = allocate(that, length) 11185 11186 for (var i = 0; i < length; i += 1) { 11187 that[i] = array[i] & 255 11188 } 11189 return that 11190 } 11191 11192 if (Buffer.TYPED_ARRAY_SUPPORT) { 11193 Buffer.prototype.__proto__ = Uint8Array.prototype 11194 Buffer.__proto__ = Uint8Array 11195 } else { 11196 // pre-set for values that may exist in the future 11197 Buffer.prototype.length = undefined 11198 Buffer.prototype.parent = undefined 11199 } 11200 11201 function allocate (that, length) { 11202 if (Buffer.TYPED_ARRAY_SUPPORT) { 11203 // Return an augmented `Uint8Array` instance, for best performance 11204 that = new Uint8Array(length) 11205 that.__proto__ = Buffer.prototype 11206 } else { 11207 // Fallback: Return an object instance of the Buffer class 11208 that.length = length 11209 } 11210 11211 var fromPool = length !== 0 && length <= Buffer.poolSize >>> 1 11212 if (fromPool) that.parent = rootParent 11213 11214 return that 11215 } 11216 11217 function checked (length) { 11218 // Note: cannot use `length < kMaxLength` here because that fails when 11219 // length is NaN (which is otherwise coerced to zero.) 11220 if (length >= kMaxLength()) { 11221 throw new RangeError('Attempt to allocate Buffer larger than maximum ' + 11222 'size: 0x' + kMaxLength().toString(16) + ' bytes') 11223 } 11224 return length | 0 11225 } 11226 11227 function SlowBuffer (subject, encoding) { 11228 if (!(this instanceof SlowBuffer)) return new SlowBuffer(subject, encoding) 11229 11230 var buf = new Buffer(subject, encoding) 11231 delete buf.parent 11232 return buf 11233 } 11234 11235 Buffer.isBuffer = function isBuffer (b) { 11236 return !!(b != null && b._isBuffer) 11237 } 11238 11239 Buffer.compare = function compare (a, b) { 11240 if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { 11241 throw new TypeError('Arguments must be Buffers') 11242 } 11243
11244 if (a === b) return 0 11245 11246 var x = a.length 11247 var y = b.length 11248 11249 var i = 0 11250 var len = Math.min(x, y) 11251 while (i < len) { 11252 if (a[i] !== b[i]) break 11253 11254 ++i 11255 } 11256 11257 if (i !== len) { 11258 x = a[i] 11259 y = b[i] 11260 } 11261 11262 if (x < y) return -1 11263 if (y < x) return 1 11264 return 0 11265 } 11266 11267 Buffer.isEncoding = function isEncoding (encoding) { 11268 switch (String(encoding).toLowerCase()) { 11269 case 'hex': 11270 case 'utf8': 11271 case 'utf-8': 11272 case 'ascii': 11273 case 'binary': 11274 case 'base64': 11275 case 'raw': 11276 case 'ucs2': 11277 case 'ucs-2': 11278 case 'utf16le': 11279 case 'utf-16le': 11280 return true 11281 default: 11282 return false 11283 } 11284 } 11285 11286 Buffer.concat = function concat (list, length) { 11287 if (!isArray(list)) throw new TypeError('list argument must be an Array of Buffers.') 11288 11289 if (list.length === 0) { 11290 return new Buffer(0) 11291 } 11292 11293 var i 11294 if (length === undefined) { 11295 length = 0 11296 for (i = 0; i < list.length; i++) { 11297 length += list[i].length 11298 } 11299 } 11300 11301 var buf = new Buffer(length) 11302 var pos = 0 11303 for (i = 0; i < list.length; i++) { 11304 var item = list[i] 11305 item.copy(buf, pos) 11306 pos += item.length 11307 } 11308 return buf 11309 } 11310 11311 function byteLength (string, encoding) { 11312 if (typeof string !== 'string') string = '' + string 11313 11314 var len = string.length 11315 if (len === 0) return 0 11316 11317 // Use a for loop to avoid recursion 11318 var loweredCase = false 11319 for (;;) { 11320 switch (encoding) { 11321 case 'ascii': 11322 case 'binary': 11323 // Deprecated 11324 case 'raw': 11325 case 'raws': 11326 return len 11327 case 'utf8': 11328 case 'utf-8': 11329 return utf8ToBytes(string).length 11330 case 'ucs2': 11331 case 'ucs-2': 11332 case 'utf16le': 11333 case 'utf-16le': 11334 return len * 2 11335 case 'hex': 11336 return len >>> 1 11337 case 'base64': 11338 return base64ToBytes(string).length 11339 default: 11340 if (loweredCase) return utf8ToBytes(string).length // assume utf8 11341 encoding = ('' + encoding).toLowerCase() 11342 loweredCase = true 11343 } 11344 } 11345 } 11346 Buffer.byteLength = byteLength 11347 11348 function slowToString (encoding, start, end) { 11349 var loweredCase = false 11350 11351 start = start | 0 11352 end = end === undefined || end === Infinity ? this.length : end | 0 11353 11354 if (!encoding) encoding = 'utf8' 11355 if (start < 0) start = 0 11356 if (end > this.length) end = this.length 11357 if (end <= start) return '' 11358 11359 while (true) { 11360 switch (encoding) { 11361 case 'hex': 11362 return hexSlice(this, start, end) 11363 11364 case 'utf8': 11365 case 'utf-8': 11366 return utf8Slice(this, start, end) 11367 11368 case 'ascii': 11369 return asciiSlice(this, start, end) 11370 11371 case 'binary': 11372 return binarySlice(this, start, end) 11373 11374 case 'base64': 11375 return base64Slice(this, start, end) 11376 11377 case 'ucs2': 11378 case 'ucs-2': 11379 case 'utf16le': 11380 case 'utf-16le': 11381 return utf16leSlice(this, start, end) 11382 11383 default: 11384 if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) 11385 encoding = (encoding + '').toLowerCase() 11386 loweredCase = true 11387 } 11388 } 11389 } 11390 11391 // Even though this property is private, it shouldn't be removed because it is 11392 // used by `is-buffer` to detect buffer instances in Safari 5-7. 11393 Buffer.prototype._isBuffer = true 11394 11395 Buffer.prototype.toString = function toString () { 11396 var length = this.length | 0 11397 if (length === 0) return '' 11398 if (arguments.length === 0) return utf8Slice(this, 0, length) 11399 return slowToString.apply(this, arguments) 11400 } 11401 11402 Buffer.prototype.equals = function equals (b) { 11403 if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') 11404 if (this === b) return true 11405 return Buffer.compare(this, b) === 0 11406 } 11407 11408 Buffer.prototype.inspect = function inspect () { 11409 var str = '' 11410 var max = exports.INSPECT_MAX_BYTES 11411 if (this.length > 0) { 11412 str = this.toString('hex', 0, max).match(/.{2}/g).join(' ') 11413 if (this.length > max) str += ' ... ' 11414 } 11415 return '<Buffer ' + str + '>' 11416 } 11417 11418 Buffer.prototype.compare = function compare (b) { 11419 if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') 11420 if (this === b) return 0 11421 return Buffer.compare(this, b) 11422 } 11423 11424 Buffer.prototype.indexOf = function indexOf (val, byteOffset) { 11425 if (byteOffset > 0x7fffffff) byteOffset = 0x7fffffff 11426 else if (byteOffset < -0x80000000) byteOffset = -0x80000000 11427 byteOffset >>= 0 11428 11429 if (this.length === 0) return -1 11430 if (byteOffset >= this.length) return -1 11431 11432 // Negative offsets start from the end of the buffer 11433 if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0) 11434 11435 if (typeof val === 'string') { 11436 if (val.length === 0) return -1 // special case: looking for empty string always fail
vendor: 7,009 bytes, lines 11436-11661
11436s 11437 return String.prototype.indexOf.call(this, val, byteOffset) 11438 } 11439 if (Buffer.isBuffer(val)) { 11440 return arrayIndexOf(this, val, byteOffset) 11441 } 11442 if (typeof val === 'number') { 11443 if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') { 11444 return Uint8Array.prototype.indexOf.call(this, val, byteOffset) 11445 } 11446 return arrayIndexOf(this, [ val ], byteOffset) 11447 } 11448 11449 function arrayIndexOf (arr, val, byteOffset) { 11450 var foundIndex = -1 11451 for (var i = 0; byteOffset + i < arr.length; i++) { 11452 if (arr[byteOffset + i] === val[foundIndex === -1 ? 0 : i - foundIndex]) { 11453 if (foundIndex === -1) foundIndex = i 11454 if (i - foundIndex + 1 === val.length) return byteOffset + foundIndex 11455 } else { 11456 foundIndex = -1 11457 } 11458 } 11459 return -1 11460 } 11461 11462 throw new TypeError('val must be string, number or Buffer') 11463 } 11464 11465 function hexWrite (buf, string, offset, length) { 11466 offset = Number(offset) || 0 11467 var remaining = buf.length - offset 11468 if (!length) { 11469 length = remaining 11470 } else { 11471 length = Number(length) 11472 if (length > remaining) { 11473 length = remaining 11474 } 11475 } 11476 11477 // must be an even number of digits 11478 var strLen = string.length 11479 if (strLen % 2 !== 0) throw new Error('Invalid hex string') 11480 11481 if (length > strLen / 2) { 11482 length = strLen / 2 11483 } 11484 for (var i = 0; i < length; i++) { 11485 var parsed = parseInt(string.substr(i * 2, 2), 16) 11486 if (isNaN(parsed)) throw new Error('Invalid hex string') 11487 buf[offset + i] = parsed 11488 } 11489 return i 11490 } 11491 11492 function utf8Write (buf, string, offset, length) { 11493 return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) 11494 } 11495 11496 function asciiWrite (buf, string, offset, length) { 11497 return blitBuffer(asciiToBytes(string), buf, offset, length) 11498 } 11499 11500 function binaryWrite (buf, string, offset, length) { 11501 return asciiWrite(buf, string, offset, length) 11502 } 11503 11504 function base64Write (buf, string, offset, length) { 11505 return blitBuffer(base64ToBytes(string), buf, offset, length) 11506 } 11507 11508 function ucs2Write (buf, string, offset, length) { 11509 return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) 11510 } 11511 11512 Buffer.prototype.write = function write (string, offset, length, encoding) { 11513 // Buffer#write(string) 11514 if (offset === undefined) { 11515 encoding = 'utf8' 11516 length = this.length 11517 offset = 0 11518 // Buffer#write(string, encoding) 11519 } else if (length === undefined && typeof offset === 'string') { 11520 encoding = offset 11521 length = this.length 11522 offset = 0 11523 // Buffer#write(string, offset[, length][, encoding]) 11524 } else if (isFinite(offset)) { 11525 offset = offset | 0 11526 if (isFinite(length)) { 11527 length = length | 0 11528 if (encoding === undefined) encoding = 'utf8' 11529 } else { 11530 encoding = length 11531 length = undefined 11532 } 11533 // legacy write(string, encoding, offset, length) - remove in v0.13 11534 } else { 11535 var swap = encoding 11536 encoding = offset 11537 offset = length | 0 11538 length = swap 11539 } 11540 11541 var remaining = this.length - offset 11542 if (length === undefined || length > remaining) length = remaining 11543 11544 if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { 11545 throw new RangeError('attempt to write outside buffer bounds') 11546 } 11547 11548 if (!encoding) encoding = 'utf8' 11549 11550 var loweredCase = false 11551 for (;;) { 11552 switch (encoding) { 11553 case 'hex': 11554 return hexWrite(this, string, offset, length) 11555 11556 case 'utf8': 11557 case 'utf-8': 11558 return utf8Write(this, string, offset, length) 11559 11560 case 'ascii': 11561 return asciiWrite(this, string, offset, length) 11562 11563 case 'binary': 11564 return binaryWrite(this, string, offset, length) 11565 11566 case 'base64': 11567 // Warning: maxLength not taken into account in base64Write 11568 return base64Write(this, string, offset, length) 11569 11570 case 'ucs2': 11571 case 'ucs-2': 11572 case 'utf16le': 11573 case 'utf-16le': 11574 return ucs2Write(this, string, offset, length) 11575 11576 default: 11577 if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) 11578 encoding = ('' + encoding).toLowerCase() 11579 loweredCase = true 11580 } 11581 } 11582 } 11583 11584 Buffer.prototype.toJSON = function toJSON () { 11585 return { 11586 type: 'Buffer', 11587 data: Array.prototype.slice.call(this._arr || this, 0) 11588 } 11589 } 11590 11591 function base64Slice (buf, start, end) { 11592 if (start === 0 && end === buf.length) { 11593 return base64.fromByteArray(buf) 11594 } else { 11595 return base64.fromByteArray(buf.slice(start, end)) 11596 } 11597 } 11598 11599 function utf8Slice (buf, start, end) { 11600 end = Math.min(buf.length, end) 11601 var res = [] 11602 11603 var i = start 11604 while (i < end) { 11605 var firstByte = buf[i] 11606 var codePoint = null 11607 var bytesPerSequence = (firstByte > 0xEF) ? 4 11608 : (firstByte > 0xDF) ? 3 11609 : (firstByte > 0xBF) ? 2 11610 : 1 11611 11612 if (i + bytesPerSequence <= end) { 11613 var secondByte, thirdByte, fourthByte, tempCodePoint 11614 11615 switch (bytesPerSequence) { 11616 case 1: 11617 if (firstByte < 0x80) { 11618 codePoint = firstByte 11619 } 11620 break 11621 case 2: 11622 secondByte = buf[i + 1] 11623 if ((secondByte & 0xC0) === 0x80) { 11624 tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) 11625 if (tempCodePoint > 0x7F) { 11626 codePoint = tempCodePoint 11627 } 11628 } 11629 break 11630 case 3: 11631 secondByte = buf[i + 1] 11632 thirdByte = buf[i + 2] 11633 if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { 11634 tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) 11635 if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { 11636 codePoint = tempCodePoint 11637 } 11638 } 11639 break 11640 case 4: 11641 secondByte = buf[i + 1] 11642 thirdByte = buf[i + 2] 11643 fourthByte = buf[i + 3] 11644 if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { 11645 tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) 11646 if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { 11647 codePoint = tempCodePoint 11648 } 11649 } 11650 } 11651 } 11652 11653 if (codePoint === null) { 11654 // we did not generate a valid codePoint so insert a 11655 // replacement char (U+FFFD) and advance only 1 byte 11656 codePoint = 0xFFFD 11657 bytesPerSequence = 1 11658 } else if (codePoint > 0xFFFF) { 11659 // encode to utf16 (surrogate pair dance) 11660 codePoint -= 0x10000 11661 res.push(codePoint >>>
vendor: 2,699 bytes, lines 11661-11777
11661 10 & 0x3FF | 0xD800) 11662 codePoint = 0xDC00 | codePoint & 0x3FF 11663 } 11664 11665 res.push(codePoint) 11666 i += bytesPerSequence 11667 } 11668 11669 return decodeCodePointsArray(res) 11670 } 11671 11672 // Based on http://stackoverflow.com/a/22747272/680742, the browser with 11673 // the lowest limit is Chrome, with 0x10000 args. 11674 // We go 1 magnitude less, for safety 11675 var MAX_ARGUMENTS_LENGTH = 0x1000 11676 11677 function decodeCodePointsArray (codePoints) { 11678 var len = codePoints.length 11679 if (len <= MAX_ARGUMENTS_LENGTH) { 11680 return String.fromCharCode.apply(String, codePoints) // avoid extra slice() 11681 } 11682 11683 // Decode in chunks to avoid "call stack size exceeded". 11684 var res = '' 11685 var i = 0 11686 while (i < len) { 11687 res += String.fromCharCode.apply( 11688 String, 11689 codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) 11690 ) 11691 } 11692 return res 11693 } 11694 11695 function asciiSlice (buf, start, end) { 11696 var ret = '' 11697 end = Math.min(buf.length, end) 11698 11699 for (var i = start; i < end; i++) { 11700 ret += String.fromCharCode(buf[i] & 0x7F) 11701 } 11702 return ret 11703 } 11704 11705 function binarySlice (buf, start, end) { 11706 var ret = '' 11707 end = Math.min(buf.length, end) 11708 11709 for (var i = start; i < end; i++) { 11710 ret += String.fromCharCode(buf[i]) 11711 } 11712 return ret 11713 } 11714 11715 function hexSlice (buf, start, end) { 11716 var len = buf.length 11717 11718 if (!start || start < 0) start = 0 11719 if (!end || end < 0 || end > len) end = len 11720 11721 var out = '' 11722 for (var i = start; i < end; i++) { 11723 out += toHex(buf[i]) 11724 } 11725 return out 11726 } 11727 11728 function utf16leSlice (buf, start, end) { 11729 var bytes = buf.slice(start, end) 11730 var res = '' 11731 for (var i = 0; i < bytes.length; i += 2) { 11732 res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256) 11733 } 11734 return res 11735 } 11736 11737 Buffer.prototype.slice = function slice (start, end) { 11738 var len = this.length 11739 start = ~~start 11740 end = end === undefined ? len : ~~end 11741 11742 if (start < 0) { 11743 start += len 11744 if (start < 0) start = 0 11745 } else if (start > len) { 11746 start = len 11747 } 11748 11749 if (end < 0) { 11750 end += len 11751 if (end < 0) end = 0 11752 } else if (end > len) { 11753 end = len 11754 } 11755 11756 if (end < start) end = start 11757 11758 var newBuf 11759 if (Buffer.TYPED_ARRAY_SUPPORT) { 11760 newBuf = this.subarray(start, end) 11761 newBuf.__proto__ = Buffer.prototype 11762 } else { 11763 var sliceLen = end - start 11764 newBuf = new Buffer(sliceLen, undefined) 11765 for (var i = 0; i < sliceLen; i++) { 11766 newBuf[i] = this[i + start] 11767 } 11768 } 11769 11770 if (newBuf.length) newBuf.parent = this.parent || this 11771 11772 return newBuf 11773 } 11774 11775 /* 11776 * Need to make sure that buffer isn't trying to write out of bounds. 11777 */
vendor: 10,339 bytes, lines 11778-12106
11778 function checkOffset (offset, ext, length) { 11779 if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') 11780 if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') 11781 } 11782 11783 Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { 11784 offset = offset | 0 11785 byteLength = byteLength | 0 11786 if (!noAssert) checkOffset(offset, byteLength, this.length) 11787 11788 var val = this[offset] 11789 var mul = 1 11790 var i = 0 11791 while (++i < byteLength && (mul *= 0x100)) { 11792 val += this[offset + i] * mul 11793 } 11794 11795 return val 11796 } 11797 11798 Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { 11799 offset = offset | 0 11800 byteLength = byteLength | 0 11801 if (!noAssert) { 11802 checkOffset(offset, byteLength, this.length) 11803 } 11804 11805 var val = this[offset + --byteLength] 11806 var mul = 1 11807 while (byteLength > 0 && (mul *= 0x100)) { 11808 val += this[offset + --byteLength] * mul 11809 } 11810 11811 return val 11812 } 11813 11814 Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { 11815 if (!noAssert) checkOffset(offset, 1, this.length) 11816 return this[offset] 11817 } 11818 11819 Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { 11820 if (!noAssert) checkOffset(offset, 2, this.length) 11821 return this[offset] | (this[offset + 1] << 8) 11822 } 11823 11824 Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { 11825 if (!noAssert) checkOffset(offset, 2, this.length) 11826 return (this[offset] << 8) | this[offset + 1] 11827 } 11828 11829 Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { 11830 if (!noAssert) checkOffset(offset, 4, this.length) 11831 11832 return ((this[offset]) | 11833 (this[offset + 1] << 8) | 11834 (this[offset + 2] << 16)) + 11835 (this[offset + 3] * 0x1000000) 11836 } 11837 11838 Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { 11839 if (!noAssert) checkOffset(offset, 4, this.length) 11840 11841 return (this[offset] * 0x1000000) + 11842 ((this[offset + 1] << 16) | 11843 (this[offset + 2] << 8) | 11844 this[offset + 3]) 11845 } 11846 11847 Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { 11848 offset = offset | 0 11849 byteLength = byteLength | 0 11850 if (!noAssert) checkOffset(offset, byteLength, this.length) 11851 11852 var val = this[offset] 11853 var mul = 1 11854 var i = 0 11855 while (++i < byteLength && (mul *= 0x100)) { 11856 val += this[offset + i] * mul 11857 } 11858 mul *= 0x80 11859 11860 if (val >= mul) val -= Math.pow(2, 8 * byteLength) 11861 11862 return val 11863 } 11864 11865 Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { 11866 offset = offset | 0 11867 byteLength = byteLength | 0 11868 if (!noAssert) checkOffset(offset, byteLength, this.length) 11869 11870 var i = byteLength 11871 var mul = 1 11872 var val = this[offset + --i] 11873 while (i > 0 && (mul *= 0x100)) { 11874 val += this[offset + --i] * mul 11875 } 11876 mul *= 0x80 11877 11878 if (val >= mul) val -= Math.pow(2, 8 * byteLength) 11879 11880 return val 11881 } 11882 11883 Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { 11884 if (!noAssert) checkOffset(offset, 1, this.length) 11885 if (!(this[offset] & 0x80)) return (this[offset]) 11886 return ((0xff - this[offset] + 1) * -1) 11887 } 11888 11889 Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { 11890 if (!noAssert) checkOffset(offset, 2, this.length) 11891 var val = this[offset] | (this[offset + 1] << 8) 11892 return (val & 0x8000) ? val | 0xFFFF0000 : val 11893 } 11894 11895 Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { 11896 if (!noAssert) checkOffset(offset, 2, this.length) 11897 var val = this[offset + 1] | (this[offset] << 8) 11898 return (val & 0x8000) ? val | 0xFFFF0000 : val 11899 } 11900 11901 Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { 11902 if (!noAssert) checkOffset(offset, 4, this.length) 11903 11904 return (this[offset]) | 11905 (this[offset + 1] << 8) | 11906 (this[offset + 2] << 16) | 11907 (this[offset + 3] << 24) 11908 } 11909 11910 Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { 11911 if (!noAssert) checkOffset(offset, 4, this.length) 11912 11913 return (this[offset] << 24) | 11914 (this[offset + 1] << 16) | 11915 (this[offset + 2] << 8) | 11916 (this[offset + 3]) 11917 } 11918 11919 Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { 11920 if (!noAssert) checkOffset(offset, 4, this.length) 11921 return ieee754.read(this, offset, true, 23, 4) 11922 } 11923 11924 Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { 11925 if (!noAssert) checkOffset(offset, 4, this.length) 11926 return ieee754.read(this, offset, false, 23, 4) 11927 } 11928 11929 Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { 11930 if (!noAssert) checkOffset(offset, 8, this.length) 11931 return ieee754.read(this, offset, true, 52, 8) 11932 } 11933 11934 Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { 11935 if (!noAssert) checkOffset(offset, 8, this.length) 11936 return ieee754.read(this, offset, false, 52, 8) 11937 } 11938 11939 function checkInt (buf, value, offset, ext, max, min) { 11940 if (!Buffer.isBuffer(buf)) throw new TypeError('buffer must be a Buffer instance') 11941 if (value > max || value < min) throw new RangeError('value is out of bounds') 11942 if (offset + ext > buf.length) throw new RangeError('index out of range') 11943 } 11944 11945 Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { 11946 value = +value 11947 offset = offset | 0 11948 byteLength = byteLength | 0 11949 if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) 11950 11951 var mul = 1 11952 var i = 0 11953 this[offset] = value & 0xFF 11954 while (++i < byteLength && (mul *= 0x100)) { 11955 this[offset + i] = (value / mul) & 0xFF 11956 } 11957 11958 return offset + byteLength 11959 } 11960 11961 Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { 11962 value = +value 11963 offset = offset | 0 11964 byteLength = byteLength | 0 11965 if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) 11966 11967 var i = byteLength - 1 11968 var mul = 1 11969 this[offset + i] = value & 0xFF 11970 while (--i >= 0 && (mul *= 0x100)) { 11971 this[offset + i] = (value / mul) & 0xFF 11972 } 11973 11974 return offset + byteLength 11975 } 11976 11977 Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { 11978 value = +value 11979 offset = offset | 0 11980 if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) 11981 if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) 11982 this[offset] = (value & 0xff) 11983 return offset + 1 11984 } 11985 11986 function objectWriteUInt16 (buf, value, offset, littleEndian) { 11987 if (value < 0) value = 0xffff + value + 1 11988 for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) { 11989 buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> 11990 (littleEndian ? i : 1 - i) * 8 11991 } 11992 } 11993 11994 Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { 11995 value = +value 11996 offset = offset | 0 11997 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) 11998 if (Buffer.TYPED_ARRAY_SUPPORT) { 11999 this[offset] = (value & 0xff) 12000 this[offset + 1] = (value >>> 8) 12001 } else { 12002 objectWriteUInt16(this, value, offset, true) 12003 } 12004 return offset + 2 12005 } 12006 12007 Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { 12008 value = +value 12009 offset = offset | 0 12010 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) 12011 if (Buffer.TYPED_ARRAY_SUPPORT) { 12012 this[offset] = (value >>> 8) 12013 this[offset + 1] = (value & 0xff) 12014 } else { 12015 objectWriteUInt16(this, value, offset, false) 12016 } 12017 return offset + 2 12018 } 12019 12020 function objectWriteUInt32 (buf, value, offset, littleEndian) { 12021 if (value < 0) value = 0xffffffff + value + 1 12022 for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) { 12023 buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff 12024 } 12025 } 12026 12027 Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { 12028 value = +value 12029 offset = offset | 0 12030 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) 12031 if (Buffer.TYPED_ARRAY_SUPPORT) { 12032 this[offset + 3] = (value >>> 24) 12033 this[offset + 2] = (value >>> 16) 12034 this[offset + 1] = (value >>> 8) 12035 this[offset] = (value & 0xff) 12036 } else { 12037 objectWriteUInt32(this, value, offset, true) 12038 } 12039 return offset + 4 12040 } 12041 12042 Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { 12043 value = +value 12044 offset = offset | 0 12045 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) 12046 if (Buffer.TYPED_ARRAY_SUPPORT) { 12047 this[offset] = (value >>> 24) 12048 this[offset + 1] = (value >>> 16) 12049 this[offset + 2] = (value >>> 8) 12050 this[offset + 3] = (value & 0xff) 12051 } else { 12052 objectWriteUInt32(this, value, offset, false) 12053 } 12054 return offset + 4 12055 } 12056 12057 Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { 12058 value = +value 12059 offset = offset | 0 12060 if (!noAssert) { 12061 var limit = Math.pow(2, 8 * byteLength - 1) 12062 12063 checkInt(this, value, offset, byteLength, limit - 1, -limit) 12064 } 12065 12066 var i = 0 12067 var mul = 1 12068 var sub = value < 0 ? 1 : 0 12069 this[offset] = value & 0xFF 12070 while (++i < byteLength && (mul *= 0x100)) { 12071 this[offset + i] = ((value / mul) >> 0) - sub & 0xFF 12072 } 12073 12074 return offset + byteLength 12075 } 12076 12077 Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { 12078 value = +value 12079 offset = offset | 0 12080 if (!noAssert) { 12081 var limit = Math.pow(2, 8 * byteLength - 1) 12082 12083 checkInt(this, value, offset, byteLength, limit - 1, -limit) 12084 } 12085 12086 var i = byteLength - 1 12087 var mul = 1 12088 var sub = value < 0 ? 1 : 0 12089 this[offset + i] = value & 0xFF 12090 while (--i >= 0 && (mul *= 0x100)) { 12091 this[offset + i] = ((value / mul) >> 0) - sub & 0xFF 12092 } 12093 12094 return offset + byteLength 12095 } 12096 12097 Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { 12098 value = +value 12099 offset = offset | 0 12100 if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) 12101 if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) 12102 if (value < 0) value = 0xff + value + 1 12103 this[offset] = (value & 0xff) 12104 return offset + 1 12105 } 12106
vendor: 4,365 bytes, lines 12107-12230
12107 Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { 12108 value = +value 12109 offset = offset | 0 12110 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) 12111 if (Buffer.TYPED_ARRAY_SUPPORT) { 12112 this[offset] = (value & 0xff) 12113 this[offset + 1] = (value >>> 8) 12114 } else { 12115 objectWriteUInt16(this, value, offset, true) 12116 } 12117 return offset + 2 12118 } 12119 12120 Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { 12121 value = +value 12122 offset = offset | 0 12123 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) 12124 if (Buffer.TYPED_ARRAY_SUPPORT) { 12125 this[offset] = (value >>> 8) 12126 this[offset + 1] = (value & 0xff) 12127 } else { 12128 objectWriteUInt16(this, value, offset, false) 12129 } 12130 return offset + 2 12131 } 12132 12133 Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { 12134 value = +value 12135 offset = offset | 0 12136 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) 12137 if (Buffer.TYPED_ARRAY_SUPPORT) { 12138 this[offset] = (value & 0xff) 12139 this[offset + 1] = (value >>> 8) 12140 this[offset + 2] = (value >>> 16) 12141 this[offset + 3] = (value >>> 24) 12142 } else { 12143 objectWriteUInt32(this, value, offset, true) 12144 } 12145 return offset + 4 12146 } 12147 12148 Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { 12149 value = +value 12150 offset = offset | 0 12151 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) 12152 if (value < 0) value = 0xffffffff + value + 1 12153 if (Buffer.TYPED_ARRAY_SUPPORT) { 12154 this[offset] = (value >>> 24) 12155 this[offset + 1] = (value >>> 16) 12156 this[offset + 2] = (value >>> 8) 12157 this[offset + 3] = (value & 0xff) 12158 } else { 12159 objectWriteUInt32(this, value, offset, false) 12160 } 12161 return offset + 4 12162 } 12163 12164 function checkIEEE754 (buf, value, offset, ext, max, min) { 12165 if (value > max || value < min) throw new RangeError('value is out of bounds') 12166 if (offset + ext > buf.length) throw new RangeError('index out of range') 12167 if (offset < 0) throw new RangeError('index out of range') 12168 } 12169 12170 function writeFloat (buf, value, offset, littleEndian, noAssert) { 12171 if (!noAssert) { 12172 checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) 12173 } 12174 ieee754.write(buf, value, offset, littleEndian, 23, 4) 12175 return offset + 4 12176 } 12177 12178 Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { 12179 return writeFloat(this, value, offset, true, noAssert) 12180 } 12181 12182 Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { 12183 return writeFloat(this, value, offset, false, noAssert) 12184 } 12185 12186 function writeDouble (buf, value, offset, littleEndian, noAssert) { 12187 if (!noAssert) { 12188 checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) 12189 } 12190 ieee754.write(buf, value, offset, littleEndian, 52, 8) 12191 return offset + 8 12192 } 12193 12194 Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { 12195 return writeDouble(this, value, offset, true, noAssert) 12196 } 12197 12198 Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { 12199 return writeDouble(this, value, offset, false, noAssert) 12200 } 12201 12202 // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) 12203 Buffer.prototype.copy = function copy (target, targetStart, start, end) { 12204 if (!start) start = 0 12205 if (!end && end !== 0) end = this.length 12206 if (targetStart >= target.length) targetStart = target.length 12207 if (!targetStart) targetStart = 0 12208 if (end > 0 && end < start) end = start 12209 12210 // Copy 0 bytes; we're done 12211 if (end === start) return 0 12212 if (target.length === 0 || this.length === 0) return 0 12213 12214 // Fatal error conditions 12215 if (targetStart < 0) { 12216 throw new RangeError('targetStart out of bounds') 12217 } 12218 if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds') 12219 if (end < 0) throw new RangeError('sourceEnd out of bounds') 12220 12221 // Are we oob? 12222 if (end > this.length) end = this.length 12223 if (target.length - targetStart < end - start) { 12224 end = target.length - targetStart + start 12225 } 12226 12227 var len = end - start 12228 var i 12229 12230 if (this === target && start < targetStart && targetStart < end) {
vendor: 5,459 bytes, lines 12231-12431
12231 // descending copy from end 12232 for (i = len - 1; i >= 0; i--) { 12233 target[i + targetStart] = this[i + start] 12234 } 12235 } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) { 12236 // ascending copy from start 12237 for (i = 0; i < len; i++) { 12238 target[i + targetStart] = this[i + start] 12239 } 12240 } else { 12241 Uint8Array.prototype.set.call( 12242 target, 12243 this.subarray(start, start + len), 12244 targetStart 12245 ) 12246 } 12247 12248 return len 12249 } 12250 12251 // fill(value, start=0, end=buffer.length) 12252 Buffer.prototype.fill = function fill (value, start, end) { 12253 if (!value) value = 0 12254 if (!start) start = 0 12255 if (!end) end = this.length 12256 12257 if (end < start) throw new RangeError('end < start') 12258 12259 // Fill 0 bytes; we're done 12260 if (end === start) return 12261 if (this.length === 0) return 12262 12263 if (start < 0 || start >= this.length) throw new RangeError('start out of bounds') 12264 if (end < 0 || end > this.length) throw new RangeError('end out of bounds') 12265 12266 var i 12267 if (typeof value === 'number') { 12268 for (i = start; i < end; i++) { 12269 this[i] = value 12270 } 12271 } else { 12272 var bytes = utf8ToBytes(value.toString()) 12273 var len = bytes.length 12274 for (i = start; i < end; i++) { 12275 this[i] = bytes[i % len] 12276 } 12277 } 12278 12279 return this 12280 } 12281 12282 // HELPER FUNCTIONS 12283 // ================ 12284 12285 var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g 12286 12287 function base64clean (str) { 12288 // Node strips out invalid characters like \n and \t from the string, base64-js does not 12289 str = stringtrim(str).replace(INVALID_BASE64_RE, '') 12290 // Node converts strings with length < 2 to '' 12291 if (str.length < 2) return '' 12292 // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not 12293 while (str.length % 4 !== 0) { 12294 str = str + '=' 12295 } 12296 return str 12297 } 12298 12299 function stringtrim (str) { 12300 if (str.trim) return str.trim() 12301 return str.replace(/^\s+|\s+$/g, '') 12302 } 12303 12304 function toHex (n) { 12305 if (n < 16) return '0' + n.toString(16) 12306 return n.toString(16) 12307 } 12308 12309 function utf8ToBytes (string, units) { 12310 units = units || Infinity 12311 var codePoint 12312 var length = string.length 12313 var leadSurrogate = null 12314 var bytes = [] 12315 12316 for (var i = 0; i < length; i++) { 12317 codePoint = string.charCodeAt(i) 12318 12319 // is surrogate component 12320 if (codePoint > 0xD7FF && codePoint < 0xE000) { 12321 // last char was a lead 12322 if (!leadSurrogate) { 12323 // no lead yet 12324 if (codePoint > 0xDBFF) { 12325 // unexpected trail 12326 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) 12327 continue 12328 } else if (i + 1 === length) { 12329 // unpaired lead 12330 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) 12331 continue 12332 } 12333 12334 // valid lead 12335 leadSurrogate = codePoint 12336 12337 continue 12338 } 12339 12340 // 2 leads in a row 12341 if (codePoint < 0xDC00) { 12342 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) 12343 leadSurrogate = codePoint 12344 continue 12345 } 12346 12347 // valid surrogate pair 12348 codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 12349 } else if (leadSurrogate) { 12350 // valid bmp char, but last char was a lead 12351 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) 12352 } 12353 12354 leadSurrogate = null 12355 12356 // encode utf8 12357 if (codePoint < 0x80) { 12358 if ((units -= 1) < 0) break 12359 bytes.push(codePoint) 12360 } else if (codePoint < 0x800) { 12361 if ((units -= 2) < 0) break 12362 bytes.push( 12363 codePoint >> 0x6 | 0xC0, 12364 codePoint & 0x3F | 0x80 12365 ) 12366 } else if (codePoint < 0x10000) { 12367 if ((units -= 3) < 0) break 12368 bytes.push( 12369 codePoint >> 0xC | 0xE0, 12370 codePoint >> 0x6 & 0x3F | 0x80, 12371 codePoint & 0x3F | 0x80 12372 ) 12373 } else if (codePoint < 0x110000) { 12374 if ((units -= 4) < 0) break 12375 bytes.push( 12376 codePoint >> 0x12 | 0xF0, 12377 codePoint >> 0xC & 0x3F | 0x80, 12378 codePoint >> 0x6 & 0x3F | 0x80, 12379 codePoint & 0x3F | 0x80 12380 ) 12381 } else { 12382 throw new Error('Invalid code point') 12383 } 12384 } 12385 12386 return bytes 12387 } 12388 12389 function asciiToBytes (str) { 12390 var byteArray = [] 12391 for (var i = 0; i < str.length; i++) { 12392 // Node's code seems to be doing this and not & 0x7F.. 12393 byteArray.push(str.charCodeAt(i) & 0xFF) 12394 } 12395 return byteArray 12396 } 12397 12398 function utf16leToBytes (str, units) { 12399 var c, hi, lo 12400 var byteArray = [] 12401 for (var i = 0; i < str.length; i++) { 12402 if ((units -= 2) < 0) break 12403 12404 c = str.charCodeAt(i) 12405 hi = c >> 8 12406 lo = c % 256 12407 byteArray.push(lo) 12408 byteArray.push(hi) 12409 } 12410 12411 return byteArray 12412 } 12413 12414 function base64ToBytes (str) { 12415 return base64.toByteArray(base64clean(str)) 12416 } 12417 12418 function blitBuffer (src, dst, offset, length) { 12419 for (var i = 0; i < length; i++) { 12420 if ((i + offset >= dst.length) || (i >= src.length)) break 12421 dst[i + offset] = src[i] 12422 } 12423 return i 12424 } 12425 12426 }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 12427 },{"base64-js":38,"ieee754":39,"isarray":40}],38:[function(require,module,exports){ 12428 ;(function (exports) { 12429 'use strict' 12430 12431 var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
vendor: 4,088 bytes, lines 12431-12552
12431+/' 12432 12433 var Arr = (typeof Uint8Array !== 'undefined') 12434 ? Uint8Array 12435 : Array 12436 12437 var PLUS = '+'.charCodeAt(0) 12438 var SLASH = '/'.charCodeAt(0) 12439 var NUMBER = '0'.charCodeAt(0) 12440 var LOWER = 'a'.charCodeAt(0) 12441 var UPPER = 'A'.charCodeAt(0) 12442 var PLUS_URL_SAFE = '-'.charCodeAt(0) 12443 var SLASH_URL_SAFE = '_'.charCodeAt(0) 12444 12445 function decode (elt) { 12446 var code = elt.charCodeAt(0) 12447 if (code === PLUS || code === PLUS_URL_SAFE) return 62 // '+' 12448 if (code === SLASH || code === SLASH_URL_SAFE) return 63 // '/' 12449 if (code < NUMBER) return -1 // no match 12450 if (code < NUMBER + 10) return code - NUMBER + 26 + 26 12451 if (code < UPPER + 26) return code - UPPER 12452 if (code < LOWER + 26) return code - LOWER + 26 12453 } 12454 12455 function b64ToByteArray (b64) { 12456 var i, j, l, tmp, placeHolders, arr 12457 12458 if (b64.length % 4 > 0) { 12459 throw new Error('Invalid string. Length must be a multiple of 4') 12460 } 12461 12462 // the number of equal signs (place holders) 12463 // if there are two placeholders, than the two characters before it 12464 // represent one byte 12465 // if there is only one, then the three characters before it represent 2 bytes 12466 // this is just a cheap hack to not do indexOf twice 12467 var len = b64.length 12468 placeHolders = b64.charAt(len - 2) === '=' ? 2 : b64.charAt(len - 1) === '=' ? 1 : 0 12469 12470 // base64 is 4/3 + up to two characters of the original data 12471 arr = new Arr(b64.length * 3 / 4 - placeHolders) 12472 12473 // if there are placeholders, only get up to the last complete 4 chars 12474 l = placeHolders > 0 ? b64.length - 4 : b64.length 12475 12476 var L = 0 12477 12478 function push (v) { 12479 arr[L++] = v 12480 } 12481 12482 for (i = 0, j = 0; i < l; i += 4, j += 3) { 12483 tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) 12484 push((tmp & 0xFF0000) >> 16) 12485 push((tmp & 0xFF00) >> 8) 12486 push(tmp & 0xFF) 12487 } 12488 12489 if (placeHolders === 2) { 12490 tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) 12491 push(tmp & 0xFF) 12492 } else if (placeHolders === 1) { 12493 tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) 12494 push((tmp >> 8) & 0xFF) 12495 push(tmp & 0xFF) 12496 } 12497 12498 return arr 12499 } 12500 12501 function uint8ToBase64 (uint8) { 12502 var i 12503 var extraBytes = uint8.length % 3 // if we have 1 byte left, pad 2 bytes 12504 var output = '' 12505 var temp, length 12506 12507 function encode (num) { 12508 return lookup.charAt(num) 12509 } 12510 12511 function tripletToBase64 (num) { 12512 return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) 12513 } 12514 12515 // go through the array every three bytes, we'll deal with trailing stuff later 12516 for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { 12517 temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) 12518 output += tripletToBase64(temp) 12519 } 12520 12521 // pad the end with zeros, but make sure to not forget the extra bytes 12522 switch (extraBytes) { 12523 case 1: 12524 temp = uint8[uint8.length - 1] 12525 output += encode(temp >> 2) 12526 output += encode((temp << 4) & 0x3F) 12527 output += '==' 12528 break 12529 case 2: 12530 temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) 12531 output += encode(temp >> 10) 12532 output += encode((temp >> 4) & 0x3F) 12533 output += encode((temp << 2) & 0x3F) 12534 output += '=' 12535 break 12536 default: 12537 break 12538 } 12539 12540 return output 12541 } 12542 12543 exports.toByteArray = b64ToByteArray 12544 exports.fromByteArray = uint8ToBase64 12545 }(typeof exports === 'undefined' ? (this.base64js = {}) : exports)) 12546 12547 },{}],39:[function(require,module,exports){ 12548 exports.read = function (buffer, offset, isLE, mLen, nBytes) { 12549 var e, m 12550 var eLen = nBytes * 8 - mLen - 1 12551 var eMax = (1 << eLen) - 1 12552 var eBias = eMax >> 1
vendor: 2,246 bytes, lines 12553-12640
12553 var nBits = -7 12554 var i = isLE ? (nBytes - 1) : 0 12555 var d = isLE ? -1 : 1 12556 var s = buffer[offset + i] 12557 12558 i += d 12559 12560 e = s & ((1 << (-nBits)) - 1) 12561 s >>= (-nBits) 12562 nBits += eLen 12563 for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} 12564 12565 m = e & ((1 << (-nBits)) - 1) 12566 e >>= (-nBits) 12567 nBits += mLen 12568 for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} 12569 12570 if (e === 0) { 12571 e = 1 - eBias 12572 } else if (e === eMax) { 12573 return m ? NaN : ((s ? -1 : 1) * Infinity) 12574 } else { 12575 m = m + Math.pow(2, mLen) 12576 e = e - eBias 12577 } 12578 return (s ? -1 : 1) * m * Math.pow(2, e - mLen) 12579 } 12580 12581 exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { 12582 var e, m, c 12583 var eLen = nBytes * 8 - mLen - 1 12584 var eMax = (1 << eLen) - 1 12585 var eBias = eMax >> 1 12586 var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) 12587 var i = isLE ? 0 : (nBytes - 1) 12588 var d = isLE ? 1 : -1 12589 var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 12590 12591 value = Math.abs(value) 12592 12593 if (isNaN(value) || value === Infinity) { 12594 m = isNaN(value) ? 1 : 0 12595 e = eMax 12596 } else { 12597 e = Math.floor(Math.log(value) / Math.LN2) 12598 if (value * (c = Math.pow(2, -e)) < 1) { 12599 e-- 12600 c *= 2 12601 } 12602 if (e + eBias >= 1) { 12603 value += rt / c 12604 } else { 12605 value += rt * Math.pow(2, 1 - eBias) 12606 } 12607 if (value * c >= 2) { 12608 e++ 12609 c /= 2 12610 } 12611 12612 if (e + eBias >= eMax) { 12613 m = 0 12614 e = eMax 12615 } else if (e + eBias >= 1) { 12616 m = (value * c - 1) * Math.pow(2, mLen) 12617 e = e + eBias 12618 } else { 12619 m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) 12620 e = 0 12621 } 12622 } 12623 12624 for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} 12625 12626 e = (e << mLen) | m 12627 eLen += mLen 12628 for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} 12629 12630 buffer[offset + i - d] |= s * 128 12631 } 12632 12633 },{}],40:[function(require,module,exports){ 12634 var toString = {}.toString; 12635 12636 module.exports = Array.isArray || function (arr) { 12637 return toString.call(arr) == '[object Array]'; 12638 }; 12639 12640},{}]},{},[36]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.