wai-website-theme 1.3.1 → 1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/_includes/different.html +2 -1
  3. data/_includes/external.html +2 -1
  4. data/_includes/header.html +2 -1
  5. data/_includes/menuitem.html +6 -2
  6. data/_includes/peoplelist.html +21 -0
  7. data/_includes/prevnext-navigation.html +56 -0
  8. data/_includes/{prevnext.html → prevnext-order.html} +9 -0
  9. data/_includes/translation-note-msg.html +5 -3
  10. data/_includes/video-player.html +2 -2
  11. data/_layouts/default.html +8 -1
  12. data/_layouts/news.html +7 -1
  13. data/_layouts/policy.html +7 -1
  14. data/_layouts/sidenav.html +8 -1
  15. data/_layouts/sidenavsidebar.html +8 -1
  16. data/assets/ableplayer/Gruntfile.js +2 -1
  17. data/assets/ableplayer/README.md +158 -85
  18. data/assets/ableplayer/build/ableplayer.dist.js +15445 -13823
  19. data/assets/ableplayer/build/ableplayer.js +15445 -13823
  20. data/assets/ableplayer/build/ableplayer.min.css +1 -2
  21. data/assets/ableplayer/build/ableplayer.min.js +3 -10
  22. data/assets/ableplayer/package-lock.json +944 -346
  23. data/assets/ableplayer/package.json +8 -8
  24. data/assets/ableplayer/scripts/ableplayer-base.js +515 -524
  25. data/assets/ableplayer/scripts/browser.js +158 -158
  26. data/assets/ableplayer/scripts/buildplayer.js +1750 -1682
  27. data/assets/ableplayer/scripts/caption.js +424 -401
  28. data/assets/ableplayer/scripts/chapters.js +259 -259
  29. data/assets/ableplayer/scripts/control.js +1831 -1594
  30. data/assets/ableplayer/scripts/description.js +333 -256
  31. data/assets/ableplayer/scripts/dialog.js +145 -145
  32. data/assets/ableplayer/scripts/dragdrop.js +746 -749
  33. data/assets/ableplayer/scripts/event.js +875 -696
  34. data/assets/ableplayer/scripts/initialize.js +819 -912
  35. data/assets/ableplayer/scripts/langs.js +979 -743
  36. data/assets/ableplayer/scripts/metadata.js +124 -124
  37. data/assets/ableplayer/scripts/misc.js +170 -137
  38. data/assets/ableplayer/scripts/preference.js +904 -904
  39. data/assets/ableplayer/scripts/search.js +172 -172
  40. data/assets/ableplayer/scripts/sign.js +82 -78
  41. data/assets/ableplayer/scripts/slider.js +449 -448
  42. data/assets/ableplayer/scripts/track.js +409 -309
  43. data/assets/ableplayer/scripts/transcript.js +684 -595
  44. data/assets/ableplayer/scripts/translation.js +63 -67
  45. data/assets/ableplayer/scripts/ttml2webvtt.js +85 -85
  46. data/assets/ableplayer/scripts/vimeo.js +448 -0
  47. data/assets/ableplayer/scripts/volume.js +395 -380
  48. data/assets/ableplayer/scripts/vts.js +1077 -1077
  49. data/assets/ableplayer/scripts/webvtt.js +766 -763
  50. data/assets/ableplayer/scripts/youtube.js +695 -478
  51. data/assets/ableplayer/styles/ableplayer.css +54 -46
  52. data/assets/ableplayer/translations/nl.js +54 -54
  53. data/assets/ableplayer/translations/pt-br.js +311 -0
  54. data/assets/ableplayer/translations/tr.js +311 -0
  55. data/assets/ableplayer/translations/zh-tw.js +1 -1
  56. data/assets/css/style.css +1 -1
  57. data/assets/css/style.css.map +1 -1
  58. data/assets/images/icons.svg +5 -5
  59. data/assets/scripts/main.js +7 -0
  60. data/assets/search/tipuesearch.js +3 -3
  61. metadata +8 -3
