Easy Video Player - Version 1.0.9

Version Description

  • Easy Video Player is now compatible with WordPress 4.3
Download this release

Release Info

Developer naa986
Plugin Icon 128x128 Easy Video Player
Version 1.0.9
Comparing to
See all releases

Code changes from version 1.0.2 to 1.0.9

Files changed (47) hide show
  1. easy-video-player.php +174 -131
  2. lib/embed.min.js +1 -0
  3. lib/flowplayer.js +886 -423
  4. lib/flowplayer.min.js +7 -0
  5. lib/flowplayer.swf +0 -0
  6. lib/img/Thumbs.db +0 -0
  7. lib/img/black.png +0 -0
  8. lib/img/black@x2.png +0 -0
  9. lib/img/play_black.png +0 -0
  10. lib/img/play_black@x2.png +0 -0
  11. lib/img/play_white.png +0 -0
  12. lib/img/play_white@x2.png +0 -0
  13. lib/img/playful_black.png +0 -0
  14. lib/img/playful_white.png +0 -0
  15. lib/img/white.png +0 -0
  16. lib/img/white@x2.png +0 -0
  17. lib/{all-skins.css → skin/all-skins.css} +289 -64
  18. lib/{functional.css → skin/functional.css} +95 -21
  19. lib/skin/img/black.png +0 -0
  20. lib/skin/img/black@x2.png +0 -0
  21. lib/skin/img/black_rtl.png +0 -0
  22. lib/skin/img/black_rtl@x2.png +0 -0
  23. lib/skin/img/flowplayer.png +0 -0
  24. lib/skin/img/flowplayer@2x.png +0 -0
  25. lib/skin/img/play_black.png +0 -0
  26. lib/skin/img/play_black@x2.png +0 -0
  27. lib/skin/img/play_black_rtl.png +0 -0
  28. lib/skin/img/play_black_rtl@x2.png +0 -0
  29. lib/skin/img/play_white.png +0 -0
  30. lib/skin/img/play_white@x2.png +0 -0
  31. lib/skin/img/play_white_rtl.png +0 -0
  32. lib/skin/img/play_white_rtl@x2.png +0 -0
  33. lib/skin/img/playful_black.png +0 -0
  34. lib/{img → skin/img}/playful_black@x2.png +0 -0
  35. lib/skin/img/playful_black_rtl.png +0 -0
  36. lib/skin/img/playful_black_rtl@x2.png +0 -0
  37. lib/skin/img/playful_white.png +0 -0
  38. lib/{img → skin/img}/playful_white@x2.png +0 -0
  39. lib/skin/img/playful_white_rtl.png +0 -0
  40. lib/skin/img/playful_white_rtl@x2.png +0 -0
  41. lib/skin/img/white.png +0 -0
  42. lib/skin/img/white@x2.png +0 -0
  43. lib/skin/img/white_rtl.png +0 -0
  44. lib/skin/img/white_rtl@x2.png +0 -0
  45. lib/{minimalist.css → skin/minimalist.css} +95 -21
  46. lib/{playful.css → skin/playful.css} +99 -22
  47. readme.txt +51 -7
easy-video-player.php CHANGED
@@ -1,152 +1,195 @@
1
  <?php
2
  /*
3
- Plugin Name: Easy Video Player
4
- Version: 1.0.2
5
- Plugin URI: http://easywpguide.wordpress.com/?p=25
6
- Author: naa986
7
- Author URI: http://easywpguide.wordpress.com/
8
- Description: Easily embed videos into your WordPress blog
9
- */
10
-
11
- if(!defined('ABSPATH')) exit;
12
- if(!class_exists('EASY_VIDEO_PLAYER'))
13
- {
14
- class EASY_VIDEO_PLAYER
15
- {
16
- var $plugin_version = '1.0.2';
17
- function __construct()
18
- {
19
- define('EASY_VIDEO_PLAYER_VERSION', $this->plugin_version);
20
- $this->plugin_includes();
21
- }
22
- function plugin_includes()
23
- {
24
- if(is_admin( ) )
25
- {
26
- add_filter('plugin_action_links', array(&$this,'easy_video_player_plugin_action_links'), 10, 2 );
27
- }
28
- add_action('wp_enqueue_scripts', 'easy_video_player_enqueue_scripts');
29
- add_action('admin_menu', array( &$this, 'easy_video_player_add_options_menu' ));
30
- add_shortcode('evp_embed_video','evp_embed_video_handler');
31
- }
32
- function plugin_url()
33
- {
34
- if($this->plugin_url) return $this->plugin_url;
35
- return $this->plugin_url = plugins_url( basename( plugin_dir_path(__FILE__) ), basename( __FILE__ ) );
36
- }
37
- function easy_video_player_plugin_action_links($links, $file)
38
- {
39
- if ( $file == plugin_basename( dirname( __FILE__ ) . '/easy-video-player.php' ) )
40
- {
41
- $links[] = '<a href="options-general.php?page=easy-video-player-settings">Settings</a>';
42
- }
43
- return $links;
44
- }
45
-
46
- function easy_video_player_add_options_menu()
47
- {
48
- if(is_admin())
49
- {
50
- add_options_page('Easy Video Player Settings', 'Easy Video Player', 'manage_options', 'easy-video-player-settings', array(&$this, 'easy_video_player_options_page'));
51
- }
52
- add_action('admin_init', array(&$this, 'easy_video_player_add_settings'));
53
- }
54
- function easy_video_player_add_settings()
55
- {
56
- register_setting('easy-video-player-settings-group', 'evp_enable_jquery');
57
- }
58
- function easy_video_player_options_page()
59
- {
60
-
61
- ?>
62
- <div class="wrap">
63
- <div id="poststuff"><div id="post-body">
64
-
65
- <h2>Easy Video Player - v<?php echo $this->plugin_version;?></h2>
 
 
 
 
 
 
 
 
66
  <div class="postbox">
67
- <h3><label for="title">Setup Guide</label></h3>
68
- <div class="inside">
69
- <p>For detailed documentation please visit the plugin homepage <a href="http://easywpguide.wordpress.com/?p=25" target="_blank">here</a></p>
70
- </div></div>
71
- <div class="postbox">
72
- <h3><label for="title">General Settings</label></h3>
73
- <div class="inside">
74
- <form method="post" action="options.php">
75
- <?php settings_fields('easy-video-player-settings-group'); ?>
76
- <table class="form-table">
77
- <tr valign="top">
78
- <th scope="row">Enable jQuery</th>
79
- <td><input type="checkbox" id="evp_enable_jquery" name="evp_enable_jquery" value="1" <?php echo checked(1, get_option('evp_enable_jquery'), false) ?> />
80
- <p><i>By default this option should always be checked.</i></p>
81
- </td>
82
- </tr>
83
- </table>
84
-
85
- <p class="submit">
86
- <input type="submit" class="button-primary" value="<?php _e('Save Changes') ?>" />
87
- </p>
88
- </form>
89
- </div></div>
90
-
91
- </div></div>
92
- </div>
93
- <?php
94
- }
95
- }
96
- $GLOBALS['easy_video_player'] = new EASY_VIDEO_PLAYER();
97
  }
98
 
99
- function easy_video_player_enqueue_scripts()
100
- {
101
- if (!is_admin())
102
- {
103
- $plugin_url = plugins_url('',__FILE__);
104
  $enable_jquery = get_option('evp_enable_jquery');
105
- if($enable_jquery)
106
- {
107
  wp_enqueue_script('jquery');
108
  }
109
- wp_register_script('flowplayer-js', $plugin_url.'/lib/flowplayer.js');
110
- wp_enqueue_script('flowplayer-js');
111
- wp_register_style('flowplayer-css', $plugin_url.'/lib/minimalist.css');
112
- wp_enqueue_style('flowplayer-css');
113
- }
114
  }
115
 
116
- function evp_embed_video_handler($atts)
117
- {
118
- extract(shortcode_atts(array(
119
- 'url' => '',
120
- 'width' => '',
121
- 'height' => '',
122
- 'ratio' => '0.417',
123
- 'autoplay' => 'false',
124
- ), $atts));
125
- if($autoplay=="true"){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  $autoplay = " autoplay";
 
 
 
 
 
127
  }
128
  else{
129
- $autoplay = "";
130
  }
131
- $player = "fp".uniqid();
132
- $styles = "";
133
- if(!empty($width) && !empty($height)){
134
- $styles = <<<EOT
135
- <style>
136
- #$player {
137
- width: {$width}px;
138
- height: {$height}px;
139
- }
140
- </style>
141
- EOT;
142
  }
143
- $output = <<<EOT
144
- <div id="$player" data-ratio="$ratio" class="flowplayer">
145
- <video{$autoplay}>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  <source type="video/mp4" src="$url"/>
147
  </video>
148
  </div>
149
  $styles
150
  EOT;
151
- return $output;
152
  }
1
  <?php
