phocoder-rails 0.0.33

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/.autotest +46 -0
  2. data/.document +5 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +44 -0
  5. data/LICENSE.txt +20 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.rdoc +3 -0
  8. data/Rakefile +56 -0
  9. data/VERSION +5 -0
  10. data/app/controllers/phocoder_controller.rb +118 -0
  11. data/app/helpers/phocoder_helper.rb +320 -0
  12. data/app/models/encodable_job.rb +91 -0
  13. data/app/views/phocoder/_offline_video_embed.html.erb +19 -0
  14. data/app/views/phocoder/_thumbnail_update.html.erb +3 -0
  15. data/app/views/phocoder/_video_embed.html.erb +23 -0
  16. data/app/views/phocoder/multi_thumbnail_update.json.erb +7 -0
  17. data/app/views/phocoder/thumbnail_update.js.erb +9 -0
  18. data/config/routes.rb +8 -0
  19. data/lib/generators/phocoder_rails/model_update_generator.rb +52 -0
  20. data/lib/generators/phocoder_rails/scaffold_generator.rb +94 -0
  21. data/lib/generators/phocoder_rails/setup_generator.rb +33 -0
  22. data/lib/generators/phocoder_rails/templates/controller.rb +71 -0
  23. data/lib/generators/phocoder_rails/templates/helper.rb +5 -0
  24. data/lib/generators/phocoder_rails/templates/migration.rb +24 -0
  25. data/lib/generators/phocoder_rails/templates/model.rb +20 -0
  26. data/lib/generators/phocoder_rails/templates/model_migration.rb +56 -0
  27. data/lib/generators/phocoder_rails/templates/model_thumbnail.rb +5 -0
  28. data/lib/generators/phocoder_rails/templates/model_update_migration.rb +64 -0
  29. data/lib/generators/phocoder_rails/templates/phocodable.yml +28 -0
  30. data/lib/generators/phocoder_rails/templates/views/_form.html.erb.tt +23 -0
  31. data/lib/generators/phocoder_rails/templates/views/index.html.erb.tt +26 -0
  32. data/lib/generators/phocoder_rails/templates/views/new.html.erb.tt +5 -0
  33. data/lib/generators/phocoder_rails/templates/views/show.html.erb.tt +12 -0
  34. data/lib/phocoder_rails.rb +12 -0
  35. data/lib/phocoder_rails/acts_as_phocodable.rb +1153 -0
  36. data/lib/phocoder_rails/engine.rb +24 -0
  37. data/lib/phocoder_rails/errors.rb +46 -0
  38. data/phocoder-rails.gemspec +219 -0
  39. data/public/images/building.gif +0 -0
  40. data/public/images/error.png +0 -0
  41. data/public/images/play_small.png +0 -0
  42. data/public/images/storing.gif +0 -0
  43. data/public/images/waiting.gif +0 -0
  44. data/public/javascripts/phocodable.js +110 -0
  45. data/public/javascripts/video-js-2.0.2/.DS_Store +0 -0
  46. data/public/javascripts/video-js-2.0.2/LICENSE.txt +165 -0
  47. data/public/javascripts/video-js-2.0.2/README.markdown +202 -0
  48. data/public/javascripts/video-js-2.0.2/demo-subtitles.srt +13 -0
  49. data/public/javascripts/video-js-2.0.2/demo.html +101 -0
  50. data/public/javascripts/video-js-2.0.2/skins/hu.css +116 -0
  51. data/public/javascripts/video-js-2.0.2/skins/tube.css +111 -0
  52. data/public/javascripts/video-js-2.0.2/skins/vim.css +89 -0
  53. data/public/javascripts/video-js-2.0.2/video-js.css +242 -0
  54. data/public/javascripts/video-js-2.0.2/video.js +1758 -0
  55. data/public/stylesheets/phocodable.css +19 -0
  56. data/spec/controllers/phocoder_controller_spec.rb +123 -0
  57. data/spec/dummy/Rakefile +7 -0
  58. data/spec/dummy/app/controllers/application_controller.rb +7 -0
  59. data/spec/dummy/app/controllers/images_controller.rb +72 -0
  60. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  61. data/spec/dummy/app/helpers/images_helper.rb +5 -0
  62. data/spec/dummy/app/models/image.rb +20 -0
  63. data/spec/dummy/app/models/image_thumbnail.rb +5 -0
  64. data/spec/dummy/app/models/image_upload.rb +11 -0
  65. data/spec/dummy/app/views/images/_form.html.erb +23 -0
  66. data/spec/dummy/app/views/images/index.html.erb +26 -0
  67. data/spec/dummy/app/views/images/new.html.erb +5 -0
  68. data/spec/dummy/app/views/images/show.html.erb +12 -0
  69. data/spec/dummy/app/views/layouts/application.html.erb +18 -0
  70. data/spec/dummy/config.ru +4 -0
  71. data/spec/dummy/config/application.rb +45 -0
  72. data/spec/dummy/config/boot.rb +10 -0
  73. data/spec/dummy/config/database.yml +25 -0
  74. data/spec/dummy/config/environment.rb +8 -0
  75. data/spec/dummy/config/environments/development.rb +26 -0
  76. data/spec/dummy/config/environments/production.rb +49 -0
  77. data/spec/dummy/config/environments/test.rb +40 -0
  78. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  79. data/spec/dummy/config/initializers/inflections.rb +10 -0
  80. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  81. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  82. data/spec/dummy/config/initializers/session_store.rb +8 -0
  83. data/spec/dummy/config/locales/en.yml +5 -0
  84. data/spec/dummy/config/routes.rb +60 -0
  85. data/spec/dummy/db/migrate/001_create_image_uploads.rb +37 -0
  86. data/spec/dummy/db/migrate/20110523165213_add_parent_type_to_image_uploads.rb +11 -0
  87. data/spec/dummy/db/migrate/20110523165522_create_encodable_jobs.rb +24 -0
  88. data/spec/dummy/db/migrate/20111101024507_create_images.rb +56 -0
  89. data/spec/dummy/db/schema.rb +99 -0
  90. data/spec/dummy/public/404.html +26 -0
  91. data/spec/dummy/public/422.html +26 -0
  92. data/spec/dummy/public/500.html +26 -0
  93. data/spec/dummy/public/favicon.ico +0 -0
  94. data/spec/dummy/public/index.html +239 -0
  95. data/spec/dummy/public/javascripts/application.js +2 -0
  96. data/spec/dummy/public/javascripts/controls.js +965 -0
  97. data/spec/dummy/public/javascripts/dragdrop.js +974 -0
  98. data/spec/dummy/public/javascripts/effects.js +1123 -0
  99. data/spec/dummy/public/javascripts/jquery-1.6.4.js +9046 -0
  100. data/spec/dummy/public/javascripts/prototype.js +6001 -0
  101. data/spec/dummy/public/javascripts/rails.js +175 -0
  102. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  103. data/spec/dummy/script/rails +6 -0
  104. data/spec/engine_spec.rb +12 -0
  105. data/spec/fixtures/big_eye_tiny.jpg +0 -0
  106. data/spec/fixtures/octologo.png +0 -0
  107. data/spec/fixtures/test.txt +2 -0
  108. data/spec/fixtures/video-test.mov +0 -0
  109. data/spec/helpers/phocoder_helper_spec.rb +421 -0
  110. data/spec/integration/navigation_spec.rb +10 -0
  111. data/spec/models/acts_as_phocodable_spec.rb +664 -0
  112. data/spec/models/encodable_job_spec.rb +50 -0
  113. data/spec/phocoder_rails_spec.rb +8 -0
  114. data/spec/routing/phocoder_routing_spec.rb +19 -0
  115. data/spec/spec_helper.rb +75 -0
  116. metadata +375 -0
