Index: README.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ooyala/README.txt,v
retrieving revision 1.1
diff -u -r1.1 README.txt
--- README.txt	13 Dec 2010 04:00:27 -0000	1.1
+++ README.txt	13 Dec 2010 04:37:48 -0000
@@ -21,4 +21,12 @@
 ImageCache (for display of Ooyala video thumbnails)
 Views (display lists of videos as a player)
 
+About Ooyala chapter markers (bundled module)
+----------------------------------------------
+The Ooyala chapter markers module implements a new CCK field type that may
+collaborate with an Ooyala video field provided by on the same content type. It
+allows users to specify chapter titles and times in long video files. When
+displayed, users can click the chapter title and the Ooyala player will jump to
+the chapter time.
+
 $Id: README.txt,v 1.1 2010/12/13 04:00:27 quicksketch Exp $
Index: ooyala.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ooyala/ooyala.info,v
retrieving revision 1.2
diff -u -r1.2 ooyala.info
--- ooyala.info	12 Dec 2010 23:47:10 -0000	1.2
+++ ooyala.info	13 Dec 2010 04:37:48 -0000
@@ -1,5 +1,5 @@
 name = Ooyala
 description = Allows uploading and display of videos from the <a href="http://www.ooyala.com">Ooyala</a> video service.
 core = 6.x
-package = CCK
+package = Ooyala
 dependencies[] = content
