From becc0d7944f40b5754fe014eb18f36571a6e3a67 Mon Sep 17 00:00:00 2001
From: Ron Shimshock <ron@shimshockgroup.com>
Date: Tue, 26 Apr 2016 08:24:25 -0500
Subject: [PATCH] Refactor theming

---
 README.txt              |  40 ++++
 js/jw_player.drupal.js  |  21 ++
 jw_player.api.php       |  13 +-
 jw_player.module        | 560 +++++++++++++++++++++++++++++-------------------
 theme/jw_player.tpl.php |  12 +-
 5 files changed, 416 insertions(+), 230 deletions(-)
 create mode 100644 js/jw_player.drupal.js

diff --git a/README.txt b/README.txt
index a6542a0..65fd240 100644
--- a/README.txt
+++ b/README.txt
@@ -50,6 +50,46 @@ click on the cog beside the field to select the preset you'd like to apply to
 the file. That's it - videos uploaded to this field should now be displayed
 using JW Player!
 
+-- PLAYLISTS --
+
+To create playlists with JW Player 7, add a file field to an entity that allows
+for more than one file to be uploaded. Select the "Description" checkbox to add
+titles for each playlist file. When editing the field formatter, select a player
+type of "Playlist player".
+
+-- PREVIEW IMAGES --
+
+Preview images are supported with JW Player 7. Preview images (also known as
+poster images) are static pictures displayed on a player before playback starts.
+Add preview images to fields by performing the following steps:
+* Create an image field on the entity where the JW Player will be rendered.
+* Set the number of images that can be uploaded to match the number of files
+  to be rendered by JW Player. This will allow for a unique preview image to
+  be matched with each player file.
+* Add the image to the field formatter "Preview image" setting. Optionally
+  select an image style to be applied to the image.
+
+-- YOUTUBE VIDEOS --
+
+YouTube videos can be displayed in JW Player using JW Player 7. Create a text
+or link field in the entity, and format the field to use JW Player. Paste URLs
+to the YouTube's video page in the field, and the video will be rendered.
+
+-- ALTER PLAYER IN CODE --
+
+The player can be modified programmatically by using an available hook
+(hook_jw_player_build_player_alter). See jw_player.api.php for details.
+
+-- PLAYER STYLING --
+
+A major change between JW Player 6 and 7 is that the skinning model uses CSS
+and web fonts. Regardless of rendering mode, all controls are overlaid on top
+of the player with HTML. To create a player that meets your needs using
+JW Player 7, follow the CSS options found on the JW Player developer site:
+* https://developer.jwplayer.com/jw-player/docs/developer-guide/customization/css-skinning/skins_reference/
+* https://developer.jwplayer.com/jw-player/docs/developer-guide/customization/css-skinning/skins_fonts/
+* https://developer.jwplayer.com/jw-player/docs/developer-guide/customization/css-skinning/skins_example/
+
 -- URL BASED SEEKING --
 
 You can create permanent links that make jWPlayer start playing at a given
diff --git a/js/jw_player.drupal.js b/js/jw_player.drupal.js
new file mode 100644
index 0000000..2f38958
--- /dev/null
+++ b/js/jw_player.drupal.js
@@ -0,0 +1,21 @@
+(function ($) {
+
+  Drupal.behaviors.jw_player = {
+    attach: function (context, settings) {
+
+      $(settings.jw_player).each(function () {
+        // Load an object with all of our player options
+        var options = {};
+        for (var option_key in this.options) {
+          if (this.options.hasOwnProperty(option_key)) {
+            options[option_key] = this.options[option_key];
+          }
+        }
+
+        jwplayer(this.html_id).setup(options);
+      });
+
+    }
+  };
+
+})(jQuery);
diff --git a/jw_player.api.php b/jw_player.api.php
index 5fcb359..3962164 100644
--- a/jw_player.api.php
+++ b/jw_player.api.php
@@ -34,6 +34,17 @@ function hook_jw_player_plugin_info($preset) {
   );
   return $plugins;
 }