@@ -0,0 +1,89 @@
1
+ /*
2
+ VideoJS VimCSS Skin (http://videojs.com)
3
+ Version 2.0.0
4
+ */
5
+
6
+ .vim-css .vjs-controls {
7
+ height: 60px; opacity: 0.9; color: #fff;
8
+ }
9
+ .vim-css .vjs-controls > div {
10
+ height: 32px; top: 18px; padding: 0; text-align: center; background: rgba(23, 35, 34, 0.746094);
11
+ border-radius: 0; -webkit-border-radius: 0; -moz-border-radius: 0;
12
+ box-shadow: none; -webkit-box-shadow: none; -moz-box-shadow: none;
13
+ }
14
+ /* Placement of Control Items */
15
+ .vim-css .vjs-controls > div.vjs-play-control { width: 65px; left: 10px; }
16
+ .vim-css .vjs-controls > div.vjs-progress-control { left: 85px; right: 160px; }
17
+ .vim-css .vjs-controls > div.vjs-time-control { width: 75px; right: 85px; }
18
+ .vim-css .vjs-controls > div.vjs-volume-control { width: 50px; right: 35px; }
19
+ .vim-css .vjs-controls > div.vjs-fullscreen-control { width: 25px; right: 10px; }
20
+
21
+ /* Play/Pause
22
+ -------------------------------------------------------------------------------- */
23
+ .vim-css .vjs-controls .vjs-play-control { top: 10px; margin: 0; height: 40px; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; }
24
+ .vim-css .vjs-play-control:hover { background: #00ADEF; }
25
+ .vim-css.vjs-paused .vjs-play-control span { border-left-color: #fff; border-top-width: 9px; border-left-width: 18px; border-bottom-width: 9px; margin: 11px 0 0 24px; }
26
+ .vim-css.vjs-playing .vjs-play-control span { width: 5px; height: 18px; margin: 5px auto 0; border-left: 5px solid #fff; border-right: 5px solid #fff; margin: 11px 0 0 24px; }
27
+
28
+ /* Progress
29
+ -------------------------------------------------------------------------------- */
30
+ .vim-css .vjs-controls .vjs-progress-control { border-radius: 5px 0 0 5px; -webkit-border-radius: 5px 0 0 5px; -moz-border-radius: 5px 0 0 5px; }
31
+ .vim-css .vjs-progress-control .vjs-progress-holder { height: 8px; padding: 1px; margin: 10px 5px 0 10px; border-color: #666666; border-radius: 0; -webkit-border-radius: 0; -moz-border-radius: 0; }
32
+ .vim-css .vjs-progress-control .vjs-play-progress { height: 8px; background: #00ADEF; border-radius: 0; -webkit-border-radius: 0; -moz-border-radius: 0; }
33
+ .vim-css .vjs-progress-control .vjs-load-progress { height: 8px; background: #898F8F; border-radius: 0; -webkit-border-radius: 0; -moz-border-radius: 0; }
34
+
35
+ /* Time Display
36
+ -------------------------------------------------------------------------------- */
37
+ .vim-css .vjs-controls .vjs-time-control { font-size: 11px; }
38
+ .vim-css .vjs-controls .vjs-time-control span { line-height: 32px; /* Centering vertically */ }
39
+
40
+ /* Volume
41
+ -------------------------------------------------------------------------------- */
42
+ .vim-css .vjs-volume-control div { padding: 7px 0 0 5px; width: 30px; }
43
+ .vim-css .vjs-volume-control div span {
44
+ float: left; margin: 0 2px 0 0; padding: 0; width: 3px; height: 3px; border-bottom: 12px solid #666666;
45
+ -webkit-transition: all 100ms linear; -moz-transition: all 100ms linear;
46
+ }
47
+ .vim-css .vjs-volume-control div span.vjs-volume-level-on { border-color: #00ADEF; }
48
+ .vim-css .vjs-volume-control div span:hover { height: 0; border-bottom-width: 15px; }
49
+
50
+ /* Fullscreen
51
+ -------------------------------------------------------------------------------- */
52
+ .vim-css .vjs-fullscreen-control div { margin: 10px 0 0 0; }
53
+ .vim-css .vjs-controls .vjs-fullscreen-control { border-radius: 0 5px 5px 0; -webkit-border-radius: 0 5px 5px 0; -moz-border-radius: 0 5px 5px 0; }
54
+ /* Making default fullscreen icon smaller */
55
+ .vim-css .vjs-fullscreen-control div span:nth-child(1) { margin: 0 4px 4px 0; border: none; border-top: 4px solid #fff; border-right: 4px solid rgba(0,0,0,0); }
56
+ .vim-css .vjs-fullscreen-control div span:nth-child(2) { border: none; border-top: 4px solid #fff; border-left: 4px solid rgba(0,0,0,0); }
57
+ .vim-css .vjs-fullscreen-control div span:nth-child(3) { clear: both; margin: 0 4px 0 0; border: none; border-bottom: 4px solid #fff; border-right: 4px solid rgba(0,0,0,0); }
58
+ .vim-css .vjs-fullscreen-control div span:nth-child(4) { border: none; border-bottom: 4px solid #fff; border-left: 4px solid rgba(0,0,0,0); }
59
+ .vim-css.vjs-fullscreen .vjs-fullscreen-control div span:nth-child(1) { border: none; border-bottom: 4px solid #fff; border-left: 4px solid rgba(0,0,0,0); }
60
+ .vim-css.vjs-fullscreen .vjs-fullscreen-control div span:nth-child(2) { border: none; border-bottom: 4px solid #fff; border-right: 4px solid rgba(0,0,0,0); }
61
+ .vim-css.vjs-fullscreen .vjs-fullscreen-control div span:nth-child(3) { border: none; border-top: 4px solid #fff; border-left: 4px solid rgba(0,0,0,0); }
62
+ .vim-css.vjs-fullscreen .vjs-fullscreen-control div span:nth-child(4) { border: none; border-top: 4px solid #fff; border-right: 4px solid rgba(0,0,0,0); }
63
+ /* Fullscreen control hovering */
64
+ .vim-css .vjs-fullscreen-control:hover div span:nth-child(3), .vim-css .vjs-fullscreen-control:hover div span:nth-child(4), .vim-css.vjs-fullscreen .vjs-fullscreen-control:hover div span:nth-child(1), .vim-css.vjs-fullscreen .vjs-fullscreen-control:hover div span:nth-child(2) { border-bottom-color: #00ADEF; }
65
+ .vim-css .vjs-fullscreen-control:hover div span:nth-child(1), .vim-css .vjs-fullscreen-control:hover div span:nth-child(2), .vim-css.vjs-fullscreen .vjs-fullscreen-control:hover div span:nth-child(3), .vim-css.vjs-fullscreen .vjs-fullscreen-control:hover div span:nth-child(4) { border-top-color: #00ADEF; }
66
+
67
+ /* Big Play Button (at start)
68
+ ---------------------------------------------------------*/
69
+ .vim-css div.vjs-big-play-button {
70
+ width: 130px; height: 80px; margin: -40px 0 0 -65px;
71
+ border: none; opacity: 0.9;
72
+ border-radius: 10px; -webkit-border-radius: 10px; -moz-border-radius: 10px;
73
+
74
+ background: rgba(23, 35, 34, 0.746094);
75
+
76
+ /* CSS Shadows */
77
+ box-shadow: none; -webkit-box-shadow: none; -moz-box-shadow: none;
78
+ }
79
+ .vim-css div.vjs-big-play-button:hover {
80
+ background: #00ADEF;
81
+ opacity: 1;
82
+ }
83
+ .vim-css div.vjs-big-play-button span {
84
+ margin: 22px 0 0 48px;
85
+ /* Drawing the play triangle with borders - http://www.infimum.dk/HTML/slantinfo.html */
86
+ border-left: 36px solid #fff; /* Width & Color of play icon */
87
+ /* Height of play icon is total top & bottom border widths. Color is transparent. */
88
+ border-top: 18px solid rgba(0,0,0,0); border-bottom: 18px solid rgba(0,0,0,0);
89
+ }
@@ -0,0 +1,242 @@
1
+ /*
2
+ VideoJS Default Styles (http://videojs.com)
3
+ Version 2.0.2
4
+
5
+ REQUIRED STYLES (be careful overriding)
6
+ ================================================================================ */
7
+ /* Box containing video, controls, and download links.
8
+ Will be set to the width of the video element through JS
9
+ If you want to add some kind of frame or special positioning, use another containing element, not video-js-box. */
10
+ .video-js-box { text-align: left; position: relative; line-height: 0 !important; margin: 0; padding: 0 !important; border: none !important; }
11
+
12
+ /* Video Element */
13
+ video.video-js { background-color: #000; position: relative; padding: 0; }
14
+
15
+ .vjs-flash-fallback { display: block; }
16
+
17
+ /* Poster Overlay Style */
18
+ .video-js-box img.vjs-poster { display: block; position: absolute; left: 0; top: 0; width: 100%; height: 100%; margin: 0; padding: 0; cursor: pointer; }
19
+ /* Subtiles Style */
20
+ .video-js-box .vjs-subtitles { color: #fff; font-size: 20px; text-align: center; position: absolute; bottom: 40px; left: 0; right: 0; }
21
+
22
+ /* Fullscreen styles for main elements */
23
+ .video-js-box.vjs-fullscreen { position: fixed; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; z-index: 1000; }
24
+ .video-js-box.vjs-fullscreen video.video-js,
25
+ .video-js-box.vjs-fullscreen .vjs-flash-fallback { position: relative; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000; }
26
+ .video-js-box.vjs-fullscreen img.vjs-poster { z-index: 1001; }
27
+ .video-js-box.vjs-fullscreen .vjs-spinner { z-index: 1001; }
28
+ .video-js-box.vjs-fullscreen .vjs-controls { z-index: 1003; }
29
+ .video-js-box.vjs-fullscreen .vjs-big-play-button { z-index: 1004; }
30
+ .video-js-box.vjs-fullscreen .vjs-subtitles { z-index: 1004; }
31
+
32
+ /* Styles Loaded Check */
33
+ .vjs-styles-check { height: 5px; position: absolute; }
34
+ /* Controls Below Video */
35
+ .video-js-box.vjs-controls-below .vjs-controls { position: relative; opacity: 1; background-color: #000; }
36
+ .video-js-box.vjs-controls-below .vjs-subtitles { bottom: 75px; } /* Account for height of controls below video */
37
+
38
+ /* DEFAULT SKIN (override in another file)
39
+ ================================================================================
40
+ Using all CSS to draw the controls. Images could be used if desired.
41
+ Instead of editing this file, I recommend creating your own skin CSS file to be included after this file,
42
+ so you can upgrade to newer versions easier. */
43
+
44
+ /* Controls Layout
45
+ Using absolute positioning to position controls */
46
+ .video-js-box .vjs-controls {
47
+ position: absolute; margin: 0; opacity: 0.85; color: #fff;
48
+ display: none; /* Start hidden */
49
+ left: 0; right: 0; /* 100% width of video-js-box */
50
+ width: 100%;
51
+ bottom: 0px; /* Distance from the bottom of the box/video. Keep 0. Use height to add more bottom margin. */
52
+ height: 35px; /* Including any margin you want above or below control items */
53
+ padding: 0; /* Controls are absolutely position, so no padding necessary */
54
+ }
55
+
56
+ .video-js-box .vjs-controls > div { /* Direct div children of control bar */
57
+ position: absolute; /* Use top, bottom, left, and right to specifically position the control. */
58
+ text-align: center; margin: 0; padding: 0;
59
+ height: 25px; /* Default height of individual controls */
60
+ top: 5px; /* Top margin to put space between video and controls when controls are below */
61
+
62
+ /* CSS Background Gradients
63
+ Using to give the aqua-ish look. */
64
+ /* Default */ background-color: #0B151A;
65
+ /* Webkit */ background: #1F3744 -webkit-gradient(linear, left top, left bottom, from(#0B151A), to(#1F3744)) left 12px;
66
+ /* Firefox */ background: #1F3744 -moz-linear-gradient(top, #0B151A, #1F3744) left 12px;
67
+
68
+ /* CSS Curved Corners */
69
+ border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px;
70
+
71
+ /* CSS Shadows */
72
+ box-shadow: 1px 1px 2px #000; -webkit-box-shadow: 1px 1px 2px #000; -moz-box-shadow: 1px 1px 2px #000;
73
+ }
74
+
75
+ /* Placement of Control Items
76
+ - Left side of pogress bar, use left & width
77
+ - Rigth side of progress bar, use right & width
78
+ - Expand with the video (like progress bar) use left & right */
79
+ .vjs-controls > div.vjs-play-control { left: 5px; width: 25px; }
80
+ .vjs-controls > div.vjs-progress-control { left: 35px; right: 165px; } /* Using left & right so it expands with the width of the video */
81
+ .vjs-controls > div.vjs-time-control { width: 75px; right: 90px; } /* Time control and progress bar are combined to look like one */
82
+ .vjs-controls > div.vjs-volume-control { width: 50px; right: 35px; }
83
+ .vjs-controls > div.vjs-fullscreen-control { width: 25px; right: 5px; }
84
+
85
+ /* Removing curved corners on progress control and time control to join them. */
86
+ .vjs-controls > div.vjs-progress-control {
87
+ border-top-right-radius: 0; -webkit-border-top-right-radius: 0; -moz-border-radius-topright: 0;
88
+ border-bottom-right-radius: 0; -webkit-border-bottom-right-radius: 0; -moz-border-radius-bottomright: 0;
89
+ }
90
+ .vjs-controls > div.vjs-time-control {
91
+ border-top-left-radius: 0; -webkit-border-top-left-radius: 0; -moz-border-radius-topleft: 0;
92
+ border-bottom-left-radius: 0; -webkit-border-bottom-left-radius: 0; -moz-border-radius-bottomleft: 0;
93
+ }
94
+
95
+ /* Play/Pause
96
+ -------------------------------------------------------------------------------- */
97
+ .vjs-play-control { cursor: pointer !important; }
98
+ /* Play Icon */
99
+ .vjs-play-control span { display: block; font-size: 0; line-height: 0; }
100
+ .vjs-paused .vjs-play-control span {
101
+ width: 0; height: 0; margin: 8px 0 0 8px;
102
+ /* Drawing the play triangle with borders - http://www.infimum.dk/HTML/slantinfo.html */
103
+ border-left: 10px solid #fff; /* Width & Color of play icon */
104
+ /* Height of play icon is total top & bottom border widths. Color is transparent. */
105
+ border-top: 5px solid rgba(0,0,0,0); border-bottom: 5px solid rgba(0,0,0,0);
106
+ }
107
+ .vjs-playing .vjs-play-control span {
108
+ width: 3px; height: 10px; margin: 8px auto 0;
109
+ /* Drawing the pause bars with borders */
110
+ border-top: 0px; border-left: 3px solid #fff; border-bottom: 0px; border-right: 3px solid #fff;
111
+ }
112
+
113
+ /* Progress
114
+ -------------------------------------------------------------------------------- */
115
+ .vjs-progress-holder { /* Box containing play and load progresses */
116
+ position: relative; padding: 0; overflow:hidden; cursor: pointer !important;
117
+ height: 9px; border: 1px solid #777;
118
+ margin: 7px 1px 0 5px; /* Placement within the progress control item */
119
+ border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px;
120
+ }
121
+ .vjs-progress-holder div { /* Progress Bars */
122
+ position: absolute; display: block; width: 0; height: 9px; margin: 0; padding: 0;
123
+ border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px;
124
+ }
125
+ .vjs-play-progress {
126
+ /* CSS Gradient */
127
+ /* Default */ background: #fff;
128
+ /* Webkit */ background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#777));
129
+ /* Firefox */ background: -moz-linear-gradient(top, #fff, #777);
130
+ }
131
+ .vjs-load-progress {
132
+ opacity: 0.8;
133
+ /* CSS Gradient */
134
+ /* Default */ background-color: #555;
135
+ /* Webkit */ background: -webkit-gradient(linear, left top, left bottom, from(#555), to(#aaa));
136
+ /* Firefox */ background: -moz-linear-gradient(top, #555, #aaa);
137
+ }
138
+
139
+ /* Time Display
140
+ -------------------------------------------------------------------------------- */
141
+ .vjs-controls .vjs-time-control { font-size: 10px; line-height: 1; font-weight: normal; font-family: Helvetica, Arial, sans-serif; }
142
+ .vjs-controls .vjs-time-control span { line-height: 25px; /* Centering vertically */ }
143
+
144
+ /* Volume
145
+ -------------------------------------------------------------------------------- */
146
+ .vjs-volume-control { cursor: pointer !important; }
147
+ .vjs-volume-control div { display: block; margin: 0 5px 0 5px; padding: 4px 0 0 0; }
148
+ /* Drawing the volume icon using 6 span elements */
149
+ .vjs-volume-control div span { /* Individual volume bars */
150
+ float: left; padding: 0;
151
+ margin: 0 2px 0 0; /* Space between */
152
+ width: 5px; height: 0px; /* Total height is height + bottom border */
153
+ border-bottom: 18px solid #555; /* Default (off) color and height of visible portion */
154
+ }
155
+ .vjs-volume-control div span.vjs-volume-level-on { border-color: #fff; /* Volume on bar color */ }
156
+ /* Creating differnt bar heights through height (transparent) and bottom border (visible). */
157
+ .vjs-volume-control div span:nth-child(1) { border-bottom-width: 2px; height: 16px; }
158
+ .vjs-volume-control div span:nth-child(2) { border-bottom-width: 4px; height: 14px; }
159
+ .vjs-volume-control div span:nth-child(3) { border-bottom-width: 7px; height: 11px; }
160
+ .vjs-volume-control div span:nth-child(4) { border-bottom-width: 10px; height: 8px; }
161
+ .vjs-volume-control div span:nth-child(5) { border-bottom-width: 14px; height: 4px; }
162
+ .vjs-volume-control div span:nth-child(6) { margin-right: 0; }
163
+
164
+ /* Fullscreen
165
+ -------------------------------------------------------------------------------- */
166
+ .vjs-fullscreen-control { cursor: pointer !important; }
167
+ .vjs-fullscreen-control div {
168
+ padding: 0; text-align: left; vertical-align: top; cursor: pointer !important;
169
+ margin: 5px 0 0 5px; /* Placement within the fullscreen control item */
170
+ width: 20px; height: 20px;
171
+ }
172
+ /* Drawing the fullscreen icon using 4 span elements */
173
+ .vjs-fullscreen-control div span { float: left; margin: 0; padding: 0; font-size: 0; line-height: 0; width: 0; text-align: left; vertical-align: top; }
174
+ .vjs-fullscreen-control div span:nth-child(1) { /* Top-left triangle */
175
+ margin-right: 3px; /* Space between top-left and top-right */
176
+ margin-bottom: 3px; /* Space between top-left and bottom-left */
177
+ border-top: 6px solid #fff; /* Height and color */
178
+ border-right: 6px solid rgba(0,0,0,0); /* Width */
179
+ }
180
+ .vjs-fullscreen-control div span:nth-child(2) { border-top: 6px solid #fff; border-left: 6px solid rgba(0,0,0,0); }
181
+ .vjs-fullscreen-control div span:nth-child(3) { clear: both; margin: 0 3px 0 0; border-bottom: 6px solid #fff; border-right: 6px solid rgba(0,0,0,0); }
182
+ .vjs-fullscreen-control div span:nth-child(4) { border-bottom: 6px solid #fff; border-left: 6px solid rgba(0,0,0,0); }
183
+ /* Icon when video is in fullscreen mode */
184
+ .vjs-fullscreen .vjs-fullscreen-control div span:nth-child(1) { border: none; border-bottom: 6px solid #fff; border-left: 6px solid rgba(0,0,0,0); }
185
+ .vjs-fullscreen .vjs-fullscreen-control div span:nth-child(2) { border: none; border-bottom: 6px solid #fff; border-right: 6px solid rgba(0,0,0,0); }
186
+ .vjs-fullscreen .vjs-fullscreen-control div span:nth-child(3) { border: none; border-top: 6px solid #fff; border-left: 6px solid rgba(0,0,0,0); }
187
+ .vjs-fullscreen .vjs-fullscreen-control div span:nth-child(4) { border: none; border-top: 6px solid #fff; border-right: 6px solid rgba(0,0,0,0); }
188
+
189
+ /* Download Links - Used for browsers that don't support any video.
190
+ ---------------------------------------------------------*/
191
+ .vjs-no-video { font-size: small; line-height: 1.5; }
192
+
193
+ /* Big Play Button (at start)
194
+ ---------------------------------------------------------*/
195
+ div.vjs-big-play-button {
196
+ display: none; /* Start hidden */ z-index: 2;
197
+ position: absolute; top: 50%; left: 50%; width: 80px; height: 80px; margin: -43px 0 0 -43px; text-align: center; vertical-align: center; cursor: pointer !important;
198
+ border: 3px solid #fff; opacity: 0.9;
199
+ border-radius: 20px; -webkit-border-radius: 20px; -moz-border-radius: 20px;
200
+
201
+ /* CSS Background Gradients */
202
+ /* Default */ background-color: #0B151A;
203
+ /* Webkit */ background: #1F3744 -webkit-gradient(linear, left top, left bottom, from(#0B151A), to(#1F3744)) left 40px;
204
+ /* Firefox */ background: #1F3744 -moz-linear-gradient(top, #0B151A, #1F3744) left 40px;
205
+
206
+ /* CSS Shadows */
207
+ box-shadow: 4px 4px 8px #000; -webkit-box-shadow: 4px 4px 8px #000; -moz-box-shadow: 4px 4px 8px #000;
208
+ }
209
+ div.vjs-big-play-button:hover {
210
+ box-shadow: 0px 0px 80px #fff; -webkit-box-shadow: 0px 0px 80px #fff; -moz-box-shadow: 0px 0px 80px #fff;
211
+ }
212
+
213
+ div.vjs-big-play-button span {
214
+ display: block; font-size: 0; line-height: 0;
215
+ width: 0; height: 0; margin: 20px 0 0 23px;
216
+ /* Drawing the play triangle with borders - http://www.infimum.dk/HTML/slantinfo.html */
217
+ border-left: 40px solid #fff; /* Width & Color of play icon */
218
+ /* Height of play icon is total top & bottom border widths. Color is transparent. */
219
+ border-top: 20px solid rgba(0,0,0,0); border-bottom: 20px solid rgba(0,0,0,0);
220
+ }
221
+
222
+ /* Spinner Styles
223
+ ---------------------------------------------------------*/
224
+ /* CSS Spinners by Kilian Valkhof - http://kilianvalkhof.com/2010/css-xhtml/css3-loading-spinners-without-images/ */
225
+ .vjs-spinner { display: none; position: absolute; top: 50%; left: 50%; width: 100px; height: 100px; z-index: 1; margin: -50px 0 0 -50px;
226
+ /* Scaling makes the circles look smoother. */
227
+ transform: scale(0.5); -webkit-transform:scale(0.5); -moz-transform:scale(0.5);
228
+ }
229
+ /* Spinner circles */
230
+ .vjs-spinner div { position:absolute; left: 40px; top: 40px; width: 20px; height: 20px; background: #fff;
231
+ border-radius: 20px; -webkit-border-radius: 20px; -moz-border-radius: 20px;
232
+ border: 1px solid #ccc; /* Added border so can be visible on white backgrounds */
233
+ }
234
+ /* Each circle */
235
+ .vjs-spinner div:nth-child(1) { opacity: 0.12; transform: rotate(000deg) translate(0, -40px) scale(0.1); -webkit-transform: rotate(000deg) translate(0, -40px) scale(0.1); -moz-transform: rotate(000deg) translate(0, -40px) scale(0.1); }
236
+ .vjs-spinner div:nth-child(2) { opacity: 0.25; transform: rotate(045deg) translate(0, -40px) scale(0.2); -webkit-transform: rotate(045deg) translate(0, -40px) scale(0.2); -moz-transform: rotate(045deg) translate(0, -40px) scale(0.2); }
237
+ .vjs-spinner div:nth-child(3) { opacity: 0.37; transform: rotate(090deg) translate(0, -40px) scale(0.4); -webkit-transform: rotate(090deg) translate(0, -40px) scale(0.4); -moz-transform: rotate(090deg) translate(0, -40px) scale(0.4); }
238
+ .vjs-spinner div:nth-child(4) { opacity: 0.50; transform: rotate(135deg) translate(0, -40px) scale(0.6); -webkit-transform: rotate(135deg) translate(0, -40px) scale(0.6); -moz-transform: rotate(135deg) translate(0, -40px) scale(0.6); }
239
+ .vjs-spinner div:nth-child(5) { opacity: 0.62; transform: rotate(180deg) translate(0, -40px) scale(0.8); -webkit-transform: rotate(180deg) translate(0, -40px) scale(0.8); -moz-transform: rotate(180deg) translate(0, -40px) scale(0.8); }
240
+ .vjs-spinner div:nth-child(6) { opacity: 0.75; transform: rotate(225deg) translate(0, -40px) scale(1.0); -webkit-transform: rotate(225deg) translate(0, -40px) scale(1.0); -moz-transform: rotate(225deg) translate(0, -40px) scale(1.0); }
241
+ .vjs-spinner div:nth-child(7) { opacity: 0.87; transform: rotate(270deg) translate(0, -40px) scale(1.1); -webkit-transform: rotate(270deg) translate(0, -40px) scale(1.1); -moz-transform: rotate(270deg) translate(0, -40px) scale(1.1); }
242
+ .vjs-spinner div:nth-child(8) { opacity: 1.00; transform: rotate(315deg) translate(0, -40px) scale(1.3); -webkit-transform: rotate(315deg) translate(0, -40px) scale(1.3); -moz-transform: rotate(315deg) translate(0, -40px) scale(1.3); }
@@ -0,0 +1,1758 @@
1
+ /*
2
+ VideoJS - HTML5 Video Player
3
+ v2.0.2
4
+
5
+ This file is part of VideoJS. Copyright 2010 Zencoder, Inc.
6
+
7
+ VideoJS is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ VideoJS is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with VideoJS. If not, see <http://www.gnu.org/licenses/>.
19
+ */
20
+
21
+ // Self-executing function to prevent global vars and help with minification
22
+ (function(window, undefined){
23
+ var document = window.document;
24
+
25
+ // Using jresig's Class implementation http://ejohn.org/blog/simple-javascript-inheritance/
26
+ (function(){var initializing=false, fnTest=/xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; this.JRClass = function(){}; JRClass.extend = function(prop) { var _super = this.prototype; initializing = true; var prototype = new this(); initializing = false; for (var name in prop) { prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn){ return function() { var tmp = this._super; this._super = _super[name]; var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]) : prop[name]; } function JRClass() { if ( !initializing && this.init ) this.init.apply(this, arguments); } JRClass.prototype = prototype; JRClass.constructor = JRClass; JRClass.extend = arguments.callee; return JRClass;};})();
27
+
28
+ // Video JS Player Class
29
+ var VideoJS = JRClass.extend({
30
+
31
+ // Initialize the player for the supplied video tag element
32
+ // element: video tag
33
+ init: function(element, setOptions){
34
+
35
+ // Allow an ID string or an element
36
+ if (typeof element == 'string') {
37
+ this.video = document.getElementById(element);
38
+ } else {
39
+ this.video = element;
40
+ }
41
+ // Store reference to player on the video element.
42
+ // So you can acess the player later: document.getElementById("video_id").player.play();
43
+ this.video.player = this;
44
+ this.values = {}; // Cache video values.
45
+ this.elements = {}; // Store refs to controls elements.
46
+
47
+ // Default Options
48
+ this.options = {
49
+ autoplay: false,
50
+ preload: true,
51
+ useBuiltInControls: false, // Use the browser's controls (iPhone)
52
+ controlsBelow: false, // Display control bar below video vs. in front of
53
+ controlsAtStart: false, // Make controls visible when page loads
54
+ controlsHiding: true, // Hide controls when not over the video
55
+ defaultVolume: 0.85, // Will be overridden by localStorage volume if available
56
+ playerFallbackOrder: ["html5", "flash", "links"], // Players and order to use them
57
+ flashPlayer: "htmlObject",
58
+ flashPlayerVersion: false // Required flash version for fallback
59
+ };
60
+ // Override default options with global options
61
+ if (typeof VideoJS.options == "object") { _V_.merge(this.options, VideoJS.options); }
62
+ // Override default & global options with options specific to this player
63
+ if (typeof setOptions == "object") { _V_.merge(this.options, setOptions); }
64
+ // Override preload & autoplay with video attributes
65
+ if (this.getPreloadAttribute() !== undefined) { this.options.preload = this.getPreloadAttribute(); }
66
+ if (this.getAutoplayAttribute() !== undefined) { this.options.autoplay = this.getAutoplayAttribute(); }
67
+
68
+ // Store reference to embed code pieces
69
+ this.box = this.video.parentNode;
70
+ this.linksFallback = this.getLinksFallback();
71
+ this.hideLinksFallback(); // Will be shown again if "links" player is used
72
+
73
+ // Loop through the player names list in options, "html5" etc.
74
+ // For each player name, initialize the player with that name under VideoJS.players
75
+ // If the player successfully initializes, we're done
76
+ // If not, try the next player in the list
77
+ this.each(this.options.playerFallbackOrder, function(playerType){
78
+ if (this[playerType+"Supported"]()) { // Check if player type is supported
79
+ this[playerType+"Init"](); // Initialize player type
80
+ return true; // Stop looping though players
81
+ }
82
+ });
83
+
84
+ // Start Global Listeners - API doesn't exist before now
85
+ this.activateElement(this, "player");
86
+ this.activateElement(this.box, "box");
87
+ },
88
+ /* Behaviors
89
+ ================================================================================ */
90
+ behaviors: {},
91
+ newBehavior: function(name, activate, functions){
92
+ this.behaviors[name] = activate;
93
+ this.extend(functions);
94
+ },
95
+ activateElement: function(element, behavior){
96
+ // Allow passing and ID string
97
+ if (typeof element == "string") { element = document.getElementById(element); }
98
+ this.behaviors[behavior].call(this, element);
99
+ },
100
+ /* Errors/Warnings
101
+ ================================================================================ */
102
+ errors: [], // Array to track errors
103
+ warnings: [],
104
+ warning: function(warning){
105
+ this.warnings.push(warning);
106
+ this.log(warning);
107
+ },
108
+ /* History of errors/events (not quite there yet)
109
+ ================================================================================ */
110
+ history: [],
111
+ log: function(event){
112
+ if (!event) { return; }
113
+ if (typeof event == "string") { event = { type: event }; }
114
+ if (event.type) { this.history.push(event.type); }
115
+ if (this.history.length >= 50) { this.history.shift(); }
116
+ try { console.log(event.type); } catch(e) { try { opera.postError(event.type); } catch(e){} }
117
+ },
118
+ /* Local Storage
119
+ ================================================================================ */
120
+ setLocalStorage: function(key, value){
121
+ if (!localStorage) { return; }
122
+ try {
123
+ localStorage[key] = value;
124
+ } catch(e) {
125
+ if (e.code == 22 || e.code == 1014) { // Webkit == 22 / Firefox == 1014
126
+ this.warning(VideoJS.warnings.localStorageFull);
127
+ }
128
+ }
129
+ },
130
+ /* Helpers
131
+ ================================================================================ */
132
+ getPreloadAttribute: function(){
133
+ if (typeof this.video.hasAttribute == "function" && this.video.hasAttribute("preload")) {
134
+ var preload = this.video.getAttribute("preload");
135
+ // Only included the attribute, thinking it was boolean
136
+ if (preload === "" || preload === "true") { return "auto"; }
137
+ if (preload === "false") { return "none"; }
138
+ return preload;
139
+ }
140
+ },
141
+ getAutoplayAttribute: function(){
142
+ if (typeof this.video.hasAttribute == "function" && this.video.hasAttribute("autoplay")) {
143
+ var autoplay = this.video.getAttribute("autoplay");
144
+ if (autoplay === "false") { return false; }
145
+ return true;
146
+ }
147
+ },
148
+ // Calculates amoutn of buffer is full
149
+ bufferedPercent: function(){ return (this.duration()) ? this.buffered()[1] / this.duration() : 0; },
150
+ // Each that maintains player as context
151
+ // Break if true is returned
152
+ each: function(arr, fn){
153
+ if (!arr || arr.length === 0) { return; }
154
+ for (var i=0,j=arr.length; i<j; i++) {
155
+ if (fn.call(this, arr[i], i)) { break; }
156
+ }
157
+ },
158
+ extend: function(obj){
159
+ for (var attrname in obj) {
160
+ if (obj.hasOwnProperty(attrname)) { this[attrname]=obj[attrname]; }
161
+ }
162
+ }
163
+ });
164
+ VideoJS.player = VideoJS.prototype;
165
+
166
+ ////////////////////////////////////////////////////////////////////////////////
167
+ // Player Types
168
+ ////////////////////////////////////////////////////////////////////////////////
169
+
170
+ /* Flash Object Fallback (Player Type)
171
+ ================================================================================ */
172
+ VideoJS.player.extend({
173
+ flashSupported: function(){
174
+ if (!this.flashElement) { this.flashElement = this.getFlashElement(); }
175
+ // Check if object exists & Flash Player version is supported
176
+ if (this.flashElement && this.flashPlayerVersionSupported()) {
177
+ return true;
178
+ } else {
179
+ return false;
180
+ }
181
+ },
182
+ flashInit: function(){
183
+ this.replaceWithFlash();
184
+ this.element = this.flashElement;
185
+ this.video.src = ""; // Stop video from downloading if HTML5 is still supported
186
+ var flashPlayerType = VideoJS.flashPlayers[this.options.flashPlayer];
187
+ this.extend(VideoJS.flashPlayers[this.options.flashPlayer].api);
188
+ (flashPlayerType.init.context(this))();
189
+ },
190
+ // Get Flash Fallback object element from Embed Code
191
+ getFlashElement: function(){
192
+ var children = this.video.children;
193
+ for (var i=0,j=children.length; i<j; i++) {
194
+ if (children[i].className == "vjs-flash-fallback") {
195
+ return children[i];
196
+ }
197
+ }
198
+ },
199
+ // Used to force a browser to fall back when it's an HTML5 browser but there's no supported sources
200
+ replaceWithFlash: function(){
201
+ // this.flashElement = this.video.removeChild(this.flashElement);
202
+ if (this.flashElement) {
203
+ this.box.insertBefore(this.flashElement, this.video);
204
+ this.video.style.display = "none"; // Removing it was breaking later players
205
+ }
206
+ },
207
+ // Check if browser can use this flash player
208
+ flashPlayerVersionSupported: function(){
209
+ var playerVersion = (this.options.flashPlayerVersion) ? this.options.flashPlayerVersion : VideoJS.flashPlayers[this.options.flashPlayer].flashPlayerVersion;
210
+ return VideoJS.getFlashVersion() >= playerVersion;
211
+ }
212
+ });
213
+ VideoJS.flashPlayers = {};
214
+ VideoJS.flashPlayers.htmlObject = {
215
+ flashPlayerVersion: 9,
216
+ init: function() { return true; },
217
+ api: { // No video API available with HTML Object embed method
218
+ width: function(width){
219
+ if (width !== undefined) {
220
+ this.element.width = width;
221
+ this.box.style.width = width+"px";
222
+ this.triggerResizeListeners();
223
+ return this;
224
+ }
225
+ return this.element.width;
226
+ },
227
+ height: function(height){
228
+ if (height !== undefined) {
229
+ this.element.height = height;
230
+ this.box.style.height = height+"px";
231
+ this.triggerResizeListeners();
232
+ return this;
233
+ }
234
+ return this.element.height;
235
+ }
236
+ }
237
+ };
238
+
239
+
240
+ /* Download Links Fallback (Player Type)
241
+ ================================================================================ */
242
+ VideoJS.player.extend({
243
+ linksSupported: function(){ return true; },
244
+ linksInit: function(){
245
+ this.showLinksFallback();
246
+ this.element = this.video;
247
+ },
248
+ // Get the download links block element
249
+ getLinksFallback: function(){ return this.box.getElementsByTagName("P")[0]; },
250
+ // Hide no-video download paragraph
251
+ hideLinksFallback: function(){
252
+ if (this.linksFallback) { this.linksFallback.style.display = "none"; }
253
+ },
254
+ // Hide no-video download paragraph
255
+ showLinksFallback: function(){
256
+ if (this.linksFallback) { this.linksFallback.style.display = "block"; }
257
+ }
258
+ });
259
+
260
+ ////////////////////////////////////////////////////////////////////////////////
261
+ // Class Methods
262
+ // Functions that don't apply to individual videos.
263
+ ////////////////////////////////////////////////////////////////////////////////
264
+
265
+ // Combine Objects - Use "safe" to protect from overwriting existing items
266
+ VideoJS.merge = function(obj1, obj2, safe){
267
+ for (var attrname in obj2){
268
+ if (obj2.hasOwnProperty(attrname) && (!safe || !obj1.hasOwnProperty(attrname))) { obj1[attrname]=obj2[attrname]; }
269
+ }
270
+ return obj1;
271
+ };
272
+ VideoJS.extend = function(obj){ this.merge(this, obj, true); };
273
+
274
+ VideoJS.extend({
275
+ // Add VideoJS to all video tags with the video-js class when the DOM is ready
276
+ setupAllWhenReady: function(options){
277
+ // Options is stored globally, and added ot any new player on init
278
+ VideoJS.options = options;
279
+ VideoJS.DOMReady(VideoJS.setup);
280
+ },
281
+
282
+ // Run the supplied function when the DOM is ready
283
+ DOMReady: function(fn){
284
+ VideoJS.addToDOMReady(fn);
285
+ },
286
+
287
+ // Set up a specific video or array of video elements
288
+ // "video" can be:
289
+ // false, undefined, or "All": set up all videos with the video-js class
290
+ // A video tag ID or video tag element: set up one video and return one player
291
+ // An array of video tag elements/IDs: set up each and return an array of players
292
+ setup: function(videos, options){
293
+ var returnSingular = false,
294
+ playerList = [],
295
+ videoElement;
296
+
297
+ // If videos is undefined or "All", set up all videos with the video-js class
298
+ if (!videos || videos == "All") {
299
+ videos = VideoJS.getVideoJSTags();
300
+ // If videos is not an array, add to an array
301
+ } else if (typeof videos != 'object' || videos.nodeType == 1) {
302
+ videos = [videos];
303
+ returnSingular = true;
304
+ }
305
+
306
+ // Loop through videos and create players for them
307
+ for (var i=0; i<videos.length; i++) {
308
+ if (typeof videos[i] == 'string') {
309
+ videoElement = document.getElementById(videos[i]);
310
+ } else { // assume DOM object
311
+ videoElement = videos[i];
312
+ }
313
+ playerList.push(new VideoJS(videoElement, options));
314
+ }
315
+
316
+ // Return one or all depending on what was passed in
317
+ return (returnSingular) ? playerList[0] : playerList;
318
+ },
319
+
320
+ // Find video tags with the video-js class
321
+ getVideoJSTags: function() {
322
+ var videoTags = document.getElementsByTagName("video"),
323
+ videoJSTags = [], videoTag;
324
+
325
+ for (var i=0,j=videoTags.length; i<j; i++) {
326
+ videoTag = videoTags[i];
327
+ if (videoTag.className.indexOf("video-js") != -1) {
328
+ videoJSTags.push(videoTag);
329
+ }
330
+ }
331
+ return videoJSTags;
332
+ },
333
+
334
+ // Check if the browser supports video.
335
+ browserSupportsVideo: function() {
336
+ if (typeof VideoJS.videoSupport != "undefined") { return VideoJS.videoSupport; }
337
+ VideoJS.videoSupport = !!document.createElement('video').canPlayType;
338
+ return VideoJS.videoSupport;
339
+ },
340
+
341
+ getFlashVersion: function(){
342
+ // Cache Version
343
+ if (typeof VideoJS.flashVersion != "undefined") { return VideoJS.flashVersion; }
344
+ var version = 0, desc;
345
+ if (typeof navigator.plugins != "undefined" && typeof navigator.plugins["Shockwave Flash"] == "object") {
346
+ desc = navigator.plugins["Shockwave Flash"].description;
347
+ if (desc && !(typeof navigator.mimeTypes != "undefined" && navigator.mimeTypes["application/x-shockwave-flash"] && !navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin)) {
348
+ version = parseInt(desc.match(/^.*\s+([^\s]+)\.[^\s]+\s+[^\s]+$/)[1], 10);
349
+ }
350
+ } else if (typeof window.ActiveXObject != "undefined") {
351
+ try {
352
+ var testObject = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
353
+ if (testObject) {
354
+ version = parseInt(testObject.GetVariable("$version").match(/^[^\s]+\s(\d+)/)[1], 10);
355
+ }
356
+ }
357
+ catch(e) {}
358
+ }
359
+ VideoJS.flashVersion = version;
360
+ return VideoJS.flashVersion;
361
+ },
362
+
363
+ // Browser & Device Checks
364
+ isIE: function(){ return !+"\v1"; },
365
+ isIPad: function(){ return navigator.userAgent.match(/iPad/i) !== null; },
366
+ isIPhone: function(){ return navigator.userAgent.match(/iPhone/i) !== null; },
367
+ isIOS: function(){ return VideoJS.isIPhone() || VideoJS.isIPad(); },
368
+ iOSVersion: function() {
369
+ var match = navigator.userAgent.match(/OS (\d+)_/i);
370
+ if (match && match[1]) { return match[1]; }
371
+ },
372
+ isAndroid: function(){ return navigator.userAgent.match(/Android/i) !== null; },
373
+ androidVersion: function() {
374
+ var match = navigator.userAgent.match(/Android (\d+)\./i);
375
+ if (match && match[1]) { return match[1]; }
376
+ },
377
+
378
+ warnings: {
379
+ // Safari errors if you call functions on a video that hasn't loaded yet
380
+ videoNotReady: "Video is not ready yet (try playing the video first).",
381
+ // Getting a QUOTA_EXCEEDED_ERR when setting local storage occasionally
382
+ localStorageFull: "Local Storage is Full"
383
+ }
384
+ });
385
+
386
+ // Shim to make Video tag valid in IE
387
+ if(VideoJS.isIE()) { document.createElement("video"); }
388
+
389
+ // Expose to global
390
+ window.VideoJS = window._V_ = VideoJS;
391
+
392
+ /* HTML5 Player Type
393
+ ================================================================================ */
394
+ VideoJS.player.extend({
395
+ html5Supported: function(){
396
+ if (VideoJS.browserSupportsVideo() && this.canPlaySource()) {
397
+ return true;
398
+ } else {
399
+ return false;
400
+ }
401
+ },
402
+ html5Init: function(){
403
+ this.element = this.video;
404
+
405
+ this.fixPreloading(); // Support old browsers that used autobuffer
406
+ this.supportProgressEvents(); // Support browsers that don't use 'buffered'
407
+
408
+ // Set to stored volume OR 85%
409
+ this.volume((localStorage && localStorage.volume) || this.options.defaultVolume);
410
+
411
+ // Update interface for device needs
412
+ if (VideoJS.isIOS()) {
413
+ this.options.useBuiltInControls = true;
414
+ this.iOSInterface();
415
+ } else if (VideoJS.isAndroid()) {
416
+ this.options.useBuiltInControls = true;
417
+ this.androidInterface();
418
+ }
419
+
420
+ // Add VideoJS Controls
421
+ if (!this.options.useBuiltInControls) {
422
+ this.video.controls = false;
423
+
424
+ if (this.options.controlsBelow) { _V_.addClass(this.box, "vjs-controls-below"); }
425
+
426
+ // Make a click on th video act as a play button
427
+ this.activateElement(this.video, "playToggle");
428
+
429
+ // Build Interface
430
+ this.buildStylesCheckDiv(); // Used to check if style are loaded
431
+ this.buildAndActivatePoster();
432
+ this.buildBigPlayButton();
433
+ this.buildAndActivateSpinner();
434
+ this.buildAndActivateControlBar();
435
+ this.loadInterface(); // Show everything once styles are loaded
436
+ this.getSubtitles();
437
+ }
438
+ },
439
+ /* Source Managemet
440
+ ================================================================================ */
441
+ canPlaySource: function(){
442
+ // Cache Result
443
+ if (this.canPlaySourceResult) { return this.canPlaySourceResult; }
444
+ // Loop through sources and check if any can play
445
+ var children = this.video.children;
446
+ for (var i=0,j=children.length; i<j; i++) {
447
+ if (children[i].tagName.toUpperCase() == "SOURCE") {
448
+ var canPlay = this.video.canPlayType(children[i].type) || this.canPlayExt(children[i].src);
449
+ if (canPlay == "probably" || canPlay == "maybe") {
450
+ this.firstPlayableSource = children[i];
451
+ this.canPlaySourceResult = true;
452
+ return true;
453
+ }
454
+ }
455
+ }
456
+ this.canPlaySourceResult = false;
457
+ return false;
458
+ },
459
+ // Check if the extention is compatible, for when type won't work
460
+ canPlayExt: function(src){
461
+ if (!src) { return ""; }
462
+ var match = src.match(/\.([^\.]+)$/);
463
+ if (match && match[1]) {
464
+ var ext = match[1].toLowerCase();
465
+ // Android canPlayType doesn't work
466
+ if (VideoJS.isAndroid()) {
467
+ if (ext == "mp4" || ext == "m4v") { return "maybe"; }
468
+ // Allow Apple HTTP Streaming for iOS
469
+ } else if (VideoJS.isIOS()) {
470
+ if (ext == "m3u8") { return "maybe"; }
471
+ }
472
+ }
473
+ return "";
474
+ },
475
+ // Force the video source - Helps fix loading bugs in a handful of devices, like the iPad/iPhone poster bug
476
+ // And iPad/iPhone javascript include location bug. And Android type attribute bug
477
+ forceTheSource: function(){
478
+ this.video.src = this.firstPlayableSource.src; // From canPlaySource()
479
+ this.video.load();
480
+ },
481
+ /* Device Fixes
482
+ ================================================================================ */
483
+ // Support older browsers that used "autobuffer"
484
+ fixPreloading: function(){
485
+ if (typeof this.video.hasAttribute == "function" && this.video.hasAttribute("preload") && this.video.preload != "none") {
486
+ this.video.autobuffer = true; // Was a boolean
487
+ } else {
488
+ this.video.autobuffer = false;
489
+ this.video.preload = "none";
490
+ }
491
+ },
492
+
493
+ // Listen for Video Load Progress (currently does not if html file is local)
494
+ // Buffered does't work in all browsers, so watching progress as well
495
+ supportProgressEvents: function(e){
496
+ _V_.addListener(this.video, 'progress', this.playerOnVideoProgress.context(this));
497
+ },
498
+ playerOnVideoProgress: function(event){
499
+ this.setBufferedFromProgress(event);
500
+ },
501
+ setBufferedFromProgress: function(event){ // HTML5 Only
502
+ if(event.total > 0) {
503
+ var newBufferEnd = (event.loaded / event.total) * this.duration();
504
+ if (newBufferEnd > this.values.bufferEnd) { this.values.bufferEnd = newBufferEnd; }
505
+ }
506
+ },
507
+
508
+ iOSInterface: function(){
509
+ if(VideoJS.iOSVersion() < 4) { this.forceTheSource(); } // Fix loading issues
510
+ if(VideoJS.isIPad()) { // iPad could work with controlsBelow
511
+ this.buildAndActivateSpinner(); // Spinner still works well on iPad, since iPad doesn't have one
512
+ }
513
+ },
514
+
515
+ // Fix android specific quirks
516
+ // Use built-in controls, but add the big play button, since android doesn't have one.
517
+ androidInterface: function(){
518
+ this.forceTheSource(); // Fix loading issues
519
+ _V_.addListener(this.video, "click", function(){ this.play(); }); // Required to play
520
+ this.buildBigPlayButton(); // But don't activate the normal way. Pause doesn't work right on android.
521
+ _V_.addListener(this.bigPlayButton, "click", function(){ this.play(); }.context(this));
522
+ this.positionBox();
523
+ this.showBigPlayButtons();
524
+ },
525
+ /* Wait for styles (TODO: move to _V_)
526
+ ================================================================================ */
527
+ loadInterface: function(){
528
+ if(!this.stylesHaveLoaded()) {
529
+ // Don't want to create an endless loop either.
530
+ if (!this.positionRetries) { this.positionRetries = 1; }
531
+ if (this.positionRetries++ < 100) {
532
+ setTimeout(this.loadInterface.context(this),10);
533
+ return;
534
+ }
535
+ }
536
+ this.hideStylesCheckDiv();
537
+ this.showPoster();
538
+ if (this.video.paused !== false) { this.showBigPlayButtons(); }
539
+ if (this.options.controlsAtStart) { this.showControlBars(); }
540
+ this.positionAll();
541
+ },
542
+ /* Control Bar
543
+ ================================================================================ */
544
+ buildAndActivateControlBar: function(){
545
+ /* Creating this HTML
546
+ <div class="vjs-controls">
547
+ <div class="vjs-play-control">
548
+ <span></span>
549
+ </div>
550
+ <div class="vjs-progress-control">
551
+ <div class="vjs-progress-holder">
552
+ <div class="vjs-load-progress"></div>
553
+ <div class="vjs-play-progress"></div>
554
+ </div>
555
+ </div>
556
+ <div class="vjs-time-control">
557
+ <span class="vjs-current-time-display">00:00</span><span> / </span><span class="vjs-duration-display">00:00</span>
558
+ </div>
559
+ <div class="vjs-volume-control">
560
+ <div>
561
+ <span></span><span></span><span></span><span></span><span></span><span></span>
562
+ </div>
563
+ </div>
564
+ <div class="vjs-fullscreen-control">
565
+ <div>
566
+ <span></span><span></span><span></span><span></span>
567
+ </div>
568
+ </div>
569
+ </div>
570
+ */
571
+
572
+ // Create a div to hold the different controls
573
+ this.controls = _V_.createElement("div", { className: "vjs-controls" });
574
+ // Add the controls to the video's container
575
+ this.box.appendChild(this.controls);
576
+ this.activateElement(this.controls, "controlBar");
577
+ this.activateElement(this.controls, "mouseOverVideoReporter");
578
+
579
+ // Build the play control
580
+ this.playControl = _V_.createElement("div", { className: "vjs-play-control", innerHTML: "<span></span>" });
581
+ this.controls.appendChild(this.playControl);
582
+ this.activateElement(this.playControl, "playToggle");
583
+
584
+ // Build the progress control
585
+ this.progressControl = _V_.createElement("div", { className: "vjs-progress-control" });
586
+ this.controls.appendChild(this.progressControl);
587
+
588
+ // Create a holder for the progress bars
589
+ this.progressHolder = _V_.createElement("div", { className: "vjs-progress-holder" });
590
+ this.progressControl.appendChild(this.progressHolder);
591
+ this.activateElement(this.progressHolder, "currentTimeScrubber");
592
+
593
+ // Create the loading progress display
594
+ this.loadProgressBar = _V_.createElement("div", { className: "vjs-load-progress" });
595
+ this.progressHolder.appendChild(this.loadProgressBar);
596
+ this.activateElement(this.loadProgressBar, "loadProgressBar");
597
+
598
+ // Create the playing progress display
599
+ this.playProgressBar = _V_.createElement("div", { className: "vjs-play-progress" });
600
+ this.progressHolder.appendChild(this.playProgressBar);
601
+ this.activateElement(this.playProgressBar, "playProgressBar");
602
+
603
+ // Create the progress time display (00:00 / 00:00)
604
+ this.timeControl = _V_.createElement("div", { className: "vjs-time-control" });
605
+ this.controls.appendChild(this.timeControl);
606
+
607
+ // Create the current play time display
608
+ this.currentTimeDisplay = _V_.createElement("span", { className: "vjs-current-time-display", innerHTML: "00:00" });
609
+ this.timeControl.appendChild(this.currentTimeDisplay);
610
+ this.activateElement(this.currentTimeDisplay, "currentTimeDisplay");
611
+
612
+ // Add time separator
613
+ this.timeSeparator = _V_.createElement("span", { innerHTML: " / " });
614
+ this.timeControl.appendChild(this.timeSeparator);
615
+
616
+ // Create the total duration display
617
+ this.durationDisplay = _V_.createElement("span", { className: "vjs-duration-display", innerHTML: "00:00" });
618
+ this.timeControl.appendChild(this.durationDisplay);
619
+ this.activateElement(this.durationDisplay, "durationDisplay");
620
+
621
+ // Create the volumne control
622
+ this.volumeControl = _V_.createElement("div", {
623
+ className: "vjs-volume-control",
624
+ innerHTML: "<div><span></span><span></span><span></span><span></span><span></span><span></span></div>"
625
+ });
626
+ this.controls.appendChild(this.volumeControl);
627
+ this.activateElement(this.volumeControl, "volumeScrubber");
628
+
629
+ this.volumeDisplay = this.volumeControl.children[0];
630
+ this.activateElement(this.volumeDisplay, "volumeDisplay");
631
+
632
+ // Crete the fullscreen control
633
+ this.fullscreenControl = _V_.createElement("div", {
634
+ className: "vjs-fullscreen-control",
635
+ innerHTML: "<div><span></span><span></span><span></span><span></span></div>"
636
+ });
637
+ this.controls.appendChild(this.fullscreenControl);
638
+ this.activateElement(this.fullscreenControl, "fullscreenToggle");
639
+ },
640
+ /* Poster Image
641
+ ================================================================================ */
642
+ buildAndActivatePoster: function(){
643
+ this.updatePosterSource();
644
+ if (this.video.poster) {
645
+ this.poster = document.createElement("img");
646
+ // Add poster to video box
647
+ this.box.appendChild(this.poster);
648
+
649
+ // Add poster image data
650
+ this.poster.src = this.video.poster;
651
+ // Add poster styles
652
+ this.poster.className = "vjs-poster";
653
+ this.activateElement(this.poster, "poster");
654
+ } else {
655
+ this.poster = false;
656
+ }
657
+ },
658
+ /* Big Play Button
659
+ ================================================================================ */
660
+ buildBigPlayButton: function(){
661
+ /* Creating this HTML
662
+ <div class="vjs-big-play-button"><span></span></div>
663
+ */
664
+ this.bigPlayButton = _V_.createElement("div", {
665
+ className: "vjs-big-play-button",
666
+ innerHTML: "<span></span>"
667
+ });
668
+ this.box.appendChild(this.bigPlayButton);
669
+ this.activateElement(this.bigPlayButton, "bigPlayButton");
670
+ },
671
+ /* Spinner (Loading)
672
+ ================================================================================ */
673
+ buildAndActivateSpinner: function(){
674
+ this.spinner = _V_.createElement("div", {
675
+ className: "vjs-spinner",
676
+ innerHTML: "<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>"
677
+ });
678
+ this.box.appendChild(this.spinner);
679
+ this.activateElement(this.spinner, "spinner");
680
+ },
681
+ /* Styles Check - Check if styles are loaded (move ot _V_)
682
+ ================================================================================ */
683
+ // Sometimes the CSS styles haven't been applied to the controls yet
684
+ // when we're trying to calculate the height and position them correctly.
685
+ // This causes a flicker where the controls are out of place.
686
+ buildStylesCheckDiv: function(){
687
+ this.stylesCheckDiv = _V_.createElement("div", { className: "vjs-styles-check" });
688
+ this.stylesCheckDiv.style.position = "absolute";
689
+ this.box.appendChild(this.stylesCheckDiv);
690
+ },
691
+ hideStylesCheckDiv: function(){ this.stylesCheckDiv.style.display = "none"; },
692
+ stylesHaveLoaded: function(){
693
+ if (this.stylesCheckDiv.offsetHeight != 5) {
694
+ return false;
695
+ } else {
696
+ return true;
697
+ }
698
+ },
699
+ /* VideoJS Box - Holds all elements
700
+ ================================================================================ */
701
+ positionAll: function(){
702
+ this.positionBox();
703
+ this.positionControlBars();
704
+ this.positionPoster();
705
+ },
706
+ positionBox: function(){
707
+ // Set width based on fullscreen or not.
708
+ if (this.videoIsFullScreen) {
709
+ this.box.style.width = "";
710
+ this.element.style.height="";
711
+ if (this.options.controlsBelow) {
712
+ this.box.style.height = "";
713
+ this.element.style.height = (this.box.offsetHeight - this.controls.offsetHeight) + "px";
714
+ }
715
+ } else {
716
+ this.box.style.width = this.width() + "px";
717
+ this.element.style.height=this.height()+"px";
718
+ if (this.options.controlsBelow) {
719
+ this.element.style.height = "";
720
+ // this.box.style.height = this.video.offsetHeight + this.controls.offsetHeight + "px";
721
+ }
722
+ }
723
+ },
724
+ /* Subtitles
725
+ ================================================================================ */
726
+ getSubtitles: function(){
727
+ var tracks = this.video.getElementsByTagName("TRACK");
728
+ for (var i=0,j=tracks.length; i<j; i++) {
729
+ if (tracks[i].getAttribute("kind") == "subtitles" && tracks[i].getAttribute("src")) {
730
+ this.subtitlesSource = tracks[i].getAttribute("src");
731
+ this.loadSubtitles();
732
+ this.buildSubtitles();
733
+ }
734
+ }
735
+ },
736
+ loadSubtitles: function() { _V_.get(this.subtitlesSource, this.parseSubtitles.context(this)); },
737
+ parseSubtitles: function(subText) {
738
+ var lines = subText.split("\n"),
739
+ line = "",
740
+ subtitle, time, text;
741
+ this.subtitles = [];
742
+ this.currentSubtitle = false;
743
+ this.lastSubtitleIndex = 0;
744
+
745
+ for (var i=0; i<lines.length; i++) {
746
+ line = _V_.trim(lines[i]); // Trim whitespace and linebreaks
747
+ if (line) { // Loop until a line with content
748
+
749
+ // First line - Number
750
+ subtitle = {
751
+ id: line, // Subtitle Number
752
+ index: this.subtitles.length // Position in Array
753
+ };
754
+
755
+ // Second line - Time
756
+ line = _V_.trim(lines[++i]);
757
+ time = line.split(" --> ");
758
+ subtitle.start = this.parseSubtitleTime(time[0]);
759
+ subtitle.end = this.parseSubtitleTime(time[1]);
760
+
761
+ // Additional lines - Subtitle Text
762
+ text = [];
763
+ for (var j=i; j<lines.length; j++) { // Loop until a blank line or end of lines
764
+ line = _V_.trim(lines[++i]);
765
+ if (!line) { break; }
766
+ text.push(line);
767
+ }
768
+ subtitle.text = text.join('<br/>');
769
+
770
+ // Add this subtitle
771
+ this.subtitles.push(subtitle);
772
+ }
773
+ }
774
+ },
775
+
776
+ parseSubtitleTime: function(timeText) {
777
+ var parts = timeText.split(':'),
778
+ time = 0;
779
+ // hours => seconds
780
+ time += parseFloat(parts[0])*60*60;
781
+ // minutes => seconds
782
+ time += parseFloat(parts[1])*60;
783
+ // get seconds
784
+ var seconds = parts[2].split(/\.|,/); // Either . or ,
785
+ time += parseFloat(seconds[0]);
786
+ // add miliseconds
787
+ ms = parseFloat(seconds[1]);
788
+ if (ms) { time += ms/1000; }
789
+ return time;
790
+ },
791
+
792
+ buildSubtitles: function(){
793
+ /* Creating this HTML
794
+ <div class="vjs-subtitles"></div>
795
+ */
796
+ this.subtitlesDisplay = _V_.createElement("div", { className: 'vjs-subtitles' });
797
+ this.box.appendChild(this.subtitlesDisplay);
798
+ this.activateElement(this.subtitlesDisplay, "subtitlesDisplay");
799
+ },
800
+
801
+ /* Player API - Translate functionality from player to video
802
+ ================================================================================ */
803
+ addVideoListener: function(type, fn){ _V_.addListener(this.video, type, fn.rEvtContext(this)); },
804
+
805
+ play: function(){
806
+ this.video.play();
807
+ return this;
808
+ },
809
+ onPlay: function(fn){ this.addVideoListener("play", fn); return this; },
810
+
811
+ pause: function(){
812
+ this.video.pause();
813
+ return this;
814
+ },
815
+ onPause: function(fn){ this.addVideoListener("pause", fn); return this; },
816
+ paused: function() { return this.video.paused; },
817
+
818
+ currentTime: function(seconds){
819
+ if (seconds !== undefined) {
820
+ try { this.video.currentTime = seconds; }
821
+ catch(e) { this.warning(VideoJS.warnings.videoNotReady); }
822
+ this.values.currentTime = seconds;
823
+ return this;
824
+ }
825
+ return this.video.currentTime;
826
+ },
827
+ onCurrentTimeUpdate: function(fn){
828
+ this.currentTimeListeners.push(fn);
829
+ },
830
+
831
+ duration: function(){
832
+ return this.video.duration;
833
+ },
834
+
835
+ buffered: function(){
836
+ // Storing values allows them be overridden by setBufferedFromProgress
837
+ if (this.values.bufferStart === undefined) {
838
+ this.values.bufferStart = 0;
839
+ this.values.bufferEnd = 0;
840
+ }
841
+ if (this.video.buffered && this.video.buffered.length > 0) {
842
+ var newEnd = this.video.buffered.end(0);
843
+ if (newEnd > this.values.bufferEnd) { this.values.bufferEnd = newEnd; }
844
+ }
845
+ return [this.values.bufferStart, this.values.bufferEnd];
846
+ },
847
+
848
+ volume: function(percentAsDecimal){
849
+ if (percentAsDecimal !== undefined) {
850
+ // Force value to between 0 and 1
851
+ this.values.volume = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
852
+ this.video.volume = this.values.volume;
853
+ this.setLocalStorage("volume", this.values.volume);
854
+ return this;
855
+ }
856
+ if (this.values.volume) { return this.values.volume; }
857
+ return this.video.volume;
858
+ },
859
+ onVolumeChange: function(fn){ _V_.addListener(this.video, 'volumechange', fn.rEvtContext(this)); },
860
+
861
+ width: function(width){
862
+ if (width !== undefined) {
863
+ this.video.width = width; // Not using style so it can be overridden on fullscreen.
864
+ this.box.style.width = width+"px";
865
+ this.triggerResizeListeners();
866
+ return this;
867
+ }
868
+ return this.video.offsetWidth;
869
+ },
870
+ height: function(height){
871
+ if (height !== undefined) {
872
+ this.video.height = height;
873
+ this.box.style.height = height+"px";
874
+ this.triggerResizeListeners();
875
+ return this;
876
+ }
877
+ return this.video.offsetHeight;
878
+ },
879
+
880
+ supportsFullScreen: function(){
881
+ if(typeof this.video.webkitEnterFullScreen == 'function') {
882
+ // Seems to be broken in Chromium/Chrome
883
+ if (!navigator.userAgent.match("Chrome") && !navigator.userAgent.match("Mac OS X 10.5")) {
884
+ return true;
885
+ }
886
+ }
887
+ return false;
888
+ },
889
+
890
+ html5EnterNativeFullScreen: function(){
891
+ try {
892
+ this.video.webkitEnterFullScreen();
893
+ } catch (e) {
894
+ if (e.code == 11) { this.warning(VideoJS.warnings.videoNotReady); }
895
+ }
896
+ return this;
897
+ },
898
+
899
+ // Turn on fullscreen (window) mode
900
+ // Real fullscreen isn't available in browsers quite yet.
901
+ enterFullScreen: function(){
902
+ if (this.supportsFullScreen()) {
903
+ this.html5EnterNativeFullScreen();
904
+ } else {
905
+ this.enterFullWindow();
906
+ }
907
+ },
908
+
909
+ exitFullScreen: function(){
910
+ if (this.supportsFullScreen()) {
911
+ // Shouldn't be called
912
+ } else {
913
+ this.exitFullWindow();
914
+ }
915
+ },
916
+
917
+ enterFullWindow: function(){
918
+ this.videoIsFullScreen = true;
919
+ // Storing original doc overflow value to return to when fullscreen is off
920
+ this.docOrigOverflow = document.documentElement.style.overflow;
921
+ // Add listener for esc key to exit fullscreen
922
+ _V_.addListener(document, "keydown", this.fullscreenOnEscKey.rEvtContext(this));
923
+ // Add listener for a window resize
924
+ _V_.addListener(window, "resize", this.fullscreenOnWindowResize.rEvtContext(this));
925
+ // Hide any scroll bars
926
+ document.documentElement.style.overflow = 'hidden';
927
+ // Apply fullscreen styles
928
+ _V_.addClass(this.box, "vjs-fullscreen");
929
+ // Resize the box, controller, and poster
930
+ this.positionAll();
931
+ },
932
+
933
+ // Turn off fullscreen (window) mode
934
+ exitFullWindow: function(){
935
+ this.videoIsFullScreen = false;
936
+ document.removeEventListener("keydown", this.fullscreenOnEscKey, false);
937
+ window.removeEventListener("resize", this.fullscreenOnWindowResize, false);
938
+ // Unhide scroll bars.
939
+ document.documentElement.style.overflow = this.docOrigOverflow;
940
+ // Remove fullscreen styles
941
+ _V_.removeClass(this.box, "vjs-fullscreen");
942
+ // Resize the box, controller, and poster to original sizes
943
+ this.positionAll();
944
+ },
945
+
946
+ onError: function(fn){ this.addVideoListener("error", fn); return this; },
947
+ onEnded: function(fn){
948
+ this.addVideoListener("ended", fn); return this;
949
+ }
950
+ });
951
+
952
+ ////////////////////////////////////////////////////////////////////////////////
953
+ // Element Behaviors
954
+ // Tell elements how to act or react
955
+ ////////////////////////////////////////////////////////////////////////////////
956
+
957
+ /* Player Behaviors - How VideoJS reacts to what the video is doing.
958
+ ================================================================================ */
959
+ VideoJS.player.newBehavior("player", function(player){
960
+ this.onError(this.playerOnVideoError);
961
+ // Listen for when the video is played
962
+ this.onPlay(this.playerOnVideoPlay);
963
+ this.onPlay(this.trackCurrentTime);
964
+ // Listen for when the video is paused
965
+ this.onPause(this.playerOnVideoPause);
966
+ this.onPause(this.stopTrackingCurrentTime);
967
+ // Listen for when the video ends
968
+ this.onEnded(this.playerOnVideoEnded);
969
+ // Set interval for load progress using buffer watching method
970
+ // this.trackCurrentTime();
971
+ this.trackBuffered();
972
+ // Buffer Full
973
+ this.onBufferedUpdate(this.isBufferFull);
974
+ },{
975
+ playerOnVideoError: function(event){
976
+ this.log(event);
977
+ this.log(this.video.error);
978
+ },
979
+ playerOnVideoPlay: function(event){ this.hasPlayed = true; },
980
+ playerOnVideoPause: function(event){},
981
+ playerOnVideoEnded: function(event){
982
+ this.currentTime(0);
983
+ this.pause();
984
+ },
985
+
986
+ /* Load Tracking -------------------------------------------------------------- */
987
+ // Buffer watching method for load progress.
988
+ // Used for browsers that don't support the progress event
989
+ trackBuffered: function(){
990
+ this.bufferedInterval = setInterval(this.triggerBufferedListeners.context(this), 500);
991
+ },
992
+ stopTrackingBuffered: function(){ clearInterval(this.bufferedInterval); },
993
+ bufferedListeners: [],
994
+ onBufferedUpdate: function(fn){
995
+ this.bufferedListeners.push(fn);
996
+ },
997
+ triggerBufferedListeners: function(){
998
+ this.isBufferFull();
999
+ this.each(this.bufferedListeners, function(listener){
1000
+ (listener.context(this))();
1001
+ });
1002
+ },
1003
+ isBufferFull: function(){
1004
+ if (this.bufferedPercent() == 1) { this.stopTrackingBuffered(); }
1005
+ },
1006
+
1007
+ /* Time Tracking -------------------------------------------------------------- */
1008
+ trackCurrentTime: function(){
1009
+ if (this.currentTimeInterval) { clearInterval(this.currentTimeInterval); }
1010
+ this.currentTimeInterval = setInterval(this.triggerCurrentTimeListeners.context(this), 100); // 42 = 24 fps
1011
+ this.trackingCurrentTime = true;
1012
+ },
1013
+ // Turn off play progress tracking (when paused or dragging)
1014
+ stopTrackingCurrentTime: function(){
1015
+ clearInterval(this.currentTimeInterval);
1016
+ this.trackingCurrentTime = false;
1017
+ },
1018
+ currentTimeListeners: [],
1019
+ // onCurrentTimeUpdate is in API section now
1020
+ triggerCurrentTimeListeners: function(late, newTime){ // FF passes milliseconds late as the first argument
1021
+ this.each(this.currentTimeListeners, function(listener){
1022
+ (listener.context(this))(newTime || this.currentTime());
1023
+ });
1024
+ },
1025
+
1026
+ /* Resize Tracking -------------------------------------------------------------- */
1027
+ resizeListeners: [],
1028
+ onResize: function(fn){
1029
+ this.resizeListeners.push(fn);
1030
+ },
1031
+ // Trigger anywhere the video/box size is changed.
1032
+ triggerResizeListeners: function(){
1033
+ this.each(this.resizeListeners, function(listener){
1034
+ (listener.context(this))();
1035
+ });
1036
+ }
1037
+ }
1038
+ );
1039
+ /* Mouse Over Video Reporter Behaviors - i.e. Controls hiding based on mouse location
1040
+ ================================================================================ */
1041
+ VideoJS.player.newBehavior("mouseOverVideoReporter", function(element){
1042
+ // Listen for the mouse move the video. Used to reveal the controller.
1043
+ _V_.addListener(element, "mousemove", this.mouseOverVideoReporterOnMouseMove.context(this));
1044
+ // Listen for the mouse moving out of the video. Used to hide the controller.
1045
+ _V_.addListener(element, "mouseout", this.mouseOverVideoReporterOnMouseOut.context(this));
1046
+ },{
1047
+ mouseOverVideoReporterOnMouseMove: function(){
1048
+ this.showControlBars();
1049
+ clearInterval(this.mouseMoveTimeout);
1050
+ this.mouseMoveTimeout = setTimeout(this.hideControlBars.context(this), 4000);
1051
+ },
1052
+ mouseOverVideoReporterOnMouseOut: function(event){
1053
+ // Prevent flicker by making sure mouse hasn't left the video
1054
+ var parent = event.relatedTarget;
1055
+ while (parent && parent !== this.box) {
1056
+ parent = parent.parentNode;
1057
+ }
1058
+ if (parent !== this.box) {
1059
+ this.hideControlBars();
1060
+ }
1061
+ }
1062
+ }
1063
+ );
1064
+ /* Mouse Over Video Reporter Behaviors - i.e. Controls hiding based on mouse location
1065
+ ================================================================================ */
1066
+ VideoJS.player.newBehavior("box", function(element){
1067
+ this.positionBox();
1068
+ _V_.addClass(element, "vjs-paused");
1069
+ this.activateElement(element, "mouseOverVideoReporter");
1070
+ this.onPlay(this.boxOnVideoPlay);
1071
+ this.onPause(this.boxOnVideoPause);
1072
+ },{
1073
+ boxOnVideoPlay: function(){
1074
+ _V_.removeClass(this.box, "vjs-paused");
1075
+ _V_.addClass(this.box, "vjs-playing");
1076
+ },
1077
+ boxOnVideoPause: function(){
1078
+ _V_.removeClass(this.box, "vjs-playing");
1079
+ _V_.addClass(this.box, "vjs-paused");
1080
+ }
1081
+ }
1082
+ );
1083
+ /* Poster Image Overlay
1084
+ ================================================================================ */
1085
+ VideoJS.player.newBehavior("poster", function(element){
1086
+ this.activateElement(element, "mouseOverVideoReporter");
1087
+ this.activateElement(element, "playButton");
1088
+ this.onPlay(this.hidePoster);
1089
+ this.onEnded(this.showPoster);
1090
+ this.onResize(this.positionPoster);
1091
+ },{
1092
+ showPoster: function(){
1093
+ if (!this.poster) { return; }
1094
+ this.poster.style.display = "block";
1095
+ this.positionPoster();
1096
+ },
1097
+ positionPoster: function(){
1098
+ // Only if the poster is visible
1099
+ if (!this.poster || this.poster.style.display == 'none') { return; }
1100
+ this.poster.style.height = this.height() + "px"; // Need incase controlsBelow
1101
+ this.poster.style.width = this.width() + "px"; // Could probably do 100% of box
1102
+ },
1103
+ hidePoster: function(){
1104
+ if (!this.poster) { return; }
1105
+ this.poster.style.display = "none";
1106
+ },
1107
+ // Update poster source from attribute or fallback image
1108
+ // iPad breaks if you include a poster attribute, so this fixes that
1109
+ updatePosterSource: function(){
1110
+ if (!this.video.poster) {
1111
+ var images = this.video.getElementsByTagName("img");
1112
+ if (images.length > 0) { this.video.poster = images[0].src; }
1113
+ }
1114
+ }
1115
+ }
1116
+ );
1117
+ /* Control Bar Behaviors
1118
+ ================================================================================ */
1119
+ VideoJS.player.newBehavior("controlBar", function(element){
1120
+ if (!this.controlBars) {
1121
+ this.controlBars = [];
1122
+ this.onResize(this.positionControlBars);
1123
+ }
1124
+ this.controlBars.push(element);
1125
+ _V_.addListener(element, "mousemove", this.onControlBarsMouseMove.context(this));
1126
+ _V_.addListener(element, "mouseout", this.onControlBarsMouseOut.context(this));
1127
+ },{
1128
+ showControlBars: function(){
1129
+ if (!this.options.controlsAtStart && !this.hasPlayed) { return; }
1130
+ this.each(this.controlBars, function(bar){
1131
+ bar.style.display = "block";
1132
+ });
1133
+ },
1134
+ // Place controller relative to the video's position (now just resizing bars)
1135
+ positionControlBars: function(){
1136
+ this.updatePlayProgressBars();
1137
+ this.updateLoadProgressBars();
1138
+ },
1139
+ hideControlBars: function(){
1140
+ if (this.options.controlsHiding && !this.mouseIsOverControls) {
1141
+ this.each(this.controlBars, function(bar){
1142
+ bar.style.display = "none";
1143
+ });
1144
+ }
1145
+ },
1146
+ // Block controls from hiding when mouse is over them.
1147
+ onControlBarsMouseMove: function(){ this.mouseIsOverControls = true; },
1148
+ onControlBarsMouseOut: function(event){
1149
+ this.mouseIsOverControls = false;
1150
+ }
1151
+ }
1152
+ );
1153
+ /* PlayToggle, PlayButton, PauseButton Behaviors
1154
+ ================================================================================ */
1155
+ // Play Toggle
1156
+ VideoJS.player.newBehavior("playToggle", function(element){
1157
+ if (!this.elements.playToggles) {
1158
+ this.elements.playToggles = [];
1159
+ this.onPlay(this.playTogglesOnPlay);
1160
+ this.onPause(this.playTogglesOnPause);
1161
+ }
1162
+ this.elements.playToggles.push(element);
1163
+ _V_.addListener(element, "click", this.onPlayToggleClick.context(this));
1164
+ },{
1165
+ onPlayToggleClick: function(event){
1166
+ if (this.paused()) {
1167
+ this.play();
1168
+ } else {
1169
+ this.pause();
1170
+ }
1171
+ },
1172
+ playTogglesOnPlay: function(event){
1173
+ this.each(this.elements.playToggles, function(toggle){
1174
+ _V_.removeClass(toggle, "vjs-paused");
1175
+ _V_.addClass(toggle, "vjs-playing");
1176
+ });
1177
+ },
1178
+ playTogglesOnPause: function(event){
1179
+ this.each(this.elements.playToggles, function(toggle){
1180
+ _V_.removeClass(toggle, "vjs-playing");
1181
+ _V_.addClass(toggle, "vjs-paused");
1182
+ });
1183
+ }
1184
+ }
1185
+ );
1186
+ // Play
1187
+ VideoJS.player.newBehavior("playButton", function(element){
1188
+ _V_.addListener(element, "click", this.onPlayButtonClick.context(this));
1189
+ },{
1190
+ onPlayButtonClick: function(event){ this.play(); }
1191
+ }
1192
+ );
1193
+ // Pause
1194
+ VideoJS.player.newBehavior("pauseButton", function(element){
1195
+ _V_.addListener(element, "click", this.onPauseButtonClick.context(this));
1196
+ },{
1197
+ onPauseButtonClick: function(event){ this.pause(); }
1198
+ }
1199
+ );
1200
+ /* Play Progress Bar Behaviors
1201
+ ================================================================================ */
1202
+ VideoJS.player.newBehavior("playProgressBar", function(element){
1203
+ if (!this.playProgressBars) {
1204
+ this.playProgressBars = [];
1205
+ this.onCurrentTimeUpdate(this.updatePlayProgressBars);
1206
+ }
1207
+ this.playProgressBars.push(element);
1208
+ },{
1209
+ // Ajust the play progress bar's width based on the current play time
1210
+ updatePlayProgressBars: function(newTime){
1211
+ var progress = (newTime !== undefined) ? newTime / this.duration() : this.currentTime() / this.duration();
1212
+ if (isNaN(progress)) { progress = 0; }
1213
+ this.each(this.playProgressBars, function(bar){
1214
+ if (bar.style) { bar.style.width = _V_.round(progress * 100, 2) + "%"; }
1215
+ });
1216
+ }
1217
+ }
1218
+ );
1219
+ /* Load Progress Bar Behaviors
1220
+ ================================================================================ */
1221
+ VideoJS.player.newBehavior("loadProgressBar", function(element){
1222
+ if (!this.loadProgressBars) { this.loadProgressBars = []; }
1223
+ this.loadProgressBars.push(element);
1224
+ this.onBufferedUpdate(this.updateLoadProgressBars);
1225
+ },{
1226
+ updateLoadProgressBars: function(){
1227
+ this.each(this.loadProgressBars, function(bar){
1228
+ if (bar.style) { bar.style.width = _V_.round(this.bufferedPercent() * 100, 2) + "%"; }
1229
+ });
1230
+ }
1231
+ }
1232
+ );
1233
+
1234
+ /* Current Time Display Behaviors
1235
+ ================================================================================ */
1236
+ VideoJS.player.newBehavior("currentTimeDisplay", function(element){
1237
+ if (!this.currentTimeDisplays) {
1238
+ this.currentTimeDisplays = [];
1239
+ this.onCurrentTimeUpdate(this.updateCurrentTimeDisplays);
1240
+ }
1241
+ this.currentTimeDisplays.push(element);
1242
+ },{
1243
+ // Update the displayed time (00:00)
1244
+ updateCurrentTimeDisplays: function(newTime){
1245
+ if (!this.currentTimeDisplays) { return; }
1246
+ // Allows for smooth scrubbing, when player can't keep up.
1247
+ var time = (newTime) ? newTime : this.currentTime();
1248
+ this.each(this.currentTimeDisplays, function(dis){
1249
+ dis.innerHTML = _V_.formatTime(time);
1250
+ });
1251
+ }
1252
+ }
1253
+ );
1254
+
1255
+ /* Duration Display Behaviors
1256
+ ================================================================================ */
1257
+ VideoJS.player.newBehavior("durationDisplay", function(element){
1258
+ if (!this.durationDisplays) {
1259
+ this.durationDisplays = [];
1260
+ this.onCurrentTimeUpdate(this.updateDurationDisplays);
1261
+ }
1262
+ this.durationDisplays.push(element);
1263
+ },{
1264
+ updateDurationDisplays: function(){
1265
+ if (!this.durationDisplays) { return; }
1266
+ this.each(this.durationDisplays, function(dis){
1267
+ if (this.duration()) { dis.innerHTML = _V_.formatTime(this.duration()); }
1268
+ });
1269
+ }
1270
+ }
1271
+ );
1272
+
1273
+ /* Current Time Scrubber Behaviors
1274
+ ================================================================================ */
1275
+ VideoJS.player.newBehavior("currentTimeScrubber", function(element){
1276
+ _V_.addListener(element, "mousedown", this.onCurrentTimeScrubberMouseDown.rEvtContext(this));
1277
+ },{
1278
+ // Adjust the play position when the user drags on the progress bar
1279
+ onCurrentTimeScrubberMouseDown: function(event, scrubber){
1280
+ event.preventDefault();
1281
+ this.currentScrubber = scrubber;
1282
+
1283
+ this.stopTrackingCurrentTime(); // Allows for smooth scrubbing
1284
+
1285
+ this.videoWasPlaying = !this.paused();
1286
+ this.pause();
1287
+
1288
+ _V_.blockTextSelection();
1289
+ this.setCurrentTimeWithScrubber(event);
1290
+ _V_.addListener(document, "mousemove", this.onCurrentTimeScrubberMouseMove.rEvtContext(this));
1291
+ _V_.addListener(document, "mouseup", this.onCurrentTimeScrubberMouseUp.rEvtContext(this));
1292
+ },
1293
+ onCurrentTimeScrubberMouseMove: function(event){ // Removeable
1294
+ this.setCurrentTimeWithScrubber(event);
1295
+ },
1296
+ onCurrentTimeScrubberMouseUp: function(event){ // Removeable
1297
+ _V_.unblockTextSelection();
1298
+ document.removeEventListener("mousemove", this.onCurrentTimeScrubberMouseMove, false);
1299
+ document.removeEventListener("mouseup", this.onCurrentTimeScrubberMouseUp, false);
1300
+ if (this.videoWasPlaying) {
1301
+ this.play();
1302
+ this.trackCurrentTime();
1303
+ }
1304
+ },
1305
+ setCurrentTimeWithScrubber: function(event){
1306
+ var newProgress = _V_.getRelativePosition(event.pageX, this.currentScrubber);
1307
+ var newTime = newProgress * this.duration();
1308
+ this.triggerCurrentTimeListeners(0, newTime); // Allows for smooth scrubbing
1309
+ // Don't let video end while scrubbing.
1310
+ if (newTime == this.duration()) { newTime = newTime - 0.1; }
1311
+ this.currentTime(newTime);
1312
+ }
1313
+ }
1314
+ );
1315
+ /* Volume Display Behaviors
1316
+ ================================================================================ */
1317
+ VideoJS.player.newBehavior("volumeDisplay", function(element){
1318
+ if (!this.volumeDisplays) {
1319
+ this.volumeDisplays = [];
1320
+ this.onVolumeChange(this.updateVolumeDisplays);
1321
+ }
1322
+ this.volumeDisplays.push(element);
1323
+ this.updateVolumeDisplay(element); // Set the display to the initial volume
1324
+ },{
1325
+ // Update the volume control display
1326
+ // Unique to these default controls. Uses borders to create the look of bars.
1327
+ updateVolumeDisplays: function(){
1328
+ if (!this.volumeDisplays) { return; }
1329
+ this.each(this.volumeDisplays, function(dis){
1330
+ this.updateVolumeDisplay(dis);
1331
+ });
1332
+ },
1333
+ updateVolumeDisplay: function(display){
1334
+ var volNum = Math.ceil(this.volume() * 6);
1335
+ this.each(display.children, function(child, num){
1336
+ if (num < volNum) {
1337
+ _V_.addClass(child, "vjs-volume-level-on");
1338
+ } else {
1339
+ _V_.removeClass(child, "vjs-volume-level-on");
1340
+ }
1341
+ });
1342
+ }
1343
+ }
1344
+ );
1345
+ /* Volume Scrubber Behaviors
1346
+ ================================================================================ */
1347
+ VideoJS.player.newBehavior("volumeScrubber", function(element){
1348
+ _V_.addListener(element, "mousedown", this.onVolumeScrubberMouseDown.rEvtContext(this));
1349
+ },{
1350
+ // Adjust the volume when the user drags on the volume control
1351
+ onVolumeScrubberMouseDown: function(event, scrubber){
1352
+ // event.preventDefault();
1353
+ _V_.blockTextSelection();
1354
+ this.currentScrubber = scrubber;
1355
+ this.setVolumeWithScrubber(event);
1356
+ _V_.addListener(document, "mousemove", this.onVolumeScrubberMouseMove.rEvtContext(this));
1357
+ _V_.addListener(document, "mouseup", this.onVolumeScrubberMouseUp.rEvtContext(this));
1358
+ },
1359
+ onVolumeScrubberMouseMove: function(event){
1360
+ this.setVolumeWithScrubber(event);
1361
+ },
1362
+ onVolumeScrubberMouseUp: function(event){
1363
+ this.setVolumeWithScrubber(event);
1364
+ _V_.unblockTextSelection();
1365
+ document.removeEventListener("mousemove", this.onVolumeScrubberMouseMove, false);
1366
+ document.removeEventListener("mouseup", this.onVolumeScrubberMouseUp, false);
1367
+ },
1368
+ setVolumeWithScrubber: function(event){
1369
+ var newVol = _V_.getRelativePosition(event.pageX, this.currentScrubber);
1370
+ this.volume(newVol);
1371
+ }
1372
+ }
1373
+ );
1374
+ /* Fullscreen Toggle Behaviors
1375
+ ================================================================================ */
1376
+ VideoJS.player.newBehavior("fullscreenToggle", function(element){
1377
+ _V_.addListener(element, "click", this.onFullscreenToggleClick.context(this));
1378
+ },{
1379
+ // When the user clicks on the fullscreen button, update fullscreen setting
1380
+ onFullscreenToggleClick: function(event){
1381
+ if (!this.videoIsFullScreen) {
1382
+ this.enterFullScreen();
1383
+ } else {
1384
+ this.exitFullScreen();
1385
+ }
1386
+ },
1387
+
1388
+ fullscreenOnWindowResize: function(event){ // Removeable
1389
+ this.positionControlBars();
1390
+ },
1391
+ // Create listener for esc key while in full screen mode
1392
+ fullscreenOnEscKey: function(event){ // Removeable
1393
+ if (event.keyCode == 27) {
1394
+ this.exitFullScreen();
1395
+ }
1396
+ }
1397
+ }
1398
+ );
1399
+ /* Big Play Button Behaviors
1400
+ ================================================================================ */
1401
+ VideoJS.player.newBehavior("bigPlayButton", function(element){
1402
+ if (!this.elements.bigPlayButtons) {
1403
+ this.elements.bigPlayButtons = [];
1404
+ this.onPlay(this.bigPlayButtonsOnPlay);
1405
+ this.onEnded(this.bigPlayButtonsOnEnded);
1406
+ }
1407
+ this.elements.bigPlayButtons.push(element);
1408
+ this.activateElement(element, "playButton");
1409
+ },{
1410
+ bigPlayButtonsOnPlay: function(event){ this.hideBigPlayButtons(); },
1411
+ bigPlayButtonsOnEnded: function(event){ this.showBigPlayButtons(); },
1412
+ showBigPlayButtons: function(){
1413
+ this.each(this.elements.bigPlayButtons, function(element){
1414
+ element.style.display = "block";
1415
+ });
1416
+ },
1417
+ hideBigPlayButtons: function(){
1418
+ this.each(this.elements.bigPlayButtons, function(element){
1419
+ element.style.display = "none";
1420
+ });
1421
+ }
1422
+ }
1423
+ );
1424
+ /* Spinner
1425
+ ================================================================================ */
1426
+ VideoJS.player.newBehavior("spinner", function(element){
1427
+ if (!this.spinners) {
1428
+ this.spinners = [];
1429
+ _V_.addListener(this.video, "loadeddata", this.spinnersOnVideoLoadedData.context(this));
1430
+ _V_.addListener(this.video, "loadstart", this.spinnersOnVideoLoadStart.context(this));
1431
+ _V_.addListener(this.video, "seeking", this.spinnersOnVideoSeeking.context(this));
1432
+ _V_.addListener(this.video, "seeked", this.spinnersOnVideoSeeked.context(this));
1433
+ _V_.addListener(this.video, "canplay", this.spinnersOnVideoCanPlay.context(this));
1434
+ _V_.addListener(this.video, "canplaythrough", this.spinnersOnVideoCanPlayThrough.context(this));
1435
+ _V_.addListener(this.video, "waiting", this.spinnersOnVideoWaiting.context(this));
1436
+ _V_.addListener(this.video, "stalled", this.spinnersOnVideoStalled.context(this));
1437
+ _V_.addListener(this.video, "suspend", this.spinnersOnVideoSuspend.context(this));
1438
+ _V_.addListener(this.video, "playing", this.spinnersOnVideoPlaying.context(this));
1439
+ _V_.addListener(this.video, "timeupdate", this.spinnersOnVideoTimeUpdate.context(this));
1440
+ }
1441
+ this.spinners.push(element);
1442
+ },{
1443
+ showSpinners: function(){
1444
+ this.each(this.spinners, function(spinner){
1445
+ spinner.style.display = "block";
1446
+ });
1447
+ clearInterval(this.spinnerInterval);
1448
+ this.spinnerInterval = setInterval(this.rotateSpinners.context(this), 100);
1449
+ },
1450
+ hideSpinners: function(){
1451
+ this.each(this.spinners, function(spinner){
1452
+ spinner.style.display = "none";
1453
+ });
1454
+ clearInterval(this.spinnerInterval);
1455
+ },
1456
+ spinnersRotated: 0,
1457
+ rotateSpinners: function(){
1458
+ this.each(this.spinners, function(spinner){
1459
+ // spinner.style.transform = 'scale(0.5) rotate('+this.spinnersRotated+'deg)';
1460
+ spinner.style.WebkitTransform = 'scale(0.5) rotate('+this.spinnersRotated+'deg)';
1461
+ spinner.style.MozTransform = 'scale(0.5) rotate('+this.spinnersRotated+'deg)';
1462
+ });
1463
+ if (this.spinnersRotated == 360) { this.spinnersRotated = 0; }
1464
+ this.spinnersRotated += 45;
1465
+ },
1466
+ spinnersOnVideoLoadedData: function(event){ this.hideSpinners(); },
1467
+ spinnersOnVideoLoadStart: function(event){ this.showSpinners(); },
1468
+ spinnersOnVideoSeeking: function(event){ /* this.showSpinners(); */ },
1469
+ spinnersOnVideoSeeked: function(event){ /* this.hideSpinners(); */ },
1470
+ spinnersOnVideoCanPlay: function(event){ /* this.hideSpinners(); */ },
1471
+ spinnersOnVideoCanPlayThrough: function(event){ this.hideSpinners(); },
1472
+ spinnersOnVideoWaiting: function(event){
1473
+ // Safari sometimes triggers waiting inappropriately
1474
+ // Like after video has played, any you play again.
1475
+ this.showSpinners();
1476
+ },
1477
+ spinnersOnVideoStalled: function(event){},
1478
+ spinnersOnVideoSuspend: function(event){},
1479
+ spinnersOnVideoPlaying: function(event){ this.hideSpinners(); },
1480
+ spinnersOnVideoTimeUpdate: function(event){
1481
+ // Safari sometimes calls waiting and doesn't recover
1482
+ if(this.spinner.style.display == "block") { this.hideSpinners(); }
1483
+ }
1484
+ }
1485
+ );
1486
+ /* Subtitles
1487
+ ================================================================================ */
1488
+ VideoJS.player.newBehavior("subtitlesDisplay", function(element){
1489
+ if (!this.subtitleDisplays) {
1490
+ this.subtitleDisplays = [];
1491
+ this.onCurrentTimeUpdate(this.subtitleDisplaysOnVideoTimeUpdate);
1492
+ this.onEnded(function() { this.lastSubtitleIndex = 0; }.context(this));
1493
+ }
1494
+ this.subtitleDisplays.push(element);
1495
+ },{
1496
+ subtitleDisplaysOnVideoTimeUpdate: function(time){
1497
+ // Assuming all subtitles are in order by time, and do not overlap
1498
+ if (this.subtitles) {
1499
+ // If current subtitle should stay showing, don't do anything. Otherwise, find new subtitle.
1500
+ if (!this.currentSubtitle || this.currentSubtitle.start >= time || this.currentSubtitle.end < time) {
1501
+ var newSubIndex = false,
1502
+ // Loop in reverse if lastSubtitle is after current time (optimization)
1503
+ // Meaning the user is scrubbing in reverse or rewinding
1504
+ reverse = (this.subtitles[this.lastSubtitleIndex].start > time),
1505
+ // If reverse, step back 1 becase we know it's not the lastSubtitle
1506
+ i = this.lastSubtitleIndex - (reverse) ? 1 : 0;
1507
+ while (true) { // Loop until broken
1508
+ if (reverse) { // Looping in reverse
1509
+ // Stop if no more, or this subtitle ends before the current time (no earlier subtitles should apply)
1510
+ if (i < 0 || this.subtitles[i].end < time) { break; }
1511
+ // End is greater than time, so if start is less, show this subtitle
1512
+ if (this.subtitles[i].start < time) {
1513
+ newSubIndex = i;
1514
+ break;
1515
+ }
1516
+ i--;
1517
+ } else { // Looping forward
1518
+ // Stop if no more, or this subtitle starts after time (no later subtitles should apply)
1519
+ if (i >= this.subtitles.length || this.subtitles[i].start > time) { break; }
1520
+ // Start is less than time, so if end is later, show this subtitle
1521
+ if (this.subtitles[i].end > time) {
1522
+ newSubIndex = i;
1523
+ break;
1524
+ }
1525
+ i++;
1526
+ }
1527
+ }
1528
+
1529
+ // Set or clear current subtitle
1530
+ if (newSubIndex !== false) {
1531
+ this.currentSubtitle = this.subtitles[newSubIndex];
1532
+ this.lastSubtitleIndex = newSubIndex;
1533
+ this.updateSubtitleDisplays(this.currentSubtitle.text);
1534
+ } else if (this.currentSubtitle) {
1535
+ this.currentSubtitle = false;
1536
+ this.updateSubtitleDisplays("");
1537
+ }
1538
+ }
1539
+ }
1540
+ },
1541
+ updateSubtitleDisplays: function(val){
1542
+ this.each(this.subtitleDisplays, function(disp){
1543
+ disp.innerHTML = val;
1544
+ });
1545
+ }
1546
+ }
1547
+ );
1548
+
1549
+ ////////////////////////////////////////////////////////////////////////////////
1550
+ // Convenience Functions (mini library)
1551
+ // Functions not specific to video or VideoJS and could probably be replaced with a library like jQuery
1552
+ ////////////////////////////////////////////////////////////////////////////////
1553
+
1554
+ VideoJS.extend({
1555
+
1556
+ addClass: function(element, classToAdd){
1557
+ if ((" "+element.className+" ").indexOf(" "+classToAdd+" ") == -1) {
1558
+ element.className = element.className === "" ? classToAdd : element.className + " " + classToAdd;
1559
+ }
1560
+ },
1561
+ removeClass: function(element, classToRemove){
1562
+ if (element.className.indexOf(classToRemove) == -1) { return; }
1563
+ var classNames = element.className.split(/\s+/);
1564
+ classNames.splice(classNames.lastIndexOf(classToRemove),1);
1565
+ element.className = classNames.join(" ");
1566
+ },
1567
+ createElement: function(tagName, attributes){
1568
+ return this.merge(document.createElement(tagName), attributes);
1569
+ },
1570
+
1571
+ // Attempt to block the ability to select text while dragging controls
1572
+ blockTextSelection: function(){
1573
+ document.body.focus();
1574
+ document.onselectstart = function () { return false; };
1575
+ },
1576
+ // Turn off text selection blocking
1577
+ unblockTextSelection: function(){ document.onselectstart = function () { return true; }; },
1578
+
1579
+ // Return seconds as MM:SS
1580
+ formatTime: function(secs) {
1581
+ var seconds = Math.round(secs);
1582
+ var minutes = Math.floor(seconds / 60);
1583
+ minutes = (minutes >= 10) ? minutes : "0" + minutes;
1584
+ seconds = Math.floor(seconds % 60);
1585
+ seconds = (seconds >= 10) ? seconds : "0" + seconds;
1586
+ return minutes + ":" + seconds;
1587
+ },
1588
+
1589
+ // Return the relative horizonal position of an event as a value from 0-1
1590
+ getRelativePosition: function(x, relativeElement){
1591
+ return Math.max(0, Math.min(1, (x - this.findPosX(relativeElement)) / relativeElement.offsetWidth));
1592
+ },
1593
+ // Get an objects position on the page
1594
+ findPosX: function(obj) {
1595
+ var curleft = obj.offsetLeft;
1596
+ while(obj = obj.offsetParent) {
1597
+ curleft += obj.offsetLeft;
1598
+ }
1599
+ return curleft;
1600
+ },
1601
+ getComputedStyleValue: function(element, style){
1602
+ return window.getComputedStyle(element, null).getPropertyValue(style);
1603
+ },
1604
+
1605
+ round: function(num, dec) {
1606
+ if (!dec) { dec = 0; }
1607
+ return Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
1608
+ },
1609
+
1610
+ addListener: function(element, type, handler){
1611
+ if (element.addEventListener) {
1612
+ element.addEventListener(type, handler, false);
1613
+ } else if (element.attachEvent) {
1614
+ element.attachEvent("on"+type, handler);
1615
+ }
1616
+ },
1617
+ removeListener: function(element, type, handler){
1618
+ if (element.removeEventListener) {
1619
+ element.removeEventListener(type, handler, false);
1620
+ } else if (element.attachEvent) {
1621
+ element.detachEvent("on"+type, handler);
1622
+ }
1623
+ },
1624
+
1625
+ get: function(url, onSuccess){
1626
+ if (typeof XMLHttpRequest == "undefined") {
1627
+ XMLHttpRequest = function () {
1628
+ try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
1629
+ try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (f) {}
1630
+ try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (g) {}
1631
+ //Microsoft.XMLHTTP points to Msxml2.XMLHTTP.3.0 and is redundant
1632
+ throw new Error("This browser does not support XMLHttpRequest.");
1633
+ };
1634
+ }
1635
+ var request = new XMLHttpRequest();
1636
+ request.open("GET",url);
1637
+ request.onreadystatechange = function() {
1638
+ if (request.readyState == 4 && request.status == 200) {
1639
+ onSuccess(request.responseText);
1640
+ }
1641
+ }.context(this);
1642
+ request.send();
1643
+ },
1644
+
1645
+ trim: function(string){ return string.toString().replace(/^\s+/, "").replace(/\s+$/, ""); },
1646
+
1647
+ // DOM Ready functionality adapted from jQuery. http://jquery.com/
1648
+ bindDOMReady: function(){
1649
+ if (document.readyState === "complete") {
1650
+ return VideoJS.onDOMReady();
1651
+ }
1652
+ if (document.addEventListener) {
1653
+ document.addEventListener("DOMContentLoaded", VideoJS.DOMContentLoaded, false);
1654
+ window.addEventListener("load", VideoJS.onDOMReady, false);
1655
+ } else if (document.attachEvent) {
1656
+ document.attachEvent("onreadystatechange", VideoJS.DOMContentLoaded);
1657
+ window.attachEvent("onload", VideoJS.onDOMReady);
1658
+ }
1659
+ },
1660
+
1661
+ DOMContentLoaded: function(){
1662
+ if (document.addEventListener) {
1663
+ document.removeEventListener( "DOMContentLoaded", VideoJS.DOMContentLoaded, false);
1664
+ VideoJS.onDOMReady();
1665
+ } else if ( document.attachEvent ) {
1666
+ if ( document.readyState === "complete" ) {
1667
+ document.detachEvent("onreadystatechange", VideoJS.DOMContentLoaded);
1668
+ VideoJS.onDOMReady();
1669
+ }
1670
+ }
1671
+ },
1672
+
1673
+ // Functions to be run once the DOM is loaded
1674
+ DOMReadyList: [],
1675
+ addToDOMReady: function(fn){
1676
+ if (VideoJS.DOMIsReady) {
1677
+ fn.call(document);
1678
+ } else {
1679
+ VideoJS.DOMReadyList.push(fn);
1680
+ }
1681
+ },
1682
+
1683
+ DOMIsReady: false,
1684
+ onDOMReady: function(){
1685
+ if (VideoJS.DOMIsReady) { return; }
1686
+ if (!document.body) { return setTimeout(VideoJS.onDOMReady, 13); }
1687
+ VideoJS.DOMIsReady = true;
1688
+ if (VideoJS.DOMReadyList) {
1689
+ for (var i=0; i<VideoJS.DOMReadyList.length; i++) {
1690
+ VideoJS.DOMReadyList[i].call(document);
1691
+ }
1692
+ VideoJS.DOMReadyList = null;
1693
+ }
1694
+ }
1695
+ });
1696
+ VideoJS.bindDOMReady();
1697
+
1698
+ // Allows for binding context to functions
1699
+ // when using in event listeners and timeouts
1700
+ Function.prototype.context = function(obj){
1701
+ var method = this,
1702
+ temp = function(){
1703
+ return method.apply(obj, arguments);
1704
+ };
1705
+ return temp;
1706
+ };
1707
+
1708
+ // Like context, in that it creates a closure
1709
+ // But insteaad keep "this" intact, and passes the var as the second argument of the function
1710
+ // Need for event listeners where you need to know what called the event
1711
+ // Only use with event callbacks
1712
+ Function.prototype.evtContext = function(obj){
1713
+ var method = this,
1714
+ temp = function(){
1715
+ var origContext = this;
1716
+ return method.call(obj, arguments[0], origContext);
1717
+ };
1718
+ return temp;
1719
+ };
1720
+
1721
+ // Removeable Event listener with Context
1722
+ // Replaces the original function with a version that has context
1723
+ // So it can be removed using the original function name.
1724
+ // In order to work, a version of the function must already exist in the player/prototype
1725
+ Function.prototype.rEvtContext = function(obj, funcParent){
1726
+ if (this.hasContext === true) { return this; }
1727
+ if (!funcParent) { funcParent = obj; }
1728
+ for (var attrname in funcParent) {
1729
+ if (funcParent[attrname] == this) {
1730
+ funcParent[attrname] = this.evtContext(obj);
1731
+ funcParent[attrname].hasContext = true;
1732
+ return funcParent[attrname];
1733
+ }
1734
+ }
1735
+ return this.evtContext(obj);
1736
+ };
1737
+
1738
+ // jQuery Plugin
1739
+ if (window.jQuery) {
1740
+ (function($) {
1741
+ $.fn.VideoJS = function(options) {
1742
+ this.each(function() {
1743
+ VideoJS.setup(this, options);
1744
+ });
1745
+ return this;
1746
+ };
1747
+ $.fn.player = function() {
1748
+ return this[0].player;
1749
+ };
1750
+ })(jQuery);
1751
+ }
1752
+
1753
+
1754
+ // Expose to global
1755
+ window.VideoJS = window._V_ = VideoJS;
1756
+
1757
+ // End self-executing function
1758
+ })(window);