2
  /*
3
+ Plugin Name: Easy Video Player
4
+ Version: 1.0.9
5
+ Plugin URI: http://noorsplugin.com/wordpress-video-plugin/
6
+ Author: naa986
7
+ Author URI: http://noorsplugin.com/
8
+ Description: Easily embed videos into your WordPress blog
9
+ */
10
+
11
+ if (!defined('ABSPATH')) {
12
+ exit;
13
+ }
14
+ if (!class_exists('EASY_VIDEO_PLAYER')) {
15
+
16
+ class EASY_VIDEO_PLAYER {
17
+
18
+ var $plugin_version = '1.0.9';
19
+
20
+ function __construct() {
21
+ define('EASY_VIDEO_PLAYER_VERSION', $this->plugin_version);
22
+ $this->plugin_includes();
23
+ }
24
+
25
+ function plugin_includes() {
26
+ if (is_admin()) {
27
+ add_filter('plugin_action_links', array(&$this, 'easy_video_player_plugin_action_links'), 10, 2);
28
+ }
29
+ add_action('wp_enqueue_scripts', 'easy_video_player_enqueue_scripts');
30
+ add_action('admin_menu', array(&$this, 'easy_video_player_add_options_menu'));
31
+ add_action('wp_head', 'easy_video_player_header');
32
+ add_shortcode('evp_embed_video', 'evp_embed_video_handler');
33
+ //allows shortcode execution in the widget, excerpt and content
34
+ add_filter('widget_text', 'do_shortcode');
35
+ add_filter('the_excerpt', 'do_shortcode', 11);
36
+ add_filter('the_content', 'do_shortcode', 11);
37
+ }
38
+
39
+ function plugin_url() {
40
+ if ($this->plugin_url)
41
+ return $this->plugin_url;
42
+ return $this->plugin_url = plugins_url(basename(plugin_dir_path(__FILE__)), basename(__FILE__));
43
+ }
44
+
45
+ function easy_video_player_plugin_action_links($links, $file) {
46
+ if ($file == plugin_basename(dirname(__FILE__) . '/easy-video-player.php')) {
47
+ $links[] = '<a href="options-general.php?page=easy-video-player-settings">Settings</a>';
48
+ }
49
+ return $links;
50
+ }
51
+
52
+ function easy_video_player_add_options_menu() {
53
+ if (is_admin()) {
54
+ add_options_page('Easy Video Player Settings', 'Easy Video Player', 'manage_options', 'easy-video-player-settings', array(&$this, 'easy_video_player_options_page'));
55
+ }
56
+ add_action('admin_init', array(&$this, 'easy_video_player_add_settings'));
57
+ }
58
+
59
+ function easy_video_player_add_settings() {
60
+ register_setting('easy-video-player-settings-group', 'evp_enable_jquery');
61
+ }
62
+
63
+ function easy_video_player_options_page() {
64
+ ?>
65
+ <div class="wrap">
66
+ <div id="poststuff"><div id="post-body">
67
+
68
+ <h2>Easy Video Player - v<?php echo $this->plugin_version; ?></h2>
69
+ <div class="postbox">
70
+ <h3><label for="title">Setup Guide</label></h3>
71
+ <div class="inside">
72
+ <p>For detailed documentation please visit the plugin homepage <a href="http://noorsplugin.com/wordpress-video-plugin/" target="_blank">here</a></p>
73
+ </div></div>
74
  <div class="postbox">
75
+ <h3><label for="title">General Settings</label></h3>
76
+ <div class="inside">
77
+ <form method="post" action="options.php">
78
+ <?php settings_fields('easy-video-player-settings-group'); ?>
79
+ <table class="form-table">
80
+ <tr valign="top">
81
+ <th scope="row">Enable jQuery</th>
82
+ <td><input type="checkbox" id="evp_enable_jquery" name="evp_enable_jquery" value="1" <?php echo checked(1, get_option('evp_enable_jquery'), false) ?> />
83
+ <p><i>By default this option should always be checked.</i></p>
84
+ </td>
85
+ </tr>
86
+ </table>
87
+
88
+ <p class="submit">
89
+ <input type="submit" class="button-primary" value="<?php _e('Save Changes') ?>" />
90
+ </p>
91
+ </form>
92
+ </div></div>
93
+
94
+ </div></div>
95
+ </div>
96
+ <?php
97
+ }
98
+
99
+ }
100
+
101
+ $GLOBALS['easy_video_player'] = new EASY_VIDEO_PLAYER();
 
 
 
102
  }
103
 
104
+ function easy_video_player_enqueue_scripts() {
105
+ if (!is_admin()) {
106
+ $plugin_url = plugins_url('', __FILE__);
 
 
107
  $enable_jquery = get_option('evp_enable_jquery');
108
+ if ($enable_jquery) {
 
109
  wp_enqueue_script('jquery');
110
  }
111
+ wp_register_script('flowplayer-js', $plugin_url . '/lib/flowplayer.min.js');
112
+ wp_enqueue_script('flowplayer-js');
113
+ wp_register_style('flowplayer-css', $plugin_url . '/lib/skin/all-skins.css');
114
+ wp_enqueue_style('flowplayer-css');
115
+ }
116
  }
117
 
118
+ function easy_video_player_header() {
119
+ if (!is_admin()) {
120
+ $fp_config = '<!-- This content is generated with the Easy Video Player plugin v' . EASY_VIDEO_PLAYER_VERSION . ' - http://noorsplugin.com/wordpress-video-plugin/ -->';
121
+ $fp_config .= '<script>';
122
+ $fp_config .= 'flowplayer.conf.embed = false;';
123
+ $fp_config .= 'flowplayer.conf.keyboard = false;';
124
+ $fp_config .= '</script>';
125
+ $fp_config .= '<!-- Easy Video Player plugin -->';
126
+ echo $fp_config;
127
+ }
128
+ }
129
+
130
+ function evp_embed_video_handler($atts) {
131
+ extract(shortcode_atts(array(
132
+ 'url' => '',
133
+ 'width' => '',
134
+ 'height' => '',
135
+ 'ratio' => '0.417',
136
+ 'autoplay' => 'false',
137
+ 'poster' => '',
138
+ 'loop' => '',
139
+ 'class' => '',
140
+ ), $atts));
141
+ if ($autoplay == "true") {
142
  $autoplay = " autoplay";
143
+ } else {
144
+ $autoplay = "";
145
+ }
146
+ if ($loop == "true") {
147
+ $loop= " loop";
148
  }
149
  else{
150
+ $loop= "";
151
  }
152
+ $player = "fp" . uniqid();
153
+ $color = '';
154
+ if (!empty($poster)) {
155
+ $color = 'background: #000 url('.$poster.') 0 0 no-repeat;background-size: 100%;';
156
+ } else {
157
+ $color = 'background-color: #000;';
158
+ }
159
+ $size_attr = "";
160
+ if (!empty($width)) {
161
+ $size_attr = "max-width: {$width}px;max-height: auto;";
 
162
  }
163
+ $class_array = array('flowplayer', 'minimalist');
164
+ if(!empty($class)){
165
+ $shortcode_class_array = array_map('trim', explode(' ', $class));
166
+ $shortcode_class_array = array_filter( $shortcode_class_array, 'strlen' );
167
+ $shortcode_class_array = array_values($shortcode_class_array);
168
+ if(in_array("functional", $shortcode_class_array) || in_array("playful", $shortcode_class_array)){
169
+ $class_array = array_diff($class_array, array('minimalist'));
170
+ }
171
+ $class_array = array_merge($class_array, $shortcode_class_array);
172
+ $class_array = array_unique($class_array);
173
+ $class_array = array_values($class_array);
174
+ }
175
+
176
+ $classes = implode(" ", $class_array);
177
+ $styles = <<<EOT
178
+ <style>
179
+ #$player {
180
+ $size_attr
181
+ $color
182
+ }
183
+ </style>
184
+ EOT;
185
+
186
+ $output = <<<EOT
187
+ <div id="$player" data-ratio="$ratio" class="{$classes}">
188
+ <video{$autoplay}{$loop}>
189
  <source type="video/mp4" src="$url"/>
190
  </video>
191
  </div>
192
  $styles
193
  EOT;
194
+ return $output;
195
  }
lib/embed.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(){function e(e,t,n){if(e)return n(e);if(window.fp5ecc[t])return window.fp5ecc[t].push(n);window.fp5ecc[t]=[n];var r=document.createElement("script");r.onload=r.onreadystatechange=function(){var e=r.readyState;if(void 0===e||/complete|loaded/.test(e))for(var n=window.fp5ecc[t],i=0;i<n.length;i++)n[i]()},r.async=!0,r.src=t,a.insertBefore(r,a.firstChild)}function t(e){if(!window.fp5ecssc[e]){window.fp5ecssc[e]=!0;var t=document.createElement("link");t.rel="stylesheet",t.type="text/css",t.href=e,a.insertBefore(t,a.firstChild)}}for(var n,r=document.getElementsByTagName("script"),i=document.createElement("div"),o=0;o<r.length;o++)if(-1!==r[o].innerHTML.indexOf("<video")){n=r[o],n.parentNode.insertBefore(i,n),n.parentNode.removeChild(n);break}var a=document.getElementsByTagName("head")[0],c="//releases.flowplayer.org/5.5.0/commercial",f=n.getAttribute("data-library")||c+"/flowplayer.min.js",d=n.getAttribute("data-swf")||c+"/flowplayer.swf",s=n.getAttribute("data-skin")||c+"/skin/minimalist.css",l="//www.google-analytics.com/ga.js",u="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js";window.fp5ecc=window.fp5ecc||{},window.fp5ecssc=window.fp5ecssc||{};var p="undefined"!=typeof jQuery?jQuery().jquery:"",w=p.split("."),m="undefined"!=typeof $&&jQuery!=$;e(Number(w[0])>=1&&Number(w[1])>=7,u,function(r){var o;!r&&m&&(o=jQuery.noConflict());var a=!r&&""!==p;e("undefined"!=typeof _gat,l,function(){e("undefined"!=typeof flowplayer,f,function(e){e||t(s),o=o||jQuery;var r=o(n),c=r.attr("src"),l=o(r.html().replace(/^[ \t\n\r]+/gm,"").replace(/[ \n\t\r]+$/gm,""));o(i).replaceWith(l),l.find(":not(video, source)").remove(),l.flowplayer({swf:d,e:1}),l.data("flowplayer").conf.embed={library:f,swf:d,script:c},a&&o.noConflict(!0)})})})}();
lib/flowplayer.js CHANGED
@@ -1,6 +1,6 @@
1
  /*!
2
 
3
- Flowplayer v5.3.2 (Monday, 28. January 2013 10:02AM) | flowplayer.org/license
4
 
5
  */