Index: ooyala_markers/ooyala_markers.css
===================================================================
RCS file: ooyala_markers/ooyala_markers.css
diff -N ooyala_markers/ooyala_markers.css
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ooyala_markers/ooyala_markers.css	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,10 @@
+/* $Id$ */
+
+div.ooyala-markers-field-column {
+  float: left;
+  width: 48%;
+}
+
+div.ooyala-markers-field-column .form-text {
+  width: 95%;
+}
Index: ooyala_markers/ooyala_markers.info
===================================================================
RCS file: ooyala_markers/ooyala_markers.info
diff -N ooyala_markers/ooyala_markers.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ooyala_markers/ooyala_markers.info	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,5 @@
+name = Ooyala chapter markers
+description = Allows users to skip to designated points (chapters) in a long video.
+core = 6.x
+package = Ooyala
+dependencies[] = ooyala
Index: ooyala_markers/ooyala_markers.js
===================================================================
RCS file: ooyala_markers/ooyala_markers.js
diff -N ooyala_markers/ooyala_markers.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ooyala_markers/ooyala_markers.js	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,65 @@
+// $Id$
+
+/**
+ * Ooyala marker behavior.
+ */
+Drupal.behaviors.ooyalaMarkersJumpToAttach = function(context) {
+  // Find all the chapter marker links.
+  $('a.ooyala-markers-marker').click(function() {
+    var href = $(this).attr('href');
+    var fragment = href.slice(href.indexOf('#') + 1).split(':');
+
+    player = document.getElementById(fragment[0] + '_ooyala_player');
+
+    if (!player) {
+      player = document.getElementById('ooyala_player');
+    }
+
+    if (parseInt(fragment[1])) {
+      Drupal.ooyalaMarkersJumpTo(player, fragment[1]);
+    }
+
+    // Set the browser's URL so people can link directly to this chapter.
+    window.location = href;
+    return false;
+  });
+};
+
+/**
+ * Tell the video player to jump to the specified time within the current video.
+ *
+ * @param player
+ *   Player DOM object.
+ * @param time
+ *   Time in milliseconds to jump to.
+ */
+Drupal.ooyalaMarkersJumpTo = function(player, time) {
+  // Jump the player to the appropriate time.
+  // The time variable is converted to seconds here. Ooyala API claims that
+  // it has millisecond accuracy so we denote times in milliseconds, however
+  // the setPlayHeadTime method takes the number of seconds as an argument.
+  player.setPlayheadTime(time/1000);
+  player.playMovie();
+};
+
+// Initialize Drupal.ooyala if it hasn't been yet since it is possible for this
+// module's javascript to be loadded before ooyala_player.js
+Drupal.ooyala = Drupal.ooyala || {'listeners': {}};
+
+/**
+ * Register event listener for ooyala API callbacks.
+ */
+Drupal.ooyala.listeners.ooyalaMarkers = function(player, eventName, p) {
+  switch(eventName) {
+    case 'apiReady':
+      // If there is a #time in the current href jump to that point in the
+      // video.
+      var href = window.location.href;
+      var fragment = href.slice(href.indexOf('#') + 1).split(':');
+      if (fragment[0] && parseInt(fragment[1])) {
+        Drupal.ooyalaMarkersJumpTo(player, fragment[1]);
+      }
+    break;
+  }
+  return;
+};
Index: ooyala_markers/ooyala_markers.module
===================================================================
RCS file: ooyala_markers/ooyala_markers.module
diff -N ooyala_markers/ooyala_markers.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ooyala_markers/ooyala_markers.module	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,248 @@
+<?php
+// $Id:$
+
+/**
+ * @file
+ * Provides a CCK "Ooyala Chapter Marker" field, which allows users to jump to
+ * predefined points in a long video.
+ */
+
+/**
+ * Implements hook_theme().
+ */
+function ooyala_markers_theme() {
+  return array(
+    'ooyala_marker' => array(
+      'arguments' => array('element' => NULL),
+    ),
+    'ooyala_markers_formatter_default' => array(
+      'arguments' => array('element' => NULL),
+    ),
+  );
+}
+
+/**
+ * Impelements hook_field_info().
+ */
+function ooyala_markers_field_info() {
+  return array(
+    'ooyala_marker' => array(
+      'label' => t('Ooyala Chapter Marker'),
+      'description' => t('Store chapter markers for Ooyala video fields.'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_field_settings().
+ */
+function ooyala_markers_field_settings($op, $field) {
+  switch ($op) {
+    case 'database columns':
+      $columns = array(
+        'title' => array(
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => FALSE,
+          'sortable' => TRUE,
+        ),
+        'time' => array(
+          'type' => 'varchar',
+          'length' => 8,
+          'not null' => FALSE,
+          'sortable' => TRUE,
+        ),
+      );
+      return $columns;
+  }
+}
+
+/**
+ * Implements hook_elements().
+ */
+function ooyala_markers_elements() {
+  $elements = array();
+  $elements['ooyala_marker'] = array(
+    '#input' => TRUE,
+    '#columns' => array('title', 'time'),
+    '#process' => array('ooyala_markers_process'),
+  );
+  return $elements;
+}
+
+/**
+ * Implements hook_widget_info().
+ */
+function ooyala_markers_widget_info() {
+  return array(
+    'ooyala_marker' => array(
+      'label' => t('Chapter Marker'),
+      'field types' => array('ooyala_marker'),
+      'multiple values' => CONTENT_HANDLE_CORE,
+    ),
+  );
+}
+
+/**
+ * Implements hook_widget_settings().
+ */
+function ooyala_markers_widget_settings($op, $widget) {
+  switch ($op) {
+    case 'form':
+      $form = array();
+      $type = str_replace('-', '_', arg(3));
+      $fields = content_fields();
+      $options = array();
+      foreach ($fields as $field) {
+        if ($field['type'] == 'ooyala' && $field['type_name'] == $type) {
+          $options[$field['field_name']] = t('@label (@field)', array('@label' => $field['widget']['label'], '@field' => $field['field_name']));
+        }
+      }
+      $form['ooyala_video_field'] = array(
+        '#type' => 'select',
+        '#title' => t('Ooyala video field'),
+        '#description' => t('The Ooyala video field the chapter markers should apply to.'),
+        '#options' => $options,
+        '#default_value' => -1,
+      );
+      return $form;
+    case 'save':
+      return array('ooyala_video_field');
+  }
+}
+
+/**
+ * Implements hook_widget().
+ */
+function ooyala_markers_widget(&$form, &$form_state, $field, $items, $delta = 0) {
+  $element = array(
+    '#type' => $field['widget']['type'],
+    '#default_value' => isset($items[$delta]) ? $items[$delta] : '',
+  );
+  return $element;
+}
+
+/**
+ * Impelemnt hook_content_is_empty().
+ */
+function ooyala_markers_content_is_empty($item, $field) {
+  if (empty($item['title']) && empty($item['time'])) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * Form API #process function for the ooyala_marker type element.
+ */
+function ooyala_markers_process($element, $edit, &$form_state, $form) {
+  $field_name = $element['#field_name'];
+  $field = $form['#field_info'][$field_name];
+  $element['#field'] = $field;
+  $element['#theme'] = 'ooyala_marker';
+  $element['#element_validate'] = array('ooyala_markers_validate');
+
+  $element['title'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Title'),
+    '#size' => 40,
+    '#maxlength' => 255,
+    '#default_value' => isset($element['#value']['title']) ? $element['#value']['title'] : '',
+  );
+  $element['time'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Time'),
+    '#size' => 8,
+    '#maxlength' => 8,
+    '#description' => t('Time should be entered in the format hh:mm:ss'),
+    '#default_value' => isset($element['#value']['time']) ? $element['#value']['time'] : '',
+    '#element_validate' => array('ooyala_markers_validate_time'),
+  );
+
+  // Set #element_validate in a way that it will not wipe out other
+  // validation functions already set by other modules.
+  if (empty($element['#element_validate'])) {
+    $element['#element_validate'] = array();
+  }
+
+  return $element;
+}
+
+/**
+ * Form API validation function for ooyala_marker type elements.
+ */
+function ooyala_markers_validate($element) {
+  // If the user enetered a title or a time make sure they entered both.
+  if (!empty($element['title']['#value']) || !empty($element['time']['#value'])) {
+    if (empty($element['title']['#value'])) {
+      form_error($element['title'], t('Both title and time fields must be filled out.'));
+    }
+    if (empty($element['time']['#value'])) {
+      form_error($element['time'], t('Both title and time fields must be filled out.'));
+    }
+  }
+}
+
+/**
+ * Form API validation function for time fields of ooyala_marker type elements.
+ */
+function ooyala_markers_validate_time($element) {
+  // Verify that input is the format hh:mm:ss.
+  $pattern = '/^(\d{1,2}):(\d{2}):(\d{2})?$/';
+  if (!empty($element['#value']) && !preg_match($pattern, $element['#value'])) {
+    form_error($element, t('Must be in the format hh:mm:ss'));
+  }
+}
+
+/**
+ * Implements hook_field_formatter_info().
+ */
+function ooyala_markers_field_formatter_info() {
+  return array(
+    'default' => array(
+      'label' => t('Title/Time as link'),
+      'field types' => array('ooyala_marker'),
+      'multiple values' => CONTENT_HANDLE_CORE,
+    ),
+  );
+}
+
+/**
+ * Form API theme for an individual text elements in an ooyala_marker field.
+ */
+function theme_ooyala_marker($element) {
+  drupal_add_css(drupal_get_path('module', 'ooyala_markers') .'/ooyala_markers.css');
+
+  $output = '<div class="ooyala-markers-field clear-block">';
+  $output .= check_plain($element['#title']);
+  $output .= '<div class="ooyala-markers-field-title ooyala-markers-field-column">' . drupal_render($element['title']) . '</div>';
+  $output .= '<div class="ooyala-markers-field-time ooyala-markers-field-column">' . drupal_render($element['time']) . '</div>';
+  $output .= '</div>';
+  return $output;
+}
+
+/**
+ * Theme function to output JavaScript marker links into the content.
+ */
+function theme_ooyala_markers_formatter_default($element) {
+  static $ooyala_markers_js_added;
+
+  if (empty($element['#item']['title']) || empty($element['#item']['time'])) {
+    return;
+  }
+
+  $field = content_fields($element['#field_name'], $element['#type_name']);
+  $video_field = $field['widget']['ooyala_video_field'];
+
+  if (!isset($ooyala_markers_js_added)) {
+    drupal_add_js(drupal_get_path('module', 'ooyala_markers') . '/ooyala_markers.js');
+    $ooyala_markers_js_added = TRUE;
+  }
+
+  // Generate a #<video_player_id>:<time> fragment.
+  $time = explode(':', $element['#item']['time']);
+  $time_in_milliseconds = ($time[0] * 3600000) + ($time[1] * 60000) + ($time[2] * 1000);
+  $fragment = $field['widget']['ooyala_video_field'] . ':' . $time_in_milliseconds;
+
+  return l($element['#item']['title'], 'node/' . $element['#node']->nid, array('absolute' => TRUE, 'fragment' => $fragment, 'attributes' => array('class' => 'ooyala-markers-marker')));
+}
