1var cvpHandlers = { 2 canvasClickHandler: null, 3 videoTimeUpdateHandler: null, 4 videoCanPlayHandler: null, 5 windowResizeHandler: null 6}; 7 8var CanvasVideoPlayer = function(options) { 9 var i; 10 11 this.options = { 12 framesPerSecond: 25, 13 hideVideo: true, 14 autoplay: false, 15 makeLoop: false, 16 pauseOnClick: true, 17 audio: false, 18 timelineSelector: false, 19 resetOnLastFrame: true 20 }; 21 22 for (i in options) { 23 this.options[i] = options[i]; 24 } 25 26 this.video = document.querySelector(this.options.videoSelector); 27 this.canvas = document.querySelector(this.options.canvasSelector); 28 this.timeline = document.querySelector(this.options.timelineSelector); 29 this.timelinePassed = document.querySelector(this.options.timelineSelector + '> div'); 30 31 if (!this.options.videoSelector || !this.video) { 32 console.error('No "videoSelector" property, or the element is not found'); 33 return; 34 } 35 36 if (!this.options.canvasSelector || !this.canvas) { 37 console.error('No "canvasSelector" property, or the element is not found'); 38 return; 39 } 40 41 if (this.options.timelineSelector && !this.timeline) { 42 console.error('Element for the "timelineSelector" selector not found'); 43 return; 44 } 45 46 if (this.options.timelineSelector && !this.timelinePassed) { 47 console.error('Element for the "timelinePassed" not found'); 48 return; 49 } 50 51 if (this.options.audio) { 52 if (typeof(this.options.audio) === 'string'){ 53 // Use audio selector from options if specified 54 this.audio = document.querySelectorAll(this.options.audio)[0]; 55 56 if (!this.audio) { 57 console.error('Element for the "audio" not found'); 58 return; 59 } 60 } else { 61 // Creates audio element which uses same video sources 62 this.audio = document.createElement('audio'); 63 this.audio.innerHTML = this.video.innerHTML; 64 this.video.parentNode.insertBefore(this.audio, this.video); 65 this.audio.load(); 66 } 67 68 var iOS = /iPad|iPhone|iPod/.test(navigator.platform); 69 if (iOS) { 70 // Autoplay doesn't work with audio on iOS 71 // User have to manually start the audio 72 this.options.autoplay = false; 73 } 74 } 75 76 // Canvas context 77 this.ctx = this.canvas.getContext('2d'); 78 79 this.playing = false; 80 81 this.resizeTimeoutReference = false; 82 this.RESIZE_TIMEOUT = 1000; 83 84 this.init(); 85 this.bind(); 86}; 87 88CanvasVideoPlayer.prototype.init = function() { 89 this.video.load(); 90 91 this.setCanvasSize(); 92 93 if (this.options.hideVideo) { 94 this.video.style.display = 'none'; 95 } 96}; 97 98// Used most of the jQuery code for the .offset() method 99CanvasVideoPlayer.prototype.getOffset = function(elem) { 100 var docElem, rect, doc; 101 102 if (!elem) { 103 return; 104 } 105 106 rect = elem.getBoundingClientRect(); 107 108 // Make sure element is not hidden (display: none) or disconnected 109 if (rect.width || rect.height || elem.getClientRects().length) { 110 doc = elem.ownerDocument; 111 docElem = doc.documentElement; 112 113 return { 114 top: rect.top + window.pageYOffset - docElem.clientTop, 115 left: rect.left + window.pageXOffset - docElem.clientLeft 116 }; 117 } 118}; 119 120CanvasVideoPlayer.prototype.jumpTo = function(percentage) { 121 this.video.currentTime = this.video.duration * percentage; 122 123 if (this.options.audio) { 124 this.audio.currentTime = this.audio.duration * percentage; 125 } 126}; 127 128CanvasVideoPlayer.prototype.bind = function() { 129 var self = this; 130 131 // Playes or pauses video on canvas click 132 if(this.options.pauseOnClick === true){ 133 this.canvas.addEventListener('click', cvpHandlers.canvasClickHandler = function() { 134 self.playPause(); 135 }); 136 } 137 138 // On every time update draws frame 139 this.video.addEventListener('timeupdate', cvpHandlers.videoTimeUpdateHandler = function() { 140 self.drawFrame(); 141 if (self.options.timelineSelector) { 142 self.updateTimeline(); 143 } 144 }); 145 146 // Draws first frame 147 this.video.addEventListener('canplay', cvpHandlers.videoCanPlayHandler = function() { 148 self.drawFrame(); 149 }); 150 151 // To be sure 'canplay' event that isn't already fired 152 if (this.video.readyState >= 2) { 153 self.drawFrame(); 154 } 155 156 157 if (self.options.autoplay) { 158 self.play(); 159 } 160 161 // Click on the video seek video 162 if (self.options.timelineSelector) { 163 this.timeline.addEventListener('click', function(e) { 164 var offset = e.clientX - self.getOffset(self.canvas).left; 165 var percentage = offset / self.timeline.offsetWidth; 166 self.jumpTo(percentage); 167 }); 168 } 169 170 // Cache canvas size on resize (doing it only once in a second) 171 window.addEventListener('resize', cvpHandlers.windowResizeHandler = function() { 172 clearTimeout(self.resizeTimeoutReference); 173 174 self.resizeTimeoutReference = setTimeout(function() { 175 self.setCanvasSize(); 176 self.drawFrame(); 177 }, self.RESIZE_TIMEOUT); 178 }); 179 180 this.unbind = function() { 181 this.canvas.removeEventListener('click', cvpHandlers.canvasClickHandler); 182 this.video.removeEventListener('timeupdate', cvpHandlers.videoTimeUpdateHandler); 183 this.video.removeEventListener('canplay', cvpHandlers.videoCanPlayHandler); 184 window.removeEventListener('resize', cvpHandlers.windowResizeHandler); 185 186 if (this.options.audio) { 187 this.audio.parentNode.removeChild(this.audio); 188 } 189 }; 190}; 191 192CanvasVideoPlayer.prototype.updateTimeline = function() { 193 var percentage = (this.video.currentTime * 100 / this.video.duration).toFixed(2); 194 this.timelinePassed.style.width = percentage + '%'; 195}; 196 197CanvasVideoPlayer.prototype.setCanvasSize = function() { 198 this.width = this.canvas.clientWidth; 199 this.height = this.canvas.clientHeight; 200 201 this.canvas.setAttribute('width', this.width); 202 this.canvas.setAttribute('height', this.height); 203}; 204 205CanvasVideoPlayer.prototype.play = function() { 206 this.lastTime = Date.now(); 207 this.playing = true; 208 this.loop(); 209 210 if (this.options.audio) { 211 // Resync audio and video 212 this.audio.currentTime = this.video.currentTime; 213 this.audio.play(); 214 } 215}; 216 217CanvasVideoPlayer.prototype.pause = function() { 218 this.playing = false;
219 220 if (this.options.audio) { 221 this.audio.pause(); 222 } 223}; 224 225CanvasVideoPlayer.prototype.playPause = function() { 226 if (this.playing) { 227 this.pause(); 228 } 229 else { 230 this.play(); 231 } 232}; 233 234CanvasVideoPlayer.prototype.loop = function() { 235 var self = this; 236 237 var time = Date.now(); 238 var elapsed = (time - this.lastTime) / 1000; 239 240 // Render 241 if(elapsed >= (1 / this.options.framesPerSecond)) { 242 this.video.currentTime = this.video.currentTime + elapsed; 243 this.lastTime = time; 244 // Resync audio and video if they drift more than 300ms apart 245 if(this.audio && Math.abs(this.audio.currentTime - this.video.currentTime) > .3){ 246 this.audio.currentTime = this.video.currentTime; 247 } 248 } 249 250 // If we are at the end of the video stop 251 if (this.video.currentTime >= this.video.duration) { 252 if(this.options.makeLoop === false){ 253 this.playing = false; 254 } 255 256 if (this.options.resetOnLastFrame === true) { 257 this.video.currentTime = 0; 258 } 259 } 260 261 if (this.playing) { 262 this.animationFrame = requestAnimationFrame(function(){ 263 self.loop(); 264 }); 265 } 266 else { 267 cancelAnimationFrame(this.animationFrame); 268 } 269}; 270 271CanvasVideoPlayer.prototype.drawFrame = function() { 272 this.ctx.drawImage(this.video, 0, 0, this.width, this.height); 273};
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.