1//QualitySelector 2class QualitySelector extends videojs.getComponent('MenuButton') { 3 constructor(player, options) { 4 super(player, options); 5 6 this.addClass('vjs-quality-selector'); 7 var menuTitleText = "Quality"; 8 if(typeof(vjsLang)!="undefined"){ 9 menuTitleText = vjsLang["Quality"]; 10 } 11 this.controlText(menuTitleText); 12 var MenuItem = videojs.getComponent('MenuItem'); 13 var menuTitle = new MenuItem(player, { 14 el: videojs.dom.createEl('li', { 15 className: 'vjs-menu-title', 16 innerHTML: menuTitleText 17 }) 18 }); 19 this.menu.addChild(menuTitle); 20 21 //add menu item 22 var sources = player.options_.sources || []; 23 sources.forEach((source) => { 24 var thisMenu = this.menu; 25 var selected = source.selected==true; 26 var menuItem = new MenuItem(player, { 27 label: source.label, 28 quality: source.src, 29 type: source.type, 30 selectable: true, 31 selected: selected 32 }); 33 34 if(selected){ 35 player.src({ type: source.type, src: source.src }); 36 } 37 38 //click 39 menuItem.on('pointerup', function(){ 40 var selectedQuality = this.options_.quality; 41 var label = this.options_.label; 42 this.player_.trigger('changeQuality', { 43 quality: selectedQuality, 44 label: label, 45 type: this.options_.type 46 }); 47 48 var menuItems = thisMenu.children_; 49 menuItems.forEach(function(menuItem) { 50 menuItem.selected(false); 51 }); 52 this.selected(true); 53 }) 54 55 //add menu item 56 thisMenu.addChild(menuItem); 57 }); 58 59 60 player.on('changeQuality', (event, data) => { 61 var currentTime = player.currentTime(); 62 player.src({ type: data.type, src: data.quality }); 63 player.one('loadeddata', () => { 64 player.currentTime(currentTime); 65 player.play(); 66 }); 67 }); 68 69 this.timerShowBigPlayButton = null; 70 player.on('seeking', function() { 71 player.bigPlayButton.hide(); 72 try{ clearTimeout(this.timerShowBigPlayButton); } catch(e){} 73 }); 74 75 player.on('seeked', function() { 76 this.timerShowBigPlayButton = setTimeout(function(){ player.bigPlayButton.show(); },1000); 77 }); 78 79 var thisObj = this; 80 player.on('pointerup',function(){ 81 if(thisObj.menuButton_.el() != event.target.parentNode){ 82 thisObj.unpressButton(); 83 } 84 }); 85 } 86} 87 88videojs.registerComponent('qualitySelector', QualitySelector); 89 90 91//Smooth drag timeline seekbar 92const SeekBar = videojs.getComponent('SeekBar') 93/* 94SeekBar.prototype.getPercent = function getPercent() { 95 const time = this.player_.currentTime() 96 const percent = time / this.player_.duration() 97 return percent >= 1 ? 1 : percent 98} 99*/ 100SeekBar.prototype.handleMouseMove = function handleMouseMove(event) { 101 let newTime = this.calculateDistance(event) * this.player_.duration() 102 if (newTime === this.player_.duration()) { 103 newTime = newTime - 0.1 104 } 105 this.player_.currentTime(newTime) 106 this.update() 107} 108 109 110/* 111LandscapeFullscreen 112Modified from https://github.com/prateekrastogi/videojs-landscape-fullscreen 113*/ 114class LandscapeFullscreen extends videojs.getPlugin('plugin'){ 115 defaults = { 116 enterOnRotate: true, 117 exitOnRotate: true, 118 alwaysInLandscapeMode: true, 119 iOS: true 120 }; 121 122 constructor(player, options) { 123 super(player, options); 124 125 126 127 this.player = player; 128 this.options = videojs.mergeOptions(this.defaults, options) 129 130 var screen = window.screen; 131 screen.lockOrientationUniversal = (mode) => screen.orientation && screen.orientation.lock(mode).then(() => {}, err =>{} ) || screen.mozLockOrientation && screen.mozLockOrientation(mode) || screen.msLockOrientation && screen.msLockOrientation(mode); 132 133 player.addClass('vjs-landscape-fullscreen'); 134 135 if (this.options.iOS && 136 videojs.browser.IS_IOS && videojs.browser.IOS_VERSION > 9 && 137 !player.el_.ownerDocument.querySelector('.bc-iframe')) { 138 139 player.tech_.el_.setAttribute('playsinline', 'playsinline'); 140 player.tech_.supportsFullScreen = function() { 141 return false; 142 }; 143 } 144 145 if (videojs.browser.IS_IOS) { 146 window.addEventListener('orientationchange', this.rotationHandler); 147 } else if (screen && screen.orientation) { 148 // addEventListener('orientationchange') is not a user interaction on Android 149 screen.orientation.onchange = this.rotationHandler; 150 } 151 152 player.on('fullscreenchange', e => { 153 if (!this.angle() && player.isFullscreen() && this.options.alwaysInLandscapeMode) { 154 screen.lockOrientationUniversal('landscape'); 155 } 156 }); 157 158 player.on('dispose', () => { 159 window.removeEventListener('orientationchange', this.rotationHandler) 160 }) 161 162 } 163 164 165 rotationHandler = () => { 166 const currentAngle = this.angle(); 167 168 if (currentAngle === 90 || currentAngle === 270 || currentAngle === -90) { 169 if (this.options.enterOnRotate && this.player.paused() === false) { 170 this.player.requestFullscreen(); 171 screen.lockOrientationUniversal('landscape'); 172 } 173 } 174 if (currentAngle === 0 || currentAngle === 180) { 175 if (this.options.exitOnRotate && this.player.isFullscreen()) { 176 this.player.exitFullscreen(); 177 } 178 } 179 }; 180 181 angle = () => { 182 // iOS 183 if (typeof window.orientation === 'number') { 184 return window.orientation; 185 } 186 // Android 187 if (screen && screen.orientation && screen.orientation.angle) { 188 return window.orientation; 189 } 190 191 return 0; 192 }; 193 194} 195videojs.registerPlugin('landscapeFullscreen', LandscapeFullscreen); 196 197 198/* 199videojs-mobile-ui 200Modified from https://www.npmjs.com/package/videojs-mobile-ui 201*/ 202class TouchOverlay extends videojs.getComponent('Component') { 203 defaults = { 204 seekSeconds: 10, 205 tapTimeout: 300, 206 disableOnEnd: false, 207 disabled: false 208 }; 209 210 constructor(player, options) { 211 super(player, options); 212 213 var options = videojs.mergeOptions(this.defaults, options) 214 this.seekSeconds = options.seekSeconds; 215 this.tapTimeout = options.tapTimeout; 216 this.addChild('playToggle', {}); 217 218 player.on(['playing', 'userinactive'], e => { 219 this.removeClass('show-play-toggle'); 220 });
221 222 // A 0 inactivity timeout won't work here 223 if (this.player_.options_.inactivityTimeout === 0) { 224 this.player_.options_.inactivityTimeout = 5000; 225 } 226 227 this.enable(); 228 } 229 230 //createEl() 231 createEl() { 232 const el = videojs.dom.createEl('div', { 233 className: 'vjs-touch-overlay', 234 tabIndex: -1 235 }); 236 237 var html = '<div class="seek-arrows-container">'; 238 html += '<div class="seek-arrows">'; 239 html += '<div class="arrows-left">'; 240 html += '<div class="arrows">'; 241 html += '<span class="seek-base-arrow"></span>'; 242 html += '<span class="seek-base-arrow"></span>'; 243 html += '<span class="seek-base-arrow"></span>'; 244 html += '</div>'; 245 html += '<div class="seek-info">10s</div>'; 246 html += '</div>'; 247 248 html += '<div class="arrows-right">'; 249 html += '<div class="arrows">'; 250 html += '<span class="seek-base-arrow"></span>'; 251 html += '<span class="seek-base-arrow"></span>'; 252 html += '<span class="seek-base-arrow"></span>'; 253 html += '</div>'; 254 html += '<div class="seek-info">10s</div>'; 255 html += '</div>'; 256 html += '</div>'; 257 html += '</div>'; 258 el.innerHTML = html; 259 return el; 260 } 261 262 //enable tap 263 enable() { 264 this.firstTapCaptured = false; 265 this.on('touchend', this.handleTap); 266 } 267 268 //disable tap 269 disable() { 270 this.off('touchend', this.handleTap); 271 } 272 273 //handleTap 274 handleTap(event) { 275 //Don't handle taps on the play button 276 if (event.target !== this.el_) { 277 //return; 278 } 279 280 event.preventDefault(); 281 282 //is DoubleTap 283 if (this.firstTapCaptured) { 284 this.firstTapCaptured = false; 285 if (this.timeout) { 286 window.clearTimeout(this.timeout); 287 } 288 this.handleDoubleTap(event); 289 //is SingleTap 290 } else { 291 this.firstTapCaptured = true; 292 this.timeout = window.setTimeout(() => { 293 this.firstTapCaptured = false; 294 this.handleSingleTap(event); 295 }, this.tapTimeout); 296 } 297 } 298 299 //Single Tap 300 handleSingleTap(event) { 301 this.removeClass('skip'); 302 this.removeClass('reverse'); 303 this.toggleClass('show-play-toggle'); 304 } 305 306 //Double Tap 307 handleDoubleTap(event) { 308 const rect = this.el_.getBoundingClientRect(); 309 const x = event.changedTouches[0].clientX - rect.left; 310 // Check if double tap is in left or right area 311 this.removeClass('skip'); 312 this.removeClass('reverse'); 313 if (x < rect.width * 0.4) { 314 this.player_.currentTime(Math.max(0, this.player_.currentTime() - this.seekSeconds)); 315 setTimeout(()=>{this.addClass('reverse');},10); 316 } else if (x > rect.width - (rect.width * 0.4)) { 317 this.player_.currentTime(Math.min(this.player_.duration(), this.player_.currentTime() + this.seekSeconds)); 318 setTimeout(()=>{this.addClass('skip');},10); 319 } else { 320 return; 321 } 322 323 // Remove play toggle if showing 324 this.removeClass('show-play-toggle'); 325 } 326} 327 328videojs.registerComponent('TouchOverlay', TouchOverlay); 329 330//mobile, Double Tap 331class MobileUi extends videojs.getPlugin('plugin'){ 332 constructor(player, options) { 333 super(player, options); 334 let controlBarIdx = player.children_.indexOf(player.getChild('ControlBar')); 335 player.addChild('TouchOverlay', {}, controlBarIdx); 336 player.landscapeFullscreen(); 337 338 //modify loading style 339 let loading = player.el_.ownerDocument.querySelector('.vjs-loading-spinner'); 340 loading.innerHTML = '<svg class="spinner" width="65px" height="65px" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg"><circle class="path" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle></svg>'; 341 } 342} 343 344videojs.registerPlugin('mobileUi', MobileUi); 345 346 347/* 348Hotkeys 349Modified from https://github.com/ctd1500/videojs-hotkeys 350*/ 351class Hotkeys extends videojs.getPlugin('plugin'){ 352 defaults = { 353 volumeStep: 0.1, 354 seekStep: 5, 355 cPlay: 1, 356 cRewind : 2, 357 cForward : 3, 358 cVolumeUp : 4, 359 cVolumeDown : 5, 360 cMute : 6, 361 cFullscreen : 7 362 }; 363 364constructor(player, options) { 365 super(player, options); 366 367 this.options = videojs.mergeOptions(this.defaults, options); 368 this.player = player;
369 this.pEl = player.el(); 370 371 var pEl = player.el(); 372 373 // Set default player tabindex to handle keydown and doubleclick events 374 if (!pEl.hasAttribute('tabIndex')) { 375 pEl.setAttribute('tabIndex', '-1'); 376 } 377 pEl.style.outline = "none"; 378 379 player.one('play', function() { 380 pEl.focus(); // Fixes the .vjs-big-play-button handing focus back to body instead of the player 381 }); 382 383 // When the control bar fades, re-apply focus to the player if last focus was a control button 384 player.on('userinactive', function() { 385 var focusingPlayerTimeout = setTimeout(function() { 386 var activeElement = document.activeElement; 387 var controlBar = pEl.querySelector('.vjs-control-bar'); 388 if (activeElement && activeElement.parentElement == controlBar) { 389 pEl.focus(); 390 } 391 }, 10); 392 393 player.one('useractive', function() { 394 clearTimeout(focusingPlayerTimeout); 395 }); 396 }); 397 398 player.on('keydown', ()=>{ this.keyDown(event);}); 399 //player.on('mousewheel', ()=>{this.mouseScroll(event);}); 400 //player.on("DOMMouseScroll", ()=>{this.mouseScroll(event);}); 401 402 //use mobileUi Plugin, need bind event 403 var touch = player.el().querySelector('.vjs-touch-overlay'); 404 if(touch!=null){ 405 player.on(touch,'dblclick', ()=>{ this.doubleClick();}); 406 player.on(touch,'keydown', ()=>{ this.keyDown(event);}); 407 player.on(touch,'click', ()=>{ this.pause(event);}); 408 } 409 410 var volumeBar = player.el().querySelector('.vjs-volume-panel'); 411 if(volumeBar!=null){ 412 player.on(volumeBar,'mouseover', ()=>{this.pEl.focus();}); 413 player.on(volumeBar,'mousewheel', ()=>{this.mouseScroll(event);}); 414 player.on(volumeBar,"DOMMouseScroll", ()=>{this.mouseScroll(event);}); 415 } 416 417 } 418 419 //play/pause in use mobile touch 420 pause(){ 421 event.stopPropagation(); 422 if(event.target != this.player.el().querySelector('.seek-arrows')){ 423 return; 424 } 425 426 if(this.player.paused()){ 427 this.player.play(); 428 }else{ 429 this.player.pause(); 430 } 431 } 432 433 doubleClick(){ 434 event.stopPropagation(); 435 if (this.player.isFullscreen_) { 436 this.player.exitFullscreen(); 437 } else { 438 this.player.requestFullscreen(); 439 } 440 player.focus(); 441 } 442 443 checkKeys(e, player) { 444 // Play/Pause check 445 if (e.which === 32 || e.which === 179) { 446 return this.options.cPlay; 447 } 448 449 // Seek Backward check 450 if (e.which === 37 || e.which === 177) { 451 return this.options.cRewind; 452 } 453 454 // Seek Forward check 455 if (e.which === 39 || e.which === 176) { 456 return this.options.cForward; 457 } 458 459 // Volume Up check 460 if (e.which === 38) { 461 return this.options.cVolumeUp; 462 } 463 464 // Volume Down check 465 if (e.which === 40) { 466 return this.options.cVolumeDown; 467 } 468 469 // Mute check 470 if (e.which === 77) { 471 return this.options.cMute; 472 } 473 474 // Fullscreen check 475 if (e.which === 70) { 476 return this.options.cFullscreen; 477 } 478 } 479 480 //volume Down or Up 481 mouseScroll(event) { 482 483 var player = this.player; 484 var pEl = this.pEl; 485 var activeEl = document.activeElement; 486 pEl.focus(); 487 if (player.controls()) { 488 if (activeEl == pEl || 489 activeEl == pEl.querySelector('.vjs-tech') || 490 activeEl == pEl.querySelector('.iframeblocker') || 491 activeEl == pEl.querySelector('.vjs-control-bar') || 492 activeEl == pEl.querySelector('.vjs-volume-panel') || 493 this.volumeHover) { 494 event = window.event || event; 495 event.stopPropagation(); 496 event.preventDefault(); 497 var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail))); 498 499 if (delta == 1) { 500 player.volume(player.volume() + this.options.volumeStep); 501 } else if (delta == -1) { 502 player.volume(player.volume() - this.options.volumeStep); 503 } 504 505 } 506 } 507 }; 508 509 510 // 511 keyDown(event) { 512 513 var ewhich = event.which, wasPlaying, seekTime; 514 var player = this.player; 515 var duration = player.duration(); 516 var pEl = this.pEl; 517 518 // When controls are disabled, hotkeys will be disabled as well 519 if (player.controls()) { 520 521 // Don't catch keys if any control buttons are focused, unless alwaysCaptureHotkeys is true 522 var activeEl = document.activeElement; 523 if(activeEl == pEl || 524 activeEl == pEl.querySelector('.vjs-tech') || 525 activeEl == pEl.querySelector('.vjs-control-bar') || 526 activeEl == pEl.querySelector('.iframeblocker') || 527 activeEl == pEl.querySelector('.vjs-touch-overlay') 528 ) { 529 530 switch (this.checkKeys(event, player)) { 531 // Spacebar play/pause 532 case this.options.cPlay: 533 event.preventDefault(); 534 event.stopPropagation(); 535 if (player.paused()) { 536 player.play(); 537 } else { 538 player.pause(); 539 } 540 break; 541 542 // Seek Backward 543 case this.options.cRewind: 544 wasPlaying = !player.paused(); 545 event.preventDefault(); 546 event.stopPropagation(); 547 if (wasPlaying) { 548 player.pause(); 549 } 550 seekTime = player.currentTime() - this.options.seekStep; 551 // The flash player tech will allow you to seek into negative 552 // numbers and break the seekbar, so try to prevent that. 553 if (seekTime <= 0) { 554 seekTime = 0; 555 } 556 557 player.currentTime(seekTime); 558 player.play(); 559 break; 560 561 // Seek Forward 562 case this.options.cForward: 563 wasPlaying = !player.paused(); 564 event.preventDefault(); 565 event.stopPropagation(); 566 if (wasPlaying) { 567 player.pause(); 568 } 569 seekTime = player.currentTime() + this.options.seekStep; 570 // Fixes the player not sending the end event if you
571 // try to seek past the duration on the seekbar. 572 573 if (seekTime >= duration) { 574 seekTime = wasPlaying ? duration - .001 : duration; 575 } 576 577 player.currentTime(seekTime); 578 if (wasPlaying) { 579 player.play(); 580 } 581 break; 582 583 // Volume control with the up/down arrow keys 584 case this.options.cVolumeDown: 585 event.preventDefault(); 586 event.stopPropagation(); 587 player.volume(player.volume() - this.options.volumeStep); 588 break; 589 590 case this.options.cVolumeUp: 591 event.preventDefault(); 592 event.stopPropagation(); 593 player.volume(player.volume() + this.options.volumeStep); 594 break; 595 596 // Toggle Mute with the M key 597 case this.options.cMute: 598 event.preventDefault(); 599 event.stopPropagation(); 600 player.muted(!player.muted()); 601 break; 602 603 // Toggle Fullscreen with the F key 604 case this.options.cFullscreen: 605 event.preventDefault(); 606 event.stopPropagation(); 607 if (player.isFullscreen()) { 608 player.exitFullscreen(); 609 } else { 610 player.requestFullscreen(); 611 } 612 break; 613 } 614 } 615 } 616 }; 617} 618 619videojs.registerPlugin('hotkeys', Hotkeys); 620
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.