pyc-website
main website for pyc inc.
git clone https://9o.is/git/pyc-website.git
videogular.js
(25248B)
1 /**
2 * @license Videogular v0.4.0 http://videogular.com
3 * Two Fucking Developers http://twofuckingdevelopers.com
4 * License: MIT
5 */
6 "use strict";
7 angular.module("com.2fdevs.videogular", [])
8 .constant("VG_STATES", {
9 PLAY: "play",
10 PAUSE: "pause",
11 STOP: "stop"
12 })
13 .constant("VG_EVENTS", {
14 ON_PLAY: "onVgPlay",
15 ON_PAUSE: "onVgPause",
16 ON_PLAY_PAUSE: "onVgPlayPause",
17 ON_START_PLAYING: "onVgStartPlaying",
18 ON_COMPLETE: "onVgComplete",
19 ON_SET_STATE: "onVgSetState",
20 ON_SET_VOLUME: "onVgSetVolume",
21 ON_TOGGLE_FULLSCREEN: "onVgToggleFullscreen",
22 ON_ENTER_FULLSCREEN: "onVgEnterFullscreen",
23 ON_EXIT_FULLSCREEN: "onVgExitFullscreen",
24 ON_BUFFERING: "onVgBuffering",
25 ON_UPDATE_TIME: "onVgUpdateTime",
26 ON_SEEK_TIME: "onVgSeekTime",
27 ON_UPDATE_SIZE: "onVgUpdateSize",
28 ON_UPDATE_THEME: "onVgUpdateTheme",
29 ON_PLAYER_READY: "onVgPlayerReady",
30 ON_LOAD_POSTER: "onVgLoadPoster",
31 ON_ERROR: "onVgError"
32 })
33 .service("VG_UTILS", function() {
34 this.fixEventOffset = function($event) {
35 /**
36 * There's no offsetX in Firefox, so we fix that.
37 * Solution provided by Jack Moore in this post:
38 * http://www.jacklmoore.com/notes/mouse-position/
39 * @param $event
40 * @returns {*}
41 */
42 if (navigator.userAgent.match(/Firefox/i)) {
43 var style = $event.currentTarget.currentStyle || window.getComputedStyle($event.target, null);
44 var borderLeftWidth = parseInt(style['borderLeftWidth'], 10);
45 var borderTopWidth = parseInt(style['borderTopWidth'], 10);
46 var rect = $event.currentTarget.getBoundingClientRect();
47 var offsetX = $event.clientX - borderLeftWidth - rect.left;
48 var offsetY = $event.clientY - borderTopWidth - rect.top;
49
50 $event.offsetX = offsetX;
51 $event.offsetY = offsetY;
52 }
53
54 return $event;
55 };
56 /**
57 * Inspired by Paul Irish
58 * https://gist.github.com/paulirish/211209
59 * @returns {number}
60 */
61 this.getZIndex = function() {
62 var zIndex = 1;
63
64 angular.element('*')
65 .filter(function(){ return angular.element(this).css('zIndex') !== 'auto'; })
66 .each(function(){
67 var thisZIndex = parseInt(angular.element(this).css('zIndex'));
68 if (zIndex < thisZIndex) zIndex = thisZIndex + 1;
69 });
70
71 return zIndex;
72 };
73
74 // Very simple mobile detection, not 100% reliable
75 this.isMobileDevice = function() {
76 return (typeof window.orientation !== "undefined") || (navigator.userAgent.indexOf("IEMobile") !== -1);
77 };
78
79 this.isiOSDevice = function() {
80 return (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/iPad/i));
81 };
82 })
83 .run(["$window", "VG_UTILS",
84 function($window, VG_UTILS) {
85 // Native fullscreen polyfill
86 var fullScreenAPI;
87 var APIs = {
88 w3: {
89 enabled: "fullscreenEnabled",
90 element: "fullscreenElement",
91 request: "requestFullscreen",
92 exit: "exitFullscreen",
93 onchange: "fullscreenchange",
94 onerror: "fullscreenerror"
95 },
96 newWebkit: {
97 enabled: "webkitFullscreenEnabled",
98 element: "webkitFullscreenElement",
99 request: "webkitRequestFullscreen",
100 exit: "webkitExitFullscreen",
101 onchange: "webkitfullscreenchange",
102 onerror: "webkitfullscreenerror"
103 },
104 oldWebkit: {
105 enabled: "webkitIsFullScreen",
106 element: "webkitCurrentFullScreenElement",
107 request: "webkitRequestFullScreen",
108 exit: "webkitCancelFullScreen",
109 onchange: "webkitfullscreenchange",
110 onerror: "webkitfullscreenerror"
111 },
112 moz: {
113 enabled: "mozFullScreen",
114 element: "mozFullScreenElement",
115 request: "mozRequestFullScreen",
116 exit: "mozCancelFullScreen",
117 onchange: "mozfullscreenchange",
118 onerror: "mozfullscreenerror"
119 },
120 ios: {
121 enabled: "webkitFullscreenEnabled",
122 element: "webkitFullscreenElement",
123 request: "webkitEnterFullscreen",
124 exit: undefined,
125 onexit: "webkitendfullscreen",
126 onchange: "webkitfullscreenchange",
127 onerror: "webkitfullscreenerror"
128 },
129 ms: {
130 enabled: "msFullscreenEnabled",
131 element: "msFullscreenElement",
132 request: "msRequestFullscreen",
133 exit: "msExitFullscreen",
134 onchange: "msfullscreenchange",
135 onerror: "msfullscreenerror"
136 }
137 };
138
139 for (var browser in APIs) {
140 if (APIs[browser].enabled in document) {
141 fullScreenAPI = APIs[browser];
142 fullScreenAPI.isFullScreen = function () {
143 return (document[this.element] != null);
144 };
145
146 break;
147 }
148 }
149
150 // Override APIs on iOS
151 if (VG_UTILS.isiOSDevice()) {
152 fullScreenAPI = APIs.ios;
153 fullScreenAPI.isFullScreen = function () {
154 return (document[this.element] != null);
155 };
156 }
157
158 angular.element($window)[0].fullScreenAPI = fullScreenAPI;
159 }
160 ])
161 /**
162 * @ngdoc directive
163 * @name com.2fdevs.videogular.videogular:videogular
164 * @restrict E
165 * @description
166 * Main directive that must wrap a <video> tag and all plugins.
167 *
168 * <video> tag usually will be above plugin tags, that's because plugins should be in a layer over the <video>.
169 *
170 * You can customize `videogular` with these attributes:
171 *
172 * @param {number or string} vgWidth This directive sets width for the entire player. Passing a number will set the width normally. Passing a string will create a binding with a scope variable in case it exists.
173 *
174 * If `vgWidth` or `vgHeight` are not declared, or `vgResponsive` is `"true"`, player will enter in a responsive mode and width will be 100% and height will be calculated through video metadata to preserve aspect ratio.
175 *
176 * @param {number or string} vgHeight This directive sets height for the entire player. Passing a number will set the height normally. Passing a string will create a binding with a scope variable in case it exists.
177 *
178 * If `vgWidth` or `vgHeight` are not declared, or `vgResponsive` is `"true"`, player will enter in a responsive mode and width will be 100% and height will be calculated through video metadata to preserve aspect ratio.
179 *
180 * @param {string} vgTheme String with a scope name variable. This directive will inject a CSS link in the header of your page.
181 * **This parameter is required.**
182 *
183 * @param {boolean or string} [autoPlay=false] vgAutoplay Boolean value or a String with a scope name variable to auto start playing video when it is initialized.
184 *
185 * **This parameter is disabled in mobile devices** because user must click on content to prevent consuming mobile data plans.
186 *
187 * @param {string} [stretch=none] vgStretch String value representing a stretch mode. This value controls how image will scale inside its container. Stretch modes available are "none", "fit" or "fill".
188 *
189 * - **"none"**: Will set the image in its original size.
190 * - **"fit"**: Will try to show always all the image leaving black bars above and below.
191 * - **"fill"**: Will try to cover all video player area to never show black bars above and below.
192 *
193 * Content will always appear centered.
194 *
195 * @param {boolean or string} [isResponsive=false] vgResponsive Boolean value or a String with a scope name variable to auto start playing video when it is initialized.
196 *
197 * @param {function} vgComplete Function name in controller's scope to call when video have been completed.
198 * @param {function} vgUpdateVolume Function name in controller's scope to call when volume changes. Receives a param with the new volume.
199 * @param {function} vgUpdateTime Function name in controller's scope to call when video playback time is updated. Receives two params with current time and duration in milliseconds.
200 * @param {function} vgUpdateSize Function name in controller's scope to call when videogular size is updated. Receives two param with the new width and height.
201 * @param {function} vgUpdateState Function name in controller's scope to call when video state changes. Receives a param with the new state. Possible values are "play", "stop" or "pause".
202 * @param {function} vgPlayerReady Function name in controller's scope to call when video have been initialized. Receives a param with the videogular API.
203 * @param {function} vgChangeSource Function name in controller's scope to change current video source. Receives a param with the new video.
204 * This is a free parameter and it could be values like "new.mp4", "320" or "sd". This will allow you to use this to change a video or video quality.
205 * This callback will not change the video, you should do that by updating your sources scope variable.
206 *
207 */
208 .directive(
209 "videogular",
210 ["$window", "VG_STATES", "VG_EVENTS", "VG_UTILS", function($window, VG_STATES, VG_EVENTS, VG_UTILS) {
211 return {
212 restrict: "E",
213 scope: {
214 playerWidth: "=vgWidth",
215 playerHeight: "=vgHeight",
216 theme: "=vgTheme",
217 autoPlay: "=vgAutoplay",
218 responsive: "=vgResponsive",
219 stretch: "=vgStretch",
220 vgComplete: "&",
221 vgUpdateVolume: "&",
222 vgUpdateTime: "&",
223 vgUpdateSize: "&",
224 vgUpdateState: "&",
225 vgPlayerReady: "&",
226 vgChangeSource: "&"
227 },
228 controller: ['$scope','$timeout', function($scope,$timeout) {
229 var currentTheme = null;
230 var currentWidth = null;
231 var currentHeight = null;
232
233 var currentStretch = $scope.stretch;
234 var playerWidth = 0;
235 var playerHeight = 0;
236 var isFullScreenPressed = false;
237 var isFullScreen = false;
238 var isMetaDataLoaded = false;
239 var isElementReady = false;
240 var isVideoReady = false;
241 var isPlayerReady = false;
242 var isResponsive = false;
243 var vg = this;
244
245 var vgCompleteCallBack = $scope.vgComplete();
246 var vgUpdateVolumeCallBack = $scope.vgUpdateVolume();
247 var vgUpdateTimeCallBack = $scope.vgUpdateTime();
248 var vgUpdateSizeCallBack = $scope.vgUpdateSize();
249 var vgUpdateStateCallBack = $scope.vgUpdateState();
250 var vgPlayerReadyCallBack = $scope.vgPlayerReady();
251 var vgChangeSourceCallBack = $scope.vgChangeSource();
252
253 $scope.currentState = VG_STATES.STOP;
254
255 // PUBLIC $API
256 this.$on = function() {
257 $scope.$on.apply($scope, arguments);
258 };
259
260 this.isPlayerReady = function() {
261 return isPlayerReady;
262 };
263
264 this.seekTime = function(value, byPercent) {
265 var second;
266 if (byPercent) {
267 second = value * this.videoElement[0].duration / 100;
268 this.videoElement[0].currentTime = second;
269 }
270 else {
271 second = value;
272 this.videoElement[0].currentTime = second;
273 }
274
275 $scope.$emit(VG_EVENTS.ON_SEEK_TIME, [second]);
276 };
277
278 this.playPause = function() {
279 if (this.videoElement[0].paused) {
280 this.play();
281 }
282 else {
283 this.pause();
284 }
285 };
286
287 this.setState = function(newState) {
288 if (newState && newState != $scope.currentState) {
289 if ($scope.vgUpdateState()) {
290 vgUpdateStateCallBack = $scope.vgUpdateState();
291 vgUpdateStateCallBack(newState);
292 }
293
294 $scope.currentState = newState;
295 $scope.$emit(VG_EVENTS.ON_SET_STATE, [$scope.currentState]);
296 }
297
298 return $scope.currentState;
299 };
300
301 this.play = function() {
302 this.videoElement[0].play();
303 this.setState(VG_STATES.PLAY);
304 $scope.$emit(VG_EVENTS.ON_PLAY);
305 };
306
307 this.pause = function() {
308 this.videoElement[0].pause();
309 this.setState(VG_STATES.PAUSE);
310 $scope.$emit(VG_EVENTS.ON_PAUSE);
311 };
312
313 this.stop = function() {
314 this.videoElement[0].pause();
315 this.videoElement[0].currentTime = 0;
316 this.setState(VG_STATES.STOP);
317 $scope.$emit(VG_EVENTS.ON_COMPLETE);
318 };
319
320 this.toggleFullScreen = function() {
321 // There is no native full screen support
322 if (!angular.element($window)[0].fullScreenAPI) {
323 if (isFullScreen) {
324 this.videogularElement.removeClass("fullscreen");
325 this.videogularElement.css("z-index", 0);
326 }
327 else {
328 this.videogularElement.addClass("fullscreen");
329 this.videogularElement.css("z-index", VG_UTILS.getZIndex());
330 }
331
332 isFullScreen = !isFullScreen;
333
334 $scope.updateSize();
335 }
336 // Perform native full screen support
337 else {
338 if (angular.element($window)[0].fullScreenAPI.isFullScreen()) {
339 if (!VG_UTILS.isMobileDevice()) {
340 document[angular.element($window)[0].fullScreenAPI.exit]();
341 }
342 }
343 else {
344 // On mobile devices we should make fullscreen only the video object
345 if (VG_UTILS.isMobileDevice()) {
346 // On iOS we should check if user pressed before fullscreen button
347 // and also if metadata is loaded
348 if (VG_UTILS.isiOSDevice()) {
349 if (isMetaDataLoaded) {
350 this.enterElementInFullScreen(this.videoElement[0]);
351 }
352 else {
353 isFullScreenPressed = true;
354 this.play();
355 }
356 }
357 else {
358 this.enterElementInFullScreen(this.videoElement[0]);
359 }
360 }
361 else {
362 this.enterElementInFullScreen(this.elementScope[0]);
363 }
364 }
365 }
366 };
367
368 this.enterElementInFullScreen = function(element) {
369 element[angular.element($window)[0].fullScreenAPI.request]();
370 };
371
372 this.changeSource = function(newValue) {
373 if ($scope.vgChangeSource()) {
374 vgChangeSourceCallBack = $scope.vgChangeSource();
375 vgChangeSourceCallBack(newValue);
376 }
377 };
378
379 this.setVolume = function(newVolume) {
380 if ($scope.vgUpdateVolume()) {
381 vgUpdateVolumeCallBack = $scope.vgUpdateVolume();
382 vgUpdateVolumeCallBack(newVolume);
383 }
384
385 this.videoElement[0].volume = newVolume;
386 $scope.$emit(VG_EVENTS.ON_SET_VOLUME, [newVolume]);
387 };
388
389 this.updateTheme = function(value) {
390 if (currentTheme) {
391 // Remove previous theme
392 var links = document.getElementsByTagName("link");
393 for (var i=0, l=links.length; i<l; i++) {
394 if (links[i].outerHTML.indexOf(currentTheme) >= 0) {
395 links[i].parentNode.removeChild(links[i]);
396 }
397 }
398 }
399
400 if (value) {
401 var headElem = angular.element(document).find("head");
402 headElem.append("<link rel='stylesheet' href='" + value + "'>");
403
404 currentTheme = value;
405 }
406 };
407
408 this.updateStretch = function(value) {
409 currentStretch = value;
410 $scope.updateSize();
411 };
412
413 this.setSize = function(newWidth, newHeight) {
414 currentWidth = newWidth;
415 currentHeight = newHeight;
416
417 $scope.updateSize();
418 };
419
420 this.getSize = function() {
421 return {width: currentWidth, height: currentHeight};
422 };
423
424 // PRIVATE FUNCTIONS
425 $scope.API = this;
426
427 $scope.init = function() {
428 vg.updateTheme($scope.theme);
429 $scope.addBindings();
430
431 if ($scope.playerWidth == undefined || $scope.playerHeight == undefined || $scope.responsive == true) {
432 isResponsive = true;
433 angular.element($window).bind("resize", $scope.onResizeBrowser);
434 }
435 else {
436 playerWidth = $scope.playerWidth;
437 playerHeight = $scope.playerHeight;
438
439 vg.setSize(playerWidth, playerHeight);
440 }
441
442 if (angular.element($window)[0].fullScreenAPI) {
443 document.addEventListener(angular.element($window)[0].fullScreenAPI.onchange, $scope.onFullScreenChange);
444 }
445 };
446
447 $scope.addBindings = function() {
448 $scope.$watch("playerWidth", function(newValue, oldValue) {
449 if (newValue != oldValue){
450 vg.setSize(newValue, currentHeight);
451 }
452 });
453
454 $scope.$watch("playerHeight", function(newValue, oldValue) {
455 if (newValue != oldValue){
456 vg.setSize(currentWidth, newValue);
457 }
458 });
459
460 $scope.$watch("theme", function(newValue, oldValue) {
461 if (newValue != oldValue){
462 vg.updateTheme(newValue);
463 }
464 });
465
466 $scope.$watch("stretch", function(newValue, oldValue) {
467 if (newValue != oldValue){
468 vg.updateStretch(newValue);
469 }
470 });
471
472 $scope.$watch("autoPlay", function(newValue, oldValue) {
473 if (newValue != oldValue){
474 vg.play();
475 }
476 });
477
478 $scope.$watch("responsive", function(newValue, oldValue) {
479 if (newValue != oldValue){
480 isResponsive = newValue;
481
482 if (isResponsive) {
483 angular.element($window).bind("resize", $scope.onResizeBrowser);
484 $scope.onResizeBrowser();
485 }
486 else {
487 angular.element($window).unbind("resize", $scope.onResizeBrowser);
488 currentWidth = $scope.playerWidth;
489 currentHeight = $scope.playerHeight;
490 $scope.updateSize();
491 }
492 }
493 });
494 };
495
496 $scope.onElementReady = function() {
497 isElementReady = true;
498
499 if (isVideoReady) {
500 $scope.onPlayerReady();
501 }
502 };
503
504 $scope.onVideoReady = function() {
505 isVideoReady = true;
506
507 if (isElementReady){
508 $scope.onPlayerReady();
509 }
510 };
511
512 $scope.onPlayerReady = function() {
513 vg.videoElement[0].addEventListener("loadedmetadata", $scope.onLoadedMetaData);
514
515 $scope.doPlayerReady();
516 };
517
518 $scope.onLoadedMetaData = function() {
519 isMetaDataLoaded = true;
520 $scope.doPlayerReady();
521 };
522
523 $scope.doPlayerReady = function() {
524 if (isResponsive) {
525 var percentWidth = vg.elementScope[0].parentNode.clientWidth * 100 / vg.videoElement[0].videoWidth;
526 var videoHeight = vg.videoElement[0].videoHeight * percentWidth / 100;
527 currentWidth = vg.elementScope[0].parentNode.clientWidth;
528 currentHeight = videoHeight;
529 }
530
531 isPlayerReady = true;
532 $scope.updateSize();
533 if ($scope.vgPlayerReady()) {
534 vgPlayerReadyCallBack = $scope.vgPlayerReady();
535 vgPlayerReadyCallBack(vg);
536 }
537 $scope.$emit(VG_EVENTS.ON_PLAYER_READY);
538
539 if ($scope.autoPlay && !VG_UTILS.isMobileDevice() || $scope.currentState === VG_STATES.PLAY){
540 $timeout(function() {
541 vg.play();
542 })
543 }
544 };
545
546 $scope.updateSize = function() {
547 if (isPlayerReady) {
548 var videoSize;
549 var videoTop;
550 var videoLeft;
551
552 if (angular.element($window)[0].fullScreenAPI && angular.element($window)[0].fullScreenAPI.isFullScreen() || isFullScreen) {
553 vg.elementScope.css("width", parseInt(window.screen.width, 10) + "px");
554 vg.elementScope.css("height", parseInt(window.screen.height, 10) + "px");
555
556 videoSize = $scope.getVideoSize(window.screen.width, window.screen.height);
557
558 if (isFullScreen) {
559 playerWidth = $window.innerWidth;
560 playerHeight = $window.innerHeight;
561 }
562 else {
563 playerWidth = $window.screen.width;
564 playerHeight = $window.screen.height;
565 }
566 }
567 else {
568 vg.elementScope.css("width", parseInt(currentWidth, 10) + "px");
569 vg.elementScope.css("height", parseInt(currentHeight, 10) + "px");
570
571 videoSize = $scope.getVideoSize(currentWidth, currentHeight);
572
573 playerWidth = currentWidth;
574 playerHeight = currentHeight;
575 }
576
577 if (currentHeight == 0 || isNaN(currentHeight)) {
578 playerWidth = videoSize.width;
579 playerHeight = videoSize.height;
580 }
581
582 if (videoSize.width == 0 || isNaN(videoSize.width)) videoSize.width = currentWidth;
583 if (videoSize.height == 0 || isNaN(videoSize.height)) videoSize.height = currentHeight;
584
585 videoLeft = (playerWidth - videoSize.width) / 2;
586 videoTop = (playerHeight - videoSize.height) / 2;
587
588 vg.videoElement.attr("width", parseInt(videoSize.width, 10));
589 vg.videoElement.attr("height", parseInt(videoSize.height, 10));
590 vg.videoElement.css("width", parseInt(videoSize.width, 10) + "px");
591 vg.videoElement.css("height", parseInt(videoSize.height, 10) + "px");
592 vg.videoElement.css("top", videoTop + "px");
593 vg.videoElement.css("left", videoLeft + "px");
594
595 vg.elementScope.css("width", parseInt(playerWidth, 10) + "px");
596 vg.elementScope.css("height", parseInt(playerHeight, 10) + "px");
597
598 if ($scope.vgUpdateSize()) {
599 vgUpdateSizeCallBack = $scope.vgUpdateSize();
600 vgUpdateSizeCallBack(playerWidth, playerHeight);
601 }
602
603 $scope.$emit(VG_EVENTS.ON_UPDATE_SIZE, [playerWidth, playerHeight]);
604 }
605 };
606
607 $scope.onResizeBrowser = function() {
608 var percentWidth = vg.elementScope[0].parentNode.clientWidth * 100 / vg.videoElement[0].videoWidth;
609 var videoHeight = vg.videoElement[0].videoHeight * percentWidth / 100;
610
611 currentWidth = vg.elementScope[0].parentNode.clientWidth;
612 currentHeight = videoHeight;
613
614 $scope.updateSize();
615 };
616
617 $scope.onFullScreenChange = function(event) {
618 if (angular.element($window)[0].fullScreenAPI.isFullScreen()) {
619 $scope.$emit(VG_EVENTS.ON_ENTER_FULLSCREEN);
620 }
621 else {
622 $scope.$emit(VG_EVENTS.ON_EXIT_FULLSCREEN);
623 }
624
625 $scope.updateSize();
626 };
627
628 $scope.onComplete = function(event) {
629 if ($scope.vgComplete()) {
630 vgCompleteCallBack = $scope.vgComplete();
631 vgCompleteCallBack();
632 }
633
634 vg.setState(VG_STATES.STOP);
635 $scope.$emit(VG_EVENTS.ON_COMPLETE);
636 };
637
638 $scope.onStartBuffering = function(event) {
639 $scope.$emit(VG_EVENTS.ON_BUFFERING);
640 };
641
642 $scope.onStartPlaying = function(event) {
643 // Chrome fix: Chrome needs to update the video tag size or it will show a white screen
644 event.target.width++;
645 event.target.width--;
646
647 $scope.$emit(VG_EVENTS.ON_START_PLAYING, [event.target.duration]);
648 };
649
650 $scope.onUpdateTime = function(event) {
651 if ($scope.vgUpdateTime()) {
652 vgUpdateTimeCallBack = $scope.vgUpdateTime();
653 vgUpdateTimeCallBack(event.target.currentTime, event.target.duration);
654 }
655
656 $scope.$emit(VG_EVENTS.ON_UPDATE_TIME, [event.target.currentTime, event.target.duration]);
657 };
658
659 $scope.getVideoSize = function(w, h) {
660 var percentageWidth;
661 var percentageHeight;
662 var result = {};
663 var wider = vg.videoElement[0].videoWidth / vg.videoElement[0].videoHeight > w / h;
664 result.width = w;
665 result.height = h;
666
667 if (currentStretch == "fit" && wider || currentStretch == "fill" && !wider) {
668 percentageWidth = w * 100 / vg.videoElement[0].videoWidth;
669 result.height = vg.videoElement[0].videoHeight * percentageWidth / 100;
670 } else if (currentStretch == "fill" && wider || currentStretch == "fit" && !wider) {
671 percentageHeight = h * 100 / vg.videoElement[0].videoHeight;
672 result.width = vg.videoElement[0].videoWidth * percentageHeight / 100;
673 } else {
674 result.width = vg.videoElement[0].videoWidth;
675 result.height = vg.videoElement[0].videoHeight;
676 }
677
678 // Metadata has not been loaded or any problem has been happened
679 if (result.height == 0 || isNaN(result.height)) {
680 result.width = vg.elementScope[0].parentElement.clientWidth;
681 result.height = result.width * 9 / 16;
682 }
683
684 return result;
685 };
686
687 $scope.init();
688 }],
689 link: {
690 pre: function(scope, elem, attr, controller) {
691 controller.videogularElement = elem;
692 controller.elementScope = angular.element(elem);
693 controller.videoElement = controller.elementScope.find("video");
694
695 controller.videoElement[0].addEventListener("waiting", scope.onStartBuffering, false);
696 controller.videoElement[0].addEventListener("ended", scope.onComplete, false);
697 controller.videoElement[0].addEventListener("playing", scope.onStartPlaying, false);
698 controller.videoElement[0].addEventListener("timeupdate", scope.onUpdateTime, false);
699
700 controller.elementScope.ready(scope.onElementReady);
701 controller.videoElement.ready(scope.onVideoReady);
702 }
703 }
704 }
705 }
706 ])
707 .directive("vgSrc",
708 ["VG_EVENTS", "VG_UTILS", function(VG_EVENTS, VG_UTILS) {
709 return {
710 restrict: "A",
711 link: {
712 pre: function(scope, elem, attr) {
713 var element = elem;
714 var sources;
715 var canPlay;
716
717 function changeSource() {
718 canPlay = "";
719
720 // It's a cool browser
721 if (element[0].canPlayType) {
722 for (var i = 0, l = sources.length; i < l; i++) {
723 canPlay = element[0].canPlayType(sources[i].type);
724
725 if (canPlay == "maybe" || canPlay == "probably") {
726 element.attr("src", sources[i].src);
727 element.attr("type", sources[i].type);
728 break;
729 }
730 }
731 }
732 // It's a crappy browser and it doesn't deserve any respect
733 else {
734 // Get H264 or the first one
735 element.attr("src", sources[0].src);
736 element.attr("type", sources[0].type);
737 }
738
739 if (canPlay == "") {
740 scope.$broadcast(VG_EVENTS.ON_ERROR, {type: "Can't play file"})
741 }
742 }
743
744 scope.$watch(attr.vgSrc, function(newValue, oldValue) {
745 if (!sources || newValue != oldValue) {
746 sources = newValue;
747 changeSource();
748 }
749 });
750 }
751 }
752 }
753 }
754 ]);