Commit RELEASE-NOTES line for the wgCategories js variable I added some time ago.
[lhc/web/wiklou.git] / js2 / mwEmbed / libTimedText / mvTextInterface.js
1 loadGM( {
2 "mwe-select_transcript_set" : "Select subtitles",
3 "mwe-auto_scroll" : "auto scroll",
4 "mwe-close" : "close",
5 "mwe-improve_transcript" : "Improve",
6 "mwe-no_text_tracks_found" : "No text subtitles found",
7 "mwe-add-edit-subs" : "Add/edit subtitles"
8 } )
9 // text interface object (for inline display captions)
10 var mvTextInterface = function( parentEmbed ) {
11 return this.init( parentEmbed );
12 }
13 mvTextInterface.prototype = {
14 text_lookahead_time:0,
15 body_ready:false,
16 default_time_range: "source", // by default just use the source don't get a time-range
17 transcript_set:null,
18 autoscroll:true,
19 add_to_end_on_this_pass:false,
20 scrollTimerId:0,
21 editlink: '',
22 suportedMime: {
23 'srt': 'text/x-srt',
24 'cmml': 'text/cmml'
25 },
26 init:function( parentEmbed ) {
27 // init a new availableTracks obj:
28 this.availableTracks = new Array();
29 // set the parent embed object:
30 this.pe = parentEmbed;
31 // parse roe if not already done:
32 this.getTextTracks();
33 },
34 // @@todo separate out data loader & data display
35 getTextTracks:function() {
36 // js_log("load timed text from roe: "+ this.pe.roe);
37 var _this = this;
38 // if roe not yet loaded do load it:
39 if ( this.pe.roe || _this.pe.wikiTitleKey ) {
40 if ( !this.pe.media_element.addedROEData ) {
41 $j( '#mv_txt_load_' + _this.pe.id ).show(); // show the loading icon
42 if ( _this.pe.roe ) {
43 do_request( _this.pe.roe, function( data )
44 {
45 _this.pe.media_element.addROE( data );
46 _this.getParseTimedText_rowReady();
47 } );
48 } else if ( _this.pe.wikiTitleKey ) {
49 // check for a clear namespace key:
50 _this.getTextTracksWikiTitle()
51 }
52 } else {
53 js_log( 'row data ready (no roe request)' );
54 _this.getParseTimedText_rowReady();
55 }
56 } else {
57 if ( this.pe.media_element.timedTextSources() ) {
58 _this.getParseTimedText_rowReady();
59 } else {
60 js_log( 'no roe data or timed text sources' );
61 }
62 }
63 },
64 getTextTracksWikiTitle:function() {
65 var apiUrl = mw.getLocalApiUrl();
66 var _this = this;
67
68 var timedtext_ns = 102;
69 if ( typeof wgNamespaceIds != 'undefined' && wgNamespaceIds['timedtext'] ) {
70 timedtext_ns = wgNamespaceIds['timedtext'];
71 }
72 do_api_req( {
73 'url' : apiUrl,
74 'data': {
75 'list' : 'allpages',
76 'apprefix' : _this.pe.wikiTitleKey,
77 'apnamespace' : timedtext_ns,
78 'prop':'revisions'
79 }
80 }, function( subData ) {
81 if ( subData.error && subData.error.code == 'apunknown_apnamespace' ) {
82 do_api_req( {
83 'url' : apiUrl,
84 'data': {
85 'list' : 'allpages',
86 'apprefix' : 'TimedText:' + _this.pe.wikiTitleKey,
87 }
88 }, function( subData ) {
89 _this.doProcSubPages( subData, wgServer + wgScriptPath );
90 } );
91 } else {
92 _this.doProcSubPages( subData, wgServer + wgScriptPath );
93 }
94 } );
95 },
96 doProcSubPages: function( subData, hostPath ) {
97 var _this = this;
98 // look for text tracks:
99 var foundTextTracks = false;
100
101 // get all the known languages:
102 do_api_req( {
103 'url': hostPath + '/api.php',
104 'data': {
105 'meta' : 'siteinfo',
106 'siprop' : 'languages'
107 }
108 }, function( langDataRaw ) {
109 var langData = { };
110 var lagRaw = langDataRaw.query.languages;
111 for ( var j in lagRaw ) {
112 langData[ lagRaw[j].code ] = lagRaw[j]['*'];
113 }
114 for ( var i in subData.query.allpages ) {
115 var subPage = subData.query.allpages[i];
116 var langKey = subPage.title.split( '.' );
117 var extension = langKey.pop();
118 langKey = langKey.pop();
119 if ( ! _this.suportedMime[ extension ] ) {
120 js_log( 'Error: unknown extension:' + extension );
121 continue;
122 }
123
124 if ( !langData[ langKey] ) {
125 js_log( 'Error: langkey:' + langKey + ' not found' );
126 } else {
127 var textElm = document.createElement( 'text' );
128 $j( textElm ).attr( {
129 'category' : 'SUB',
130 'lang' : langKey,
131 'type' : _this.suportedMime[ extension ],
132 'title' : langData[ langKey]
133 } );
134 // We use the api since ?action raw on the real title has cache issues
135 $j( textElm ).attr( {
136 'apisrc' : hostPath + '/api.php',
137 'titleKey' : subPage.title
138 } );
139 _this.pe.media_element.tryAddSource( textElm );
140 foundTextTracks = true;
141 }
142 }
143 // after all text loaded (or we have allready checked commons
144 if ( foundTextTracks || hostPath.indexOf( 'commons.wikimedia' ) !== -1 ) {
145 // alert('calling getParseTimedText_rowReady ');
146 _this.getParseTimedText_rowReady();
147 } else {
148 _this.checkSharedRepo();
149 }
150 } ); // do_api_req({
151 },
152 checkSharedRepo:function() {
153 var _this = this;
154 js_log( 'checking for shared value of image' );
155 // check if its a shared repo
156 do_api_req( {
157 'data': {
158 'action':'query',
159 'titles': 'File:' + _this.pe.wikiTitleKey,
160 'prop' : 'imageinfo'
161 }
162 }, function( data ) {
163 if ( data.query.pages && data.query.pages['-1'] && data.query.pages['-1'].imagerepository == 'shared' ) {
164 js_log( 'image is shared checking commons for subtitles' );
165 // found shared repo assume commons:
166 do_api_req( {
167 'url': mw.commons_api_url,
168 'data': {
169 'list' : 'allpages',
170 'apprefix' : _this.pe.wikiTitleKey,
171 'apnamespace' : 102
172 }
173 }, function( data ) {
174 _this.editlink = 'http://commons.wikimedia.org/wiki/TimedText:' + _this.pe.wikiTitleKey + '.' + wgUserLanguage + '.srt';
175 _this.doProcSubPages( data, 'http://commons.wikimedia.org/w/' );
176 } );
177 } else {
178 // no shared repo do normal proc
179 _this.getParseTimedText_rowReady();
180 }
181 } );
182 },
183 getParseTimedText_rowReady: function () {
184 var _this = this;
185 var found_tracks = false;
186 // create timedTextObj
187 js_log( "mv_txt_load_:SHOW mv_txt_load_" );
188 $j( '#mv_txt_load_' + _this.pe.id ).show(); // show the loading icon
189
190 // setup edit link:
191 if ( _this.editlink == '' ) {
192 if ( this.pe.media_element.linkback ) {
193 _this.editlink = this.pe.media_element.linkback;
194 } else if ( this.pe.wikiTitleKey && wgServer && wgScript ) { // check for wikiTitleKey (for edit linkback)
195 // only local:
196 _this.editlink = wgServer + wgScript + '?title=TimedText:' + this.pe.wikiTitleKey + '.' + wgUserLanguage + '.srt&action=edit';
197 }
198 }
199 $j.each( this.pe.media_element.sources, function( inx, source ) {
200 if ( typeof source.id == 'undefined' || source.id == null ) {
201 source.id = 'text_' + inx;
202 }
203 var tObj = new timedTextObj( source );
204 // make sure its a valid timed text format (we have not loaded or parsed yet) : (
205 if ( tObj.lib != null ) {
206 _this.availableTracks.push( tObj );
207 // display requested language if we can know that:
208 if ( ( typeof wgUserLanguage != 'undefined' && source['lang'] == wgUserLanguage ) || source['default'] == "true" ) {
209 // we did set at least one track by default tag
210 found_tracks = true;
211 _this.loadAndDisplay( _this.availableTracks.length - 1 );
212 } else {
213 // don't load the track and don't display
214 }
215 }
216 } );
217
218 // no default clip found take the userLanguage key if set:
219 if ( !found_tracks ) {
220 $j.each( _this.availableTracks, function( inx, source ) {
221 _this.loadAndDisplay( inx );
222 found_tracks = true;
223 // return after loading first available
224 return false;
225 } );
226 }
227
228 // if nothing found anywhere give the not found msg:
229 if ( !found_tracks ) {
230 $j( '#metaBox_' + _this.pe.id ).html( '' +
231 '<h3>' + gM( 'mwe-no_text_tracks_found' ) + '</h3>' +
232 '<a href="' + _this.editlink + '">' + gM( 'mwe-add-edit-subs' ) + '</a>'
233 );
234 }
235 },
236 loadAndDisplay: function ( track_id ) {
237 var _this = this;
238 $j( '#mv_txt_load_' + _this.pe.id ).show();// show the loading icon
239 _this.availableTracks[ track_id ].load( _this.default_time_range, function() {
240 $j( '#mv_txt_load_' + _this.pe.id ).hide();
241 _this.addTrack( track_id );
242 } );
243 },
244 addTrack: function( track_id ) {
245 js_log( 'f:displayTrack:' + track_id );
246 var _this = this;
247 // set the display flag to true:
248 _this.availableTracks[ track_id ].display = true;
249 // setup the layout:
250 this.setup_layout();
251 js_log( "SHOULD ADD: track:" + track_id + ' count:' + _this.availableTracks[ track_id ].textNodes.length );
252
253 // a flag to avoid checking all clips if we know we are adding to the end:
254 _this.add_to_end_on_this_pass = false;
255
256 // run clip adding on a timed interval to not lock the browser on large srt file merges (should use worker threads)
257 var i = 0;
258 var track_id = track_id;
259 var addNextClip = function() {
260 var text_clip = _this.availableTracks[ track_id ].textNodes[i];
261 if ( text_clip ) {
262 _this.add_merge_text_clip( text_clip, track_id );
263 i++;
264 if ( i < _this.availableTracks[ track_id ].textNodes.length ) {
265 setTimeout( addNextClip, 1 );
266 }
267 }
268 }
269 addNextClip();
270 },
271 add_merge_text_clip: function( text_clip, track_id ) {
272 var _this = this;
273 // make sure the clip does not already exist:
274 if ( $j( '#tc_' + text_clip.id ).length == 0 ) {
275 var inserted = false;
276 var text_clip_start_time = npt2seconds( text_clip.start );
277
278 var insertHTML = '<div id="tc_' + text_clip.id + '" ' +
279 'start_sec="' + text_clip_start_time + '" ' +
280 'start="' + text_clip.start + '" end="' + text_clip.end + '" ' +
281 'class="mvtt track_' + track_id + '">' +
282 '<div class="mvttseek" style="top:0px;left:0px;right:0px;height:20px;font-size:small">' +
283 text_clip.start + ' to ' + text_clip.end +
284 '</div>' +
285 text_clip.body +
286 '</div>';
287 if ( !_this.add_to_end_on_this_pass ) {
288 $j( '#mmbody_' + this.pe.id + ' .mvtt' ).each( function() {
289 if ( !inserted ) {
290 if ( $j( this ).attr( 'start_sec' ) > text_clip_start_time ) {
291 inserted = true;
292 $j( this ).before( insertHTML );
293 }
294 } else {
295 _this.add_to_end = true;
296 }
297 } );
298 }
299 // js_log('should just add to end: '+insertHTML);
300 if ( !inserted ) {
301 $j( '#mmbody_' + this.pe.id ).append( insertHTML );
302 }
303
304 // apply the mouse over transcript seek/click functions:
305 $j( ".mvttseek" ).click( function() {
306 _this.pe.doSeek( $j( this ).parent().attr( "start_sec" ) / _this.pe.getDuration() );
307 } );
308 $j( ".mvttseek" ).hoverIntent( {
309 interval:200, // polling interval
310 timeout:200, // delay before onMouseOut
311 over:function () {
312 js_log( 'mvttseek: over' );
313 $j( this ).parent().addClass( 'tt_highlight' );
314 // do section highlight
315 _this.pe.highlightPlaySection( {
316 'start' : $j( this ).parent().attr( "start" ),
317 'end' : $j( this ).parent().attr( "end" )
318 } );
319 },
320 out:function () {
321 js_log( 'mvttseek: out' );
322 $j( this ).parent().removeClass( 'tt_highlight' );
323 // de highlight section
324 _this.pe.hideHighlight();
325 }
326 }
327 );
328 }
329 },
330 setup_layout:function() {
331 var _this = this;
332 // check if we have already loaded the menu/body:
333 if ( $j( '#tt_mmenu_' + this.pe.id ).length == 0 ) {
334 // alert( this.availableTracks.length );
335 if ( this.availableTracks.length != 0 ) {
336 $j( '#metaBox_' + this.pe.id ).html(
337 this.getMenu() +
338 this.getBody()
339 );
340 this.doMenuBindings();
341 }
342 }
343 },
344 show:function() {
345 // setup layout if not already done:
346 this.setup_layout();
347 // display the interface if not already displayed:
348 $j( '#metaBox_' + this.pe.id ).fadeIn( "fast" );
349 // start the autoscroll timer:
350 if ( this.autoscroll )
351 this.setAutoScroll();
352 },
353 close:function() {
354 // the meta box:
355 $j( '#metaBox_' + this.pe.id ).fadeOut( 'fast' );
356 // the icon link:
357 $j( '#metaButton_' + this.pe.id ).fadeIn( 'fast' );
358 },
359 getBody:function() {
360 return '<div id="mmbody_' + this.pe.id + '" ' +
361 'style="position:absolute;top:30px;left:0px;' +
362 'right:0px;bottom:0px;' +
363 'height:' + ( this.pe.height - 30 ) +
364 'px;overflow:auto;"><span style="display:none;" id="mv_txt_load_' + this.pe.id + '">' +
365 mv_get_loading_img() + '</span>' +
366 '</div>';
367 },
368 getTsSelect:function() {
369 var _this = this;
370 js_log( 'getTsSelect' );
371 var selHTML = '<div id="mvtsel_' + this.pe.id + '" style="position:absolute;background:#FFF;top:30px;left:0px;right:0px;bottom:0px;overflow:auto;">';
372 selHTML += '<b>' + gM( 'mwe-select_transcript_set' ) + '</b><ul>';
373 // debugger;
374 for ( var i in _this.availableTracks ) { // for in loop ok on object
375 var checked = ( _this.availableTracks[i].display ) ? 'checked' : '';
376 selHTML += '<li><input name="language" value="' + i + '" class="mvTsSelect" type="radio" ' + checked + '>' +
377 _this.availableTracks[i].getTitle() + '</li>';
378 }
379 selHTML += '</ul>' +
380 '</div>';
381 $j( '#metaBox_' + _this.pe.id ).append( selHTML );
382 $j( '.mvTsSelect' ).click( function() {
383 _this.applyTsSelect();
384 } );
385 },
386 applyTsSelect:function() {
387 var _this = this;
388 // update availableTracks
389 $j( '#mvtsel_' + this.pe.id + ' .mvTsSelect' ).each( function() {
390 var track_id = $j( this ).val();
391 if ( this.checked ) {
392 // if not yet loaded now would be a good time
393 if ( ! _this.availableTracks[ track_id ].loaded ) {
394 _this.loadAndDisplay( track_id );
395 } else {
396 _this.availableTracks[track_id].display = true;
397 // display the named class:
398 $j( '#mmbody_' + _this.pe.id + ' .track_' + track_id ).show();
399 }
400 } else {
401 if ( _this.availableTracks[track_id].display ) {
402 _this.availableTracks[track_id].display = false;
403 // hide unchecked
404 $j( '#mmbody_' + _this.pe.id + ' .track_' + track_id ).hide();
405 }
406 }
407 } );
408 $j( '#mvtsel_' + _this.pe.id ).fadeOut( "fast" ).remove();
409 },
410 monitor:function() {
411 _this = this;
412 // grab the time from the video object
413 var cur_time = this.pe.currentTime ;
414 if ( cur_time != 0 ) {
415 var search_for_range = true;
416 // check if the current transcript is already where we want:
417 if ( $j( '#mmbody_' + this.pe.id + ' .tt_scroll_highlight' ).length != 0 ) {
418 var curhl = $j( '#mmbody_' + this.pe.id + ' .tt_scroll_highlight' ).get( 0 );
419 if ( npt2seconds( $j( curhl ).attr( 'start' ) ) < cur_time &&
420 npt2seconds( $j( curhl ).attr( 'end' ) ) > cur_time ) {
421 /*js_log('in range of current hl: ' +
422 npt2seconds($j(curhl).attr('start')) + ' to ' + npt2seconds($j(curhl).attr('end')));
423 */
424 search_for_range = false;
425 } else {
426 search_for_range = true;
427 // remove the highlight from all:
428 $j( '#mmbody_' + this.pe.id + ' .tt_scroll_highlight' ).removeClass( 'tt_scroll_highlight' );
429 }
430 };
431 /*js_log('search_for_range:'+search_for_range + ' for: '+ cur_time);*/
432 if ( search_for_range ) {
433 // search for current time: add tt_scroll_highlight to clip
434 // optimize:
435 // should do binnary search not iterative
436 // avoid jquery function calls do native loops
437 $j( '#mmbody_' + this.pe.id + ' .mvtt' ).each( function() {
438 if ( npt2seconds( $j( this ).attr( 'start' ) ) < cur_time &&
439 npt2seconds( $j( this ).attr( 'end' ) ) > cur_time ) {
440 _this.prevTimeScroll = cur_time;
441 $j( '#mmbody_' + _this.pe.id ).animate( {
442 scrollTop: $j( this ).get( 0 ).offsetTop
443 }, 'slow' );
444 $j( this ).addClass( 'tt_scroll_highlight' );
445 // js_log('should add class to: ' + $j(this).attr('id'));
446 // done with loop
447 return false;
448 }
449 } );
450 }
451 }
452 },
453 setAutoScroll:function( timer ) {
454 var _this = this;
455 this.autoscroll = ( typeof timer == 'undefined' ) ? this.autoscroll:timer;
456 if ( this.autoscroll ) {
457 // start the timer if its not already running
458 if ( !this.scrollTimerId ) {
459 var mvElm = $j('#' + _this.id ).get(0);
460 if( mvElm )
461 this.scrollTimerId = setInterval( mvElm.textInterface.monitor(), 500 );
462 }
463 // jump to the current position:
464 var cur_time = parseInt ( this.pe.currentTime );
465 js_log( 'cur time: ' + cur_time );
466
467 _this = this;
468 var scroll_to_id = '';
469 $j( '#mmbody_' + this.pe.id + ' .mvtt' ).each( function() {
470 if ( cur_time > npt2seconds( $j( this ).attr( 'start' ) ) ) {
471 _this.prevTimeScroll = cur_time;
472 if ( $j( this ).attr( 'id' ) )
473 scroll_to_id = $j( this ).attr( 'id' );
474 }
475 } );
476 if ( scroll_to_id != '' )
477 $j( '#mmbody_' + _this.pe.id ).animate( { scrollTop: $j( '#' + scroll_to_id ).position().top } , 'slow' );
478 } else {
479 // stop the timer
480 clearInterval( this.scrollTimerId );
481 this.scrollTimerId = 0;
482 }
483 },
484 getMenu:function() {
485 var out = '';
486 var _this = this;
487 // add in loading icon:
488 var as_checked = ( this.autoscroll ) ? 'checked':'';
489 out += '<div id="tt_mmenu_' + this.pe.id + '" class="ui-widget-header" style="font-size:.6em;position:absolute;top:0;height:30px;left:0px;right:0px;">';
490 out += $j.btnHtml( gM( 'mwe-select_transcript_set' ), 'tt-select', 'shuffle' );
491
492 if ( _this.editlink != '' )
493 out += ' ' + $j.btnHtml( gM( 'mwe-improve_transcript' ), 'tt-improve' );
494
495 out += '<input class="tt-scroll" type="checkbox" ' + as_checked + '>' + gM( 'mwe-auto_scroll' );
496
497 out += ' ' + $j.btnHtml( gM( 'mwe-close' ), 'tt-close', 'circle-close' );
498
499 out += '</div>';
500 return out;
501 },
502 doMenuBindings:function() {
503 var _this = this;
504 var mt = '#tt_mmenu_' + _this.pe.id;
505 $j( mt + ' .tt-close' ).unbind().btnBind().click( function() {
506 $j( '#' + _this.pe.id ).get( 0 ).closeTextInterface();
507 return false;
508 } );
509 $j( mt + ' .tt-select' ).unbind().btnBind().click( function() {
510 $j( '#' + _this.pe.id ).get( 0 ).textInterface.getTsSelect();
511 return false;
512 } );
513 $j( mt + ' .tt-scroll' ).click( function() {
514 _this.setAutoScroll( this.checked );
515 } );
516 $j( mt + ' .tt-improve' ).unbind().btnBind().click( function() {
517 document.location.href = _this.editlink;
518 } );
519 }
520 }
521
522 /* text format objects
523 * @@todo allow loading from external lib set
524 */
525 var timedTextObj = function( source ) {
526 // @@todo in the future we could support timed text in oggs if they can be accessed via javascript
527 // we should be able to do a HEAD request to see if we can read transcripts from the file.
528 switch( source.mime_type ) {
529 case 'text/cmml':
530 this.lib = 'CMML';
531 break;
532 case 'text/srt':
533 case 'text/x-srt':
534 this.lib = 'SRT';
535 break;
536 default:
537 js_log( source.mime_type + ' is not suported timed text fromat' );
538 return ;
539 break;
540 }
541 // extend with the per-mime type lib:
542 eval( 'var tObj = timedText' + this.lib + ';' );
543 for ( var i in tObj ) {
544 this[ i ] = tObj[i];
545 }
546 return this.init( source );
547 }
548
549 // base timedText object
550 timedTextObj.prototype = {
551 loaded: false,
552 lib:null,
553 display: false,
554 textNodes:new Array(),
555 init: function( source ) {
556 // copy source properties
557 this.source = source;
558 this.id = source.id;
559 },
560 getTitle:function() {
561 return this.source.title;
562 },
563 getSRC:function() {
564 return this.source.src;
565 }
566 }
567
568 // Specific Timed Text formats:
569
570 timedTextCMML = {
571 load: function( range, callback ) {
572 var _this = this;
573 js_log( 'textCMML: loading track: ' + this.src );
574
575 // :: Load transcript range ::
576 var pcurl = mw.parseUri( _this.getSRC() );
577 // check for urls without time keys:
578 if ( typeof pcurl.queryKey['t'] == 'undefined' ) {
579 // in which case just get the full time req:
580 do_request( this.getSRC(), function( data ) {
581 _this.doParse( data );
582 _this.loaded = true;
583 callback();
584 } );
585 return ;
586 }
587 // temporal urls:
588 var req_time = pcurl.queryKey['t'].split( '/' );
589 req_time[0] = npt2seconds( req_time[0] );
590 req_time[1] = npt2seconds( req_time[1] );
591 if ( req_time[1] - req_time[0] > _this.request_length ) {
592 // longer than 5 min will only issue a (request 5 min)
593 req_time[1] = req_time[0] + _this.request_length;
594 }
595 // set up request url:
596 url = pcurl.protocol + '://' + pcurl.authority + pcurl.path + '?';
597 $j.each( pcurl.queryKey, function( key, val ) {
598 if ( key != 't' ) {
599 url += key + '=' + val + '&';
600 } else {
601 url += 't=' + seconds2npt( req_time[0] ) + '/' + seconds2npt( req_time[1] ) + '&';
602 }
603 } );
604 do_request( url, function( data ) {
605 js_log( "load track clip count:" + data.getElementsByTagName( 'clip' ).length );
606 _this.doParse( data );
607 _this.loaded = true;
608 callback();
609 } );
610 },
611 doParse: function( data ) {
612 var _this = this;
613 $j.each( data.getElementsByTagName( 'clip' ), function( inx, clip ) {
614 // js_log(' on clip ' + clip.id);
615 var text_clip = {
616 start: $j( clip ).attr( 'start' ).replace( 'npt:', '' ),
617 end: $j( clip ).attr( 'end' ).replace( 'npt:', '' ),
618 type_id: _this.id,
619 id: $j( clip ).attr( 'id' )
620 }
621 $j.each( clip.getElementsByTagName( 'body' ), function( binx, bn ) {
622 if ( bn.textContent ) {
623 text_clip.body = bn.textContent;
624 } else if ( bn.text ) {
625 text_clip.body = bn.text;
626 }
627 } );
628 _this.textNodes.push( text_clip );
629 } );
630 }
631 }
632 timedTextSRT = {
633 load: function( range, callback ) {
634 var _this = this;
635 js_log( 'textSRT: loading : ' + _this.getSRC() );
636 if ( _this.getSRC() ) {
637 do_request( _this.getSRC() , function( data ) {
638 _this.doParse( data );
639 _this.loaded = true;
640 callback();
641 } );
642 } else if ( _this.source.apisrc ) {
643 do_api_req( {
644 'url' : _this.source.apisrc,
645 'data': {
646 'titles': _this.source.titleKey,
647 'prop':'revisions',
648 'rvprop':'content'
649 }
650 }, function( data ) {
651 if ( data && data.query && data.query.pages ) {
652 for ( var i in data.query.pages ) {
653 var page = data.query.pages[i];
654 if ( page.revisions ) {
655 for ( var j in page.revisions ) {
656 if ( page.revisions[j]['*'] ) {
657 _this.doParse( page.revisions[j]['*'] );
658 _this.loaded = true;
659 callback();
660 }
661 }
662 }
663 }
664 }
665 } );
666 }
667 },
668 doParse:function( data ) {
669 // split up the transcript chunks:
670 // strip any \r's
671 var tc = data.split( /[\r]?\n[\r]?\n/ );
672 // pushing can take time
673 for ( var s = 0; s < tc.length ; s++ ) {
674 var st = tc[s].split( '\n' );
675 if ( st.length >= 2 ) {
676 var n = st[0];
677 var i = st[1].split( ' --> ' )[0].replace( /^\s+|\s+$/g, "" );
678 var o = st[1].split( ' --> ' )[1].replace( /^\s+|\s+$/g, "" );
679 var t = st[2];
680 if ( st.length > 2 ) {
681 for ( j = 3; j < st.length; j++ )
682 t += '\n' + st[j];
683 }
684 var text_clip = {
685 "start": i,
686 "end": o,
687 "type_id": this.id,
688 "id": this.id + '_' + n,
689 "body": t
690 }
691 this.textNodes.push( text_clip );
692 }
693 }
694 }
695 };