+
+/**
+ * Alter the player and its options prior to rendering.
+ *
+ * @param array $build
+ *   A list of built players as an array.
+ */
+function hook_jw_player_build_player_alter(&$build) {
+
+}
+
 /**
  * Checks a file for JW Player supported media formats.
  *
@@ -58,4 +69,4 @@ function hook_jw_player_supports($file) {
  */
 function hook_jw_player_supported_mimetypes_alter(&$types) {
 
-}
\ No newline at end of file
+}
diff --git a/jw_player.module b/jw_player.module
index dc43d23..fce0f2a 100644
--- a/jw_player.module
+++ b/jw_player.module
@@ -64,18 +64,22 @@ function jw_player_theme() {
 function jw_player_field_formatter_info() {
   $formatters = array(
     'jw_player' => array(
-      'label' => t('JW player'),
-      'field types' => array('file', 'video'),
+      'label' => t('JW Player'),
+      'field types' => array('file', 'video', 'link_field', 'text'),
       'settings' => array(
         'jwplayer_preset' => '',
         'check_support' => FALSE,
         'preview_image_field' => NULL,
         'preview_image_style' => NULL,
+        'player_type' => 'jwplayer',
       ),
     ),
 
+    // @todo Remove in future and consolidate to single JW Player formatter.
+    // In JW Player 7, all styling differences are set through CSS, and options
+    // can be generated in one array (see code in 'jw_player_build_player').
     'jw_player_playlist' => array(
-      'label' => t('JW player playlist'),
+      'label' => t('JW Player playlist (for JW Player 6 and earlier)'),
       'field types' => array('file', 'video'),
       'settings' => array(
         'jwplayer_preset' => '',
@@ -85,8 +89,11 @@ function jw_player_field_formatter_info() {
       ),
     ),
 
+    // @todo Remove in future and consolidate to single JW Player formatter.
+    // In JW Player 7, all styling differences are set through CSS, and options
+    // can be generated in one array (see code in 'jw_player_build_player').
     'jw_player_sources' => array(
-      'label' => t('JW player sources'),
+      'label' => t('JW Player sources (for JW Player 6 and earlier)'),
       'field types' => array('file', 'video'),
       'settings' => array(
         'jwplayer_preset' => '',
@@ -109,6 +116,20 @@ function jw_player_field_formatter_settings_form($field, $instance, $view_mode,
   // If there are presets prompt the user to select one or create another.
   // If no, prompt to create a preset.
   if (!empty($presets)) {
+    if (!jw_player_use_legacy()) {
+      $element['player_type'] = array(
+        '#title' => t('Select player type'),
+        '#type' => 'select',
+        '#options' => array(
+          'player' => 'Single file player',
+          'playlist' => 'Playlist player (multiple files)',
+          'sources' => 'Sources player (file with fallback sources)',
+        ),
+        '#default_value' => ($settings['player_type']) ? $settings['player_type'] : 'player',
+        '#weight' => -1,
+      );
+    }
+
     foreach (jw_player_preset_load() as $preset => $item) {
       $options[$preset] = $item['preset_name'];
     }
@@ -217,6 +238,9 @@ function jw_player_field_formatter_settings_summary($field, $instance, $view_mod
   if (isset($presets[$settings['jwplayer_preset']])) {
     $summary[] = t('Preset: @name', array('@name' => $presets[$settings['jwplayer_preset']]['preset_name']));
     $summary[] = t('Description: @description', array('@description' => $presets[$settings['jwplayer_preset']]['description']));
+    $summary[] = t('Player type: @player_type', array(
+      '@player_type' => $settings['player_type']
+    ));
 
     $settings = $presets[$settings['jwplayer_preset']]['settings'];
     foreach ($settings as $key => $val) {
@@ -259,8 +283,10 @@ function jw_player_field_formatter_settings_summary($field, $instance, $view_mod
  */
 function jw_player_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
   $element = array();
+  $settings = $display['settings'];
+
   // Exclude files without a supported media format (if enabled).
-  if (!empty($display['settings']['check_support'])) {
+  if (!empty($settings['check_support'])) {
     $items = array_filter($items, 'jw_player_supports');
   }
   // Support for transcoded videos in Video a field.
@@ -272,69 +298,100 @@ function jw_player_field_formatter_view($entity_type, $entity, $field, $instance
     }
   }
 
-  if ($display['type'] == 'jw_player') {
-    // Prepare preview image.
-    $image = NULL;
-    if ($display['settings']['preview_image_field']) {
-      $split = explode('|', $display['settings']['preview_image_field']);
-      $preview_image_field = $split[1];
-      $preview_image_entity_type = explode(':', $split[0]);
-
-      if ($preview_items = field_get_items($preview_image_entity_type[0], $entity, $preview_image_field)) {
-        $image = image_style_url($display['settings']['preview_image_style'], $preview_items[0]['uri']);
-      }
-    }
-
-    // Process files for the theme function.
-    foreach ($items as $delta => $item) {
-      // Give each instance of the player a unique id. The combination
-      // of fid and preset are considered to be save even in cases where
-      // the entire theme functions output is cached.
-      // Prefixing the id makes sure that the id does not start with a
-      // invalid numeric value.
-      $id = $item['fid'] . $display['settings']['jwplayer_preset'];
-      $element[$delta] = array(
-        '#theme' => 'jw_player',
-        '#file' => $item,
-        '#preset' => $display['settings']['jwplayer_preset'],
-        '#html_id' => drupal_html_id('jwplayer' . $id),
-      );
-
-      // Add preview image.
-      if ($image) {
-        $element[$delta]['#image'] = $image;
+  if (count($items) == 0) {
+    return '';
+  }
+  else {
+    $default_settings = jw_player_default_settings();
+    $preset_settings = array();
+
+    if (!empty($settings['jwplayer_preset'])) {
+      $preset = jw_player_preset_load($settings['jwplayer_preset']);
+      if (!empty($preset)) {
+        $preset_settings = $preset['settings'];
+        // Primary setting: legacy support for value.
+        if (isset($preset_settings['primary']) && $preset_settings['primary'] != 'flash') {
+          unset($preset_settings['primary']);
+        }
+        // Responsive setting: responsive or fixed.
+        // Responsive needs width and aspectratio; fixed needs height and width.
+        if (!empty($preset_settings['responsive'])) {
+          unset($preset_settings['height']);
+          unset($default_settings['height']);
+          $preset_settings['width'] = $preset_settings['width'] . '%';
+        }
+        else {
+          unset($preset_settings['aspectratio']);
+        }
+        // Once we're done, remove the 'responsive' value.
+        unset($preset_settings['responsive']);
+        // Autostart setting.
+        if (isset($preset_settings['autostart']) && $preset_settings['autostart']) {
+          $preset_settings['autostart'] = true;
+        }
+        else {
+          $preset_settings['autostart'] = false;
+        }
+        // Mute setting.
+        if (isset($preset_settings['mute']) && $preset_settings['mute']) {
+          $preset_settings['mute'] = true;
+        }
+        else {
+          $preset_settings['mute'] = false;
+        }
+        // Controls setting.
+        if (isset($preset_settings['controls']) && $preset_settings['controls']) {
+          $preset_settings['controls'] = true;
+        }
+        else {
+          $preset_settings['controls'] = false;
+        }
+        // Skin setting.
+        if (jw_player_use_legacy()) {
+          $skin = !empty($preset_settings['skin']) ? jw_player_skins($preset_settings['skin']) : NULL;
+          $preset_settings['skin'] = is_object($skin) ? file_create_url($skin->uri) : $preset_settings['skin'];
+        }
+        else {
+          if (!isset($preset_settings['skin']) || empty($preset_settings['skin'])) {
+            unset($preset_settings['skin']);
+          }
+        }
+        // Sharing sites settings.
+        if (isset($preset_settings['sharing']) && $preset_settings['sharing']) {
+          $preset_settings['sharing'] = array();
+          $preset_settings['sharing']['heading'] = $preset_settings['sharing_heading'];
+          unset($preset_settings['sharing_heading']);
+          $selected_sharing = array();
+          foreach ($preset_settings['sharing_sites_show'] as $sharing_site) {
+            if (!empty($sharing_site)) {
+              $selected_sharing[$sharing_site] = $preset_settings['sharing_sites_order'][$sharing_site]['weight'];
+            }
+          }
+          asort($selected_sharing);
+          foreach ($selected_sharing as $key => $value) {
+            $preset_settings['sharing']['sites'][] = $key;
+          }
+        }
+        // Once we're done, remove the extra 'sharing_sites' arrays if they are set.
+        if (isset($preset_settings['sharing_sites_show'])) {
+          unset($preset_settings['sharing_sites_show']);
+        }
+        if (isset($preset_settings['sharing_sites_order'])) {
+          unset($preset_settings['sharing_sites_order']);
+        }
       }
     }
-  }
-
-  if ($display['type'] == 'jw_player_playlist') {
-    // Process files for the theme function.
-
-    $id = $items[0]['fid'] . $display['settings']['jwplayer_preset'];
-    $element = array(
-      '#theme' => 'jw_player',
-      '#playlist' => $items,
-      '#preset' => $display['settings']['jwplayer_preset'],
-      '#html_id' => drupal_html_id('jwplayer' . $id),
-      '#playlist_size' => $display['settings']['playlist_size'],
-      '#playlist_position' => $display['settings']['playlist_position'],
-    );
-  }
 
-  if ($display['type'] == 'jw_player_sources') {
-    // Process files for the theme function.
-    $id = $items[0]['fid'] . $display['settings']['jwplayer_preset'];
-    $element = array(
-      '#theme' => 'jw_player',
-      '#sources' => $items,
-      '#preset' => $display['settings']['jwplayer_preset'],
-      '#html_id' => drupal_html_id('jwplayer' . $id),
-    );
+    // Merge presets into default settings.
+    $options = array_merge($default_settings, $preset_settings);
 
-    if ($images = field_get_items($entity_type, $entity, 'field_video_thumbnail')) {
-      $element['#image'] = $images[0];
+    switch ($display['type']) {
+      case 'jw_player':
+      case 'jw_player_playlist':
+      case 'jw_player_sources':
+        $element[0] = jw_player_build_player($display['type'], $entity_type, $entity, $field, $items, $settings, $options);
+        break;
     }
-
   }
   return $element;
 }
@@ -425,163 +482,6 @@ function jw_player_default_settings() {
 }
 
 /**
- * Process variables for jw_player.tpl.php.
- *
- * @todo this whole function seems obsolete. The config passed to jw player
- *   should be prepared right where the theme function is called and passed
- *   down to js using js settings in #attached within the render array. Along
- *   width that, inline snippets in the template should be replaced by a
- *   a behavior that initializes the players with the options from js settings.
- */
-function template_preprocess_jw_player(&$variables) {
-  //In some instances an object is passed so convert to an array.
-  if(is_object($variables['file'])) {
-    $variables['file'] = (array) $variables['file'];
-  }
-
-  // Load defaults as the starting point.
-  $default_settings = jw_player_default_settings();
-
-  // Load preset if set.
-  $preset_settings = array();
-  if (!empty($variables['preset'])) {
-    $preset = jw_player_preset_load($variables['preset']);
-    // Additional check to ensure that the preset has actually loaded. This
-    // prevents problems where a preset has been deleted but a field is still
-    // configured to use it.
-    if (!empty($preset)) {
-      $preset_settings = $preset['settings'];
-      if (!empty($preset_settings['responsive'])) {
-        unset($preset_settings['height']);
-        $preset_settings['width'] = $preset_settings['width'] . '%';
-      }
-      else {
-        unset($preset_settings['aspectratio']);
-      }
-    }
-  }
-
-  // Get any preset override options that were sent through the formatter or
-  // theme call.
-  $options = array();
-  if (isset($variables['options'])) {
-    $options = $variables['options'];
-    unset($variables['options']);
-  }
-
-  // Merge all variables together. Preset settings take priority over defaults,
-  // variables passed directly to the theme function take priority over both.
-  $variables = array_merge($default_settings, $preset_settings, $options, $variables);
-
-  // The html_id should have been previously set. But we need to make
-  // sure we do have one.
-  if (!isset($variables['html_id'])) {
-    $id = $variables['file']['fid'] . $variables['preset'];
-    $variables['html_id'] = drupal_html_id('jwplayer' . $id);
-  }
-
-  // Check if there is one or multiple files. If one file then we set 'file', if
-  // there are multiple files we set 'levels'. Note that levels is used for both
-  // multiple video formats as well as for adaptive bitrates.
-  if (isset($variables['sources'])) {
-    $variables['config']['playlist'][0] = array();
-    $variables['config']['playlist'][0]['image'] = file_create_url($variables['image']['uri']);
-    foreach ($variables['sources'] as $key => $source) {
-      $variables['config']['playlist'][0]['sources'][$key]['file'] = file_create_url($source['uri']);
-      if (isset($source['getid3']) && $source['getid3']['width'] > 0 && $source['getid3']['height'] > 0) {
-        $variables['config']['playlist'][0]['sources'][$key]['width'] = $source['getid3']['width'];
-        $variables['config']['playlist'][0]['sources'][$key]['height'] = $source['getid3']['height'];
-      }
-      elseif (isset($source['field_width']) && isset($source['field_height'])) {
-        $variables['config']['playlist'][0]['sources'][$key]['width'] = $source['field_width'][LANGUAGE_NONE][0]['value'];
-        $variables['config']['playlist'][0]['sources'][$key]['height'] = $source['field_height'][LANGUAGE_NONE][0]['value'];
-      }
-    }
-  }
-
-  elseif (isset($variables['playlist'])) {
-    $variables['config']['playlist'] = array();
-    foreach ($variables['playlist'] as $key => $source) {
-      $variables['config']['playlist'][$key]['file'] = file_create_url($source['uri']);
-      $variables['config']['playlist'][$key]['image'] = 'none';
-      if (!empty($source['description'])) {
-        $variables['config']['playlist'][$key]['title'] = $source['description'];
-      }
-      else {
-        $pathinfo = pathinfo($source['uri']);
-        $variables['config']['playlist'][$key]['title'] = $pathinfo['filename'];
-      }
-    }
-
-    $variables['config']['listbar'] = array(
-      'position' => !empty($variables['playlist_position']) ? $variables['playlist_position'] : JW_PLAYER_DEFAULT_PLAYLIST_POSITION,
-      'size' => !empty($variables['playlist_size']) ? $variables['playlist_size'] : JW_PLAYER_DEFAULT_PLAYLIST_POSITION,
-    );
-  }
-
-  else {
-    $variables['config']['file'] = file_create_url($variables['file']['uri']);
-  }
-
-  if (jw_player_use_legacy()) {
-    // Resolve skin url.
-    $skin = !empty($variables['skin']) ? jw_player_skins($variables['skin']) : NULL;
-    $variables['skin'] = is_string($skin) ? file_create_url($skin->uri) : $variables['skin'];
-  }
-
-  // Outside of the cloud player check, has to work then as well.
-  if (!empty($variables['image'])) {
-    $variables['config']['image'] = $variables['image'];
-  }
-
-  // Don't apply preset config in case of cloud hosted players.
-  if (!variable_get('jw_player_cloud_player_default', FALSE)) {
-
-    // Copy player variables into their own array to be set as JavaScript
-    // configuration.
-    // @todo Bad smell here. Refactoring needed.
-
-    $config_variables = array(
-      'width',
-      'height',
-      'playlist.position',
-      'playlist.size',
-      'skin',
-      'stretching',
-      'aspectratio',
-    );
-    foreach ($config_variables as $key) {
-      if (!empty($variables[$key])) {
-        $variables['config'][$key] = $variables[$key];
-      }
-    }
-
-    if (isset($variables['autostart']) && $variables['autostart']) {
-      $variables['config']['autostart'] = 'true';
-    }
-
-    if (isset($variables['controls']) && !$variables['controls']) {
-      $variables['config']['controls'] = 'false';
-    }
-
-    // If the preset has the primary mode set, modify the modes array so that it
-    // comes first.
-    if (isset($variables['primary']) && $variables['primary'] == 'flash') {
-      $variables['config']['primary'] = 'flash';
-    }
-  }
-}
-
-/**
- * Implements hook_process_HOOK().
- */
-function jw_player_process_jw_player(&$variables) {
-  $variables['jw_player_inline_js_code'] = stripslashes(json_encode($variables['config']));
-  // Load the JW Player libraries (and integration into Drupal).
-  drupal_add_library('jw_player', 'jwplayer');
-}
-
-/**
  * Implements hook_libraries_info()
  */
 function jw_player_libraries_info() {
@@ -693,10 +593,230 @@ function jw_player_library() {
       }
     }
   }
+  
+  $libraries['jw_player_drupal'] = array(
+    'title' => t('jQuery support for rendering JW Player in Drupal.'),
+    'version' => VERSION,
+    'js' => array(
+      drupal_get_path('module', 'jw_player') . '/js/jw_player.drupal.js' => array(
+        'type' => 'file',
+        'weight' => -10,
+        'group' => JS_DEFAULT,
+        'scope' => 'footer',
+      ),
+    ),
+  );
+
   return $libraries;
 }
 
 /**
+ * Attach JW Player-required client files and return renderable array for a player.
+ *
+ * @param string $type
+ *   String defining type of JW Player player to be rendered.
+ * @param object $entity_type
+ *   Object defining the entity type on which the player will be rendered.
+ * @param object $entity
+ *   Object defining the entity on which the player will be rendered.
+ * @param object $field
+ *   Object defining the field from which data is being used to render the player.
+ * @param array $items
+ *   Array of field items to be rendered.
+ * @param array $settings
+ *   Array of display settings for the field item to be rendered.
+ * @param array $options
+ *   Associative array of player features.
+ *
+ * @return array
+ *   A renderable array.
+ */
+function jw_player_build_player($type, $entity_type, $entity, $field, $items, $settings, $options = array()) {
+  // Only files have an 'fid' value. URLs and text fields do not.
+  // If no 'fid', use the Drupal 8.x format instead.
+  if (isset($items[0]['fid'])) {
+    $id = $items[0]['fid'] . $settings['jwplayer_preset'];
+  }
+  else {
+    $id = '-' . md5(rand());
+  }
+  // @todo Consider using standard approach when caching patches are committed.
+  // https://www.drupal.org/node/2637552
+  // https://www.drupal.org/node/2643188
+  $html_id = drupal_html_id('jwplayer' . $id);
+
+  $build = array(
+    '#theme' => 'html_tag',
+    '#tag' => 'div',
+    '#value' => t('Loading video...'),
+    '#preset' => $settings['jwplayer_preset'],
+    '#attributes' => array(
+      'id' => $html_id,
+      'class' => 'jwplayer-video',
+    ),
+  );
+
+  switch ($type) {
+    case 'jw_player':
+      switch ($settings['player_type']) {
+        case 'player':
+        default:
+          $options['file'] = jw_player_build_file($field['type'], $items[0]);
+          if ($image = jw_player_build_preview_image($entity, 0, $settings)) {
+            $options['image'] = $image;
+          }
+          break;
+
+        case 'playlist':
+          foreach ($items as $delta => $item) {
+            $options['playlist'][] = array(
+              'file' => jw_player_build_file($field['type'], $items[$delta]),
+              'title' => isset($items[$delta]['description']) ? $items[$delta]['description'] : '',
+              'image' => jw_player_build_preview_image($entity, $delta, $settings),
+            );
+          }
+          break;
+
+        case 'sources':
+          $options['playlist'][] = array(
+            'title' => isset($items[0]['description']) ? $items[0]['description'] : '',
+            'image' => jw_player_build_preview_image($entity, 0, $settings),
+            'sources' => jw_player_build_sources($field['type'], $items),
+          );
+          break;
+      }
+      break;
+
+    // @todo Remove in future and consolidate to single JW Player formatter.
+    // In JW Player 7, all styling differences are set through CSS, and options
+    // can be generated in one array (see code above for $type == 'jw_player').
+    case 'jw_player_playlist':
+      $options['playlist_size'] = $settings['playlist_size'];
+      $options['playlist_position'] = $settings['playlist_position'];
+      $options['playlist'] = array();
+      foreach ($items as $key => $source) {
+        $options['playlist'][$key]['file'] = file_create_url($source['uri']);
+        $options['playlist'][$key]['image'] = 'none';
+        if (!empty($source['description'])) {
+          $options['playlist'][$key]['title'] = $source['description'];
+        }
+        else {
+          $pathinfo = pathinfo($source['uri']);
+          $options['playlist'][$key]['title'] = $pathinfo['filename'];
+        }
+      }
+      // This option does not exist in JW Player 7.
+      $options['listbar'] = array(
+        'position' => !empty($options['playlist_position']) ? $options['playlist_position'] : JW_PLAYER_DEFAULT_PLAYLIST_POSITION,
+        'size' => !empty($options['playlist_size']) ? $options['playlist_size'] : JW_PLAYER_DEFAULT_PLAYLIST_POSITION,
+      );
+      break;
+
+    // @todo Remove in future and consolidate to single JW Player formatter.
+    // In JW Player 7, all styling differences are set through CSS, and options
+    // can be generated in one array (see code above for $type == 'jw_player').
+    case 'jw_player_sources':
+      // Check if there is one or multiple files. If one file then we set 'file', if
+      // there are multiple files we set 'levels'. Note that levels is used for both
+      // multiple video formats as well as for adaptive bitrates.
+      $options['playlist'][0] = array();
+      $options['playlist'][0]['image'] = file_create_url($items[0]['image']['uri']);
+      foreach ($items as $key => $source) {
+        $options['playlist'][0]['sources'][$key]['file'] = file_create_url($source['uri']);
+        if (isset($source['getid3']) && $source['getid3']['width'] > 0 && $source['getid3']['height'] > 0) {
+          $options['playlist'][0]['sources'][$key]['width'] = $source['getid3']['width'];
+          $options['playlist'][0]['sources'][$key]['height'] = $source['getid3']['height'];
+        }
+        else if (isset($source['field_width']) && isset($source['field_height'])) {
+          $options['playlist'][0]['sources'][$key]['width'] = $source['field_width'][LANGUAGE_NONE][0]['value'];
+          $options['playlist'][0]['sources'][$key]['height'] = $source['field_height'][LANGUAGE_NONE][0]['value'];
+        }
+      }
+      if ($images = field_get_items($entity_type, $entity, 'field_video_thumbnail')) {
+        $build['#image'] = $images[0];
+      }
+      break;
+  }
+
+  $settings = array(
+    'html_id' => $html_id,
+    'options' => $options,
+  );
+
+  $build['#attached']['js'][] = array(
+    'data' => array('jw_player' => array($settings)),
+    'type' => 'setting',
+  );
+
+  // Load the JW Player library and supporting Drupal functions.
+  $build['#attached']['library'][] = array('jw_player', 'jwplayer');
+  $build['#attached']['library'][] = array('jw_player', 'jw_player_drupal');
+
+  // Let other modules properly attach libraries, and alter player
+  // prior to rendering.
+  drupal_alter('jw_player_build_player', $build);
+
+  return $build;
+}
+
+/*
+ * Build JW Player file.
+ * Support for file, video, text, and link field types.
+ */
+function jw_player_build_file($field_type, $item) {
+  switch ($field_type) {
+    case 'file':
+    case 'video':
+      $file = file_create_url($item['uri']);
+      break;
+    case 'text':
+      $file = file_create_url($item['value']);
+      break;
+    case 'link_field':
+      $file = file_create_url($item['url']);
+      break;
+    default:
+      $file = file_create_url($item['uri']);
+      break;
+  }
+  return preg_replace('/^https?:/i', '', $file);
+}
+
+/*
+ * Build JW Player preview image.
+ */
+function jw_player_build_preview_image($entity, $delta, $settings) {
+  if (!empty($settings['preview_image_field'])) {
+    $split = explode('|', $settings['preview_image_field']);
+    $preview_image_entity_type = explode(':', $split[0]);
+    
+    if ($images = field_get_items($preview_image_entity_type[0], $entity, $split[1])) {
+      if (isset($images[$delta]['uri'])) {
+        // Return image with applied image style.
+        if (!empty($settings['preview_image_style'])) {
+          return image_style_url($settings['preview_image_style'], $images[$delta]['uri']);
+        }
+        // If no image style is set, return the original image.
+        else {
+          return file_create_url($images[$delta]['uri']);
+        }
+      }
+    }
+  }
+  return '';
+}
+
+/*
+ * Build JW Player sources file array.
+ */
+function jw_player_build_sources($field_type, $items) {
+  $sources = array();
+  foreach ($items as $delta => $item) {
+    $sources[] = array('file' => jw_player_build_file($field_type, $items[$delta]));
+  }
+  return $sources;
+}
+
+/**
  * Retrieves all available preset plugins.
  */
 function jw_player_preset_plugins($name = NULL) {
diff --git a/theme/jw_player.tpl.php b/theme/jw_player.tpl.php
index f3b7531..644d4a0 100644
--- a/theme/jw_player.tpl.php
+++ b/theme/jw_player.tpl.php
@@ -5,16 +5,10 @@
  *
  * Variables available:
  * - $html_id: Unique id generated for each video.
- * - $jw_player_inline_js_code: JSON data with configuration settings for the video player.
  *
- * @see template_preprocess_jw_player()
+ * @see hook_jw_player_build_player_alter()
  */
 ?>
-<div id="<?php print $html_id ?>" class="jwplayer-video">
-Loading Video...
+<div id="<?php print $html_id; ?>" class="jwplayer-video">
+Loading video...
 </div>
-<?php if(isset($jw_player_inline_js_code)): ?>
-  <script type="text/javascript">
-    jwplayer('<?php print $html_id ?>').setup(<?php print $jw_player_inline_js_code?>);
-  </script>
-<?php endif ?>
-- 
2.8.1