@@ -1,765 +1,768 @@
1
1
  (function ($) {
2
- // See section 4.1 of dev.w3.org/html5/webvtt for format details.
3
- AblePlayer.prototype.parseWebVTT = function(srcFile,text) {
4
-
5
- // Normalize line ends to \n.
6
- text = text.replace(/(\r\n|\n|\r)/g,'\n');
7
-
8
- var parserState = {
9
- src: srcFile,
10
- text: text,
11
- error: null,
12
- metadata: {},
13
- cues: [],
14
- line: 1,
15
- column: 1
16
- };
17
-
18
- try {
19
- act(parserState, parseFileBody);
20
- }
21
- catch (err) {
22
- var errString = 'Invalid WebVTT file: ' + parserState.src + '\n';
23
- errString += 'Line: ' + parserState.line + ', ';
24
- errString += 'Column: ' + parserState.column + '\n';
25
- errString += err;
26
- if (console.warn) {
27
- console.warn(errString);
28
- }
29
- else if (console.log) {
30
- console.log(errString);
31
- }
32
- }
33
- return parserState;
34
- }
35
-
36
- function actList(state, list) {
37
- var results = [];
38
- for (var ii = 0; ii < list.length; ii++) {
39
- results.push(act(state, list[ii]));
40
- }
41
- return results;
42
- }
43
-
44
- // Applies the action and checks for errors.
45
- function act(state, action) {
46
- var val = action(state);
47
- if (state.error !== null) {
48
- throw state.error;
49
- }
50
- return val;
51
- }
52
-
53
- function updatePosition(state, cutText) {
54
- for (var ii = 0; ii < cutText.length; ii++) {
55
- if (cutText[ii] === '\n') {
56
- state.column = 1;
57
- state.line += 1;
58
- }
59
- else {
60
- state.column += 1;
61
- }
62
- }
63
- }
64
-
65
- function cut(state, length) {
66
- var returnText = state.text.substring(0, length);
67
- updatePosition(state, returnText);
68
- state.text = state.text.substring(length);
69
- return returnText;
70
- }
71
-
72
- function cutLine(state, length) {
73
- var nextEOL = state.text.indexOf('\n');
74
- var returnText;
75
- if (nextEOL === -1) {
76
- returnText = state.text;
77
- updatePosition(state, returnText);
78
- state.text = '';
79
- }
80
- else {
81
- returnText = state.text.substring(0, nextEOL);
82
- updatePosition(state, returnText + '\n');
83
- state.text = state.text.substring(nextEOL + 1);
84
- }
85
- return returnText;
86
- }
87
-
88
- function peekLine(state) {
89
- var nextEOL = state.text.indexOf('\n');
90
- if (nextEOL === -1) {
91
- return state.text;
92
- }
93
- else {
94
- return state.text.substring(0, nextEOL);
95
- }
96
- }
97
-
98
- function parseFileBody(state) {
99
- actList(state, [
100
- eatOptionalBOM,
101
- eatSignature]);
102
- var c = state.text[0];
103
- if (c === ' ' || c === '\t' || c === '\n') {
104
- actList(state, [
105
- eatUntilEOLInclusive,
106
- parseMetadataHeaders,
107
- eatAtLeast1EmptyLines,
108
- parseCuesAndComments]);
109
- }
110
- else {
111
- state.error = "WEBVTT signature not followed by whitespace.";
112
- }
113
- }
114
-
115
- // Parses all metadata headers until a cue is discovered.
116
- function parseMetadataHeaders(state) {
117
- while (true) {
118
- var nextLine = peekLine(state);
119
- if (nextLine.indexOf('-->') !== -1) {
120
- return;
121
- }
122
- else if (nextLine.length === 0) {
123
- return;
124
- }
125
- else {
126
- var keyValue = act(state, getMetadataKeyValue);
127
- state.metadata[keyValue[0]] = keyValue[1];
128
- act(state, eatUntilEOLInclusive);
129
- }
130
- }
131
- }
132
-
133
- function nextSpaceOrNewline(s) {
134
- var possible = [];
135
- var spaceIndex = s.indexOf(' ');
136
- if (spaceIndex >= 0) {
137
- possible.push(spaceIndex);
138
- }
139
- var tabIndex = s.indexOf('\t');
140
- if (tabIndex >= 0) {
141
- possible.push(tabIndex);
142
- }
143
- var lineIndex = s.indexOf('\n');
144
- if (lineIndex >= 0) {
145
- possible.push(lineIndex);
146
- }
147
-
148
- return Math.min.apply(null, possible);
149
- }
150
-
151
- function getMetadataKeyValue(state) {
152
- var next = state.text.indexOf('\n');
153
- var pair = cut(state, next);
154
- var colon = pair.indexOf(':');
155
- if (colon === -1) {
156
- state.error = 'Missing colon.';
157
- return;
158
- }
159
- else {
160
- var pairName = pair.substring(0, colon);
161
- var pairValue = pair.substring(colon + 1);
162
- return [pairName, pairValue];
163
- }
164
- }
165
-
166
- function getSettingsKeyValue(state) {
167
- var next = nextSpaceOrNewline(state.text);
168
- var pair = cut(state, next);
169
- var colon = pair.indexOf(':');
170
- if (colon === -1) {
171
- state.error = 'Missing colon.';
172
- return;
173
- }
174
- else {
175
- var pairName = pair.substring(0, colon);
176
- var pairValue = pair.substring(colon + 1);
177
- return [pairName, pairValue];
178
- }
179
- }
180
-
181
- function parseCuesAndComments(state) {
182
- while (true) {
183
- var nextLine = peekLine(state);
184
- // If NOTE is not on a line all its own, it must be followed by a space or tab.
185
- if (nextLine.indexOf('NOTE') === 0 && ((nextLine.length === 4) || (nextLine[4] === ' ') || (nextLine[4] === '\t'))) {
186
- actList(state, [eatComment, eatEmptyLines]);
187
- }
188
- else if ($.trim(nextLine).length === 0 && state.text.length > 0) {
189
- act(state, eatEmptyLines);
190
- }
191
- else if ($.trim(nextLine).length > 0) {
192
- act(state, parseCue);
193
- }
194
- else {
195
- // Everythings parsed!
196
- return;
197
- }
198
- }
199
- }
200
-
201
- function parseCue(state) {
202
-
203
- var nextLine = peekLine(state);
204
- var cueId;
205
- var errString;
206
-
207
- if(nextLine.indexOf('-->') === -1) {
208
- cueId = cutLine(state);
209
- nextLine = peekLine(state);
210
- if(nextLine.indexOf('-->') === -1) {
211
- errString = 'Invalid WebVTT file: ' + state.src + '\n';
212
- errString += 'Line: ' + state.line + ', ';
213
- errString += 'Column: ' + state.column + '\n';
214
- errString += 'Expected cue timing for cueId \''+cueId+'\' but found: ' + nextLine + '\n';
215
- if (console.warn) {
216
- console.warn(errString);
217
- }
218
- else if (console.log) {
219
- console.log(errString);
220
- }
221
- return; // Return leaving line for parseCuesAndComments to handle
222
- }
223
- }
224
-
225
- var cueTimings = actList(state, [getTiming,
226
- eatAtLeast1SpacesOrTabs,
227
- eatArrow,
228
- eatAtLeast1SpacesOrTabs,
229
- getTiming]);
230
-
231
- var startTime = cueTimings[0];
232
- var endTime = cueTimings[4];
233
- if (startTime >= endTime) {
234
- state.error = 'Start time is not sooner than end time.';
235
- return;
236
- }
237
-
238
- act(state, eatSpacesOrTabs);
239
- var cueSettings = act(state, getCueSettings);
240
- // Cut the newline.
241
- cut(state, 1);
242
- var components = act(state, getCuePayload);
243
-
244
- if (typeof cueId === 'undefined') {
245
- cueId = state.cues.length + 1;
246
- }
247
- state.cues.push({
248
- id: cueId,
249
- start: startTime,
250
- end: endTime,
251
- settings: cueSettings,
252
- components: components
253
- });
254
- }
255
-
256
- function getCueSettings(state) {
257
- var cueSettings = {};
258
- while (state.text.length > 0 && state.text[0] !== '\n') {
259
- var keyValue = act(state, getSettingsKeyValue);
260
- cueSettings[keyValue[0]] = keyValue[1];
261
- act(state, eatSpacesOrTabs);
262
- }
263
- return cueSettings;
264
- }
265
-
266
- function getCuePayload(state) {
267
- // Parser based on instructions in draft.
268
- var result = {type: 'internal', tagName: '', value: '', classes: [], annotation: '', parent: null, children: [], language: ''};
269
- var current = result;
270
- var languageStack = [];
271
- while (state.text.length > 0) {
272
- var nextLine = peekLine(state);
273
- if (nextLine.indexOf('-->') !== -1 || /^\s*$/.test(nextLine)) {
274
- break; // Handle empty cues
275
- }
276
- // Have to separately detect double-lines ending cue due to our non-standard parsing.
277
- // TODO: Redo outer algorithm to conform to W3 spec?
278
- if (state.text.length >= 2 && state.text[0] === '\n' && state.text[1] === '\n') {
279
- cut(state, 2);
280
- break;
281
- }
282
-
283
- var token = getCueToken(state);
284
- // We'll use the tokens themselves as objects where possible.
285
- if (token.type === 'string') {
286
- current.children.push(token);
287
- }
288
- else if (token.type === 'startTag') {
289
- token.type = token.tagName;
290
- // Define token.parent; added by Terrill to fix bug end 'endTag' loop
291
- token.parent = current;
292
- if ($.inArray(token.tagName, ['i', 'b', 'u', 'ruby']) !== -1) {
293
- if (languageStack.length > 0) {
294
- current.language = languageStack[languageStack.length - 1];
295
- }
296
- current.children.push(token);
297
- current = token;
298
- }
299
- else if (token.tagName === 'rt' && current.tagName === 'ruby') {
300
- if (languageStack.length > 0) {
301
- current.language = languageStack[languageStack.length - 1];
302
- }
303
- current.children.push(token);
304
- current = token;
305
- }
306
- else if (token.tagName === 'c') {
307
- token.value = token.annotation;
308
- if (languageStack.length > 0) {
309
- current.language = languageStack[languageStack.length - 1];
310
- }
311
- current.children.push(token);
312
- current = token;
313
- }
314
- else if (token.tagName === 'v') {
315
- token.value = token.annotation;
316
- if (languageStack.length > 0) {
317
- current.language = languageStack[languageStack.length - 1];
318
- }
319
- current.children.push(token);
320
- current = token;
321
- }
322
- else if (token.tagName === 'lang') {
323
- languageStack.push(token.annotation);
324
- if (languageStack.length > 0) {
325
- current.language = languageStack[languageStack.length - 1];
326
- }
327
- current.children.push(token);
328
- current = token;
329
- }
330
- }
331
- else if (token.type === 'endTag') {
332
- if (token.tagName === current.type && $.inArray(token.tagName, ['c', 'i', 'b', 'u', 'ruby', 'rt', 'v']) !== -1) {
333
- // NOTE from Terrill: This was resulting in an error because current.parent was undefined
334
- // Fixed (I think) by assigning current token to token.parent in 'startTag' loop
335
- current = current.parent;
336
- }
337
- else if (token.tagName === 'lang' && current.type === 'lang') {
338
- current = current.parent;
339
- languageStack.pop();
340
- }
341
- else if (token.tagName === 'ruby' && current.type === 'rt') {
342
- current = current.parent.parent;
343
- }
344
- }
345
- else if (token.type === 'timestampTag') {
346
- var tempState = {
347
- text: token.value,
348
- error: null,
349
- metadata: {},
350
- cues: [],
351
- line: 1,
352
- column: 1
353
- };
354
- try {
355
- var timing = act(tempState, getTiming);
356
- if (tempState.text.length === 0) {
357
- token.value = timing;
358
- current.push(token);
359
- }
360
- }
361
- catch (err) {
362
- }
363
- }
364
- }
365
- return result;
366
- }
367
-
368
- // Gets a single cue token; uses the method in the w3 specification.
369
- function getCueToken(state) {
370
- var tokenState = 'data';
371
- var result = [];
372
- var buffer = '';
373
- var token = {type: '', tagName: '', value: '', classes: [], annotation: '', children: []}
374
-
375
- while (true) {
376
- var c;
377
- // Double newlines indicate end of token.
378
- if (state.text.length >= 2 && state.text[0] === '\n' && state.text[1] === '\n') {
379
- c = '\u0004';
380
- }
381
- else if (state.text.length > 0) {
382
- c = state.text[0];
383
- }
384
- else {
385
- // End of file.
386
- c = '\u0004';
387
- }
388
- if (tokenState === 'data') {
389
- if (c === '&') {
390
- buffer = '&';
391
- tokenState = 'escape';
392
- }
393
- else if (c === '<') {
394
- if (result.length === 0) {
395
- tokenState = 'tag';
396
- }
397
- else {
398
- token.type = 'string';
399
- token.value = result.join('');
400
- return token;
401
- }
402
- }
403
- else if (c === '\u0004') {
404
- return {type: 'string', value: result.join('')};
405
- }
406
- else {
407
- result.push(c);
408
- }
409
- }
410
- else if (tokenState === 'escape') {
411
- if (c === '&') {
412
- result.push(buffer);
413
- buffer = '&';
414
- }
415
- else if (c.match(/[0-9a-z]/)) {
416
- buffer += c;
417
- }
418
- else if (c === ';') {
419
- if (buffer === '&amp') {
420
- result.push('&');
421
- }
422
- else if (buffer === '&lt') {
423
- result.push('<');
424
- }
425
- else if (buffer === '&gt') {
426
- result.push('>');
427
- }
428
- else if (buffer === '&lrm') {
429
- result.push('\u200e');
430
- }
431
- else if (buffer === '&rlm') {
432
- result.push('\u200f');
433
- }
434
- else if (buffer === '&nbsp') {
435
- result.push('\u00a0');
436
- }
437
- else {
438
- result.push(buffer);
439
- result.push(';');
440
- }
441
- tokenState = 'data';
442
- }
443
- else if (c === '<' || c === '\u0004') {
444
- result.push(buffer);
445
- token.type = 'string';
446
- token.value = result.join('');
447
- return token;
448
- }
449
- else if (c === '\t' || c === '\n' || c === '\u000c' || c === ' ') { // Handle unescaped & chars as strings
450
- result.push(buffer);
451
- token.type = 'string';
452
- token.value = result.join('');
453
- return token;
454
- }
455
- else {
456
- result.push(buffer);
457
- tokenState = 'data';
458
- }
459
- }
460
- else if (tokenState === 'tag') {
461
- if (c === '\t' || c === '\n' || c === '\u000c' || c === ' ') {
462
- tokenState = 'startTagAnnotation';
463
- }
464
- else if (c === '.') {
465
- tokenState = 'startTagClass';
466
- }
467
- else if (c === '/') {
468
- tokenState = 'endTag';
469
- }
470
- else if (c.match('[0-9]')) {
471
- tokenState = 'timestampTag';
472
- result.push(c);
473
- }
474
- else if (c === '>') {
475
- cut(state, 1);
476
- break;
477
- }
478
- else if (c === '\u0004') {
479
- token.tagName = '';
480
- token.type = 'startTag';
481
- return token;
482
- }
483
- else {
484
- result.push(c);
485
- tokenState = 'startTag';
486
- }
487
- }
488
- else if (tokenState === 'startTag') {
489
- if (c === '\t' || c === '\u000c' || c === ' ') {
490
- tokenState = 'startTagAnnotation';
491
- }
492
- else if (c === '\n') {
493
- buffer = c;
494
- tokenState = 'startTagAnnotation';
495
- }
496
- else if (c === '.') {
497
- tokenState = 'startTagClass';
498
- }
499
- else if (c === '>') {
500
- cut(state, 1);
501
- token.tagName = result.join('');
502
- token.type = 'startTag';
503
- return token;
504
- }
505
- else if (c === '\u0004') {
506
- token.tagName = result.join('');
507
- token.type = 'startTag';
508
- return token;
509
- }
510
- else {
511
- result.push(c);
512
- }
513
- }
514
- else if (tokenState === 'startTagClass') {
515
- if (c === '\t' || c === '\u000c' || c === ' ') {
516
- token.classes.push(buffer);
517
- buffer = '';
518
- tokenState = 'startTagAnnotation';
519
- }
520
- else if (c === '\n') {
521
- token.classes.push(buffer);
522
- buffer = c;
523
- tokenState = 'startTagAnnotation';
524
- }
525
- else if (c === '.') {
526
- token.classes.push(buffer);
527
- buffer = "";
528
- }
529
- else if (c === '>') {
530
- cut(state, 1);
531
- token.classes.push(buffer);
532
- token.type = 'startTag';
533
- token.tagName = result.join('');
534
- return token;
535
- }
536
- else if (c === '\u0004') {
537
- token.classes.push(buffer);
538
- token.type = 'startTag';
539
- token.tagName = result.join('');
540
- return token;
541
- }
542
- else {
543
- buffer += 'c';
544
- }
545
- }
546
- else if (tokenState === 'startTagAnnotation') {
547
- if (c === '>') {
548
- cut(state, 1);
549
- buffer = $.trim(buffer).replace(/ +/, ' ');
550
- token.type = 'startTag';
551
- token.tagName = result.join('');
552
- token.annotation = buffer;
553
- return token;
554
- }
555
- else if (c === '\u0004') {
556
- buffer = $.trim(buffer).replace(/ +/, ' ');
557
- token.type = 'startTag';
558
- token.tagName = result.join('');
559
- token.annotation = buffer;
560
- return token;
561
- }
562
- else {
563
- buffer += c;
564
- }
565
- }
566
- else if (tokenState === 'endTag') {
567
- if (c === '>') {
568
- cut(state, 1);
569
- token.type = 'endTag';
570
- token.tagName = result.join('');
571
- return token;
572
- }
573
- else if (c === '\u0004') {
574
- token.type = 'endTag';
575
- token.tagName = result.join('');
576
- return token;
577
- }
578
- else {
579
- result.push(c);
580
- }
581
- }
582
- else if (tokenState === 'timestampTag') {
583
- if (c === '>') {
584
- cut(state, 1);
585
- token.type = 'timestampTag';
586
- token.name = result.join('');
587
- return token;
588
- }
589
- else if (c === '\u0004') {
590
- token.type = 'timestampTag';
591
- token.name = result.join('');
592
- return token;
593
- }
594
- else {
595
- result.push(c);
596
- }
597
- }
598
- else {
599
- throw 'Unknown tokenState ' + tokenState;
600
- }
601
-
602
- cut(state, 1);
603
- }
604
- }
605
-
606
- function eatComment(state) {
607
- // Cut the NOTE line.
608
- var noteLine = cutLine(state);
609
- if (noteLine.indexOf('-->') !== -1) {
610
- state.error = 'Invalid syntax: --> in NOTE line.';
611
- return;
612
- }
613
- while (true) {
614
- var nextLine = peekLine(state);
615
- if ($.trim(nextLine).length === 0) {
616
- // End of comment.
617
- return;
618
- }
619
- else if (nextLine.indexOf('-->') !== -1) {
620
- state.error = 'Invalid syntax: --> in comment.';
621
- return;
622
- }
623
- else {
624
- cutLine(state);
625
- }
626
- }
627
- }
628
-
629
- // Initial byte order mark.
630
- function eatOptionalBOM(state) {
631
- if (state.text[0] === '\ufeff') {
632
- cut(state, 1);
633
- }
634
-
635
- }
636
-
637
- // "WEBVTT" string.
638
- function eatSignature(state) {
639
- if (state.text.substring(0,6) === 'WEBVTT') {
640
- cut(state, 6);
641
- }
642
- else {
643
- state.error = 'Invalid signature.';
644
- }
645
- }
646
-
647
- function eatArrow(state) {
648
- if (state.text.length < 3 || state.text.substring(0,3) !== '-->') {
649
- state.error = 'Missing -->';
650
- }
651
- else {
652
- cut(state, 3);
653
- }
654
- }
655
-
656
- function eatSingleSpaceOrTab(state) {
657
- if (state.text[0] === '\t' || state.text[0] === ' ') {
658
- cut(state, 1);
659
- }
660
- else {
661
- state.error = 'Missing space.';
662
- }
663
- }
664
-
665
- function eatSpacesOrTabs(state) {
666
- while (state.text[0] === '\t' || state.text[0] === ' ') {
667
- cut(state, 1);
668
- }
669
- }
670
-
671
- function eatAtLeast1SpacesOrTabs(state) {
672
- var numEaten = 0;
673
- while (state.text[0] === '\t' || state.text[0] === ' ') {
674
- cut(state, 1);
675
- numEaten += 1;
676
- }
677
- if (numEaten === 0) {
678
- state.error = 'Missing space.';
679
- }
680
- }
681
-
682
- function eatUntilEOLInclusive(state) {
683
- var nextEOL = state.text.indexOf('\n');
684
- if (nextEOL === -1) {
685
- state.error = 'Missing EOL.';
686
- }
687
- else {
688
- cut(state, nextEOL + 1);
689
- }
690
- }
691
-
692
- function eatEmptyLines(state) {
693
- while (state.text.length > 0) {
694
- var nextLine = peekLine(state);
695
- if ($.trim(nextLine).length === 0) {
696
- cutLine(state);
697
- }
698
- else {
699
- break;
700
- }
701
- }
702
- }
703
-
704
- // Eats empty lines, but throws an error if there's not at least one.
705
- function eatAtLeast1EmptyLines(state) {
706
- var linesEaten = 0;
707
- while (state.text.length > 0) {
708
- var nextLine = peekLine(state);
709
- if ($.trim(nextLine).length === 0) {
710
- cutLine(state);
711
- linesEaten += 1;
712
- }
713
- else {
714
- break;
715
- }
716
- }
717
- if (linesEaten === 0) {
718
- state.error = 'Missing empty line.';
719
- }
720
- }
721
-
722
- function getTiming(state) {
723
- var nextSpace = nextSpaceOrNewline(state.text);
724
- if (nextSpace === -1) {
725
- state.error('Missing timing.');
726
- return;
727
- }
728
- var timestamp = cut(state, nextSpace);
729
-
730
- var results = /((\d\d):)?((\d\d):)(\d\d).(\d\d\d)|(\d+).(\d\d\d)/.exec(timestamp);
731
-
732
- if (!results) {
733
- state.error = 'Unable to parse timestamp';
734
- return;
735
- }
736
- var time = 0;
737
- var hours = results[2];
738
- var minutes = results[4];
739
-
740
- if (minutes) {
741
- if (parseInt(minutes, 10) > 59) {
742
- state.error = 'Invalid minute range';
743
- return;
744
- }
745
- if (hours) {
746
- time += 3600 * parseInt(hours, 10);
747
- }
748
- time += 60 * parseInt(minutes, 10);
749
- var seconds = results[5];
750
- if (parseInt(seconds, 10) > 59) {
751
- state.error = 'Invalid second range';
752
- return;
753
- }
754
-
755
- time += parseInt(seconds, 10);
756
- time += parseInt(results[6], 10) / 1000;
757
- }
758
- else {
759
- time += parseInt(results[7], 10);
760
- time += parseInt(results[8], 10) / 1000;
761
- }
762
-
763
- return time;
764
- }
2
+ // See section 4.1 of dev.w3.org/html5/webvtt for format details.
3
+ AblePlayer.prototype.parseWebVTT = function(srcFile,text) {
4
+
5
+ // var deferred = new $.Deferred();
6
+ // var promise = deferred.promise();
7
+
8
+ // Normalize line ends to \n.
9
+ text = text.replace(/(\r\n|\n|\r)/g,'\n');
10
+
11
+ var parserState = {
12
+ src: srcFile,
13
+ text: text,
14
+ error: null,
15
+ metadata: {},
16
+ cues: [],
17
+ line: 1,
18
+ column: 1
19
+ };
20
+
21
+ try {
22
+ act(parserState, parseFileBody);
23
+ }
24
+ catch (err) {
25
+ var errString = 'Invalid WebVTT file: ' + parserState.src + '\n';
26
+ errString += 'Line: ' + parserState.line + ', ';
27
+ errString += 'Column: ' + parserState.column + '\n';
28
+ errString += err;
29
+ if (console.warn) {
30
+ console.warn(errString);
31
+ }
32
+ else if (console.log) {
33
+ console.log(errString);
34
+ }
35
+ }
36
+ return parserState;
37
+ }
38
+
39
+ function actList(state, list) {
40
+ var results = [];
41
+ for (var ii = 0; ii < list.length; ii++) {
42
+ results.push(act(state, list[ii]));
43
+ }
44
+ return results;
45
+ }
46
+
47
+ // Applies the action and checks for errors.
48
+ function act(state, action) {
49
+ var val = action(state);
50
+ if (state.error !== null) {
51
+ throw state.error;
52
+ }
53
+ return val;
54
+ }
55
+
56
+ function updatePosition(state, cutText) {
57
+ for (var ii = 0; ii < cutText.length; ii++) {
58
+ if (cutText[ii] === '\n') {
59
+ state.column = 1;
60
+ state.line += 1;
61
+ }
62
+ else {
63
+ state.column += 1;
64
+ }
65
+ }
66
+ }
67
+
68
+ function cut(state, length) {
69
+ var returnText = state.text.substring(0, length);
70
+ updatePosition(state, returnText);
71
+ state.text = state.text.substring(length);
72
+ return returnText;
73
+ }
74
+
75
+ function cutLine(state, length) {
76
+ var nextEOL = state.text.indexOf('\n');
77
+ var returnText;
78
+ if (nextEOL === -1) {
79
+ returnText = state.text;
80
+ updatePosition(state, returnText);
81
+ state.text = '';
82
+ }
83
+ else {
84
+ returnText = state.text.substring(0, nextEOL);
85
+ updatePosition(state, returnText + '\n');
86
+ state.text = state.text.substring(nextEOL + 1);
87
+ }
88
+ return returnText;
89
+ }
90
+
91
+ function peekLine(state) {
92
+ var nextEOL = state.text.indexOf('\n');
93
+ if (nextEOL === -1) {
94
+ return state.text;
95
+ }
96
+ else {
97
+ return state.text.substring(0, nextEOL);
98
+ }
99
+ }
100
+
101
+ function parseFileBody(state) {
102
+ actList(state, [
103
+ eatOptionalBOM,
104
+ eatSignature]);
105
+ var c = state.text[0];
106
+ if (c === ' ' || c === '\t' || c === '\n') {
107
+ actList(state, [
108
+ eatUntilEOLInclusive,
109
+ parseMetadataHeaders,
110
+ eatAtLeast1EmptyLines,
111
+ parseCuesAndComments]);
112
+ }
113
+ else {
114
+ state.error = "WEBVTT signature not followed by whitespace.";
115
+ }
116
+ }
117
+
118
+ // Parses all metadata headers until a cue is discovered.
119
+ function parseMetadataHeaders(state) {
120
+ while (true) {
121
+ var nextLine = peekLine(state);
122
+ if (nextLine.indexOf('-->') !== -1) {
123
+ return;
124
+ }
125
+ else if (nextLine.length === 0) {
126
+ return;
127
+ }
128
+ else {
129
+ var keyValue = act(state, getMetadataKeyValue);
130
+ state.metadata[keyValue[0]] = keyValue[1];
131
+ act(state, eatUntilEOLInclusive);
132
+ }
133
+ }
134
+ }
135
+
136
+ function nextSpaceOrNewline(s) {
137
+ var possible = [];
138
+ var spaceIndex = s.indexOf(' ');
139
+ if (spaceIndex >= 0) {
140
+ possible.push(spaceIndex);
141
+ }
142
+ var tabIndex = s.indexOf('\t');
143
+ if (tabIndex >= 0) {
144
+ possible.push(tabIndex);
145
+ }
146
+ var lineIndex = s.indexOf('\n');
147
+ if (lineIndex >= 0) {
148
+ possible.push(lineIndex);
149
+ }
150
+
151
+ return Math.min.apply(null, possible);
152
+ }
153
+
154
+ function getMetadataKeyValue(state) {
155
+ var next = state.text.indexOf('\n');
156
+ var pair = cut(state, next);
157
+ var colon = pair.indexOf(':');
158
+ if (colon === -1) {
159
+ state.error = 'Missing colon.';
160
+ return;
161
+ }
162
+ else {
163
+ var pairName = pair.substring(0, colon);
164
+ var pairValue = pair.substring(colon + 1);
165
+ return [pairName, pairValue];
166
+ }
167
+ }
168
+
169
+ function getSettingsKeyValue(state) {
170
+ var next = nextSpaceOrNewline(state.text);
171
+ var pair = cut(state, next);
172
+ var colon = pair.indexOf(':');
173
+ if (colon === -1) {
174
+ state.error = 'Missing colon.';
175
+ return;
176
+ }
177
+ else {
178
+ var pairName = pair.substring(0, colon);
179
+ var pairValue = pair.substring(colon + 1);
180
+ return [pairName, pairValue];
181
+ }
182
+ }
183
+
184
+ function parseCuesAndComments(state) {
185
+ while (true) {
186
+ var nextLine = peekLine(state);
187
+ // If NOTE is not on a line all its own, it must be followed by a space or tab.
188
+ if (nextLine.indexOf('NOTE') === 0 && ((nextLine.length === 4) || (nextLine[4] === ' ') || (nextLine[4] === '\t'))) {
189
+ actList(state, [eatComment, eatEmptyLines]);
190
+ }
191
+ else if ($.trim(nextLine).length === 0 && state.text.length > 0) {
192
+ act(state, eatEmptyLines);
193
+ }
194
+ else if ($.trim(nextLine).length > 0) {
195
+ act(state, parseCue);
196
+ }
197
+ else {
198
+ // Everythings parsed!
199
+ return;
200
+ }
201
+ }
202
+ }
203
+
204
+ function parseCue(state) {
205
+
206
+ var nextLine = peekLine(state);
207
+ var cueId;
208
+ var errString;
209
+
210
+ if(nextLine.indexOf('-->') === -1) {
211
+ cueId = cutLine(state);
212
+ nextLine = peekLine(state);
213
+ if(nextLine.indexOf('-->') === -1) {
214
+ errString = 'Invalid WebVTT file: ' + state.src + '\n';
215
+ errString += 'Line: ' + state.line + ', ';
216
+ errString += 'Column: ' + state.column + '\n';
217
+ errString += 'Expected cue timing for cueId \''+cueId+'\' but found: ' + nextLine + '\n';
218
+ if (console.warn) {
219
+ console.warn(errString);
220
+ }
221
+ else if (console.log) {
222
+ console.log(errString);
223
+ }
224
+ return; // Return leaving line for parseCuesAndComments to handle
225
+ }
226
+ }
227
+
228
+ var cueTimings = actList(state, [getTiming,
229
+ eatAtLeast1SpacesOrTabs,
230
+ eatArrow,
231
+ eatAtLeast1SpacesOrTabs,
232
+ getTiming]);
233
+
234
+ var startTime = cueTimings[0];
235
+ var endTime = cueTimings[4];
236
+ if (startTime >= endTime) {
237
+ state.error = 'Start time is not sooner than end time.';
238
+ return;
239
+ }
240
+
241
+ act(state, eatSpacesOrTabs);
242
+ var cueSettings = act(state, getCueSettings);
243
+ // Cut the newline.
244
+ cut(state, 1);
245
+ var components = act(state, getCuePayload);
246
+
247
+ if (typeof cueId === 'undefined') {
248
+ cueId = state.cues.length + 1;
249
+ }
250
+ state.cues.push({
251
+ id: cueId,
252
+ start: startTime,
253
+ end: endTime,
254
+ settings: cueSettings,
255
+ components: components
256
+ });
257
+ }
258
+
259
+ function getCueSettings(state) {
260
+ var cueSettings = {};
261
+ while (state.text.length > 0 && state.text[0] !== '\n') {
262
+ var keyValue = act(state, getSettingsKeyValue);
263
+ cueSettings[keyValue[0]] = keyValue[1];
264
+ act(state, eatSpacesOrTabs);
265
+ }
266
+ return cueSettings;
267
+ }
268
+
269
+ function getCuePayload(state) {
270
+ // Parser based on instructions in draft.
271
+ var result = {type: 'internal', tagName: '', value: '', classes: [], annotation: '', parent: null, children: [], language: ''};
272
+ var current = result;
273
+ var languageStack = [];
274
+ while (state.text.length > 0) {
275
+ var nextLine = peekLine(state);
276
+ if (nextLine.indexOf('-->') !== -1 || /^\s*$/.test(nextLine)) {
277
+ break; // Handle empty cues
278
+ }
279
+ // Have to separately detect double-lines ending cue due to our non-standard parsing.
280
+ // TODO: Redo outer algorithm to conform to W3 spec?
281
+ if (state.text.length >= 2 && state.text[0] === '\n' && state.text[1] === '\n') {
282
+ cut(state, 2);
283
+ break;
284
+ }
285
+
286
+ var token = getCueToken(state);
287
+ // We'll use the tokens themselves as objects where possible.
288
+ if (token.type === 'string') {
289
+ current.children.push(token);
290
+ }
291
+ else if (token.type === 'startTag') {
292
+ token.type = token.tagName;
293
+ // Define token.parent; added by Terrill to fix bug end 'endTag' loop
294
+ token.parent = current;
295
+ if ($.inArray(token.tagName, ['i', 'b', 'u', 'ruby']) !== -1) {
296
+ if (languageStack.length > 0) {
297
+ current.language = languageStack[languageStack.length - 1];
298
+ }
299
+ current.children.push(token);
300
+ current = token;
301
+ }
302
+ else if (token.tagName === 'rt' && current.tagName === 'ruby') {
303
+ if (languageStack.length > 0) {
304
+ current.language = languageStack[languageStack.length - 1];
305
+ }
306
+ current.children.push(token);
307
+ current = token;
308
+ }
309
+ else if (token.tagName === 'c') {
310
+ token.value = token.annotation;
311
+ if (languageStack.length > 0) {
312
+ current.language = languageStack[languageStack.length - 1];
313
+ }
314
+ current.children.push(token);
315
+ current = token;
316
+ }
317
+ else if (token.tagName === 'v') {
318
+ token.value = token.annotation;
319
+ if (languageStack.length > 0) {
320
+ current.language = languageStack[languageStack.length - 1];
321
+ }
322
+ current.children.push(token);
323
+ current = token;
324
+ }
325
+ else if (token.tagName === 'lang') {
326
+ languageStack.push(token.annotation);
327
+ if (languageStack.length > 0) {
328
+ current.language = languageStack[languageStack.length - 1];
329
+ }
330
+ current.children.push(token);
331
+ current = token;
332
+ }
333
+ }
334
+ else if (token.type === 'endTag') {
335
+ if (token.tagName === current.type && $.inArray(token.tagName, ['c', 'i', 'b', 'u', 'ruby', 'rt', 'v']) !== -1) {
336
+ // NOTE from Terrill: This was resulting in an error because current.parent was undefined
337
+ // Fixed (I think) by assigning current token to token.parent in 'startTag' loop
338
+ current = current.parent;
339
+ }
340
+ else if (token.tagName === 'lang' && current.type === 'lang') {
341
+ current = current.parent;
342
+ languageStack.pop();
343
+ }
344
+ else if (token.tagName === 'ruby' && current.type === 'rt') {
345
+ current = current.parent.parent;
346
+ }
347
+ }
348
+ else if (token.type === 'timestampTag') {
349
+ var tempState = {
350
+ text: token.value,
351
+ error: null,
352
+ metadata: {},
353
+ cues: [],
354
+ line: 1,
355
+ column: 1
356
+ };
357
+ try {
358
+ var timing = act(tempState, getTiming);
359
+ if (tempState.text.length === 0) {
360
+ token.value = timing;
361
+ current.push(token);
362
+ }
363
+ }
364
+ catch (err) {
365
+ }
366
+ }
367
+ }
368
+ return result;
369
+ }
370
+
371
+ // Gets a single cue token; uses the method in the w3 specification.
372
+ function getCueToken(state) {
373
+ var tokenState = 'data';
374
+ var result = [];
375
+ var buffer = '';
376
+ var token = {type: '', tagName: '', value: '', classes: [], annotation: '', children: []}
377
+
378
+ while (true) {
379
+ var c;
380
+ // Double newlines indicate end of token.
381
+ if (state.text.length >= 2 && state.text[0] === '\n' && state.text[1] === '\n') {
382
+ c = '\u0004';
383
+ }
384
+ else if (state.text.length > 0) {
385
+ c = state.text[0];
386
+ }
387
+ else {
388
+ // End of file.
389
+ c = '\u0004';
390
+ }
391
+ if (tokenState === 'data') {
392
+ if (c === '&') {
393
+ buffer = '&';
394
+ tokenState = 'escape';
395
+ }
396
+ else if (c === '<') {
397
+ if (result.length === 0) {
398
+ tokenState = 'tag';
399
+ }
400
+ else {
401
+ token.type = 'string';
402
+ token.value = result.join('');
403
+ return token;
404
+ }
405
+ }
406
+ else if (c === '\u0004') {
407
+ return {type: 'string', value: result.join('')};
408
+ }
409
+ else {
410
+ result.push(c);
411
+ }
412
+ }
413
+ else if (tokenState === 'escape') {
414
+ if (c === '&') {
415
+ result.push(buffer);
416
+ buffer = '&';
417
+ }
418
+ else if (c.match(/[0-9a-z]/)) {
419
+ buffer += c;
420
+ }
421
+ else if (c === ';') {
422
+ if (buffer === '&amp') {
423
+ result.push('&');
424
+ }
425
+ else if (buffer === '&lt') {
426
+ result.push('<');
427
+ }
428
+ else if (buffer === '&gt') {
429
+ result.push('>');
430
+ }
431
+ else if (buffer === '&lrm') {
432
+ result.push('\u200e');
433
+ }
434
+ else if (buffer === '&rlm') {
435
+ result.push('\u200f');
436
+ }
437
+ else if (buffer === '&nbsp') {
438
+ result.push('\u00a0');
439
+ }
440
+ else {
441
+ result.push(buffer);
442
+ result.push(';');
443
+ }
444
+ tokenState = 'data';
445
+ }
446
+ else if (c === '<' || c === '\u0004') {
447
+ result.push(buffer);
448
+ token.type = 'string';
449
+ token.value = result.join('');
450
+ return token;
451
+ }
452
+ else if (c === '\t' || c === '\n' || c === '\u000c' || c === ' ') { // Handle unescaped & chars as strings
453
+ result.push(buffer);
454
+ token.type = 'string';
455
+ token.value = result.join('');
456
+ return token;
457
+ }
458
+ else {
459
+ result.push(buffer);
460
+ tokenState = 'data';
461
+ }
462
+ }
463
+ else if (tokenState === 'tag') {
464
+ if (c === '\t' || c === '\n' || c === '\u000c' || c === ' ') {
465
+ tokenState = 'startTagAnnotation';
466
+ }
467
+ else if (c === '.') {
468
+ tokenState = 'startTagClass';
469
+ }
470
+ else if (c === '/') {
471
+ tokenState = 'endTag';
472
+ }
473
+ else if (c.match('[0-9]')) {
474
+ tokenState = 'timestampTag';
475
+ result.push(c);
476
+ }
477
+ else if (c === '>') {
478
+ cut(state, 1);
479
+ break;
480
+ }
481
+ else if (c === '\u0004') {
482
+ token.tagName = '';
483
+ token.type = 'startTag';
484
+ return token;
485
+ }
486
+ else {
487
+ result.push(c);
488
+ tokenState = 'startTag';
489
+ }
490
+ }
491
+ else if (tokenState === 'startTag') {
492
+ if (c === '\t' || c === '\u000c' || c === ' ') {
493
+ tokenState = 'startTagAnnotation';
494
+ }
495
+ else if (c === '\n') {
496
+ buffer = c;
497
+ tokenState = 'startTagAnnotation';
498
+ }
499
+ else if (c === '.') {
500
+ tokenState = 'startTagClass';
501
+ }
502
+ else if (c === '>') {
503
+ cut(state, 1);
504
+ token.tagName = result.join('');
505
+ token.type = 'startTag';
506
+ return token;
507
+ }
508
+ else if (c === '\u0004') {
509
+ token.tagName = result.join('');
510
+ token.type = 'startTag';
511
+ return token;
512
+ }
513
+ else {
514
+ result.push(c);
515
+ }
516
+ }
517
+ else if (tokenState === 'startTagClass') {
518
+ if (c === '\t' || c === '\u000c' || c === ' ') {
519
+ token.classes.push(buffer);
520
+ buffer = '';
521
+ tokenState = 'startTagAnnotation';
522
+ }
523
+ else if (c === '\n') {
524
+ token.classes.push(buffer);
525
+ buffer = c;
526
+ tokenState = 'startTagAnnotation';
527
+ }
528
+ else if (c === '.') {
529
+ token.classes.push(buffer);
530
+ buffer = "";
531
+ }
532
+ else if (c === '>') {
533
+ cut(state, 1);
534
+ token.classes.push(buffer);
535
+ token.type = 'startTag';
536
+ token.tagName = result.join('');
537
+ return token;
538
+ }
539
+ else if (c === '\u0004') {
540
+ token.classes.push(buffer);
541
+ token.type = 'startTag';
542
+ token.tagName = result.join('');
543
+ return token;
544
+ }
545
+ else {
546
+ buffer += 'c';
547
+ }
548
+ }
549
+ else if (tokenState === 'startTagAnnotation') {
550
+ if (c === '>') {
551
+ cut(state, 1);
552
+ buffer = $.trim(buffer).replace(/ +/, ' ');
553
+ token.type = 'startTag';
554
+ token.tagName = result.join('');
555
+ token.annotation = buffer;
556
+ return token;
557
+ }
558
+ else if (c === '\u0004') {
559
+ buffer = $.trim(buffer).replace(/ +/, ' ');
560
+ token.type = 'startTag';
561
+ token.tagName = result.join('');
562
+ token.annotation = buffer;
563
+ return token;
564
+ }
565
+ else {
566
+ buffer += c;
567
+ }
568
+ }
569
+ else if (tokenState === 'endTag') {
570
+ if (c === '>') {
571
+ cut(state, 1);
572
+ token.type = 'endTag';
573
+ token.tagName = result.join('');
574
+ return token;
575
+ }
576
+ else if (c === '\u0004') {
577
+ token.type = 'endTag';
578
+ token.tagName = result.join('');
579
+ return token;
580
+ }
581
+ else {
582
+ result.push(c);
583
+ }
584
+ }
585
+ else if (tokenState === 'timestampTag') {
586
+ if (c === '>') {
587
+ cut(state, 1);
588
+ token.type = 'timestampTag';
589
+ token.name = result.join('');
590
+ return token;
591
+ }
592
+ else if (c === '\u0004') {
593
+ token.type = 'timestampTag';
594
+ token.name = result.join('');
595
+ return token;
596
+ }
597
+ else {
598
+ result.push(c);
599
+ }
600
+ }
601
+ else {
602
+ throw 'Unknown tokenState ' + tokenState;
603
+ }
604
+
605
+ cut(state, 1);
606
+ }
607
+ }
608
+
609
+ function eatComment(state) {
610
+ // Cut the NOTE line.
611
+ var noteLine = cutLine(state);
612
+ if (noteLine.indexOf('-->') !== -1) {
613
+ state.error = 'Invalid syntax: --> in NOTE line.';
614
+ return;
615
+ }
616
+ while (true) {
617
+ var nextLine = peekLine(state);
618
+ if ($.trim(nextLine).length === 0) {
619
+ // End of comment.
620
+ return;
621
+ }
622
+ else if (nextLine.indexOf('-->') !== -1) {
623
+ state.error = 'Invalid syntax: --> in comment.';
624
+ return;
625
+ }
626
+ else {
627
+ cutLine(state);
628
+ }
629
+ }
630
+ }
631
+
632
+ // Initial byte order mark.
633
+ function eatOptionalBOM(state) {
634
+ if (state.text[0] === '\ufeff') {
635
+ cut(state, 1);
636
+ }
637
+
638
+ }
639
+
640
+ // "WEBVTT" string.
641
+ function eatSignature(state) {
642
+ if (state.text.substring(0,6) === 'WEBVTT') {
643
+ cut(state, 6);
644
+ }
645
+ else {
646
+ state.error = 'Invalid signature.';
647
+ }
648
+ }
649
+
650
+ function eatArrow(state) {
651
+ if (state.text.length < 3 || state.text.substring(0,3) !== '-->') {
652
+ state.error = 'Missing -->';
653
+ }
654
+ else {
655
+ cut(state, 3);
656
+ }
657
+ }
658
+
659
+ function eatSingleSpaceOrTab(state) {
660
+ if (state.text[0] === '\t' || state.text[0] === ' ') {
661
+ cut(state, 1);
662
+ }
663
+ else {
664
+ state.error = 'Missing space.';
665
+ }
666
+ }
667
+
668
+ function eatSpacesOrTabs(state) {
669
+ while (state.text[0] === '\t' || state.text[0] === ' ') {
670
+ cut(state, 1);
671
+ }
672
+ }
673
+
674
+ function eatAtLeast1SpacesOrTabs(state) {
675
+ var numEaten = 0;
676
+ while (state.text[0] === '\t' || state.text[0] === ' ') {
677
+ cut(state, 1);
678
+ numEaten += 1;
679
+ }
680
+ if (numEaten === 0) {
681
+ state.error = 'Missing space.';
682
+ }
683
+ }
684
+
685
+ function eatUntilEOLInclusive(state) {
686
+ var nextEOL = state.text.indexOf('\n');
687
+ if (nextEOL === -1) {
688
+ state.error = 'Missing EOL.';
689
+ }
690
+ else {
691
+ cut(state, nextEOL + 1);
692
+ }
693
+ }
694
+
695
+ function eatEmptyLines(state) {
696
+ while (state.text.length > 0) {
697
+ var nextLine = peekLine(state);
698
+ if ($.trim(nextLine).length === 0) {
699
+ cutLine(state);
700
+ }
701
+ else {
702
+ break;
703
+ }
704
+ }
705
+ }
706
+
707
+ // Eats empty lines, but throws an error if there's not at least one.
708
+ function eatAtLeast1EmptyLines(state) {
709
+ var linesEaten = 0;
710
+ while (state.text.length > 0) {
711
+ var nextLine = peekLine(state);
712
+ if ($.trim(nextLine).length === 0) {
713
+ cutLine(state);
714
+ linesEaten += 1;
715
+ }
716
+ else {
717
+ break;
718
+ }
719
+ }
720
+ if (linesEaten === 0) {
721
+ state.error = 'Missing empty line.';
722
+ }
723
+ }
724
+
725
+ function getTiming(state) {
726
+ var nextSpace = nextSpaceOrNewline(state.text);
727
+ if (nextSpace === -1) {
728
+ state.error('Missing timing.');
729
+ return;
730
+ }
731
+ var timestamp = cut(state, nextSpace);
732
+
733
+ var results = /((\d\d):)?((\d\d):)(\d\d).(\d\d\d)|(\d+).(\d\d\d)/.exec(timestamp);
734
+
735
+ if (!results) {
736
+ state.error = 'Unable to parse timestamp';
737
+ return;
738
+ }
739
+ var time = 0;
740
+ var hours = results[2];
741
+ var minutes = results[4];
742
+
743
+ if (minutes) {
744
+ if (parseInt(minutes, 10) > 59) {
745
+ state.error = 'Invalid minute range';
746
+ return;
747
+ }
748
+ if (hours) {
749
+ time += 3600 * parseInt(hours, 10);
750
+ }
751
+ time += 60 * parseInt(minutes, 10);
752
+ var seconds = results[5];
753
+ if (parseInt(seconds, 10) > 59) {
754
+ state.error = 'Invalid second range';
755
+ return;
756
+ }
757
+
758
+ time += parseInt(seconds, 10);
759
+ time += parseInt(results[6], 10) / 1000;
760
+ }
761
+ else {
762
+ time += parseInt(results[7], 10);
763
+ time += parseInt(results[8], 10) / 1000;
764
+ }
765
+
766
+ return time;
767
+ }
765
768
  })(jQuery);