use slide.width and heights for carousel
[HTML5TV.git] / www / jquery.sync.js
1 (function($) {
2
3     /**
4      * Event constructor.
5      */
6
7     var Event = function(startTime, endTime) {
8         this.isActive = false;
9         this.startTime = startTime;
10         this.endTime = endTime;
11         return this;
12     };
13
14     /**
15      * Returns true if the Event should be active, i.e., the media
16      * playback time lies within the start and end times of the Event.
17      */
18
19     Event.prototype.shouldBeActive = function(time) {
20         return time >= this.startTime && time < this.endTime;
21     };
22
23     /**
24      * Activates or deactivates the Event, according to the isActive flag.
25      */
26
27     Event.prototype.setActive = function(isActive) {
28         this.isActive = isActive;
29     }
30
31     /**
32      * HtmlEvent constructor.
33      */
34
35     var HtmlEvent = function(startTime, endTime, selector, html) {
36         Event.call(this, startTime, endTime);
37         this.selector = selector;
38         this.html = html;
39         return this;
40     };
41
42     HtmlEvent.prototype = new Event();
43
44     /**
45      * Activates or deactivates the HtmlEvent.
46      */
47
48     HtmlEvent.prototype.setActive = function(isActive) {
49         Event.prototype.setActive.call(this, isActive);
50
51         if (isActive) {
52             $(this.selector).html(this.html);
53         }
54         else {
55             $(this.selector).html("");
56         }
57     }
58
59     /**
60      * StyleEvent constructor.
61      */
62
63     var StyleEvent = function(startTime, endTime, selector, cssClass) {
64         Event.call(this, startTime, endTime);
65         this.selector = selector;
66         this.cssClass = cssClass;
67         return this;
68     };
69
70     StyleEvent.prototype = new Event();
71
72     StyleEvent.prototype.setActive = function(isActive) {
73         Event.prototype.setActive.call(this, isActive);
74
75         if (isActive) {
76             $(this.selector).addClass(cssClass);
77         }
78         else {
79             $(this.selector).removeClass(cssClass);
80         }
81     }
82
83     /**
84      * CustomEvent constructor.
85      */
86
87     var CustomEvent = function(startTime, endTime, action, args) {
88         Event.call(this, startTime, endTime);
89         this.action = action;
90         this.args = args;
91         return this;
92     };
93
94     CustomEvent.prototype = new Event();
95
96     /**
97      * Activates or deactivates the CustomEvent.
98      */
99
100     CustomEvent.prototype.setActive = function(isActive) {
101         Event.prototype.setActive.call(this, isActive);
102
103         if ( typeof this.action === 'function' ) this.action(isActive, this.args);
104     }
105
106     /**
107      * EventManager constructor.
108      */
109
110     var EventManager = function() {
111         this.events = [];
112         this.currentActiveEvent = null;
113         return this;
114     };
115
116     /**
117      * Adds an Event to the EventManager.
118      */
119
120     EventManager.prototype.addEvent = function(event) {
121         this.events.push(event);
122     };
123
124     /**
125      * Sorts the Events held by the EventManager by ascending start time.
126      */
127
128     EventManager.prototype.sort = function() {
129         this.events.sort(function(a, b) {
130             return a.startTime - b.startTime;
131         });
132     };
133
134     /**
135      * Updates the EventManager, given the current media playback time, which
136      * may cause events to be activated or deactivated.
137      */
138
139     EventManager.prototype.update = function(time) {
140         var event = this.findActiveEvent(time);
141
142         if (event != this.currentActiveEvent) {
143             if (this.currentActiveEvent) {
144                 this.currentActiveEvent.setActive(false);
145             }
146
147             this.currentActiveEvent = event;
148
149             if (event) {
150                 event.setActive(true);
151             }
152         }
153     };
154
155     /**
156      * Returns the event that should be active, given the current media
157      * playback time, or null if no event should be active.
158      *
159      * Note that this assumes there can only be one active event within
160      * each EventManager object at a time.
161      */
162
163     EventManager.prototype.findActiveEvent = function(time) {
164         var event = null;
165         var i, length;
166
167         /* Check whether the current event should still be active. If not,
168            search for a new event. */
169         if (this.currentActiveEvent && this.currentActiveEvent.shouldBeActive(time)) {
170             event = this.currentActiveEvent;
171         }
172         else {
173             length = this.events.length;
174
175             for (i = 0; i < length; i++) {
176                 if (this.events[i].shouldBeActive(time)) {
177                     event = this.events[i];
178                     break;
179                 }
180             }
181         }
182
183         return event;
184     };
185
186     EventManager.prototype.getEventCount = function() {
187         return this.events.length;
188     };
189
190     EventManager.prototype.getEvent = function(index) {
191         var event = null;
192
193         if (index >= 0 && index < this.events.length) {
194             event = this.events[index];
195         }
196
197         return event;
198     };
199
200     /**
201      * TimeCounter constructor.
202      */
203
204     var TimeCounter = function(selector, timeFormatter) {
205         this.counterObject = $(selector);
206         this.currentFormattedTime = "";
207
208         if (timeFormatter) {
209             this.formatTime = timeFormatter;
210         }
211     };
212
213     TimeCounter.prototype.update = function(time) {
214         var formattedTime = this.formatTime(time);
215
216         if (formattedTime != this.currentFormattedTime) {
217             this.currentFormattedTime = formattedTime;
218             this.counterObject.html(formattedTime);
219         }
220     };
221
222     TimeCounter.prototype.formatTime = function(time) {
223         return time.toFixed(2);
224     }
225
226     /**
227      * Synchroniser constructor.
228      */
229
230     var Synchroniser = function(options) {
231         this.customEventManager = null;
232         /* Array of objects that are objects that are synchronisable, i.e., have
233            an update(time) method. */
234         this.synchronisables = [];
235
236         if (options.hasOwnProperty("htmlEvents")) {
237             this.initEvents(options.htmlEvents, function(event, selector) {
238                 return new HtmlEvent(event.startTime, event.endTime, selector, event.html);
239             });
240         }
241
242         if (options.hasOwnProperty("styleEvents")) {
243             this.initEvents(options.styleEvents, function(event, selector) {
244                 return new StyleEvent(event.startTime, event.endTime, selector, options.activeStyle);
245             });
246         }
247
248         if (options.hasOwnProperty("timeCounter")) {
249             var selector = options.timeCounter.selector;
250             var timeFormatter = options.timeCounter.timeFormatter;
251
252             if (selector) {
253                 this.initCounter(selector, timeFormatter);
254             }
255         }
256
257         if (options.hasOwnProperty("customEvents")) {
258             this.customEventManager =  this.initEventManager(options.customEvents, function(event, selector) {
259                 return new CustomEvent(event.startTime, event.endTime, event.action, event.args);
260             });
261
262             this.synchronisables.push(this.customEventManager);
263         }
264     };
265
266     Synchroniser.prototype.initEvents = function(events, factory) {
267         var selector;
268         var eventManager; /* eventManager contains a set of events for a single selector. */
269
270         for (selector in events) {
271             if (events.hasOwnProperty(selector)) {
272                 eventManager = this.initEventManager(events[selector], factory, selector);
273
274                 this.synchronisables.push(eventManager);
275             }
276         }
277     }
278
279     Synchroniser.prototype.initEventManager = function(events, factory, selector) {
280         var eventManager = new EventManager();
281         var i, length = events.length;
282         var event;
283
284         for (i = 0; i < length; i++) {
285             /* Call the factory function to create the event object. */
286             event = factory.call(this, events[i], selector);
287             eventManager.addEvent(event);
288         }
289
290         eventManager.sort();
291
292         return eventManager;
293     };
294
295     Synchroniser.prototype.initCounter = function(selector, timeFormatter) {
296         var timeCounter = new TimeCounter(selector, timeFormatter);
297         this.synchronisables.push(timeCounter);
298     }
299
300     Synchroniser.prototype.update = function(time) {
301         var i, length = this.synchronisables.length;
302
303         for (i = 0; i < length; i++) {
304             this.synchronisables[i].update(time);
305         }
306     };
307
308     Synchroniser.prototype.getCustomEventManager = function() {
309         return this.customEventManager;
310     };
311
312     $.fn.sync = function(options) {
313         options = $.extend({}, $.fn.sync.defaults, options);
314
315         var synchroniser = new Synchroniser(options);
316
317         return this.each(function() {
318             $(this).data("synchroniser", synchroniser);
319
320             $(this).bind("timeupdate", function(event) {
321                 synchroniser.update(event.target.currentTime);
322             });
323         });
324     };
325
326     /* Default configuration properties. */
327     $.fn.sync.defaults = {
328         timeCounter: {
329             selector: '',
330             timeFormatter: null
331         },
332
333         /* CSS class to be added to selectors when a StyleEvent is active. */
334         activeClass: 'syncActive'
335     };
336
337 })(jQuery);