pxvideo_rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 48ec31892cfccac422575bb7c9310f849ca47ff1
4
+ data.tar.gz: 79cc37782b63f79a7fa8d1bd438d80212890b747
5
+ SHA512:
6
+ metadata.gz: 6a68c34b90f23e08ff4053784ddbac2179c58517968d1be88b74f6696739225fa6fca6764e69f44f02f33ec352381c66a1f83f381186c11e3071e294ddc98d7f
7
+ data.tar.gz: 410a4e0ee09cccc1a08a7054f3f9ff8d1d8ce93efbf3a0127b51c5b00bd670291719e2ae3832c98a7f80cd85b7feebc000ef9655dc8fa0c52f59ec8f69868233
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
data/Readme.md ADDED
@@ -0,0 +1,64 @@
1
+ # PX-Video for Asset Pipeline
2
+
3
+ html5 video plugin for rails
4
+
5
+ ## Installation
6
+
7
+ Gemfile
8
+
9
+ ```ruby
10
+ gem 'pxvideo_rails'
11
+ ```
12
+
13
+ Add the resources to your application.js file
14
+
15
+ ```coffeescript
16
+ # app/assets/javascripts/application.js
17
+ //= require px-video
18
+ ```
19
+
20
+ And that resource to application.css file
21
+
22
+ ```sass
23
+ /*
24
+ *= require px-video
25
+ */
26
+ ```
27
+
28
+ And to assets.rb add this line
29
+
30
+ ```ruby
31
+ # config/initializers/assets.rb
32
+ Rails.application.config.assets.precompile += %w( px-video-sprite.png )
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ```erb
38
+ <%= pxvideo_rails
39
+ sources: { mp4: "https://www.paypalobjects.com/webstatic/mktg/videos/PayPal_AustinSMB_baseline.mp4" } %>
40
+ ```
41
+
42
+ ### Properties
43
+
44
+ ```erb
45
+ controls: true,
46
+ setup: "{}",
47
+ width: "640",
48
+ height: "480",
49
+ captions: {en: "https://exampledomain.org/media/captions_PayPal_Austin_en.vtt"},
50
+ sources: { mp4: "https://www.paypalobjects.com/webstatic/mktg/videos/PayPal_AustinSMB_baseline.mp4" },
51
+ poster: "https://exampledomain.org/media/poster_PayPal_Austin2.jpg"
52
+ ```
53
+
54
+
55
+ ## Based by Accessible HTML5 Video Player
56
+
57
+ ### Authors
58
+ - Dennis Lembree, primary developer || [https://github.com/weboverhauls](https://github.com/weboverhauls) || [@dennisl](https://twitter.com/dennisl)
59
+ - Victor Tsaran, consultation and testing || [https://github.com/vick08](https://github.com/vick08) || [@vick08](https://twitter.com/vick08)
60
+ - Jason Gabriele, consultation
61
+ - Tim Resudek, design
62
+
63
+ ### Copyright and License
64
+ Copyright 2014, eBay Software Foundation under [the BSD license](LICENSE.md).
@@ -0,0 +1,51 @@
1
+ <div class="px-video-container" id="myvid">
2
+ <div class="px-video-img-captions-container">
3
+ <div class="px-video-captions hide" aria-hidden="true"></div>
4
+ <video
5
+ id="<%=options[:id]%>"
6
+ <%= "controls" if !!options[:controls] %>
7
+ width="<%=options[:width]%>"
8
+ height="<%=options[:height]%>"
9
+ poster="<%=options[:poster]%>"
10
+ preload="<%=options[:preload]%>"
11
+ data-setup="<%= options[:setup] %>"
12
+ >
13
+ <!-- source files -->
14
+ <% if options[:sources] %>
15
+ <%- options[:sources].each do |type, source| %>
16
+ <source src="<%= source %>" type='video/<%= type %>' />
17
+ <%- end %>
18
+ <%- end %>
19
+
20
+ <!-- text track file -->
21
+ <% if options[:captions] %>
22
+ <%- options[:captions].each do |lang, caption| %>
23
+ <track kind="captions" src="<%= caption %>" srclang="<%= lang %>" label="<%= lang %>" />
24
+ <%- end %>
25
+ <%- end %>
26
+
27
+ <!-- fallback for browsers that don't support the video element -->
28
+ <div>
29
+ <% if options[:sources] %>
30
+ <%- options[:sources].each do |type, source| %>
31
+ <a href="<%= source %>">
32
+ <img src="<%=options[:poster]%>" width="<%=options[:width]%>" height="<%=options[:height]%>" alt="download video" />
33
+ </a>
34
+ <%- end %>
35
+ <%- end %>
36
+
37
+ </div>
38
+ </video>
39
+ </div><!-- end container for captions and video -->
40
+ <div class="px-video-controls"></div>
41
+ </div><!-- end video container -->
42
+
43
+ <script>
44
+ // Initialize
45
+ new InitPxVideo({
46
+ "videoId": "myvid",
47
+ "captionsOnDefault": true,
48
+ "seekInterval": 20,
49
+ "debug": true
50
+ });
51
+ </script>
@@ -0,0 +1,2 @@
1
+ require 'pxvideo_rails/railtie'
2
+ require 'pxvideo_rails/engine'
@@ -0,0 +1,6 @@
1
+ module PxvideoRails
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ require 'pxvideo_rails/view_helpers'
2
+
3
+ module PxvideoRails
4
+ class Railtie < Rails::Railtie
5
+ initializer "pxvideo_rails.view_helpers" do
6
+ ActionView::Base.send(:include, ViewHelpers)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ module PxvideoRails
2
+ module ViewHelpers
3
+ def pxvideo_rails(*options, &blk)
4
+ default_options = {
5
+ controls: true,
6
+ setup: "{}",
7
+ preload: "auto",
8
+ width: 640,
9
+ height: 480
10
+ }
11
+ options = default_options.merge(options.extract_options!)
12
+ render partial: 'pxvideo_rails/pxvideo_rails', locals: { options: options }, &blk
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'pxvideo_rails'
3
+ s.version = '0.0.1'
4
+ s.date = '2014-09-15'
5
+ s.summary = "%q{Pxvideo html5 video plugin}"
6
+ s.description = "%q{HTML5 PxVideo plugin}"
7
+ s.authors = ["Alexandr Kardakov"]
8
+ s.email = 'razielsun@gmail.com'
9
+ s.homepage = ''
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
13
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ s.require_paths = ["lib"]
15
+ end
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2014, eBay Software Foundation
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice, this
11
+ list of conditions and the following disclaimer in the documentation and/or
12
+ other materials provided with the distribution.
13
+
14
+ * Neither the name of the eBay nor the names of its
15
+ subsidiaries or affiliates may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,538 @@
1
+
2
+ function InitPxVideo(options) {
3
+
4
+ "use strict";
5
+
6
+ // Utilities for caption time codes
7
+ function video_timecode_min(tc) {
8
+ var tcpair = [];
9
+ tcpair = tc.split(' --> ');
10
+ return videosub_tcsecs(tcpair[0]);
11
+ }
12
+
13
+ function video_timecode_max(tc) {
14
+ var tcpair = [];
15
+ tcpair = tc.split(' --> ');
16
+ return videosub_tcsecs(tcpair[1]);
17
+ }
18
+
19
+ function videosub_tcsecs(tc) {
20
+ if (tc === null || tc === undefined) {
21
+ return 0;
22
+ }
23
+ else {
24
+ var tc1 = [],
25
+ tc2 = [],
26
+ seconds;
27
+ tc1 = tc.split(',');
28
+ tc2 = tc1[0].split(':');
29
+ seconds = Math.floor(tc2[0]*60*60) + Math.floor(tc2[1]*60) + Math.floor(tc2[2]);
30
+ return seconds;
31
+ }
32
+ }
33
+
34
+ // For "manual" captions, adjust caption position when play time changed (via rewind, clicking progress bar, etc.)
35
+ function adjustManualCaptions(obj) {
36
+ obj.subcount = 0;
37
+ while (video_timecode_max(obj.captions[obj.subcount][0]) < obj.movie.currentTime.toFixed(1)) {
38
+ obj.subcount++;
39
+ if (obj.subcount > obj.captions.length-1) {
40
+ obj.subcount = obj.captions.length-1;
41
+ break;
42
+ }
43
+ }
44
+ }
45
+
46
+ // Display captions container and button (for initialization)
47
+ function showCaptionContainerAndButton(obj) {
48
+ obj.captionsBtnContainer.className = "px-video-captions-btn-container pull-left show";
49
+ if (obj.isCaptionDefault) {
50
+ obj.captionsContainer.className = "px-video-captions pull-left show";
51
+ obj.captionsBtn.setAttribute("checked", "checked");
52
+ }
53
+ }
54
+
55
+ // Unfortunately, due to scattered support, browser sniffing is required
56
+ function browserSniff() {
57
+ var nVer = navigator.appVersion,
58
+ nAgt = navigator.userAgent,
59
+ browserName = navigator.appName,
60
+ fullVersion = ''+parseFloat(navigator.appVersion),
61
+ majorVersion = parseInt(navigator.appVersion,10),
62
+ nameOffset,
63
+ verOffset,
64
+ ix;
65
+
66
+ // MSIE 11
67
+ if ((navigator.appVersion.indexOf("Windows NT") !== -1) && (navigator.appVersion.indexOf("rv:11") !== -1)) {
68
+ browserName = "IE";
69
+ fullVersion = "11;";
70
+ }
71
+ // MSIE
72
+ else if ((verOffset=nAgt.indexOf("MSIE")) !== -1) {
73
+ browserName = "IE";
74
+ fullVersion = nAgt.substring(verOffset+5);
75
+ }
76
+ // Chrome
77
+ else if ((verOffset=nAgt.indexOf("Chrome")) !== -1) {
78
+ browserName = "Chrome";
79
+ fullVersion = nAgt.substring(verOffset+7);
80
+ }
81
+ // Safari
82
+ else if ((verOffset=nAgt.indexOf("Safari")) !== -1) {
83
+ browserName = "Safari";
84
+ fullVersion = nAgt.substring(verOffset+7);
85
+ if ((verOffset=nAgt.indexOf("Version")) !== -1) {
86
+ fullVersion = nAgt.substring(verOffset+8);
87
+ }
88
+ }
89
+ // Firefox
90
+ else if ((verOffset=nAgt.indexOf("Firefox")) !== -1) {
91
+ browserName = "Firefox";
92
+ fullVersion = nAgt.substring(verOffset+8);
93
+ }
94
+ // In most other browsers, "name/version" is at the end of userAgent
95
+ else if ( (nameOffset=nAgt.lastIndexOf(' ')+1) < (verOffset=nAgt.lastIndexOf('/')) ) {
96
+ browserName = nAgt.substring(nameOffset,verOffset);
97
+ fullVersion = nAgt.substring(verOffset+1);
98
+ if (browserName.toLowerCase()==browserName.toUpperCase()) {
99
+ browserName = navigator.appName;
100
+ }
101
+ }
102
+ // Trim the fullVersion string at semicolon/space if present
103
+ if ((ix=fullVersion.indexOf(";")) !== -1) {
104
+ fullVersion=fullVersion.substring(0,ix);
105
+ }
106
+ if ((ix=fullVersion.indexOf(" ")) !== -1) {
107
+ fullVersion=fullVersion.substring(0,ix);
108
+ }
109
+ // Get major version
110
+ majorVersion = parseInt(''+fullVersion,10);
111
+ if (isNaN(majorVersion)) {
112
+ fullVersion = ''+parseFloat(navigator.appVersion);
113
+ majorVersion = parseInt(navigator.appVersion,10);
114
+ }
115
+ // Return data
116
+ return [browserName, majorVersion];
117
+ }
118
+
119
+ // Global variable
120
+ var obj = {};
121
+
122
+ obj.arBrowserInfo = browserSniff();
123
+ obj.browserName = obj.arBrowserInfo[0];
124
+ obj.browserMajorVersion = obj.arBrowserInfo[1];
125
+
126
+ // If IE8, stop customization (use fallback)
127
+ // If IE9, stop customization (use native controls)
128
+ if (obj.browserName === "IE" && (obj.browserMajorVersion === 8 || obj.browserMajorVersion === 9) ) {
129
+ return false;
130
+ }
131
+
132
+ // If smartphone or tablet, stop customization as video (and captions in latest devices) are handled natively
133
+ obj.isSmartphoneOrTablet = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
134
+ if (obj.isSmartphoneOrTablet) {
135
+ return false;
136
+ }
137
+
138
+ // Set debug mode
139
+ if (typeof(options.debug)==='undefined') {
140
+ options.debug = false;
141
+ }
142
+ obj.debug = options.debug;
143
+
144
+ // Output browser info to log if debug on
145
+ if (options.debug) {
146
+ console.log(obj.browserName + " " + obj.browserMajorVersion);
147
+ }
148
+
149
+ // Set up aria-label for Play button with the videoTitle option
150
+ if ((typeof(options.videoTitle)==='undefined') || (options.videoTitle==="")) {
151
+ obj.playAriaLabel = "Play";
152
+ }
153
+ else {
154
+ obj.playAriaLabel = "Play video, " + options.videoTitle;
155
+ }
156
+
157
+ // Get the container, video element, and controls container
158
+ obj.container = document.getElementById(options.videoId);
159
+ obj.movie = obj.container.getElementsByTagName('video')[0];
160
+ obj.controls = obj.container.getElementsByClassName('px-video-controls')[0];
161
+
162
+ // Remove native video controls
163
+ obj.movie.removeAttribute("controls");
164
+
165
+ // Generate random number for ID/FOR attribute values for controls
166
+ obj.randomNum = Math.floor(Math.random() * (10000));
167
+
168
+ // Insert custom video controls
169
+ if (options.debug) {
170
+ console.log("Inserting custom video controls");
171
+ }
172
+ obj.controls.innerHTML = '<div class="clearfix">' +
173
+ '<div class="pull-left">' +
174
+ '<button class="px-video-restart"><span class="sr-only">Restart</span></button>' +
175
+ '<button class="px-video-rewind"><span class="sr-only">rewind <span class="px-seconds">10</span> seconds</span></button>' +
176
+ '<button class="px-video-play" aria-label="'+obj.playAriaLabel+'"><span class="sr-only">Play</span></button>' +
177
+ '<button class="px-video-pause hide"><span class="sr-only">Pause</span></button>' +
178
+ '<button class="px-video-forward"><span class="sr-only">forward <span class="px-seconds">10</span> seconds</span></button>' +
179
+ '</div>' +
180
+ '<div class="px-video-mute-btn-container pull-left">' +
181
+ '<input class="px-video-mute sr-only" id="btnMute'+obj.randomNum+'" type="checkbox" />' +
182
+ '<label id="labelMute'+obj.randomNum+'" for="btnMute'+obj.randomNum+'"><span class="sr-only">Mute</span></label>' +
183
+ '</div>' +
184
+ '<div class="pull-left">' +
185
+ '<label for="volume'+obj.randomNum+'" class="sr-only">Volume:</label><input id="volume'+obj.randomNum+'" class="px-video-volume" type="range" min="0" max="10" value="5" />' +
186
+ '</div>' +
187
+ '<div class="px-video-captions-btn-container pull-left hide">' +
188
+ '<input class="px-video-btnCaptions sr-only" id="btnCaptions'+obj.randomNum+'" type="checkbox" />' +
189
+ '<label for="btnCaptions'+obj.randomNum+'"><span class="sr-only">Captions</span></label>' +
190
+ '</div>' +
191
+ '<div class="px-video-time">' +
192
+ '<span class="sr-only">time</span> <span class="px-video-duration">00:00</span>' +
193
+ '</div>' +
194
+ '</div>' +
195
+ '<div>' +
196
+ '<progress class="px-video-progress" max="100" value="0"><span>0</span>% played</progress>' +
197
+ '</div>';
198
+
199
+ // Adjust layout per width of video - container
200
+ obj.movieWidth = obj.movie.width;
201
+ if (obj.movieWidth < 360) {
202
+ obj.movieWidth = 360;
203
+ }
204
+ obj.container.setAttribute("style", "width:" + obj.movieWidth + "px");
205
+
206
+ // Adjust layout per width of video - controls/mute offset
207
+ obj.labelMute = document.getElementById("labelMute" + obj.randomNum);
208
+ obj.labelMuteOffset = obj.movieWidth - 390;
209
+ if (obj.labelMuteOffset < 0) {
210
+ obj.labelMuteOffset = 0;
211
+ }
212
+ obj.labelMute.setAttribute("style", "margin-left:" + obj.labelMuteOffset + "px");
213
+
214
+ // Get URL of caption file if exists
215
+ var captionSrc = "",
216
+ kind,
217
+ children = obj.movie.childNodes;
218
+
219
+ for (var i = 0; i < children.length; i++) {
220
+ if (children[i].nodeName.toLowerCase() === 'track') {
221
+ kind = children[i].getAttribute('kind');
222
+ if (kind === 'captions') {
223
+ captionSrc = children[i].getAttribute('src');
224
+ }
225
+ }
226
+ }
227
+
228
+ // Record if caption file exists or not
229
+ obj.captionExists = true;
230
+ if (captionSrc === "") {
231
+ obj.captionExists = false;
232
+ if (options.debug) {
233
+ console.log("No caption track found.");
234
+ }
235
+ }
236
+ else {
237
+ if (options.debug) {
238
+ console.log("Caption track found; URI: " + captionSrc);
239
+ }
240
+ }
241
+
242
+ // Set captions on/off - on by default
243
+ if (typeof(options.captionsOnDefault) === 'undefined') {
244
+ options.captionsOnDefault = true;
245
+ }
246
+ obj.isCaptionDefault = options.captionsOnDefault;
247
+
248
+ // Number of seconds for rewind and forward buttons
249
+ if (typeof(options.seekInterval) === 'undefined') {
250
+ options.seekInterval = 10;
251
+ }
252
+ obj.seekInterval = options.seekInterval;
253
+
254
+ // Get the elements for the controls
255
+ obj.btnPlay = obj.container.getElementsByClassName('px-video-play')[0];
256
+ obj.btnPause = obj.container.getElementsByClassName('px-video-pause')[0];
257
+ obj.btnRestart = obj.container.getElementsByClassName('px-video-restart')[0];
258
+ obj.btnRewind = obj.container.getElementsByClassName('px-video-rewind')[0];
259
+ obj.btnForward = obj.container.getElementsByClassName('px-video-forward')[0];
260
+ obj.btnVolume = obj.container.getElementsByClassName('px-video-volume')[0];
261
+ obj.btnMute = obj.container.getElementsByClassName('px-video-mute')[0];
262
+ obj.progressBar = obj.container.getElementsByClassName('px-video-progress')[0];
263
+ obj.progressBarSpan = obj.progressBar.getElementsByTagName('span')[0];
264
+ obj.captionsContainer = obj.container.getElementsByClassName('px-video-captions')[0];
265
+ obj.captionsBtn = obj.container.getElementsByClassName('px-video-btnCaptions')[0];
266
+ obj.captionsBtnContainer = obj.container.getElementsByClassName('px-video-captions-btn-container')[0];
267
+ obj.duration = obj.container.getElementsByClassName('px-video-duration')[0];
268
+ obj.txtSeconds = obj.container.getElementsByClassName('px-seconds');
269
+
270
+ // Update number of seconds in rewind and fast forward buttons
271
+ obj.txtSeconds[0].innerHTML = obj.seekInterval;
272
+ obj.txtSeconds[1].innerHTML = obj.seekInterval;
273
+
274
+ // Determine if HTML5 textTracks is supported (for captions)
275
+ obj.isTextTracks = false;
276
+ if (obj.movie.textTracks) {
277
+ obj.isTextTracks = true;
278
+ }
279
+
280
+ // Play
281
+ obj.btnPlay.addEventListener('click', function() {
282
+ obj.movie.play();
283
+ obj.btnPlay.className = "px-video-play hide";
284
+ obj.btnPause.className = "px-video-pause px-video-show-inline";
285
+ obj.btnPause.focus();
286
+ }, false);
287
+
288
+ // Pause
289
+ obj.btnPause.addEventListener('click', function() {
290
+ obj.movie.pause();
291
+ obj.btnPlay.className = "px-video-play px-video-show-inline";
292
+ obj.btnPause.className = "px-video-pause hide";
293
+ obj.btnPlay.focus();
294
+ }, false);
295
+
296
+ // Restart
297
+ obj.btnRestart.addEventListener('click', function() {
298
+ // Move to beginning
299
+ obj.movie.currentTime = 0;
300
+
301
+ // Special handling for "manual" captions
302
+ if (!obj.isTextTracks) {
303
+ obj.subcount = 0;
304
+ }
305
+
306
+ // Play and ensure the play button is in correct state
307
+ obj.movie.play();
308
+ obj.btnPlay.className = "px-video-play hide";
309
+ obj.btnPause.className = "px-video-pause px-video-show-inline";
310
+
311
+ }, false);
312
+
313
+ // Rewind
314
+ obj.btnRewind.addEventListener('click', function() {
315
+ var targetTime = obj.movie.currentTime - obj.seekInterval;
316
+ if (targetTime < 0) {
317
+ obj.movie.currentTime = 0;
318
+ }
319
+ else {
320
+ obj.movie.currentTime = targetTime;
321
+ }
322
+ // Special handling for "manual" captions
323
+ if (!obj.isTextTracks) {
324
+ adjustManualCaptions(obj);
325
+ }
326
+ }, false);
327
+
328
+ // Fast forward
329
+ obj.btnForward.addEventListener('click', function() {
330
+ var targetTime = obj.movie.currentTime + obj.seekInterval;
331
+ if (targetTime > obj.movie.duration) {
332
+ obj.movie.currentTime = obj.movie.duration;
333
+ }
334
+ else {
335
+ obj.movie.currentTime = targetTime;
336
+ }
337
+ // Special handling for "manual" captions
338
+ if (!obj.isTextTracks) {
339
+ adjustManualCaptions(obj);
340
+ }
341
+ }, false);
342
+
343
+ // Get the HTML5 range input element and append audio volume adjustment on change
344
+ obj.btnVolume.addEventListener('change', function() {
345
+ obj.movie.volume = parseFloat(this.value / 10);
346
+ }, false);
347
+
348
+ // Mute
349
+ obj.btnMute.addEventListener('click', function() {
350
+ if (obj.movie.muted === true) {
351
+ obj.movie.muted = false;
352
+ }
353
+ else {
354
+ obj.movie.muted = true;
355
+ }
356
+ }, false);
357
+
358
+ // Duration
359
+ obj.movie.addEventListener("timeupdate", function() {
360
+ obj.secs = parseInt(obj.movie.currentTime % 60);
361
+ obj.mins = parseInt((obj.movie.currentTime / 60) % 60);
362
+
363
+ // Ensure it's two digits. For example, 03 rather than 3.
364
+ obj.secs = ("0" + obj.secs).slice(-2);
365
+ obj.mins = ("0" + obj.mins).slice(-2);
366
+
367
+ // Render
368
+ obj.duration.innerHTML = obj.mins + ':' + obj.secs;
369
+ }, false);
370
+
371
+ // Progress bar
372
+ obj.movie.addEventListener('timeupdate', function() {
373
+ obj.percent = Math.floor((100 / obj.movie.duration) * obj.movie.currentTime);
374
+ if (obj.percent > 0) {
375
+ obj.progressBar.value = obj.percent;
376
+ obj.progressBarSpan.innerHTML = obj.percent;
377
+ }
378
+ }, false);
379
+
380
+ // Skip when clicking progress bar
381
+ obj.progressBar.addEventListener('click', function(e) {
382
+ obj.pos = (e.pageX - this.offsetLeft) / this.offsetWidth;
383
+ obj.movie.currentTime = obj.pos * obj.movie.duration;
384
+
385
+ // Special handling for "manual" captions
386
+ if (!obj.isTextTracks) {
387
+ adjustManualCaptions(obj);
388
+ }
389
+ });
390
+
391
+ // Clear captions at end of video
392
+ obj.movie.addEventListener('ended', function() {
393
+ obj.captionsContainer.innerHTML = "";
394
+ });
395
+
396
+ // ***
397
+ // Captions
398
+ // ***
399
+
400
+ // Toggle display of captions via captions button
401
+ obj.captionsBtn.addEventListener('click', function() {
402
+ if (this.checked) {
403
+ obj.captionsContainer.className = "px-video-captions show";
404
+ } else {
405
+ obj.captionsContainer.className = "px-video-captions hide";
406
+ }
407
+ }, false);
408
+
409
+ // If no caption file exists, hide container for caption text
410
+ if (!obj.captionExists) {
411
+ obj.captionsContainer.className = "px-video-captions hide";
412
+ }
413
+
414
+ // If caption file exists, process captions
415
+ else {
416
+
417
+ // If IE 10/11 or Firefox 31 or Safari 7, don't use native captioning (still doesn't work although they claim it's now supported)
418
+ if ((obj.browserName==="IE" && obj.browserMajorVersion===10) ||
419
+ (obj.browserName==="IE" && obj.browserMajorVersion===11) ||
420
+ (obj.browserName==="Firefox" && obj.browserMajorVersion>=31) ||
421
+ (obj.browserName==="Safari" && obj.browserMajorVersion===7)) {
422
+ if (options.debug) {
423
+ console.log("Detected IE 10/11 or Firefox 31+ or Safari 7");
424
+ }
425
+ // set to false so skips to 'manual' captioning
426
+ obj.isTextTracks = false;
427
+
428
+ // turn off native caption rendering to avoid double captions [doesn't work in Safari 7; see patch below]
429
+ var track = {};
430
+ var tracks = obj.movie.textTracks;
431
+ for (var j=0; j < tracks.length; j++) {
432
+ track = obj.movie.textTracks[j];
433
+ track.mode = "hidden";
434
+ }
435
+ }
436
+
437
+ // Rendering caption tracks - native support required - http://caniuse.com/webvtt
438
+ if (obj.isTextTracks) {
439
+ if (options.debug) {
440
+ console.log("textTracks supported");
441
+ }
442
+ showCaptionContainerAndButton(obj);
443
+
444
+ var track = {};
445
+ var tracks = obj.movie.textTracks;
446
+ for (var j=0; j < tracks.length; j++) {
447
+ track = obj.movie.textTracks[j];
448
+ track.mode = "hidden";
449
+ if (track.kind === "captions") {
450
+ track.addEventListener("cuechange",function() {
451
+ if (this.activeCues[0]) {
452
+ if (this.activeCues[0].hasOwnProperty("text")) {
453
+ obj.captionsContainer.innerHTML = this.activeCues[0].text;
454
+ }
455
+ }
456
+ },false);
457
+ }
458
+ }
459
+ }
460
+ // Caption tracks not natively supported
461
+ else {
462
+ if (options.debug) {
463
+ console.log("textTracks not supported so rendering captions 'manually'");
464
+ }
465
+ showCaptionContainerAndButton(obj);
466
+
467
+ // Render captions from array at apppropriate time
468
+ obj.currentCaption = '';
469
+ obj.subcount = 0;
470
+ obj.captions = [];
471
+
472
+ obj.movie.addEventListener('timeupdate', function() {
473
+ // Check if the next caption is in the current time range
474
+ if (obj.movie.currentTime.toFixed(1) > video_timecode_min(obj.captions[obj.subcount][0]) &&
475
+ obj.movie.currentTime.toFixed(1) < video_timecode_max(obj.captions[obj.subcount][0])) {
476
+ obj.currentCaption = obj.captions[obj.subcount][1];
477
+ }
478
+ // Is there a next timecode?
479
+ if (obj.movie.currentTime.toFixed(1) > video_timecode_max(obj.captions[obj.subcount][0]) &&
480
+ obj.subcount < (obj.captions.length-1)) {
481
+ obj.subcount++;
482
+ }
483
+ // Render the caption
484
+ obj.captionsContainer.innerHTML = obj.currentCaption;
485
+ }, false);
486
+
487
+ if (captionSrc != "") {
488
+ // Create XMLHttpRequest object
489
+ var xhr;
490
+ if (window.XMLHttpRequest) {
491
+ xhr = new XMLHttpRequest();
492
+ } else if (window.ActiveXObject) { // IE8
493
+ xhr = new ActiveXObject("Microsoft.XMLHTTP");
494
+ }
495
+ xhr.onreadystatechange = function() {
496
+ if (xhr.readyState === 4) {
497
+ if (xhr.status === 200) {
498
+ if (options.debug) {
499
+ console.log("xhr = 200");
500
+ }
501
+
502
+ obj.captions = [];
503
+ var records = [],
504
+ record,
505
+ req = xhr.responseText;
506
+ records = req.split('\n\n');
507
+ for (var r=0; r < records.length; r++) {
508
+ record = records[r];
509
+ obj.captions[r] = [];
510
+ obj.captions[r] = record.split('\n');
511
+ }
512
+ // Remove first element ("VTT")
513
+ obj.captions.shift();
514
+
515
+ if (options.debug) {
516
+ console.log('Successfully loaded the caption file via ajax.');
517
+ }
518
+ } else {
519
+ if (options.debug) {
520
+ console.log('There was a problem loading the caption file via ajax.');
521
+ }
522
+ }
523
+ }
524
+ }
525
+ xhr.open("get", captionSrc, true);
526
+ xhr.send();
527
+ }
528
+ }
529
+
530
+ // If Safari 7, removing track from DOM [see 'turn off native caption rendering' above]
531
+ if (obj.browserName === "Safari" && obj.browserMajorVersion === 7) {
532
+ console.log("Safari 7 detected; removing track from DOM");
533
+ var tracks = obj.movie.getElementsByTagName("track");
534
+ obj.movie.removeChild(tracks[0]);
535
+ }
536
+
537
+ }
538
+ };
@@ -0,0 +1,234 @@
1
+
2
+ /* utilities */
3
+ .pull-left {
4
+ float: left;
5
+ }
6
+ .sr-only {
7
+ position: absolute !important;
8
+ clip: rect(1px, 1px, 1px, 1px);
9
+ padding: 0 !important;
10
+ border: 0 !important;
11
+ height: 1px !important;
12
+ width: 1px !important;
13
+ overflow: hidden;
14
+ }
15
+ .hide {
16
+ display: none;
17
+ }
18
+ .show-inline {
19
+ display: inline-block;
20
+ }
21
+
22
+ /* containers */
23
+ .px-video-img-captions-container * {
24
+ box-sizing: border-box;
25
+ }
26
+
27
+ .px-video-img-captions-container {
28
+ position: relative;
29
+ }
30
+
31
+ /* progress indicator */
32
+ .px-video-progress {
33
+ width: 100%;
34
+ height: 10px;
35
+ }
36
+ .px-video-progress[value] {
37
+ /* Reset the default appearance */
38
+ -webkit-appearance: none;
39
+ border: none;
40
+ }
41
+ .px-video-progress[value]::-webkit-progress-bar {
42
+ background-color: #E6E6E6;
43
+ }
44
+ .px-video-progress[value]::-webkit-progress-value {
45
+ background-color: #009CDF;
46
+ }
47
+
48
+ /* time */
49
+ .px-video-time {
50
+ font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
51
+ float: right;
52
+ margin-top: 2px;
53
+ font-size: 14px;
54
+ }
55
+
56
+ /* caption area */
57
+ .px-video-captions {
58
+ position: absolute;
59
+ top: 0;
60
+ left: 0;
61
+ width: 100%;
62
+ padding: .5em;
63
+ min-height: 2.5em;
64
+ background-color: #000;
65
+ color: #fff;
66
+ font-size: 1.1em;
67
+ text-align: center;
68
+ opacity: 0.75;
69
+ }
70
+
71
+ /* buttons */
72
+ .px-video-controls button {
73
+ border: 1px #fff solid;
74
+ background: transparent;
75
+ padding: 0;
76
+ margin: 0 5px;
77
+ width: 25px;
78
+ height: 20px;
79
+ overflow: hidden;
80
+ background: no-repeat url('<%= asset_path('px-video-sprite.png') %>');
81
+ }
82
+ .px-video-controls button:focus {
83
+ border: 1px #999 dotted;
84
+ outline: none;
85
+ }
86
+ .px-video-controls button {
87
+ cursor: pointer;
88
+ }
89
+
90
+ /* restart button */
91
+ .px-video-controls button.px-video-restart {
92
+ background-position: -9px -333px;
93
+ }
94
+ .px-video-controls button.px-video-restart:hover,
95
+ .px-video-controls button.px-video-restart:focus {
96
+ background-position: -9px -297px;
97
+ }
98
+
99
+ /* rewind button */
100
+ .px-video-controls button.px-video-rewind {
101
+ background-position: -11px -189px;
102
+ }
103
+ .px-video-controls button.px-video-rewind:hover,
104
+ .px-video-controls button.px-video-rewind:focus {
105
+ background-position: -11px -153px;
106
+ }
107
+
108
+ /* play button */
109
+ .px-video-controls button.px-video-play {
110
+ background-position: -11px -45px;
111
+ }
112
+ .px-video-controls button.px-video-play:hover,
113
+ .px-video-controls button.px-video-play:focus {
114
+ background-position: -11px -9px;
115
+ }
116
+
117
+ /* pause button */
118
+ .px-video-controls button.px-video-pause {
119
+ background-position: -11px -117px;
120
+ }
121
+ .px-video-controls button.px-video-pause:hover,
122
+ .px-video-controls button.px-video-pause:focus {
123
+ background-position: -11px -81px;
124
+ }
125
+
126
+ /* forward button */
127
+ .px-video-controls button.px-video-forward {
128
+ background-position: -13px -261px;
129
+ }
130
+ .px-video-controls button.px-video-forward:hover,
131
+ .px-video-controls button.px-video-forward:focus {
132
+ background-position: -13px -225px;
133
+ }
134
+
135
+ /* captions button */
136
+ .px-video-captions-btn-container label {
137
+ display: inline-block;
138
+ width: 25px;
139
+ height: 20px;
140
+ margin-left: 25px;
141
+ background: no-repeat url('<%= asset_path('px-video-sprite.png') %>');
142
+ background-position: -6px -835px;
143
+ }
144
+ .px-video-captions-btn-container input[type="checkbox"]:focus+label {
145
+ outline: 1px #999 dotted;
146
+ background-position: -6px -799px;
147
+ }
148
+ .px-video-captions-btn-container input[type="checkbox"]:hover+label {
149
+ background-position: -6px -799px;
150
+ cursor: pointer;
151
+ }
152
+ .px-video-captions-btn-container input[type="checkbox"]:focus+label {
153
+ outline: 1px #999 dotted;
154
+ background-position: -6px -799px;
155
+ }
156
+ .px-video-captions-btn-container input[type="checkbox"]:checked+label {
157
+ background-position: -6px -871px;
158
+ }
159
+
160
+ /* mute button */
161
+ .px-video-mute-btn-container label {
162
+ display: inline-block;
163
+ width: 25px;
164
+ height: 20px;
165
+ margin-left: 240px;
166
+ margin-top: 2px;
167
+ background: no-repeat url('<%= asset_path('px-video-sprite.png') %>');
168
+ background-position: -6px -476px;
169
+ }
170
+ .px-video-mute-btn-container input[type="checkbox"]:focus+label {
171
+ outline: 1px #999 dotted;
172
+ background-position: -6px -440px;
173
+ }
174
+ .px-video-mute-btn-container input[type="checkbox"]:hover+label {
175
+ background-position: -6px -440px;
176
+ cursor: pointer;
177
+ }
178
+ .px-video-mute-btn-container input[type="checkbox"]:focus+label {
179
+ outline: 1px #999 dotted;
180
+ background-position: -6px -440px;
181
+ }
182
+ /* checked state of mute button */
183
+ .px-video-mute-btn-container input[type="checkbox"]:checked+label {
184
+ background-position: -6px -692px;
185
+ }
186
+ .px-video-mute-btn-container input[type="checkbox"]:checked:hover+label,
187
+ .px-video-mute-btn-container input[type="checkbox"]:checked:focus+label {
188
+ background-position: -6px -656px;
189
+ }
190
+
191
+ /* volume range input */
192
+ .px-video-controls input[type='range'] {
193
+ -webkit-appearance: none;
194
+ height: 6px;
195
+ width: 100px;
196
+ margin-top: 8px;
197
+ background-color: #E6E6E6;
198
+ outline:none;
199
+ }
200
+ .px-video-controls input[type='range']:focus::-webkit-slider-thumb {
201
+ outline: 1px #999 dotted;
202
+ }
203
+ .px-video-controls input[type='range']::-moz-range-track {
204
+ -moz-appearance: none;
205
+ height: 6px;
206
+ background-color: #E6E6E6;
207
+ border: none;
208
+ }
209
+ .px-video-controls input[type='range']::-webkit-slider-thumb {
210
+ -webkit-appearance: none !important;
211
+ height: 10px;
212
+ width: 6px;
213
+ background-color: #666;
214
+ }
215
+ .px-video-controls input[type='range']::-moz-range-thumb {
216
+ height: 12px;
217
+ width: 8px;
218
+ }
219
+ /* fixing display for IE10+ */
220
+ @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
221
+ .px-video-controls input[type='range'] {
222
+ position: relative;
223
+ padding: 0;
224
+ height: 8px;
225
+ top: -3px;
226
+ }
227
+ .px-video-time {
228
+ margin-top: 4px;
229
+ }
230
+ .px-video-captions {
231
+ padding: 8px;
232
+ min-height: 36px;
233
+ }
234
+ }
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pxvideo_rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexandr Kardakov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-15 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: '%q{HTML5 PxVideo plugin}'
14
+ email: razielsun@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - .gitignore
20
+ - Rakefile
21
+ - Readme.md
22
+ - app/views/pxvideo_rails/_pxvideo_rails.html.erb
23
+ - lib/pxvideo_rails.rb
24
+ - lib/pxvideo_rails/engine.rb
25
+ - lib/pxvideo_rails/railtie.rb
26
+ - lib/pxvideo_rails/view_helpers.rb
27
+ - pxvideo_rails.gemspec
28
+ - vendor/assets/LICENSE.md
29
+ - vendor/assets/images/px-video-sprite.png
30
+ - vendor/assets/javascripts/px-video.js
31
+ - vendor/assets/stylesheets/px-video.css.erb
32
+ homepage: ''
33
+ licenses: []
34
+ metadata: {}
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 2.4.1
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: '%q{Pxvideo html5 video plugin}'
55
+ test_files: []