6
  !function($) {
@@ -19,6 +19,7 @@
19
  var b = $.browser = {},
20
  ua = navigator.userAgent.toLowerCase(),
21
  match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
 
22
  /(webkit)[ \/]([\w.]+)/.exec(ua) ||
23
  /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
24
  /(msie) ([\w.]+)/.exec(ua) ||
@@ -41,22 +42,43 @@ $(function() {
41
 
42
  var instances = [],
43
  extensions = [],
44
- UA = navigator.userAgent,
45
- use_native = /Android/.test(UA) && /Firefox/.test(UA);
46
 
47
 
48
  /* flowplayer() */
49
  window.flowplayer = function(fn) {
50
- return use_native ? 0 :
51
- $.isFunction(fn) ? extensions.push(fn) :
52
  typeof fn == 'number' || fn === undefined ? instances[fn || 0] :
53
  $(fn).data("flowplayer");
54
  };
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
  $.extend(flowplayer, {
58
 
59
- version: '5.3.2',
60
 
61
  engine: {},
62
 
@@ -82,6 +104,8 @@ $.extend(flowplayer, {
82
  // default aspect ratio
83
  ratio: 9 / 16,
84
 
 
 
85
  // scale flash object to video's aspect ratio in normal mode?
86
  flashfit: false,
87
 
@@ -89,14 +113,16 @@ $.extend(flowplayer, {
89
 
90
  splash: false,
91
 
92
- swf: "http://releases.flowplayer.org/5.3.2/flowplayer.swf",
 
 
93
 
94
  speeds: [0.25, 0.5, 1, 1.5, 2],
95
 
96
  tooltip: true,
97
 
98
  // initial volume level
99
- volume: 1,
100
 
101
  // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#error-codes
102
  errors: [
@@ -118,19 +144,17 @@ $.extend(flowplayer, {
118
  ],
119
  errorUrls: ['','','','','','','','','','',
120
  'http://get.adobe.com/flashplayer/'
121
- ]
 
 
 
122
 
123
  }
124
 
125
  });
126
 
127
- // smartphones simply use native controls
128
- if (use_native) {
129
- return $(function() { $("video").attr("controls", "controls"); });
130
- }
131
-
132
  // keep track of players
133
- var playerCount = 0;
134
 
135
  // jQuery plugin
136
  $.fn.flowplayer = function(opts, callback) {
@@ -144,24 +168,53 @@ $.fn.flowplayer = function(opts, callback) {
144
  var root = $(this).addClass("is-loading"),
145
  conf = $.extend({}, flowplayer.defaults, flowplayer.conf, opts, root.data()),
146
  videoTag = $("video", root).addClass("fp-engine").removeAttr("controls"),
147
- urlResolver = new URLResolver(videoTag),
148
  storage = {},
149
  lastSeekPosition,
150
  engine;
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  root.data('fp-player_id', root.data('fp-player_id') || playerCount++);
153
 
154
  try {
155
- storage = window.localStorage || storage;
156
  } catch(e) {}
157
 
 
 
 
 
 
158
  /*** API ***/
159
- var api = {
160
 
161
  // properties
162
  conf: conf,
163
  currentSpeed: 1,
164
- volumeLevel: storage.volume * 1 || conf.volume,
165
  video: {},
166
 
167
  // states
@@ -173,6 +226,7 @@ $.fn.flowplayer = function(opts, callback) {
173
  playing: false,
174
  ready: false,
175
  splash: false,
 
176
 
177
  // methods
178
  load: function(video, callback) {
@@ -193,6 +247,8 @@ $.fn.flowplayer = function(opts, callback) {
193
  // callback
194
  if ($.isFunction(video)) callback = video;
195
  if (callback) root.one("ready", callback);
 
 
196
  }
197
  }
198
 
@@ -214,7 +270,7 @@ $.fn.flowplayer = function(opts, callback) {
214
 
215
  // Firefox (+others?) does not fire "resume" after finish
216
  if (api.finished) {
217
- api.trigger("resume");
218
  api.finished = false;
219
  }
220
  }
@@ -232,16 +288,22 @@ $.fn.flowplayer = function(opts, callback) {
232
  seek(false) -> 10% backward
233
  */
234
  seek: function(time, callback) {
235
- if (api.ready) {
236
 
237
  if (typeof time == "boolean") {
238
  var delta = api.video.duration * 0.1;
239
  time = api.video.time + (time ? delta : -delta);
240
  }
241
-
242
- time = lastSeekPosition = Math.min(Math.max(time, 0), api.video.duration);
243
- engine.seek(time);
244
- if ($.isFunction(callback)) root.one("seek", callback);
 
 
 
 
 
 
245
  }
246
  return api;
247
  },
@@ -259,19 +321,21 @@ $.fn.flowplayer = function(opts, callback) {
259
  },
260
 
261
  mute: function(flag) {
262
- if (flag == undefined) flag = !api.muted;
263
  storage.muted = api.muted = flag;
264
- api.volume(flag ? 0 : storage.volume);
 
265
  api.trigger("mute", flag);
 
266
  },
267
 
268
- volume: function(level) {
269
  if (api.ready) {
270
  level = Math.min(Math.max(level, 0), 1);
271
- storage.volume = level;
272
  engine.volume(level);
273
  }
274
-
275
  return api;
276
  },
277
 
@@ -322,10 +386,13 @@ $.fn.flowplayer = function(opts, callback) {
322
  api.disabled = flag;
323
  api.trigger("disable", flag);
324
  }
 
325
  }
326
 
327
  };
328
 
 
 
329
 
330
  /* event binding / unbinding */
331
  $.each(['bind', 'one', 'unbind'], function(i, key) {
@@ -342,150 +409,167 @@ $.fn.flowplayer = function(opts, callback) {
342
 
343
 
344
  /*** Behaviour ***/
 
 
345
 
346
- root.bind("boot", function() {
 
 
 
 
347
 
348
- // conf
349
- $.each(['autoplay', 'loop', 'preload', 'poster'], function(i, key) {
350
- var val = videoTag.attr(key);
351
- if (val !== undefined) conf[key] = val ? val : true;
352
- });
 
 
353
 
354
- // splash
355
- if (conf.splash || root.hasClass("is-splash") || !flowplayer.support.firstframe) {
356
- api.splash = conf.splash = conf.autoplay = true;
357
- root.addClass("is-splash");
358
- videoTag.attr("preload", "none");
359
- }
360
 
361
- // extensions
362
- $.each(extensions, function(i) {
363
- this(api, root);
364
- });
365
 
366
- // 1. use the configured engine
367
- engine = flowplayer.engine[conf.engine];
368
- if (engine) engine = engine(api, root);
369
 
370
- if (engine.pick(urlResolver.initialSources)) {
371
- api.engine = conf.engine;
372
 
373
- // 2. failed -> try another
374
- } else {
375
- $.each(flowplayer.engine, function(name, impl) {
376
- if (name != conf.engine) {
377
- engine = this(api, root);
378
- if (engine.pick(urlResolver.initialSources)) api.engine = name;
379
- return false;
380
- }
381
- });
382
- }
 
 
383
 
384
- // no engine
385
- if (!api.engine) return api.trigger("error", { code: flowplayer.support.flash ? 5 : 10 });
386
 
387
- // start
388
- conf.splash ? api.unload() : api.load();
389
 
390
- // disabled
391
- if (conf.disabled) api.disable();
392
 
393
- // initial callback
394
- root.one("ready", callback);
395
 
396
- // instances
397
- instances.push(api);
398
 
 
 
399
 
400
- }).bind("load", function(e, api, video) {
401
 
402
- // unload others
403
- if (conf.splash) {
404
- $(".flowplayer").filter(".is-ready, .is-loading").not(root).each(function() {
405
- var api = $(this).data("flowplayer");
406
- if (api.conf.splash) api.unload();
407
- });
408
- }
 
 
409
 
410
- // loading
411
- root.addClass("is-loading");
412
- api.loading = true;
413
 
414
 
415
- }).bind("ready", function(e, api, video) {
416
- video.time = 0;
417
- api.video = video;
418
 
419
- function notLoading() {
420
- root.removeClass("is-loading");
421
- api.loading = false;
422
- }
423
 
424
- if (conf.splash) root.one("progress", notLoading);
425
- else notLoading();
426
 
427
- // saved state
428
- if (api.muted) api.mute(true);
429
- else api.volume(api.volumeLevel);
430
 
 
431
 
432
- }).bind("unload", function(e) {
433
- if (conf.splash) videoTag.remove();
434
- root.removeClass("is-loading");
435
- api.loading = false;
436
 
 
 
 
 
437
 
438
- }).bind("ready unload", function(e) {
439
- var is_ready = e.type == "ready";
440
- root.toggleClass("is-splash", !is_ready).toggleClass("is-ready", is_ready);
441
- api.ready = is_ready;
442
- api.splash = !is_ready;
443
 
 
 
 
 
 
444
 
445
- }).bind("progress", function(e, api, time) {
446
- api.video.time = time;
447
 
 
 
448
 
449
- }).bind("speed", function(e, api, val) {
450
- api.currentSpeed = val;
451
 
452
- }).bind("volume", function(e, api, level) {
453
- api.volumeLevel = Math.round(level * 100) / 100;
454
- if (!api.muted) storage.volume = level;
455
- else if (level) api.mute(false);
456
 
 
 
 
 
457
 
458
- }).bind("beforeseek seek", function(e) {
459
- api.seeking = e.type == "beforeseek";
460
- root.toggleClass("is-seeking", api.seeking);
461
 
462
- }).bind("ready pause resume unload finish stop", function(e, _api, video) {
 
 
463
 
464
- // PAUSED: pause / finish
465
- api.paused = /pause|finish|unload|stop/.test(e.type);
466
 
467
- // SHAKY HACK: first-frame / preload=none
468
- if (e.type == "ready") {
469
- if (video) {
470
- api.paused = !video.duration || !conf.autoplay && (conf.preload != 'none' || api.engine == 'flash');
 
 
 
 
 
471
  }
472
- }
473
 
474
- // the opposite
475
- api.playing = !api.paused;
476
 
477
- // CSS classes
478
- root.toggleClass("is-paused", api.paused).toggleClass("is-playing", api.playing);
479
 
480
- // sanity check
481
- if (!api.load.ed) api.pause();
482
 
483
- }).bind("finish", function(e) {
484
- api.finished = true;
485
 
486
- }).bind("error", function() {
487
- videoTag.remove();
488
- });
 
489
 
490
  // boot
491
  root.trigger("boot", [api, root]).data("flowplayer", api);
@@ -496,44 +580,68 @@ $.fn.flowplayer = function(opts, callback) {
496
 
497
  !function() {
498
 
 
 
 
 
 
 
 
 
499
  var s = flowplayer.support,
500
  browser = $.browser,
501
  video = $("<video loop autoplay preload/>")[0],
502
- IS_IE = browser.msie,
503
  UA = navigator.userAgent,
504
- IS_IPAD = /iPad|MeeGo/.test(UA),
505
- IS_IPHONE = /iP(hone|od)/i.test(UA),
506
- IS_ANDROID = /Android/.test(UA),
 
 
 
507
  IS_SILK = /Silk/.test(UA),
508
- IPAD_VER = IS_IPAD ? parseFloat(/Version\/(\d\.\d)/.exec(UA)[1], 10) : 0;
509
-
510
-
 
 
511
  $.extend(s, {
512
- video: !!video.canPlayType,
513
  subtitles: !!video.addTextTrack,
514
- fullscreen: typeof document.webkitCancelFullScreen == 'function'
515
- && !/Mac OS X 10_5.+Version\/5\.0\.\d Safari/.test(UA) || document.mozFullScreenEnabled,
516
- fullscreen_keyboard: !browser.safari || browser.version > "536",
 
517
  inlineBlock: !(IS_IE && browser.version < 8),
518
  touch: ('ontouchstart' in window),
519
- dataload: !IS_IPAD && !IS_IPHONE,
520
  zeropreload: !IS_IE && !IS_ANDROID, // IE supports only preload=metadata
521
- volume: !IS_IPAD && !IS_ANDROID && !IS_IPHONE && !IS_SILK,
522
- cachedVideoTag: !IS_IPAD && !IS_IPHONE,
523
- firstframe: !IS_IPHONE && !IS_IPAD && !IS_ANDROID && !IS_SILK
 
 
 
524
  });
525
 
526
  // flashVideo
527
  try {
528
- var ver = IS_IE ? new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable('$version') :
529
- navigator.plugins["Shockwave Flash"].description;
 
 
530
 
531
- ver = ver.split(/\D+/);
532
- if (ver.length && !ver[0]) ver = ver.slice(1);
533
 
534
- s.flashVideo = ver[0] > 9 || ver[0] == 9 && ver[3] >= 115;
 
535
 
536
  } catch (ignored) {}
 
 
 
 
 
 
537
 
538
  // animation
539
  s.animation = (function() {
@@ -552,7 +660,8 @@ $.fn.flowplayer = function(opts, callback) {
552
  /* The most minimal Flash embedding */
553
 
554
  // movie required in opts
555
- function embed(swf, flashvars) {
 
556
 
557
  var id = "obj" + ("" + Math.random()).slice(2, 15),
558
  tag = '<object class="fp-engine" id="' + id+ '" name="' + id + '" ';
@@ -564,7 +673,7 @@ function embed(swf, flashvars) {
564
  width: "100%",
565
  height: "100%",
566
  allowscriptaccess: "always",
567
- wmode: "transparent",
568
  quality: "high",
569
  flashvars: "",
570
 
@@ -605,6 +714,70 @@ flowplayer.engine.flash = function(player, root) {
605
  objectTag,
606
  api;
607
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
608
  var engine = {
609
 
610
  pick: function(sources) {
@@ -617,29 +790,45 @@ flowplayer.engine.flash = function(player, root) {
617
 
618
  for (var i = 0, source; i < sources.length; i++) {
619
  source = sources[i];
620
- if (/mp4|flv/.test(source.type)) return source;
621
  }
622
  }
623
  },
624
 
625
  load: function(video) {
626
 
 
 
 
 
627
  var html5Tag = $("video", root),
628
- url = video.src.replace(/&amp;/g, '%26').replace(/&/g, '%26').replace(/=/g, '%3D'),
629
  is_absolute = /^https?:/.test(url);
630
 
631
- // html5 tag not needed (pause needed for firefox)
632
- if (html5Tag.length > 0 && flowplayer.support.video) html5Tag[0].pause();
633
- html5Tag.remove();
 
 
 
 
 
 
 
 
 
634
 
635
  // convert to absolute
636
- if (!is_absolute && !conf.rtmp) url = $("<a/>").attr("href", url)[0].href;
637
 
638
  if (api) {
639
  api.__play(url);
640
 
641
  } else {
642
 
 
 
 
643
  callbackId = "fp" + ("" + Math.random()).slice(3, 15);
644
 
645
  var opts = {
@@ -647,15 +836,28 @@ flowplayer.engine.flash = function(player, root) {
647
  url: url,
648
  callback: "jQuery."+ callbackId
649
  };
 
 
 
650
 
651
  if (is_absolute) delete conf.rtmp;
652
 
653
  // optional conf
654
- $.each(['key', 'autoplay', 'preload', 'rtmp', 'loop', 'debug'], function(i, key) {
655
- if (conf[key]) opts[key] = conf[key];
656
  });
 
 
 
 
 
 
 
 
 
 
657
 
658
- objectTag = embed(conf.swf, opts);
659
 
660
  objectTag.prependTo(root);
661
 
@@ -670,10 +872,34 @@ flowplayer.engine.flash = function(player, root) {
670
  } catch (e) {}
671
  }, 5000);
672
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
673
  // listen
674
  $[callbackId] = function(type, arg) {
675
 
676
- if (conf.debug && type != "status") console.log("--", type, arg);
677
 
678
  var event = $.Event(type);
679
 
@@ -685,23 +911,12 @@ flowplayer.engine.flash = function(player, root) {
685
  case "click": event.flash = true; break;
686
  case "keydown": event.which = arg; break;
687
  case "seek": video.time = arg; break;
688
- case "buffered": video.buffered = true; break;
689
-
690
- case "status":
691
- player.trigger("progress", arg.time);
692
-
693
- if (arg.buffer <= video.bytes && !video.buffered) {
694
- video.buffer = arg.buffer / video.bytes * video.duration;
695
- player.trigger("buffer", video.buffer);
696
-
697
- } else if (video.buffered) player.trigger("buffered");
698
-
699
- break;
700
-
701
  }
702
 
703
- // add some delay to that player is truly ready after an event
704
- setTimeout(function() { player.trigger(event, arg); }, 1)
 
 
705
 
706
  };
707
 
@@ -718,6 +933,8 @@ flowplayer.engine.flash = function(player, root) {
718
  delete $[callbackId];
719
  $("object", root).remove();
720
  api = 0;
 
 
721
  }
722
 
723
  };
@@ -725,69 +942,31 @@ flowplayer.engine.flash = function(player, root) {
725
  $.each("pause,resume,seek,volume".split(","), function(i, name) {
726
 
727
  engine[name] = function(arg) {
 
 
728
 
729
- if (player.ready) {
730
-
731
- if (name == 'seek' && player.video.time && !player.paused) {
732
- player.trigger("beforeseek");
733
- }
734
 
735
- if (arg === undefined) {
736
- api["__" + name]();
737
 
738
- } else {
739
- api["__" + name](arg);
740
- }
741
 
 
 
 
 
 
 
742
  }
743
  };
744
 
745
  });
746
 
747
- var win = $(window),
748
- origH = root.height(),
749
- origW = root.width();
750
-
751
- // handle Flash object aspect ratio
752
- player.bind("ready fullscreen fullscreen-exit", function(e) {
753
- if (player.conf.flashfit || /full/.test(e.type)) {
754
-
755
- var fs = player.isFullscreen,
756
- truefs = fs && FS_SUPPORT,
757
- ie7 = !flowplayer.support.inlineBlock,
758
- screenW = fs ? (truefs ? screen.availWidth : win.width()) : origW,
759
- screenH = fs ? (truefs ? screen.availHeight : win.height()) : origH,
760
-
761
- // default values for fullscreen-exit without flashfit
762
- hmargin = truefs ? screen.width - screen.availWidth : 0,
763
- vmargin = truefs ? screen.height - screen.availHeight : 0,
764
- objwidth = ie7 ? origW : '',
765
- objheight = ie7 ? origH : '',
766
-
767
- aspectratio, dataratio;
768
-
769
- if (player.conf.flashfit || e.type === "fullscreen") {
770
- aspectratio = player.video.width / player.video.height,
771
- dataratio = player.video.height / player.video.width,
772
- objheight = Math.max(dataratio * screenW),
773
- objwidth = Math.max(aspectratio * screenH);
774
- objheight = objheight > screenH ? objwidth * dataratio : objheight;
775
- objheight = Math.min(Math.round(objheight), screenH);
776
- objwidth = objwidth > screenW ? objheight * aspectratio : objwidth;
777
- objwidth = Math.min(Math.round(objwidth), screenW);
778
- vmargin = Math.max(Math.round((screenH + vmargin - objheight) / 2), 0);
779
- hmargin = Math.max(Math.round((screenW + hmargin - objwidth) / 2), 0);
780
- }
781
-
782
- $("object", root).css({
783
- width: objwidth,
784
- height: objheight,
785
- marginTop: vmargin,
786
- marginLeft: hmargin
787
- });
788
- }
789
- });
790
-
791
  return engine;
792
 
793
  };
@@ -806,7 +985,7 @@ var EVENTS = {
806
  timeupdate: 'progress',
807
  volumechange: 'volume',
808
  ratechange: 'speed',
809
- seeking: 'beforeseek',
810
  seeked: 'seek',
811
  // abort: 'resume',
812
 
@@ -824,8 +1003,9 @@ var EVENTS = {
824
 
825
  };
826
 
827
- function round(val) {
828
- return Math.round(val * 100) / 100;
 
829
  }
830
 
831
  function getType(type) {
@@ -833,11 +1013,18 @@ function getType(type) {
833
  }
834
 
835
  function canPlay(type) {
836
- if (!/^(video|application)/.test(type))
837
  type = getType(type);
838
  return !!VIDEO.canPlayType(type).replace("no", '');
839
  }
840
 
 
 
 
 
 
 
 
841
  var videoTagCache;
842
  var createVideoTag = function(video) {
843
  if (videoTagCache) {
@@ -848,8 +1035,9 @@ var createVideoTag = function(video) {
848
  type: getType(video.type),
849
  'class': 'fp-engine',
850
  'autoplay': 'autoplay',
851
- preload: 'none'
852
- }));
 
853
  }
854
 
855
  flowplayer.engine.html5 = function(player, root) {
@@ -860,12 +1048,17 @@ flowplayer.engine.html5 = function(player, root) {
860
  conf = player.conf,
861
  self,
862
  timer,
863
- api;
 
864
 
865
  return self = {
866
 
867
  pick: function(sources) {
868
  if (support.video) {
 
 
 
 
869
  for (var i = 0, source; i < sources.length; i++) {
870
  if (canPlay(sources[i].type)) return sources[i];
871
  }
@@ -878,15 +1071,30 @@ flowplayer.engine.html5 = function(player, root) {
878
 
879
  videoTag = createVideoTag(video).prependTo(root);
880
 
881
- if (track.length) videoTag.append(track.attr("default", ""));
 
 
 
 
 
 
 
882
 
883
  if (conf.loop) videoTag.attr("loop", "loop");
884
 
885
  api = videoTag[0];
 
 
 
886
 
887
  } else {
888
 
889
  api = videoTag[0];
 
 
 
 
 
890
 
891
  // change of clip
892
  if (player.video.src && video.src != player.video.src) {
@@ -898,12 +1106,12 @@ flowplayer.engine.html5 = function(player, root) {
898
 
899
  if (support.zeropreload) {
900
  player.trigger("ready", video).trigger("pause").one("ready", function() {
901
- root.trigger("resume");
902
  });
903
 
904
  } else {
905
  player.one("ready", function() {
906
- root.trigger("pause");
907
  });
908
  }
909
  }
@@ -913,7 +1121,7 @@ flowplayer.engine.html5 = function(player, root) {
913
  listen(api, $("source", videoTag).add(videoTag), video);
914
 
915
  // iPad (+others?) demands load()
916
- if (conf.preload != 'none' || !support.zeropreload || !support.dataload) api.load();
917
  if (conf.splash) api.load();
918
  },
919
 
@@ -931,16 +1139,21 @@ flowplayer.engine.html5 = function(player, root) {
931
 
932
  seek: function(time) {
933
  try {
 
934
  api.currentTime = time;
 
935
  } catch (ignored) {}
936
  },
937
 
938
  volume: function(level) {
939
- api.volume = level;
 
 
 
940
  },
941
 
942
  unload: function() {
943
- $("video", root).remove();
944
  if (!support.cachedVideoTag) videoTagCache = null;
945
  timer = clearInterval(timer);
946
  api = 0;
@@ -972,7 +1185,7 @@ flowplayer.engine.html5 = function(player, root) {
972
  // safari hack for bad URL (10s before fails)
973
  if (flow == "progress" && e.srcElement && e.srcElement.readyState === 0) {
974
  setTimeout(function() {
975
- if (!player.video.duration) {
976
  flow = "error";
977
  player.trigger(flow, { code: 4 });
978
  }
@@ -984,7 +1197,7 @@ flowplayer.engine.html5 = function(player, root) {
984
  // no events if player not ready
985
  if (!player.ready && !/ready|error/.test(flow) || !flow || !$("video", root).length) { return; }
986
 
987
- var event = $.Event(flow), arg;
988
 
989
  switch (flow) {
990
 
@@ -999,7 +1212,7 @@ flowplayer.engine.html5 = function(player, root) {
999
  });
1000
 
1001
  try {
1002
- arg.seekable = api.seekable && api.seekable.end(null);
1003
 
1004
  } catch (ignored) {}
1005
 
@@ -1012,7 +1225,7 @@ flowplayer.engine.html5 = function(player, root) {
1012
  } catch (ignored) {}
1013
 
1014
  if (arg.buffer) {
1015
- if (arg.buffer <= arg.duration && !arg.buffered) {
1016
  player.trigger("buffer", e);
1017
 
1018
  } else if (!arg.buffered) {
@@ -1025,15 +1238,28 @@ flowplayer.engine.html5 = function(player, root) {
1025
 
1026
  }, 250);
1027
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1028
  break;
1029
 
1030
  case "progress": case "seek":
1031
 
1032
  var dur = player.video.duration
1033
 
1034
- if (api.currentTime > 0) {
1035
  arg = Math.max(api.currentTime, 0);
1036
- if (dur && arg && arg >= dur) event.type = "finish";
1037
  break;
1038
 
1039
  } else if (flow == 'progress') {
@@ -1067,7 +1293,7 @@ flowplayer.engine.html5 = function(player, root) {
1067
  }
1068
 
1069
  };
1070
- var TYPE_RE = /.(\w{3,4})$/i;
1071
 
1072
  function parseSource(el) {
1073
 
@@ -1075,7 +1301,7 @@ function parseSource(el) {
1075
  type = el.attr("type") || "",
1076
  suffix = src.split(TYPE_RE)[1];
1077
 
1078
- type = /mpegurl/.test(type) ? "mpegurl" : type.replace("video/", "");
1079
 
1080
  return { src: src, suffix: suffix || type, type: type || suffix };
1081
  }
@@ -1101,11 +1327,12 @@ function URLResolver(videoTag) {
1101
  if ($.isArray(video)) {
1102
 
1103
  video = { sources: $.map(video, function(el) {
1104
- var type; $.each(el, function(key, value) { type = key; });
1105
- el.type = type;
1106
- el.src = el[type];
1107
- delete el[type];
1108
- return el;
 
1109
  })};
1110
 
1111
  } else if (typeof video == 'string') {
@@ -1116,7 +1343,7 @@ function URLResolver(videoTag) {
1116
  if (source.type != 'flash') {
1117
  video.sources.push({
1118
  type: source.type,
1119
- src: video.src.replace(TYPE_RE, "") + "." + source.suffix
1120
  });
1121
  }
1122
  });
@@ -1146,9 +1373,9 @@ $.throttle = function(fn, delay) {
1146
  };
1147
 
1148
 
1149
- $.fn.slider2 = function() {
1150
 
1151
- var IS_IPAD = /iPad/.test(navigator.userAgent);
1152
 
1153
  return this.each(function() {
1154
 
@@ -1163,6 +1390,7 @@ $.fn.slider2 = function() {
1163
  size,
1164
  maxValue,
1165
  max,
 
1166
 
1167
  /* private */
1168
  calc = function() {
@@ -1185,11 +1413,16 @@ $.fn.slider2 = function() {
1185
  },
1186
 
1187
  mousemove = function(e) {
1188
- var delta = vertical ? e.pageY - offset.top : e.pageX - offset.left;
 
 
 
 
1189
  delta = Math.max(0, Math.min(max || size, delta));
1190
 
1191
  var value = delta / size;
1192
  if (vertical) value = 1 - value;
 
1193
  return move(value, 0, true);
1194
  },
1195
 
@@ -1200,8 +1433,12 @@ $.fn.slider2 = function() {
1200
  var to = (Math.round(value * 1000) / 10) + "%";
1201
 
1202
  if (!maxValue || value <= maxValue) {
1203
- if (!IS_IPAD) progress.stop(); // stop() broken on iPad
1204
- progress.animate(vertical ? { height: to } : { width: to }, speed, "linear");
 
 
 
 
1205
  }
1206
 
1207
  return value;
@@ -1226,6 +1463,12 @@ $.fn.slider2 = function() {
1226
  calc();
1227
  if (fireEvent) fire(value);
1228
  move(value, speed);
 
 
 
 
 
 
1229
  }
1230
 
1231
  };
@@ -1233,8 +1476,7 @@ $.fn.slider2 = function() {
1233
  calc();
1234
 
1235
  // bound dragging into document
1236
- root.data("api", api).bind("mousedown.sld", function(e) {
1237
-
1238
  e.preventDefault();
1239
 
1240
  if (!disabled) {
@@ -1243,15 +1485,17 @@ $.fn.slider2 = function() {
1243
  var delayedFire = $.throttle(fire, 100);
1244
  calc();
1245
  api.dragging = true;
 
1246
  fire(mousemove(e));
1247
 
1248
- doc.bind("mousemove.sld", function(e) {
1249
  e.preventDefault();
1250
  delayedFire(mousemove(e));
1251
 
1252
- }).one("mouseup", function() {
1253
  api.dragging = false;
1254
- doc.unbind("mousemove.sld");
 
1255
  });
1256
 
1257
  }
@@ -1290,7 +1534,7 @@ flowplayer(function(api, root) {
1290
  var conf = api.conf,
1291
  support = flowplayer.support,
1292
  hovertimer;
1293
-
1294
  root.addClass("flowplayer").append('\
1295
  <div class="ratio"/>\
1296
  <div class="ui">\
@@ -1336,18 +1580,22 @@ flowplayer(function(api, root) {
1336
  origRatio = ratio.css("paddingTop"),
1337
 
1338
  // sliders
1339
- timeline = find("timeline").slider2(),
1340
  timelineApi = timeline.data("api"),
1341
 
1342
  volume = find("volume"),
1343
  fullscreen = find("fullscreen"),
1344
- volumeSlider = find("volumeslider").slider2(),
1345
  volumeApi = volumeSlider.data("api"),
1346
  noToggle = root.is(".fixed-controls, .no-toggle");
1347
 
 
 
1348
  // aspect ratio
1349
  function setRatio(val) {
1350
- if (!parseInt(origRatio, 10)) ratio.css("paddingTop", val * 100 + "%");
 
 
1351
  if (!support.inlineBlock) $("object", root).height(root.height());
1352
  }
1353
 
@@ -1373,9 +1621,9 @@ flowplayer(function(api, root) {
1373
 
1374
  var duration = api.video.duration;
1375
 
1376
- timelineApi.disable(!duration);
1377
 
1378
- setRatio(api.video.videoHeight / api.video.videoWidth);
1379
 
1380
  // initial time & volume
1381
  durationEl.add(remaining).html(format(duration));
@@ -1384,6 +1632,8 @@ flowplayer(function(api, root) {
1384
  ((duration >= 3600) && root.addClass('is-long')) || root.removeClass('is-long');
1385
  volumeApi.slide(api.volumeLevel);
1386
 
 
 
1387
 
1388
  }).bind("unload", function() {
1389
  if (!origRatio) ratio.css("paddingTop", "");
@@ -1393,7 +1643,7 @@ flowplayer(function(api, root) {
1393
  var video = api.video,
1394
  max = video.buffer / video.duration;
1395
 
1396
- if (!video.seekable) timelineApi.max(max);
1397
  if (max < 1) buffer.css("width", (max * 100) + "%");
1398
  else buffer.css({ width: '100%' });
1399
 
@@ -1455,7 +1705,7 @@ flowplayer(function(api, root) {
1455
  api.error = true;
1456
 
1457
  var el = $(".fp-message", root);
1458
- $("h2", el).text(api.engine + ": " + error.message);
1459
  $("p", el).text(error.url || api.video.url || api.video.src || conf.errorUrls[error.code]);
1460
  root.unbind("mouseenter click").removeClass("is-mouseover");
1461
  }
@@ -1504,6 +1754,27 @@ flowplayer(function(api, root) {
1504
  e.preventDefault();
1505
  return api.toggle();
1506
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1507
  });
1508
 
1509
  // poster -> background image
@@ -1516,7 +1787,7 @@ flowplayer(function(api, root) {
1516
  if (has_bg && !conf.splash && !conf.autoplay) {
1517
 
1518
  api.bind("ready stop", function() {
1519
- root.addClass("is-poster").one("ready progress", function() {
1520
  root.removeClass("is-poster");
1521
  });
1522
  });
@@ -1524,7 +1795,7 @@ flowplayer(function(api, root) {
1524
  }
1525
 
1526
  // default background color if not present
1527
- if (!has_bg && !support.firstframe) {
1528
  root.css("backgroundColor", "#555");
1529
  }
1530
 
@@ -1570,7 +1841,7 @@ $(document).bind("keydown.fp", function(e) {
1570
  if (!el || !conf.keyboard || el.disabled) return;
1571
 
1572
  // help dialog (shift key not truly required)
1573
- if ($.inArray(key, [63, 187, 191, 219]) != -1) {
1574
  focusedRoot.toggleClass(IS_HELP);
1575
  return false;
1576
  }
@@ -1604,7 +1875,7 @@ $(document).bind("keydown.fp", function(e) {
1604
  case 32: el.toggle(); break; // spacebar
1605
  case 70: conf.fullscreen && el.fullscreen(); break; // toggle fullscreen
1606
  case 77: el.mute(); break; // mute
1607
- case 27: el[el.isFullscreen ? "fullscreen" : "unload"](); break; // esc
1608
  }
1609
 
1610
  }
@@ -1622,15 +1893,18 @@ flowplayer(function(api, root) {
1622
  if (focused) focusedRoot = root;
1623
  });
1624
 
 
 
 
 
1625
  // TODO: add to player-layout.html
1626
  root.append('\
1627
  <div class="fp-help">\
1628
  <a class="fp-close"></a>\
1629
  <div class="fp-help-section fp-help-basics">\
1630
  <p><em>space</em>play / pause</p>\
1631
- <p><em>esc</em>stop</p>\
1632
- <p><em>f</em>fullscreen</p>\
1633
- <p><em>shift</em> + <em>&#8592;</em><em>&#8594;</em>slower / faster <small>(latest Chrome and Safari)</small></p>\
1634
  </div>\
1635
  <div class="fp-help-section">\
1636
  <p><em>&#8593;</em><em>&#8595;</em>volume</p>\
@@ -1639,7 +1913,7 @@ flowplayer(function(api, root) {
1639
  <div class="fp-help-section">\
1640
  <p><em>&#8592;</em><em>&#8594;</em>seek</p>\
1641
  <p><em>&nbsp;. </em>seek to previous\
1642
- </p><p><em>1</em><em>2</em>&hellip;<em>6</em> seek to 10%, 20%, &hellip;60% </p>\
1643
  </div>\
1644
  </div>\
1645
  ');
@@ -1660,17 +1934,20 @@ var VENDOR = $.browser.mozilla ? "moz": "webkit",
1660
  FS_ENTER = "fullscreen",
1661
  FS_EXIT = "fullscreen-exit",
1662
  FULL_PLAYER,
1663
- FS_SUPPORT = flowplayer.support.fullscreen;
 
 
 
1664
 
1665
 
1666
  // esc button
1667
- $(document).bind(VENDOR + "fullscreenchange", function(e) {
1668
- var el = $(document.webkitCurrentFullScreenElement || document.mozFullScreenElement);
1669
-
1670
- if (el.length) {
1671
  FULL_PLAYER = el.trigger(FS_ENTER, [el]);
1672
  } else {
1673
  FULL_PLAYER.trigger(FS_EXIT, [FULL_PLAYER]);
 
1674
  }
1675
 
1676
  });
@@ -1681,7 +1958,7 @@ flowplayer(function(player, root) {
1681
  if (!player.conf.fullscreen) return;
1682
 
1683
  var win = $(window),
1684
- fsSeek = {pos: 0, play: false},
1685
  scrollTop;
1686
 
1687
  player.isFullscreen = false;
@@ -1694,21 +1971,34 @@ flowplayer(function(player, root) {
1694
 
1695
  if (flag) scrollTop = win.scrollTop();
1696
 
 
 
 
1697
  if (FS_SUPPORT) {
1698
 
1699
  if (flag) {
1700
- root[0][VENDOR + 'RequestFullScreen'](
1701
- flowplayer.support.fullscreen_keyboard ? Element.ALLOW_KEYBOARD_INPUT : undefined
1702
- );
 
 
 
 
 
 
 
1703
 
1704
  } else {
1705
- document[VENDOR + 'CancelFullScreen']();
 
 
 
 
 
1706
  }
1707
 
1708
  } else {
1709
- if (player.engine === "flash" && player.conf.rtmp)
1710
- fsSeek = {pos: player.video.time, play: player.playing};
1711
- player.trigger(flag ? FS_ENTER : FS_EXIT, [player])
1712
  }
1713
 
1714
  return player;
@@ -1726,22 +2016,35 @@ flowplayer(function(player, root) {
1726
  player.isFullscreen = true;
1727
 
1728
  }).bind(FS_EXIT, function(e) {
 
 
 
 
 
1729
  root.removeClass("is-fullscreen");
 
1730
  player.isFullscreen = false;
1731
  win.scrollTop(scrollTop);
1732
 
1733
  }).bind("ready", function () {
1734
- if (fsSeek.pos && !isNaN(fsSeek.pos)) {
1735
- setTimeout(function () {
1736
- player.play(); // avoid hang in buffering state
1737
- player.seek(fsSeek.pos);
1738
- if (!fsSeek.play) {
1739
- setTimeout(function () {
1740
- player.pause();
1741
- }, 100);
1742
  }
1743
- fsSeek = {pos: 0, play: false};
1744
- }, 250);
 
 
 
 
 
 
 
 
 
1745
  }
1746
  });
1747
 
@@ -1764,44 +2067,132 @@ flowplayer(function(player, root) {
1764
 
1765
 
1766
  player.play = function(i) {
1767
- if (i === undefined) player.resume();
 
1768
  else if (typeof i != 'number') player.load.apply(null, arguments);
1769
- else els().eq(i).click();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1770
  return player;
1771
  };
1772
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1773
 
1774
  if (els().length) {
 
 
 
 
 
 
 
 
1775
 
1776
  /* click -> play */
1777
  root.on("click", conf.query, function(e) {
1778
- var el = $(e.target).closest(conf.query);
1779
- el.is("." + klass) ? player.toggle() : player.load(el.attr("href"));
1780
  e.preventDefault();
 
 
 
 
 
1781
  });
1782
 
1783
- // disable single clip looping
1784
- player.conf.loop = false;
1785
-
1786
  // playlist wide cuepoint support
1787
  var has_cuepoints = els().filter("[data-cuepoints]").length;
1788
 
1789
  // highlight
1790
  player.bind("load", function(e, api, video) {
1791
-
1792
- // active
1793
  var prev = active().removeClass(klass),
1794
- el = $("a[href*='" + video.src.replace(TYPE_RE, "") + ".']", root).addClass(klass),
1795
- clips = els(),
1796
- index = clips.index(el),
1797
- is_last = index == clips.length - 1;
1798
-
1799
  // index
1800
- root.removeClass("video" + clips.index(prev)).addClass("video" + index).toggleClass("last-video", is_last);
1801
 
1802
  // video properties
1803
- video.index = index;
1804
- video.is_last = is_last;
1805
 
1806
  // cuepoints
1807
  if (has_cuepoints) player.cuepoints = el.data("cuepoints");
@@ -1813,43 +2204,11 @@ flowplayer(function(player, root) {
1813
 
1814
  });
1815
 
1816
- // api.next() / api.prev()
1817
- $.each(['next', 'prev'], function(i, key) {
1818
-
1819
- player[key] = function(e) {
1820
- e && e.preventDefault();
1821
-
1822
- // next (or previous) entry
1823
- var el = active()[key]();
1824
-
1825
- // cycle
1826
- if (!el.length) el = els().filter(key == 'next' ? ':first' : ':last');;
1827
-
1828
- el.click();
1829
- };
1830
-
1831
- $(".fp-" + key, root).click(player[key]);
1832
- });
1833
-
1834
- if (conf.advance) {
1835
- root.unbind("finish.pl").bind("finish.pl", function() {
1836
-
1837
- // next clip is found or loop
1838
- if (active().next().length || conf.loop) {
1839
- player.next();
1840
-
1841
- // stop to last clip, play button starts from 1:st clip
1842
- } else {
1843
- root.addClass("is-playing"); // show play button
1844
-
1845
- player.one("resume", function() {
1846
- player.next();
1847
- return false;
1848
- });
1849
- }
1850
- });
1851
- }
1852
 
 
 
 
1853
  }
1854
 
1855
 
@@ -1879,7 +2238,7 @@ flowplayer(function(player, root) {
1879
  for (var i = 0, cue; i < cues.length; i++) {
1880
 
1881
  cue = cues[i];
1882
- if (1 * cue) cue = { time: cue }
1883
  if (cue.time < 0) cue.time = player.video.duration + cue.time;
1884
  cue.index = i;
1885
 
@@ -1896,7 +2255,12 @@ flowplayer(function(player, root) {
1896
 
1897
  if (player.conf.generate_cuepoints) {
1898
 
1899
- player.bind("ready", function() {
 
 
 
 
 
1900
 
1901
  var cues = player.cuepoints || [],
1902
  duration = player.video.duration,
@@ -1933,14 +2297,35 @@ flowplayer(function(player, root, engine) {
1933
 
1934
  player.subtitles = track.length && track[0].track;
1935
 
1936
- // use native when supported
1937
- if (conf.nativesubtitles && conf.engine == 'html5') return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1938
  }
1939
 
1940
  // avoid duplicate loads
1941
  track.remove();
1942
 
1943
- var TIMECODE_RE = /^(([0-9]{2}:)?[0-9]{2}:[0-9]{2}[,.]{1}[0-9]{3}) --\> (([0-9]{2}:)?[0-9]{2}:[0-9]{2}[,.]{1}[0-9]{3})(.*)/;
1944
 
1945
  function seconds(timecode) {
1946
  var els = timecode.split(':');
@@ -1953,51 +2338,51 @@ flowplayer(function(player, root, engine) {
1953
  var url = track.attr("src");
1954
 
1955
  if (!url) return;
 
 
1956
 
1957
- $.get(url, function(txt) {
1958
 
1959
- for (var i = 0, lines = txt.split("\n"), len = lines.length, entry = {}, title, timecode, text, cue; i < len; i++) {
1960
 
1961
- timecode = TIMECODE_RE.exec(lines[i]);
1962
 
1963
- if (timecode) {
 
1964
 
1965
- // title
1966
- title = lines[i - 1];
 
1967
 
1968
- // text
1969
- text = "<p>" + lines[++i] + "</p><br/>";
1970
- while ($.trim(lines[++i]) && i < lines.length) text += "<p>" + lines[i] + "</p><br/>";
 
 
 
 
1971
 
1972
- // entry
1973
- entry = {
1974
- title: title,
1975
- startTime: seconds(timecode[1]),
1976
- endTime: seconds(timecode[2] || timecode[3]),
1977
- text: text
1978
- };
1979
 
1980
- cue = { time: entry.startTime, subtitle: entry };
 
 
1981
 
1982
- player.subtitles.push(entry);
1983
- player.cuepoints.push(cue);
1984
- player.cuepoints.push({ time: entry.endTime, subtitleEnd: title });
 
1985
 
1986
- // initial cuepoint
1987
- if (entry.startTime === 0) {
1988
- player.trigger("cuepoint", cue);
1989
  }
1990
 
1991
  }
1992
 
1993
- }
1994
-
1995
- }).fail(function() {
1996
- player.trigger("error", {code: 8, url: url });
1997
- return false;
1998
  });
1999
-
2000
- var wrap = $("<div class='fp-subtitle'/>", root).appendTo(root),
2001
  currentPoint;
2002
 
2003
  player.bind("cuepoint", function(e, api, cue) {
@@ -2008,18 +2393,22 @@ flowplayer(function(player, root, engine) {
2008
 
2009
  } else if (cue.subtitleEnd) {
2010
  wrap.removeClass("fp-active");
 
2011
  }
2012
 
2013
  }).bind("seek", function(e, api, time) {
2014
-
 
 
 
 
2015
  $.each(player.cuepoints || [], function(i, cue) {
2016
  var entry = cue.subtitle;
2017
-
2018
  if (entry && currentPoint != cue.index) {
2019
- if (time >= cue.time && time <= entry.endTime) player.trigger("cuepoint", cue);
2020
- else wrap.removeClass("fp-active");
2021
- }
2022
-
2023
  });
2024
 
2025
  });
@@ -2035,7 +2424,7 @@ flowplayer(function(player, root) {
2035
  if (id) {
2036
 
2037
  // load Analytics script if needed
2038
- if (typeof _gat == 'undefined') $.getScript("http://www.google-analytics.com/ga.js");
2039
 
2040
  function track(e) {
2041
 
@@ -2072,47 +2461,101 @@ flowplayer(function(player, root) {
2072
 
2073
  }
2074
 
2075
- });
2076
- if (flowplayer.support.touch) {
2077
 
2078
  flowplayer(function(player, root) {
2079
- var isAndroid = /Android/.test(UA),
2080
- isSilk = /Silk/.test(UA);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2081
 
2082
  // hide volume
2083
  if (!flowplayer.support.volume) {
2084
  root.addClass("no-volume no-mute");
2085
  }
 
 
2086
 
2087
- // fake mouseover effect with click
2088
- root.one("touchstart", function() {
2089
- isAndroid && player.toggle();
2090
 
2091
- }).bind("touchstart", function(e) {
 
 
 
 
 
 
 
 
2092
 
2093
  if (player.playing && !root.hasClass("is-mouseover")) {
2094
  root.addClass("is-mouseover").removeClass("is-mouseout");
2095
  return false;
2096
  }
2097
 
2098
- if (player.paused && root.hasClass("is-mouseout")) {
2099
  player.toggle();
2100
  }
2101
 
 
 
 
 
2102
  });
2103
 
2104
  // native fullscreen
2105
- if (player.conf.native_fullscreen && $.browser.webkit) {
2106
  player.fullscreen = function() {
2107
- $('video', root)[0].webkitEnterFullScreen();
2108
- }
 
 
 
 
2109
  }
2110
 
2111
 
2112
  // Android browser gives video.duration == 1 until second 'timeupdate' event
2113
  (isAndroid || isSilk) && player.bind("ready", function() {
2114
 
2115
- var video = $('video', root);
2116
  video.one('canplay', function() {
2117
  video[0].play();
2118
  });
@@ -2156,16 +2599,36 @@ flowplayer(function(player, root) {
2156
  tag = $("<video/>").appendTo(el);
2157
 
2158
  // configuration
2159
- $.each(['origin', 'analytics', 'logo', 'key', 'rtmp'], function(i, key) {
2160
- if (conf[key]) el.attr("data-" + key, conf[key]);
 
 
2161
  });
2162
 
 
 
 
 
 
2163
  // sources
2164
  $.each(video.sources, function(i, src) {
2165
- tag.append($("<source/>", { type: "video/" + src.type, src: src.src }));
 
 
 
 
 
2166
  });
2167
 
2168
- var code = $("<foo/>", { src: "http://embed.flowplayer.org/5.3.2/embed.min.js" }).append(el);
 
 
 
 
 
 
 
 
2169
  return $("<p/>").append(code).html().replace(/<(\/?)foo/g, "<$1script");
2170
  };
2171
 
@@ -2219,4 +2682,4 @@ $.fn.fptip = function(trigger, active) {
2219
  };
2220
 
2221
  }(jQuery);
2222
- flowplayer(function(a,b){function j(a){var b=c("<a/>")[0];return b.href=a,b.hostname}var c=jQuery,d=a.conf,e=d.swf.indexOf("flowplayer.org")&&d.e&&d.origin,f=e?j(e):location.hostname,g=d.key;location.protocol=="file:"&&(f="localhost"),a.load.ed=1,d.hostname=f,d.origin=e||location.href,e&&b.addClass("is-embedded"),typeof g=="string"&&(g=g.split(/,\s*/));if(g&&typeof key_check=="function"&&key_check(g,f))d.logo&&b.append(c("<a>",{"class":"fp-logo",href:e,target:"_top"}).append(c("<img/>",{src:d.logo})));else{var h=c("<a/>",{href:"http://flowplayer.org",target:"_top"}).appendTo(b),i=c(".fp-controls",b);a.bind("pause resume finish unload",function(b){/pause|resume/.test(b.type)&&a.engine!="flash"?(h.show().css({position:"absolute",left:16,bottom:36,zIndex:99999,width:100,height:20,backgroundImage:"url("+[".png","logo","/",".org",".flowplayer","stream","//"].reverse().join("")+")"}),a.load.ed=h.is(":visible")):h.hide()})}});
1
  /*!
2
 
3
+ Flowplayer v5.5.2 (Thursday, 27. November 2014 10:32AM) | flowplayer.org/license
4
 
5
  */
6
  !function($) {
19
  var b = $.browser = {},
20
  ua = navigator.userAgent.toLowerCase(),
21
  match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
22
+ /(safari)[ \/]([\w.]+)/.exec(ua) ||
23
  /(webkit)[ \/]([\w.]+)/.exec(ua) ||
24
  /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
25
  /(msie) ([\w.]+)/.exec(ua) ||
42
 
43
  var instances = [],
44
  extensions = [],
45
+ UA = window.navigator.userAgent;
 
46
 
47
 
48
  /* flowplayer() */
49
  window.flowplayer = function(fn) {
50
+ return $.isFunction(fn) ? extensions.push(fn) :
 
51
  typeof fn == 'number' || fn === undefined ? instances[fn || 0] :
52
  $(fn).data("flowplayer");
53
  };
54
 
55
+ $(window).on('beforeunload', function() {
56
+ $.each(instances, function(i, api) {
57
+ if (api.conf.splash) {
58
+ api.unload();
59
+ } else {
60
+ api.bind("error", function () {
61
+ $(".flowplayer.is-error .fp-message").remove();
62
+ });
63
+ }
64
+ });
65
+ });
66
+
67
+ var supportLocalStorage = false;
68
+ try {
69
+ if (typeof window.localStorage == "object") {
70
+ window.localStorage.flowplayerTestStorage = "test";
71
+ supportLocalStorage = true;
72
+ }
73
+ } catch (ignored) {}
74
+
75
+ var isSafari = /Safari/.exec(navigator.userAgent) && !/Chrome/.exec(navigator.userAgent);
76
+ m = /(\d+\.\d+) Safari/.exec(navigator.userAgent),
77
+ safariVersion = m ? Number(m[1]) : 100;
78
 
79
  $.extend(flowplayer, {
80
 
81
+ version: '5.5.2',
82
 
83
  engine: {},
84
 
104
  // default aspect ratio
105
  ratio: 9 / 16,
106
 
107
+ adaptiveRatio: false,
108
+
109
  // scale flash object to video's aspect ratio in normal mode?
110
  flashfit: false,
111
 
113
 
114
  splash: false,
115
 
116
+ live: false,
117
+
118
+ swf: "//releases.flowplayer.org/5.5.2/flowplayer.swf",
119
 
120
  speeds: [0.25, 0.5, 1, 1.5, 2],
121
 
122
  tooltip: true,
123
 
124
  // initial volume level
125
+ volume: !supportLocalStorage ? 1 : localStorage.muted == "true" ? 0 : !isNaN(localStorage.volume) ? localStorage.volume || 1 : 1,
126
 
127
  // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#error-codes
128
  errors: [
144
  ],
145
  errorUrls: ['','','','','','','','','','',
146
  'http://get.adobe.com/flashplayer/'
147
+ ],
148
+ playlist: [],
149
+
150
+ hlsFix: isSafari && safariVersion < 8
151
 
152
  }
153
 
154
  });
155
 
 
 
 
 
 
156
  // keep track of players
157
+ var playerCount = 1;
158
 
159
  // jQuery plugin
160
  $.fn.flowplayer = function(opts, callback) {
168
  var root = $(this).addClass("is-loading"),
169
  conf = $.extend({}, flowplayer.defaults, flowplayer.conf, opts, root.data()),
170
  videoTag = $("video", root).addClass("fp-engine").removeAttr("controls"),
171
+ urlResolver = videoTag.length ? new URLResolver(videoTag) : null,
172
  storage = {},
173
  lastSeekPosition,
174
  engine;
175
 
176
+ if (conf.playlist.length) { // Create initial video tag if called without
177
+ var preload = conf.preload || videoTag.attr('preload'), placeHolder;
178
+ if (videoTag.length) videoTag.replaceWith(placeHolder = $('<p />'));
179
+ videoTag = $('<video />').addClass('fp-engine');
180
+ placeHolder ? placeHolder.replaceWith(videoTag) : root.prepend(videoTag);
181
+ if (flowplayer.support.video) videoTag.attr('preload', preload);
182
+ if (typeof conf.playlist[0] === 'string') videoTag.attr('src', conf.playlist[0]);
183
+ else {
184
+ $.each(conf.playlist[0], function(i, plObj) {
185
+ for (var type in plObj) {
186
+ if (plObj.hasOwnProperty(type)) {
187
+ videoTag.append($('<source />').attr({type: 'video/' + type, src: plObj[type]}));
188
+ }
189
+ }
190
+ });
191
+ }
192
+ urlResolver = new URLResolver(videoTag);
193
+
194
+ }
195
+
196
+ //stop old instance
197
+ var oldApi = root.data('flowplayer');
198
+ if (oldApi) oldApi.unload();
199
+
200
  root.data('fp-player_id', root.data('fp-player_id') || playerCount++);
201
 
202
  try {
203
+ storage = supportLocalStorage ? window.localStorage : storage;
204
  } catch(e) {}
205
 
206
+ var isRTL = (this.currentStyle && this.currentStyle['direction'] === 'rtl')
207
+ || (window.getComputedStyle && window.getComputedStyle(this, null).getPropertyValue('direction') === 'rtl');
208
+
209
+ if (isRTL) root.addClass('is-rtl');
210
+
211
  /*** API ***/
212
+ var api = oldApi || {
213
 
214
  // properties
215
  conf: conf,
216
  currentSpeed: 1,
217
+ volumeLevel: typeof conf.volume === "undefined" ? storage.volume * 1 : conf.volume,
218
  video: {},
219
 
220
  // states
226
  playing: false,
227
  ready: false,
228
  splash: false,
229
+ rtl: isRTL,
230
 
231
  // methods
232
  load: function(video, callback) {
247
  // callback
248
  if ($.isFunction(video)) callback = video;
249
  if (callback) root.one("ready", callback);
250
+ } else {
251
+ api.loading = false;
252
  }
253
  }
254
 
270
 
271
  // Firefox (+others?) does not fire "resume" after finish
272
  if (api.finished) {
273
+ api.trigger("resume", [api]);
274
  api.finished = false;
275
  }
276
  }
288
  seek(false) -> 10% backward
289
  */
290
  seek: function(time, callback) {
291
+ if (api.ready && !api.live) {
292
 
293
  if (typeof time == "boolean") {
294
  var delta = api.video.duration * 0.1;
295
  time = api.video.time + (time ? delta : -delta);
296
  }
297
+ time = lastSeekPosition = Math.min(Math.max(time, 0), api.video.duration).toFixed(1);
298
+ var ev = $.Event('beforeseek');
299
+ root.trigger(ev, [api, time]);
300
+ if (!ev.isDefaultPrevented()) {
301
+ engine.seek(time);
302
+ if ($.isFunction(callback)) root.one("seek", callback);
303
+ } else {
304
+ api.seeking = false;
305
+ root.toggleClass("is-seeking", api.seeking); // remove loading indicator
306
+ }
307
  }
308
  return api;
309
  },
321
  },
322
 
323
  mute: function(flag) {
324
+ if (flag === undefined) flag = !api.muted;
325
  storage.muted = api.muted = flag;
326
+ storage.volume = !isNaN(storage.volume) ? storage.volume : conf.volume; // make sure storage has volume
327
+ api.volume(flag ? 0 : storage.volume, true);
328
  api.trigger("mute", flag);
329
+ return api;
330
  },
331
 
332
+ volume: function(level, skipStore) {
333
  if (api.ready) {
334
  level = Math.min(Math.max(level, 0), 1);
335
+ if (!skipStore) storage.volume = level;
336
  engine.volume(level);
337
  }
338
+
339
  return api;
340
  },
341
 
386
  api.disabled = flag;
387
  api.trigger("disable", flag);
388
  }
389
+ return api;
390
  }
391
 
392
  };
393
 
394
+ api.conf = $.extend(api.conf, conf);
395
+
396
 
397
  /* event binding / unbinding */
398
  $.each(['bind', 'one', 'unbind'], function(i, key) {
409
 
410
 
411
  /*** Behaviour ***/
412
+ if (!root.data('flowplayer')) { // Only bind once
413
+ root.bind("boot", function() {
414
 
415
+ // conf
416
+ $.each(['autoplay', 'loop', 'preload', 'poster'], function(i, key) {
417
+ var val = videoTag.attr(key);
418
+ if (val !== undefined) conf[key] = val ? val : true;
419
+ });
420
 
421
+ // splash
422
+ if (conf.splash || root.hasClass("is-splash") || !flowplayer.support.firstframe) {
423
+ api.forcedSplash = !conf.splash && !root.hasClass("is-splash");
424
+ api.splash = conf.splash = conf.autoplay = true;
425
+ root.addClass("is-splash");
426
+ if (flowplayer.support.video) videoTag.attr("preload", "none");
427
+ }
428
 
429
+ if (conf.live || root.hasClass('is-live')) {
430
+ api.live = conf.live = true;
431
+ root.addClass('is-live');
432
+ }
 
 
433
 
434
+ // extensions
435
+ $.each(extensions, function(i) {
436
+ this(api, root);
437
+ });
438
 
439
+ // 1. use the configured engine
440
+ engine = flowplayer.engine[conf.engine];
441
+ if (engine) engine = engine(api, root);
442
 
443
+ if (engine.pick(urlResolver.initialSources)) {
444
+ api.engine = conf.engine;
445
 
446
+ // 2. failed -> try another
447
+ } else {
448
+ $.each(flowplayer.engine, function(name, impl) {
449
+ if (name != conf.engine) {
450
+ engine = this(api, root);
451
+ if (engine.pick(urlResolver.initialSources)) {
452
+ api.engine = name;
453
+ return false;
454
+ }
455
+ }
456
+ });
457
+ }
458
 
459
+ // instances
460
+ instances.push(api);
461
 
462
+ // no engine
463
+ if (!api.engine) return api.trigger("error", { code: flowplayer.support.flashVideo ? 5 : 10 });
464
 
465
+ // start
466
+ conf.splash ? api.unload() : api.load();
467
 
468
+ // disabled
469
+ if (conf.disabled) api.disable();
470
 
471
+ //initial volumelevel
472
+ engine.volume(api.volumeLevel);
473
 
474
+ // initial callback
475
+ root.one("ready", callback);
476
 
 
477
 
478
+ }).bind("load", function(e, api, video) {
479
+
480
+ // unload others
481
+ if (conf.splash) {
482
+ $(".flowplayer").filter(".is-ready, .is-loading").not(root).each(function() {
483
+ var api = $(this).data("flowplayer");
484
+ if (api.conf.splash) api.unload();
485
+ });
486
+ }
487
 
488
+ // loading
489
+ root.addClass("is-loading");
490
+ api.loading = true;
491
 
492
 
493
+ }).bind("ready", function(e, api, video) {
494
+ video.time = 0;
495
+ api.video = video;
496
 
497
+ function notLoading() {
498
+ root.removeClass("is-loading");
499
+ api.loading = false;
500
+ }
501
 
502
+ if (conf.splash) root.one("progress", notLoading);
503
+ else notLoading();
504
 
505
+ // saved state
506
+ if (api.muted) api.mute(true);
507
+ else api.volume(api.volumeLevel);
508
 
509
+ // see https://github.com/flowplayer/flowplayer/issues/479
510
 
511
+ var hlsFix = api.conf.hlsFix && /mpegurl/i.exec(video.type);
512
+ root.toggleClass('hls-fix', !!hlsFix);
 
 
513
 
514
+ }).bind("unload", function(e) {
515
+ if (conf.splash) videoTag.remove();
516
+ root.removeClass("is-loading");
517
+ api.loading = false;
518
 
 
 
 
 
 
519
 
520
+ }).bind("ready unload", function(e) {
521
+ var is_ready = e.type == "ready";
522
+ root.toggleClass("is-splash", !is_ready).toggleClass("is-ready", is_ready);
523
+ api.ready = is_ready;
524
+ api.splash = !is_ready;
525
 
 
 
526
 
527
+ }).bind("progress", function(e, api, time) {
528
+ api.video.time = time;
529
 
 
 
530
 
531
+ }).bind("speed", function(e, api, val) {
532
+ api.currentSpeed = val;
 
 
533
 
534
+ }).bind("volume", function(e, api, level) {
535
+ api.volumeLevel = Math.round(level * 100) / 100;
536
+ if (!api.muted) storage.volume = level;
537
+ else if (level) api.mute(false);
538
 
 
 
 
539
 
540
+ }).bind("beforeseek seek", function(e) {
541
+ api.seeking = e.type == "beforeseek";
542
+ root.toggleClass("is-seeking", api.seeking);
543
 
544
+ }).bind("ready pause resume unload finish stop", function(e, _api, video) {
 
545
 
546
+ // PAUSED: pause / finish
547
+ api.paused = /pause|finish|unload|stop/.test(e.type);
548
+
549
+ // SHAKY HACK: first-frame / preload=none
550
+ if (e.type == "ready") {
551
+ api.paused = conf.preload == 'none';
552
+ if (video) {
553
+ api.paused = !video.duration || !conf.autoplay && (conf.preload != 'none');
554
+ }
555
  }
 
556
 
557
+ // the opposite
558
+ api.playing = !api.paused;
559
 
560
+ // CSS classes
561
+ root.toggleClass("is-paused", api.paused).toggleClass("is-playing", api.playing);
562
 
563
+ // sanity check
564
+ if (!api.load.ed) api.pause();
565
 
566
+ }).bind("finish", function(e) {
567
+ api.finished = true;
568
 
569
+ }).bind("error", function() {
570
+ videoTag.remove();
571
+ });
572
+ }
573
 
574
  // boot
575
  root.trigger("boot", [api, root]).data("flowplayer", api);
580
 
581
  !function() {
582
 
583
+ var parseIpadVersion = function(UA) {
584
+ var e = /Version\/(\d\.\d)/.exec(UA);
585
+ if (e && e.length > 1) {
586
+ return parseFloat(e[1], 10);
587
+ }
588
+ return 0;
589
+ };
590
+
591
  var s = flowplayer.support,
592
  browser = $.browser,
593
  video = $("<video loop autoplay preload/>")[0],
 
594
  UA = navigator.userAgent,
595
+ IS_IE = browser.msie || /Trident\/7/.test(UA),
596
+ IS_IPAD = /iPad|MeeGo/.test(UA) && !/CriOS/.test(UA),
597
+ IS_IPAD_CHROME = /iPad/.test(UA) && /CriOS/.test(UA),
598
+ IS_IPHONE = /iP(hone|od)/i.test(UA) && !/iPad/.test(UA),
599
+ IS_ANDROID = /Android/.test(UA) && !/Firefox/.test(UA),
600
+ IS_ANDROID_FIREFOX = /Android/.test(UA) && /Firefox/.test(UA),
601
  IS_SILK = /Silk/.test(UA),
602
+ IS_WP = /IEMobile/.test(UA),
603
+ WP_VER = IS_WP ? parseFloat(/Windows\ Phone\ (\d+\.\d+)/.exec(UA)[1], 10) : 0,
604
+ IE_MOBILE_VER = IS_WP ? parseFloat(/IEMobile\/(\d+\.\d+)/.exec(UA)[1], 10) : 0,
605
+ IPAD_VER = IS_IPAD ? parseIpadVersion(UA) : 0,
606
+ ANDROID_VER = IS_ANDROID ? parseFloat(/Android\ (\d\.\d)/.exec(UA)[1], 10) : 0;
607
  $.extend(s, {
 
608
  subtitles: !!video.addTextTrack,
609
+ fullscreen: typeof document.webkitCancelFullScreen == 'function' && !/Mac OS X 10_5.+Version\/5\.0\.\d Safari/.test(UA) ||
610
+ document.mozFullScreenEnabled ||
611
+ typeof document.exitFullscreen == 'function' ||
612
+ typeof document.msExitFullscreen == 'function',
613
  inlineBlock: !(IS_IE && browser.version < 8),
614
  touch: ('ontouchstart' in window),
615
+ dataload: !IS_IPAD && !IS_IPHONE && !IS_WP,
616
  zeropreload: !IS_IE && !IS_ANDROID, // IE supports only preload=metadata
617
+ volume: !IS_IPAD && !IS_ANDROID && !IS_IPHONE && !IS_SILK && !IS_IPAD_CHROME,
618
+ cachedVideoTag: !IS_IPAD && !IS_IPHONE && !IS_IPAD_CHROME && !IS_WP,
619
+ firstframe: !IS_IPHONE && !IS_IPAD && !IS_ANDROID && !IS_SILK && !IS_IPAD_CHROME && !IS_WP && !IS_ANDROID_FIREFOX,
620
+ inlineVideo: !IS_IPHONE && (!IS_WP || (WP_VER >= 8.1 && IE_MOBILE_VER >= 11)) && (!IS_ANDROID || ANDROID_VER >= 3),
621
+ hlsDuration: !IS_ANDROID && (!browser.safari || IS_IPAD || IS_IPHONE || IS_IPAD_CHROME),
622
+ seekable: !IS_IPAD && !IS_IPAD_CHROME
623
  });
624
 
625
  // flashVideo
626
  try {
627
+ var plugin = navigator.plugins["Shockwave Flash"],
628
+ ver = IS_IE ? new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable('$version') : plugin.description;
629
+ if (!IS_IE && !plugin[0].enabledPlugin) s.flashVideo = false;
630
+ else {
631
 
632
+ ver = ver.split(/\D+/);
633
+ if (ver.length && !ver[0]) ver = ver.slice(1);
634
 
635
+ s.flashVideo = ver[0] > 9 || ver[0] == 9 && ver[3] >= 115;
636
+ }
637
 
638
  } catch (ignored) {}
639
+ try {
640
+ s.video = !!video.canPlayType;
641
+ s.video && video.canPlayType('video/mp4');
642
+ } catch (e) {
643
+ s.video = false;
644
+ }
645
 
646
  // animation
647
  s.animation = (function() {
660
  /* The most minimal Flash embedding */
661
 
662
  // movie required in opts
663
+ function embed(swf, flashvars, wmode) {
664
+ wmode = wmode || "transparent";
665
 
666
  var id = "obj" + ("" + Math.random()).slice(2, 15),
667
  tag = '<object class="fp-engine" id="' + id+ '" name="' + id + '" ';
673
  width: "100%",
674
  height: "100%",
675
  allowscriptaccess: "always",