podlove-web-player-rails 0.2.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,999 @@
1
+ (function ($) {
2
+ 'use strict';
3
+ var startAtTime = false,
4
+ stopAtTime = false,
5
+ // Keep all Players on site
6
+ players = [],
7
+ // Timecode as described in http://podlove.org/deep-link/
8
+ // and http://www.w3.org/TR/media-frags/#fragment-dimensions
9
+ timecodeRegExp = /(?:(\d+):)?(\d+):(\d+)(\.\d+)?([,\-](?:(\d+):)?(\d+):(\d+)(\.\d+)?)?/,
10
+ ignoreHashChange = false;
11
+
12
+ /**
13
+ * return number as string lefthand filled with zeros
14
+ * @param number number
15
+ * @param width number
16
+ * @return string
17
+ **/
18
+ var zeroFill = function (number, width) {
19
+ var s = number.toString();
20
+ while (s.length < width) {
21
+ s = "0" + s;
22
+ }
23
+ return s;
24
+ };
25
+
26
+ /**
27
+ * accepts array with start and end time in seconds
28
+ * returns timecode in deep-linking format
29
+ * @param times array
30
+ * @param forceHours bool (optional)
31
+ * @return string
32
+ **/
33
+ var generateTimecode = $.generateTimecode = function (times, leadingZeros, forceHours) {
34
+ function generatePart(time) {
35
+ var part, hours, minutes, seconds, milliseconds;
36
+ // prevent negative values from player
37
+ if (!time || time <= 0) {
38
+ return (leadingZeros || !time) ? (forceHours ? '00:00:00' : '00:00') : '--';
39
+ }
40
+
41
+ hours = Math.floor(time / 60 / 60);
42
+ minutes = Math.floor(time / 60) % 60;
43
+ seconds = Math.floor(time % 60) % 60;
44
+ milliseconds = Math.floor(time % 1 * 1000);
45
+
46
+ if (leadingZeros) {
47
+ // required (minutes : seconds)
48
+ part = zeroFill(minutes, 2) + ':' + zeroFill(seconds, 2);
49
+ hours = zeroFill(hours, 2);
50
+ hours = hours === '00' && !forceHours ? '' : hours + ':';
51
+ milliseconds = milliseconds ? '.' + zeroFill(milliseconds, 3) : '';
52
+ } else {
53
+ part = hours ? zeroFill(minutes, 2) : minutes.toString();
54
+ part += ':' + zeroFill(seconds, 2);
55
+ hours = hours ? hours + ':' : '';
56
+ milliseconds = milliseconds ? '.' + milliseconds : '';
57
+ }
58
+
59
+ return hours + part + milliseconds;
60
+ }
61
+
62
+ if (times[1] > 0 && times[1] < 9999999 && times[0] < times[1]) {
63
+ return generatePart(times[0]) + ',' + generatePart(times[1]);
64
+ }
65
+
66
+ return generatePart(times[0]);
67
+ };
68
+
69
+ /**
70
+ * parses time code into seconds
71
+ * @param string timecode
72
+ * @return number
73
+ **/
74
+ var parseTimecode = function (timecode) {
75
+ var parts, startTime = 0,
76
+ endTime = 0;
77
+
78
+ if (timecode) {
79
+ parts = timecode.match(timecodeRegExp);
80
+
81
+ if (parts && parts.length === 10) {
82
+ // hours
83
+ startTime += parts[1] ? parseInt(parts[1], 10) * 60 * 60 : 0;
84
+ // minutes
85
+ startTime += parseInt(parts[2], 10) * 60;
86
+ // seconds
87
+ startTime += parseInt(parts[3], 10);
88
+ // milliseconds
89
+ startTime += parts[4] ? parseFloat(parts[4]) : 0;
90
+ // no negative time
91
+ startTime = Math.max(startTime, 0);
92
+
93
+ // if there only a startTime but no endTime
94
+ if (parts[5] === undefined) {
95
+ return [startTime, false];
96
+ }
97
+
98
+ // hours
99
+ endTime += parts[6] ? parseInt(parts[6], 10) * 60 * 60 : 0;
100
+ // minutes
101
+ endTime += parseInt(parts[7], 10) * 60;
102
+ // seconds
103
+ endTime += parseInt(parts[8], 10);
104
+ // milliseconds
105
+ endTime += parts[9] ? parseFloat(parts[9]) : 0;
106
+ // no negative time
107
+ endTime = Math.max(endTime, 0);
108
+
109
+ return (endTime > startTime) ? [startTime, endTime] : [startTime, false];
110
+ }
111
+ }
112
+ return false;
113
+ };
114
+
115
+ var checkCurrentURL = function () {
116
+ var deepLink;
117
+ deepLink = parseTimecode(window.location.href);
118
+ if (deepLink !== false) {
119
+ startAtTime = deepLink[0];
120
+ stopAtTime = deepLink[1];
121
+ }
122
+ };
123
+
124
+ var validateURL = function (url) {
125
+ //de comment this to validate URLs, if you want use relative paths leave it so.
126
+ //var urlregex = /(^|\s)((https?:\/\/)?[\w\-]+(\.[\w\-]+)+\.?(:\d+)?(\/\S*)?)/gi;
127
+ //url = url.match(urlregex);
128
+ //return (url !== null) ? url[0] : url;
129
+ return url.trim();
130
+ };
131
+
132
+ /**
133
+ * add a string as hash in the adressbar
134
+ * @param string fragment
135
+ **/
136
+ var setFragmentURL = function (fragment) {
137
+ window.location.hash = fragment;
138
+ };
139
+
140
+ /**
141
+ * update the chapter list when the data is loaded
142
+ * @param object player
143
+ * @param object marks
144
+ **/
145
+ var updateChapterMarks = function (player, marks) {
146
+ var coverimg = marks.closest('.podlovewebplayer_wrapper').find('.coverimg');
147
+ marks.each(function () {
148
+ var deepLink, isBuffered, chapterimg = null,
149
+ mark = $(this),
150
+ startTime = mark.data('start'),
151
+ endTime = mark.data('end'),
152
+ isEnabled = mark.data('enabled'),
153
+ isActive = player.currentTime > startTime - 0.3 &&
154
+ player.currentTime <= endTime;
155
+ // prevent timing errors
156
+ if (player.buffered.length > 0) {
157
+ isBuffered = player.buffered.end(0) > startTime;
158
+ }
159
+ if (isActive) {
160
+ chapterimg = validateURL(mark.data('img'));
161
+ if ((chapterimg !== null)&&(mark.hasClass('active'))) {
162
+ if ((coverimg.attr('src') !== chapterimg)&&(chapterimg.length > 5)) {
163
+ coverimg.attr('src',chapterimg);
164
+ }
165
+ } else {
166
+ if (coverimg.attr('src') !== coverimg.data('img')) {
167
+ coverimg.attr('src', coverimg.data('img'));
168
+ }
169
+ }
170
+ mark.addClass('active').siblings().removeClass('active');
171
+ }
172
+ if (!isEnabled && isBuffered) {
173
+ deepLink = '#t=' + generateTimecode([startTime, endTime]);
174
+ $(mark).data('enabled', true).addClass('loaded').find('a[rel=player]').removeClass('disabled');
175
+ }
176
+ });
177
+ };
178
+
179
+ var checkTime = function (e) {
180
+ if (players.length > 1) {
181
+ return;
182
+ }
183
+ var player = e.data.player;
184
+ if (startAtTime !== false &&
185
+ //Kinda hackish: Make sure that the timejump is at least 1 second (fix for OGG/Firefox)
186
+ (player.lastCheck === undefined || Math.abs(startAtTime - player.lastCheck) > 1)) {
187
+ player.setCurrentTime(startAtTime);
188
+ player.lastCheck = startAtTime;
189
+ startAtTime = false;
190
+ }
191
+ if (stopAtTime !== false && player.currentTime >= stopAtTime) {
192
+ player.pause();
193
+ stopAtTime = false;
194
+ }
195
+ };
196
+
197
+ var addressCurrentTime = function (e) {
198
+ var fragment;
199
+ if (players.length === 1) {
200
+ fragment = 't=' + generateTimecode([e.data.player.currentTime]);
201
+ setFragmentURL(fragment);
202
+ }
203
+ };
204
+
205
+ /**
206
+ * Given a list of chapters, this function creates the chapter table for the player.
207
+ */
208
+ var generateChapterTable = function (params) {
209
+
210
+ var div = $('<div class="podlovewebplayer_chapterbox showonplay"><table><caption>Podcast Chapters</caption><thead><tr><th scope="col">Chapter Number</th><th scope="col">Start time</th><th scope="col">Title</th><th scope="col">Duration</th></tr></thead><tbody></tbody></table></div>'),
211
+ table = div.children('table'),
212
+ tbody = table.children('tbody');
213
+
214
+ if (params.chaptersVisible === true) {
215
+ div.addClass('active');
216
+ }
217
+
218
+ table.addClass('podlovewebplayer_chapters');
219
+ if (params.chapterlinks !== 'false') {
220
+ table.addClass('linked linked_' + params.chapterlinks);
221
+ }
222
+
223
+ //prepare row data
224
+ var tempchapters = params.chapters;
225
+ var maxchapterstart = 0;
226
+
227
+ //first round: kill empty rows and build structured object
228
+ if (typeof params.chapters === 'string') {
229
+ tempchapters = [];
230
+ $.each(params.chapters.split("\n"), function (i, chapter) {
231
+
232
+ //exit early if this line contains nothing but whitespace
233
+ if (!/\S/.test(chapter)) {
234
+ return;
235
+ }
236
+
237
+ //extract the timestamp
238
+ var line = $.trim(chapter);
239
+ var tc = parseTimecode(line.substring(0, line.indexOf(' ')));
240
+ var chaptitle = $.trim(line.substring(line.indexOf(' ')));
241
+ tempchapters.push({
242
+ start: tc[0],
243
+ code: chaptitle
244
+ });
245
+ });
246
+ } else {
247
+ // assume array of objects
248
+ $.each(tempchapters, function (key, value) {
249
+ value.code = value.title;
250
+ if (typeof value.start === 'string') {
251
+ value.start = parseTimecode(value.start)[0];
252
+ }
253
+ });
254
+ }
255
+
256
+ // order is not guaranteed: http://podlove.org/simple-chapters/
257
+ tempchapters = tempchapters.sort(function (a, b) {
258
+ return a.start - b.start;
259
+ });
260
+
261
+ //second round: collect more information
262
+ maxchapterstart = Math.max.apply(Math,
263
+ $.map(tempchapters, function (value, i) {
264
+ var next = tempchapters[i + 1];
265
+
266
+ // we use `this.end` to quickly calculate the duration in the next round
267
+ if (next) {
268
+ value.end = next.start;
269
+ }
270
+
271
+ // we need this data for proper formatting
272
+ return value.start;
273
+ }));
274
+
275
+
276
+ //this is a "template" for each chapter row
277
+ var rowDummy = $('<tr class="chaptertr" data-start="" data-end="" data-img=""><td class="starttime"><span></span></td><td class="chaptername"></td><td class="timecode">\n<span></span>\n</td>\n</tr>');
278
+
279
+ //third round: build actual dom table
280
+ $.each(tempchapters, function (i) {
281
+ var finalchapter = !tempchapters[i + 1],
282
+ duration = Math.round(this.end - this.start),
283
+ forceHours,
284
+ row = rowDummy.clone();
285
+
286
+ //make sure the duration for all chapters are equally formatted
287
+ if (!finalchapter) {
288
+ this.duration = generateTimecode([duration], false);
289
+ } else {
290
+ if (params.duration === 0) {
291
+ this.end = 9999999999;
292
+ this.duration = '…';
293
+ } else {
294
+ this.end = params.duration;
295
+ this.duration = generateTimecode([Math.round(this.end - this.start)], false);
296
+ }
297
+ }
298
+
299
+
300
+ if (i % 2) {
301
+ row.addClass('oddchapter');
302
+ }
303
+
304
+ //deeplink, start and end
305
+ row.attr({
306
+ 'data-start': this.start,
307
+ 'data-end': this.end,
308
+ 'data-img': (this.image !== undefined) ? this.image : ''
309
+ });
310
+
311
+ //if there is a chapter that starts after an hour, force '00:' on all previous chapters
312
+ forceHours = (maxchapterstart >= 3600);
313
+
314
+ //insert the chapter data
315
+ row.find('.starttime > span').text(generateTimecode([Math.round(this.start)], true, forceHours));
316
+ row.find('.chaptername').html(this.code);
317
+ row.find('.timecode > span').text(this.duration);
318
+
319
+ row.appendTo(tbody);
320
+ });
321
+ return div;
322
+ };
323
+
324
+ /**
325
+ * add chapter behavior and deeplinking: skip to referenced
326
+ * time position & write current time into address
327
+ * @param player object
328
+ */
329
+ var addBehavior = function (player, params, wrapper) {
330
+ var jqPlayer = $(player),
331
+ layoutedPlayer = jqPlayer,
332
+ canplay = false;
333
+
334
+ /**
335
+ * The `player` is an interface. It provides the play and pause functionality. The
336
+ * `layoutedPlayer` on the other hand is a DOM element. In native mode, these two
337
+ * are one and the same object. In Flash though the interface is a plain JS object.
338
+ */
339
+
340
+ if (players.length === 1) {
341
+ // check if deeplink is set
342
+ checkCurrentURL();
343
+ }
344
+
345
+ // get things straight for flash fallback
346
+ if (player.pluginType === 'flash') {
347
+ layoutedPlayer = $('#mep_' + player.id.substring(9));
348
+ }
349
+
350
+ // cache some jQ objects
351
+ var metainfo = wrapper.find('.podlovewebplayer_meta'),
352
+ summary = wrapper.find('.summary'),
353
+ podlovewebplayer_timecontrol = wrapper.find('.podlovewebplayer_timecontrol'),
354
+ podlovewebplayer_sharebuttons = wrapper.find('.podlovewebplayer_sharebuttons'),
355
+ podlovewebplayer_downloadbuttons = wrapper.find('.podlovewebplayer_downloadbuttons'),
356
+ chapterdiv = wrapper.find('.podlovewebplayer_chapterbox'),
357
+ list = wrapper.find('table'),
358
+ marks = list.find('tr');
359
+
360
+ // fix height of summary for better toggability
361
+ summary.each(function () {
362
+ $(this).data('height', $(this).height() + 10);
363
+ if (!$(this).hasClass('active')) {
364
+ $(this).height('0px');
365
+ } else {
366
+ $(this).height($(this).find('div.summarydiv').height() + 10 + 'px');
367
+ }
368
+ });
369
+
370
+ chapterdiv.each(function () {
371
+ $(this).data('height', $(this).find('.podlovewebplayer_chapters').height());
372
+ if (!$(this).hasClass('active')) {
373
+ $(this).height('0px');
374
+ } else {
375
+ $(this).height($(this).find('.podlovewebplayer_chapters').height() + 'px');
376
+ }
377
+ });
378
+
379
+ if (metainfo.length === 1) {
380
+
381
+ metainfo.find('a.infowindow').click(function () {
382
+ summary.toggleClass('active');
383
+ if (summary.hasClass('active')) {
384
+ summary.height(summary.find('div.summarydiv').height() + 10 + 'px');
385
+ } else {
386
+ summary.height('0px');
387
+ }
388
+ return false;
389
+ });
390
+
391
+ metainfo.find('a.showcontrols').on('click', function () {
392
+ podlovewebplayer_timecontrol.toggleClass('active');
393
+ if (podlovewebplayer_sharebuttons !== undefined) {
394
+ if (podlovewebplayer_sharebuttons.hasClass('active')) {
395
+ podlovewebplayer_sharebuttons.removeClass('active');
396
+ } else if (podlovewebplayer_downloadbuttons.hasClass('active')) {
397
+ podlovewebplayer_downloadbuttons.removeClass('active');
398
+ }
399
+ }
400
+ return false;
401
+ });
402
+
403
+ metainfo.find('a.showsharebuttons').on('click', function () {
404
+ podlovewebplayer_sharebuttons.toggleClass('active');
405
+ if (podlovewebplayer_timecontrol.hasClass('active')) {
406
+ podlovewebplayer_timecontrol.removeClass('active');
407
+ } else if (podlovewebplayer_downloadbuttons.hasClass('active')) {
408
+ podlovewebplayer_downloadbuttons.removeClass('active');
409
+ }
410
+ return false;
411
+ });
412
+
413
+ metainfo.find('a.showdownloadbuttons').on('click', function () {
414
+ podlovewebplayer_downloadbuttons.toggleClass('active');
415
+ if (podlovewebplayer_timecontrol.hasClass('active')) {
416
+ podlovewebplayer_timecontrol.removeClass('active');
417
+ } else if (podlovewebplayer_sharebuttons.hasClass('active')) {
418
+ podlovewebplayer_sharebuttons.removeClass('active');
419
+ }
420
+ return false;
421
+ });
422
+
423
+ metainfo.find('.bigplay').on('click', function () {
424
+ if ($(this).hasClass('bigplay')) {
425
+ var playButton = $(this).parent().find('.bigplay');
426
+
427
+ if ((typeof player.currentTime === 'number') && (player.currentTime > 0)) {
428
+ if (player.paused) {
429
+ playButton.addClass('playing');
430
+ player.play();
431
+ } else {
432
+ playButton.removeClass('playing');
433
+ player.pause();
434
+ }
435
+ } else {
436
+ if (!playButton.hasClass('playing')) {
437
+ playButton.addClass('playing');
438
+ $(this).parent().parent().find('.mejs-time-buffering').show();
439
+ }
440
+ // flash fallback needs additional pause
441
+ if (player.pluginType === 'flash') {
442
+ player.pause();
443
+ }
444
+ player.play();
445
+ }
446
+ }
447
+ return false;
448
+ });
449
+
450
+ wrapper.find('.chaptertoggle').unbind('click').click(function () {
451
+ wrapper.find('.podlovewebplayer_chapterbox').toggleClass('active');
452
+ if (wrapper.find('.podlovewebplayer_chapterbox').hasClass('active')) {
453
+ wrapper.find('.podlovewebplayer_chapterbox').height(wrapper.find('.podlovewebplayer_chapterbox').data('height') + 'px');
454
+ } else {
455
+ wrapper.find('.podlovewebplayer_chapterbox').height('0px');
456
+ }
457
+ return false;
458
+ });
459
+
460
+ wrapper.find('.prevbutton').click(function () {
461
+ if ((typeof player.currentTime === 'number') && (player.currentTime > 0)) {
462
+ if (player.currentTime > chapterdiv.find('.active').data('start') + 10) {
463
+ player.setCurrentTime(chapterdiv.find('.active').data('start'));
464
+ } else {
465
+ player.setCurrentTime(chapterdiv.find('.active').prev().data('start'));
466
+ }
467
+ } else {
468
+ player.play();
469
+ }
470
+ return false;
471
+ });
472
+
473
+ wrapper.find('.nextbutton').click(function () {
474
+ if ((typeof player.currentTime === 'number') && (player.currentTime > 0)) {
475
+ player.setCurrentTime(chapterdiv.find('.active').next().data('start'));
476
+ } else {
477
+ player.play();
478
+ }
479
+ return false;
480
+ });
481
+
482
+ wrapper.find('.rewindbutton').click(function () {
483
+ if ((typeof player.currentTime === 'number') && (player.currentTime > 0)) {
484
+ player.setCurrentTime(player.currentTime - 30);
485
+ } else {
486
+ player.play();
487
+ }
488
+ return false;
489
+ });
490
+
491
+ wrapper.find('.forwardbutton').click(function () {
492
+ if ((typeof player.currentTime === 'number') && (player.currentTime > 0)) {
493
+ player.setCurrentTime(player.currentTime + 30);
494
+ } else {
495
+ player.play();
496
+ }
497
+ return false;
498
+ });
499
+
500
+ wrapper.find('.currentbutton').click(function () {
501
+ window.prompt('This URL directly points to this episode', $(this).closest('.podlovewebplayer_wrapper').find('.episodetitle a').attr('href'));
502
+ return false;
503
+ });
504
+
505
+ wrapper.find('.tweetbutton').click(function () {
506
+ window.open('https://twitter.com/share?text=' + encodeURI($(this).closest('.podlovewebplayer_wrapper').find('.episodetitle a').text()) + '&url=' + encodeURI($(this).closest('.podlovewebplayer_wrapper').find('.episodetitle a').attr('href')), 'tweet it', 'width=550,height=420,resizable=yes');
507
+ return false;
508
+ });
509
+
510
+ wrapper.find('.fbsharebutton').click(function () {
511
+ window.open('http://www.facebook.com/share.php?t=' + encodeURI($(this).closest('.podlovewebplayer_wrapper').find('.episodetitle a').text()) + '&u=' + encodeURI($(this).closest('.podlovewebplayer_wrapper').find('.episodetitle a').attr('href')), 'share it', 'width=550,height=340,resizable=yes');
512
+ return false;
513
+ });
514
+
515
+ wrapper.find('.gplusbutton').click(function () {
516
+ window.open('https://plus.google.com/share?title=' + encodeURI($(this).closest('.podlovewebplayer_wrapper').find('.episodetitle a').text()) + '&url=' + encodeURI($(this).closest('.podlovewebplayer_wrapper').find('.episodetitle a').attr('href')), 'plus it', 'width=550,height=420,resizable=yes');
517
+ return false;
518
+ });
519
+
520
+ wrapper.find('.adnbutton').click(function () {
521
+ window.open('https://alpha.app.net/intent/post?text=' + encodeURI($(this).closest('.podlovewebplayer_wrapper').find('.episodetitle a').text()) + '%20' + encodeURI($(this).closest('.podlovewebplayer_wrapper').find('.episodetitle a').attr('href')), 'plus it', 'width=550,height=420,resizable=yes');
522
+ return false;
523
+ });
524
+
525
+ wrapper.find('.mailbutton').click(function () {
526
+ window.location = 'mailto:?subject=' + encodeURI($(this).closest('.podlovewebplayer_wrapper').find('.episodetitle a').text()) + '&body=' + encodeURI($(this).closest('.podlovewebplayer_wrapper').find('.episodetitle a').text()) + '%20%3C' + encodeURI($(this).closest('.podlovewebplayer_wrapper').find('.episodetitle a').attr('href')) + '%3E';
527
+ return false;
528
+ });
529
+
530
+ wrapper.find('.downloadbutton').click(function () {
531
+ $(this).parent().find(".fileselect option:selected").each(function () {
532
+ window.location = $(this).data('dlurl');
533
+ });
534
+ return false;
535
+ });
536
+
537
+ wrapper.find('.openfilebutton').click(function () {
538
+ $(this).parent().find(".fileselect option:selected").each(function () {
539
+ window.open($(this).data('url'), 'Podlove Popup', 'width=550,height=420,resizable=yes');
540
+ });
541
+ return false;
542
+ });
543
+
544
+ wrapper.find('.fileinfobutton').click(function () {
545
+ $(this).parent().find(".fileselect option:selected").each(function () {
546
+ window.prompt('file URL:', $(this).val());
547
+ });
548
+ return false;
549
+ });
550
+ }
551
+
552
+ // chapters list
553
+ list
554
+ .show()
555
+ .delegate('.chaptertr', 'click', function (e) {
556
+ if ($(this).closest('table').hasClass('linked_all') || $(this).closest('tr').hasClass('loaded')) {
557
+ e.preventDefault();
558
+ var mark = $(this).closest('tr'),
559
+ startTime = mark.data('start');
560
+ //endTime = mark.data('end');
561
+
562
+ // If there is only one player also set deepLink
563
+ if (players.length === 1) {
564
+ // setFragmentURL('t=' + generateTimecode([startTime, endTime]));
565
+ setFragmentURL('t=' + generateTimecode([startTime]));
566
+ } else {
567
+ if (canplay) {
568
+ // Basic Chapter Mark function (without deeplinking)
569
+ player.setCurrentTime(startTime);
570
+ } else {
571
+ jqPlayer.one('canplay', function () {
572
+ player.setCurrentTime(startTime);
573
+ });
574
+ }
575
+ }
576
+
577
+ // flash fallback needs additional pause
578
+ if (player.pluginType === 'flash') {
579
+ player.pause();
580
+ }
581
+ player.play();
582
+ }
583
+ return false;
584
+ });
585
+
586
+ // wait for the player or you'll get DOM EXCEPTIONS
587
+ // And just listen once because of a special behaviour in firefox
588
+ // --> https://bugzilla.mozilla.org/show_bug.cgi?id=664842
589
+ jqPlayer.one('canplay', function () {
590
+ canplay = true;
591
+
592
+ // add duration of final chapter
593
+ if (player.duration) {
594
+ marks.find('.timecode code').eq(-1).each(function () {
595
+ var start = Math.floor($(this).closest('tr').data('start'));
596
+ var end = Math.floor(player.duration);
597
+ $(this).text(generateTimecode([end - start]));
598
+ });
599
+ }
600
+
601
+ // add Deeplink Behavior if there is only one player on the site
602
+ if (players.length === 1) {
603
+ jqPlayer.bind('play timeupdate', {
604
+ player: player
605
+ }, checkTime)
606
+ .bind('pause', {
607
+ player: player
608
+ }, addressCurrentTime);
609
+ // disabled 'cause it overrides chapter clicks
610
+ // bind seeked to addressCurrentTime
611
+
612
+ checkCurrentURL();
613
+
614
+ // handle browser history navigation
615
+ $(window).bind('hashchange onpopstate', function(e) {
616
+ if (!ignoreHashChange) {
617
+ checkCurrentURL();
618
+ }
619
+ ignoreHashChange = false;
620
+ });
621
+ }
622
+ });
623
+
624
+ // always update Chaptermarks though
625
+ jqPlayer
626
+ .on('timeupdate', function () {
627
+ updateChapterMarks(player, marks);
628
+ })
629
+ // update play/pause status
630
+ .on('play playing', function () {
631
+ if (!player.persistingTimer) {
632
+ player.persistingTimer = window.setInterval(function() {
633
+ if (players.length === 1) {
634
+ ignoreHashChange = true;
635
+ window.location.replace('#t=' + generateTimecode([player.currentTime, false]));
636
+ }
637
+ localStorage['podloveWebPlayerTime-' + params.permalink] = player.currentTime;
638
+ }, 5000);
639
+ }
640
+ list.find('.paused').removeClass('paused');
641
+ if (metainfo.length === 1) {
642
+ metainfo.find('.bigplay').addClass('playing');
643
+ }
644
+ })
645
+ .on('pause', function () {
646
+ window.clearInterval(player.persistingTimer);
647
+ player.persistingTimer = null;
648
+
649
+ if (metainfo.length === 1) {
650
+ metainfo.find('.bigplay').removeClass('playing');
651
+ }
652
+ });
653
+ };
654
+
655
+ $.fn.podlovewebplayer = function (options) {
656
+
657
+ // MEJS options default values
658
+ var mejsoptions = {
659
+ defaultVideoWidth: 480,
660
+ defaultVideoHeight: 270,
661
+ videoWidth: -1,
662
+ videoHeight: -1,
663
+ audioWidth: -1,
664
+ audioHeight: 30,
665
+ startVolume: 0.8,
666
+ loop: false,
667
+ enableAutosize: true,
668
+ features: ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen'],
669
+ alwaysShowControls: false,
670
+ iPadUseNativeControls: false,
671
+ iPhoneUseNativeControls: false,
672
+ AndroidUseNativeControls: false,
673
+ alwaysShowHours: false,
674
+ showTimecodeFrameCount: false,
675
+ framesPerSecond: 25,
676
+ enableKeyboard: true,
677
+ pauseOtherPlayers: true,
678
+ duration: false,
679
+ plugins: ['flash', 'silverlight'],
680
+ pluginPath: './static/',
681
+ flashName: 'flashmediaelement.swf',
682
+ silverlightName: 'silverlightmediaelement.xap'
683
+ };
684
+
685
+ // Additional parameters default values
686
+ var params = $.extend({}, {
687
+ chapterlinks: 'all',
688
+ width: '100%',
689
+ duration: false,
690
+ chaptersVisible: false,
691
+ timecontrolsVisible: false,
692
+ sharebuttonsVisible: false,
693
+ downloadbuttonsVisible: false,
694
+ summaryVisible: false,
695
+ hidetimebutton: false,
696
+ hidedownloadbutton: false,
697
+ hidesharebutton: false,
698
+ sharewholeepisode: false,
699
+ sources: []
700
+ }, options);
701
+
702
+ // turn each player in the current set into a Podlove Web Player
703
+ return this.each(function (index, player) {
704
+
705
+ var richplayer = false,
706
+ haschapters = false,
707
+ hiddenTab = false,
708
+ i = 0;
709
+
710
+ //fine tuning params
711
+ if (params.width.toLowerCase() === 'auto') {
712
+ params.width = '100%';
713
+ } else {
714
+ params.width = params.width.replace('px', '');
715
+ }
716
+
717
+ //audio params
718
+ if (player.tagName === 'AUDIO') {
719
+ if (params.audioWidth !== undefined) {
720
+ params.width = params.audioWidth;
721
+ }
722
+ mejsoptions.audioWidth = params.width;
723
+
724
+ //kill fullscreen button
725
+ $.each(mejsoptions.features, function (i) {
726
+ if (this === 'fullscreen') {
727
+ mejsoptions.features.splice(i, 1);
728
+ }
729
+ });
730
+
731
+ //video params
732
+ } else if (player.tagName === 'VIDEO') {
733
+
734
+ if (params.height !== undefined) {
735
+ mejsoptions.videoWidth = params.width;
736
+ mejsoptions.videoHeight = params.height;
737
+ }
738
+
739
+ if ($(player).attr('width') !== undefined) {
740
+ params.width = $(player).attr('width');
741
+ }
742
+ }
743
+
744
+ //duration can be given in seconds or in NPT format
745
+ if (params.duration && params.duration !== parseInt(params.duration, 10)) {
746
+ var secArray = parseTimecode(params.duration);
747
+ params.duration = secArray[0];
748
+ }
749
+
750
+ //Overwrite MEJS default values with actual data
751
+ $.each(mejsoptions, function (key) {
752
+ if (params[key] !== undefined) {
753
+ mejsoptions[key] = params[key];
754
+ }
755
+ });
756
+
757
+ //wrapper and init stuff
758
+ if (params.width.toString().trim() === parseInt(params.width, 10).toString().trim()) {
759
+ params.width = params.width.toString().trim()+'px';
760
+ }
761
+
762
+ var orig = player;
763
+
764
+ player = $(player).clone().wrap('<div class="podlovewebplayer_wrapper" style="width: ' + params.width + '"></div>')[0];
765
+ var deepLink,
766
+ wrapper = $(player).parent();
767
+
768
+ players.push(player);
769
+
770
+ //add params from html fallback area and remove them from the DOM-tree
771
+ $(player).find('[data-pwp]').each(function () {
772
+ params[$(this).data('pwp')] = $(this).html();
773
+ $(this).remove();
774
+ });
775
+ //add params from audio and video elements
776
+ $(player).find('source').each(function () {
777
+ if (params.sources !== undefined) {
778
+ params.sources.push($(this).attr('src'));
779
+ } else {
780
+ params.sources[0] = $(this).attr('src');
781
+ }
782
+ });
783
+
784
+ //build rich player with meta data
785
+ if (params.chapters !== undefined ||
786
+ params.title !== undefined ||
787
+ params.subtitle !== undefined ||
788
+ params.summary !== undefined ||
789
+ params.poster !== undefined ||
790
+ $(player).attr('poster') !== undefined) {
791
+
792
+ //set status variable
793
+ richplayer = true;
794
+
795
+ wrapper.addClass('podlovewebplayer_' + player.tagName.toLowerCase());
796
+
797
+ if (player.tagName === "AUDIO") {
798
+
799
+ //kill play/pause button from miniplayer
800
+ $.each(mejsoptions.features, function (i) {
801
+ if (this === 'playpause') {
802
+ mejsoptions.features.splice(i, 1);
803
+ }
804
+ });
805
+
806
+ wrapper.prepend('<div class="podlovewebplayer_meta"></div>');
807
+
808
+ wrapper.find('.podlovewebplayer_meta').prepend('<a class="bigplay" title="Play Episode" href="#"></a>');
809
+ if (params.poster !== undefined) {
810
+ wrapper.find('.podlovewebplayer_meta').append(
811
+ '<div class="coverart"><img class="coverimg" src="' + params.poster + '" data-img="' + params.poster + '" alt=""></div>');
812
+ }
813
+ if ($(player).attr('poster') !== undefined) {
814
+ wrapper.find('.podlovewebplayer_meta').append(
815
+ '<div class="coverart"><img src="' + $(player).attr('poster') + '" alt=""></div>');
816
+ }
817
+ }
818
+
819
+ if (player.tagName === "VIDEO") {
820
+ wrapper.prepend('<div class="podlovewebplayer_top"></div>');
821
+ wrapper.append('<div class="podlovewebplayer_meta"></div>');
822
+ }
823
+
824
+ if (params.title !== undefined) {
825
+ if (params.permalink !== undefined) {
826
+ wrapper.find('.podlovewebplayer_meta').append(
827
+ '<h3 class="episodetitle"><a href="' + params.permalink + '">' + params.title + '</a></h3>');
828
+ } else {
829
+ wrapper.find('.podlovewebplayer_meta').append(
830
+ '<h3 class="episodetitle">' + params.title + '</h3>');
831
+ }
832
+ }
833
+ if (params.subtitle !== undefined) {
834
+ wrapper.find('.podlovewebplayer_meta').append(
835
+ '<div class="subtitle">' + params.subtitle + '</div>');
836
+ } else {
837
+ if (params.title !== undefined) {
838
+ if (params.title.length < 42) {
839
+ wrapper.addClass('podlovewebplayer_smallplayer');
840
+ }
841
+ }
842
+ wrapper.find('.podlovewebplayer_meta').append(
843
+ '<div class="subtitle"></div>');
844
+ }
845
+
846
+ //always render toggler buttons wrapper
847
+ wrapper.find('.podlovewebplayer_meta').append('<div class="togglers"></div>');
848
+ wrapper.on('playerresize', function () {
849
+ wrapper.find('.podlovewebplayer_chapterbox').data('height', wrapper.find('.podlovewebplayer_chapters').height());
850
+ if (wrapper.find('.podlovewebplayer_chapterbox').hasClass('active')) {
851
+ wrapper.find('.podlovewebplayer_chapterbox').height(wrapper.find('.podlovewebplayer_chapters').height() + 'px');
852
+ }
853
+ wrapper.find('.summary').data('height', wrapper.find('.summarydiv').height());
854
+ if (wrapper.find('.summary').hasClass('active')) {
855
+ wrapper.find('.summary').height(wrapper.find('.summarydiv').height() + 'px');
856
+ }
857
+ });
858
+
859
+ if (params.summary !== undefined) {
860
+ var summaryActive = "";
861
+ if (params.summaryVisible === true) {
862
+ summaryActive = " active";
863
+ }
864
+ wrapper.find('.togglers').append(
865
+ '<a href="#" class="infowindow infobuttons pwp-icon-info-circle" title="More information about this"></a>');
866
+ wrapper.find('.podlovewebplayer_meta').after(
867
+ '<div class="summary' + summaryActive + '"><div class="summarydiv">' + params.summary + '</div></div>');
868
+ }
869
+ if (params.chapters !== undefined) {
870
+ wrapper.find('.togglers').append(
871
+ '<a href="#" class="chaptertoggle infobuttons pwp-icon-list-bullet" title="Show/hide chapters"></a>');
872
+ }
873
+ if (params.hidetimebutton !== true) {
874
+ wrapper.find('.togglers').append('<a href="#" class="showcontrols infobuttons pwp-icon-clock" title="Show/hide time navigation controls"></a>');
875
+ }
876
+ }
877
+
878
+ var timecontrolsActive = "";
879
+ if (params.timecontrolsVisible === true) {
880
+ timecontrolsActive = " active";
881
+ }
882
+ var sharebuttonsActive = "";
883
+ if (params.sharebuttonsVisible === true) {
884
+ sharebuttonsActive = " active";
885
+ }
886
+ var downloadbuttonsActive = "";
887
+ if (params.downloadbuttonsVisible === true) {
888
+ downloadbuttonsActive = " active";
889
+ }
890
+
891
+ wrapper.append('<div class="podlovewebplayer_timecontrol podlovewebplayer_controlbox' + timecontrolsActive + '"></div>');
892
+
893
+ if (params.chapters !== undefined) {
894
+ wrapper.find('.podlovewebplayer_timecontrol').append('<a href="#" class="prevbutton infobuttons pwp-icon-to-start" title="Jump backward to previous chapter"></a><a href="#" class="nextbutton infobuttons pwp-icon-to-end" title="next chapter"></a>');
895
+ wrapper.find('.controlbox').append('<a href="#" class="prevbutton infobuttons pwp-icon-step-backward" title="previous chapter"></a><a href="#" class="nextbutton infobuttons pwp-icon-to-end" title="Jump to next chapter"></a>');
896
+ }
897
+ wrapper.find('.podlovewebplayer_timecontrol').append(
898
+ '<a href="#" class="rewindbutton infobuttons pwp-icon-fast-bw" title="Rewind 30 seconds"></a>');
899
+ wrapper.find('.podlovewebplayer_timecontrol').append('<a href="#" class="forwardbutton infobuttons pwp-icon-fast-fw" title="Fast forward 30 seconds"></a>');
900
+ if ((wrapper.closest('.podlovewebplayer_wrapper').find('.episodetitle a').attr('href') !== undefined) && (params.hidesharebutton !== true)) {
901
+ wrapper.append('<div class="podlovewebplayer_sharebuttons podlovewebplayer_controlbox' + sharebuttonsActive + '"></div>');
902
+ wrapper.find('.togglers').append('<a href="#" class="showsharebuttons infobuttons pwp-icon-export" title="Show/hide sharing controls"></a>');
903
+ wrapper.find('.podlovewebplayer_sharebuttons').append('<a href="#" class="currentbutton infobuttons pwp-icon-link" title="Get URL for this"></a>');
904
+ wrapper.find('.podlovewebplayer_sharebuttons').append('<a href="#" target="_blank" class="tweetbutton infobuttons pwp-icon-twitter" title="Share this on Twitter"></a>');
905
+ wrapper.find('.podlovewebplayer_sharebuttons').append('<a href="#" target="_blank" class="fbsharebutton infobuttons pwp-icon-facebook" title="Share this on Facebook"></a>');
906
+ wrapper.find('.podlovewebplayer_sharebuttons').append('<a href="#" target="_blank" class="gplusbutton infobuttons pwp-icon-gplus" title="Share this on Google+"></a>');
907
+ wrapper.find('.podlovewebplayer_sharebuttons').append('<a href="#" target="_blank" class="adnbutton infobuttons pwp-icon-appnet" title="Share this on App.net"></a>');
908
+ wrapper.find('.podlovewebplayer_sharebuttons').append('<a href="#" target="_blank" class="mailbutton infobuttons pwp-icon-mail" title="Share this via e-mail"></a>');
909
+ }
910
+ if (((params.downloads !== undefined) || (params.sources !== undefined)) && (params.hidedownloadbutton !== true)) {
911
+ var size, name, selectform = '<select name="downloads" class="fileselect" size="1" onchange="this.value=this.options[this.selectedIndex].value;">';
912
+ wrapper.append('<div class="podlovewebplayer_downloadbuttons podlovewebplayer_controlbox' + downloadbuttonsActive + '"></div>');
913
+ wrapper.find('.togglers').append('<a href="#" class="showdownloadbuttons infobuttons pwp-icon-download" title="Show/hide download bar"></a>');
914
+ if (params.downloads !== undefined) {
915
+ for (i = 0; i < params.downloads.length; i += 1) {
916
+ size = (parseInt(params.downloads[i].size, 10) < 1048704) ? Math.round(parseInt(params.downloads[i].size, 10) / 100) / 10 + 'kB' : Math.round(parseInt(params.downloads[i].size, 10) / 1000 / 100) / 10 + 'MB';
917
+ selectform += '<option value="' + params.downloads[i].url + '" data-url="' + params.downloads[i].url + '" data-dlurl="' + params.downloads[i].dlurl + '">' + params.downloads[i].name + ' (<small>' + size + '</small>)</option>';
918
+ }
919
+ } else {
920
+ for (i = 0; i < params.sources.length; i += 1) {
921
+ name = params.sources[i].split('.');
922
+ name = name[name.length - 1];
923
+ selectform += '<option value="' + params.sources[i] + '" data-url="' + params.sources[i] + '" data-dlurl="' + params.sources[i] + '">' + name + '</option>';
924
+ }
925
+ }
926
+
927
+ selectform += '</select>';
928
+ wrapper.find('.podlovewebplayer_downloadbuttons').append(selectform);
929
+ if (params.downloads !== undefined) {
930
+ wrapper.find('.podlovewebplayer_downloadbuttons').append('<a href="#" class="downloadbutton infobuttons pwp-icon-download" title="Download"></a> ');
931
+ }
932
+ wrapper.find('.podlovewebplayer_downloadbuttons').append('<a href="#" class="openfilebutton infobuttons pwp-icon-link-ext" title="Open"></a> ');
933
+ wrapper.find('.podlovewebplayer_downloadbuttons').append('<a href="#" class="fileinfobutton infobuttons pwp-icon-info-circle" title="Info"></a> ');
934
+ }
935
+
936
+ //build chapter table
937
+ if (params.chapters !== undefined) {
938
+ haschapters = true;
939
+
940
+ generateChapterTable(params).appendTo(wrapper);
941
+ }
942
+
943
+ if (richplayer || haschapters) {
944
+ wrapper.append('<div class="podlovewebplayer_tableend"></div>');
945
+ }
946
+
947
+ // parse deeplink
948
+ deepLink = parseTimecode(window.location.href);
949
+ if (deepLink !== false && players.length === 1) {
950
+ if (document.hidden !== undefined) {
951
+ hiddenTab = document.hidden;
952
+ } else if (document.mozHidden !== undefined) {
953
+ hiddenTab = document.mozHidden;
954
+ } else if (document.msHidden !== undefined) {
955
+ hiddenTab = document.msHidden;
956
+ } else if (document.webkitHidden !== undefined) {
957
+ hiddenTab = document.webkitHidden;
958
+ }
959
+
960
+ if(hiddenTab === true) {
961
+ $(player).attr({
962
+ preload: 'auto'
963
+ });
964
+ } else {
965
+ $(player).attr({
966
+ preload: 'auto',
967
+ autoplay: 'autoplay'
968
+ });
969
+ }
970
+ startAtTime = deepLink[0];
971
+ stopAtTime = deepLink[1];
972
+ } else if (params && params.permalink) {
973
+ var storageKey = 'podloveWebPlayerTime-' + params.permalink;
974
+ if (localStorage[storageKey]) {
975
+ $(player).one('canplay', function() {
976
+ this.currentTime = +localStorage[storageKey];
977
+ });
978
+ }
979
+ }
980
+
981
+ $(player).on('ended', function() {
982
+ localStorage.removeItem('podloveWebPlayerTime-' + params.permalink);
983
+ });
984
+
985
+ // init MEJS to player
986
+ mejsoptions.success = function (player) {
987
+ addBehavior(player, params, wrapper);
988
+ if (deepLink !== false && players.length === 1) {
989
+ $('html, body').delay(150).animate({
990
+ scrollTop: $('.podlovewebplayer_wrapper:first').offset().top - 25
991
+ });
992
+ }
993
+ };
994
+
995
+ $(orig).replaceWith(wrapper);
996
+ $(player).mediaelementplayer(mejsoptions);
997
+ });
998
+ };
999
+ }(jQuery));