| 1 | package com.longtailvideo.plugins.captions { |
|---|
| 2 | |
|---|
| 3 | |
|---|
| 4 | import com.longtailvideo.jwplayer.utils.Strings; |
|---|
| 5 | |
|---|
| 6 | /** |
|---|
| 7 | * Parse styling and contents of W3C Timed Text XML files. |
|---|
| 8 | **/ |
|---|
| 9 | public class DFXP { |
|---|
| 10 | |
|---|
| 11 | |
|---|
| 12 | /** Parse stylesheets from the head. */ |
|---|
| 13 | public static function parseStyles(data:XML,defaults:Object):Object { |
|---|
| 14 | var styles:Object = {}; |
|---|
| 15 | for each (var i:XML in data.children()) { |
|---|
| 16 | if (i.localName() == "head") { |
|---|
| 17 | for each (var styling:XML in i.children()) { |
|---|
| 18 | for each (var node:XML in styling.children()) { |
|---|
| 19 | if (node.localName() == 'style') { |
|---|
| 20 | // Set the defaults. |
|---|
| 21 | var rules:Object = {}; |
|---|
| 22 | for(var rule:String in defaults) { |
|---|
| 23 | rules[rule] = defaults[rule]; |
|---|
| 24 | } |
|---|
| 25 | // Loop through all attributes for overrides. |
|---|
| 26 | for each (var attrib:XML in node.attributes()) { |
|---|
| 27 | var name:String = attrib.name(); |
|---|
| 28 | if (name.indexOf("::") > -1) { |
|---|
| 29 | name = name.substring(name.indexOf("::") + 2); |
|---|
| 30 | } |
|---|
| 31 | rules[name] = attrib.toString(); |
|---|
| 32 | } |
|---|
| 33 | // Save to listing |
|---|
| 34 | if (node.@id) { |
|---|
| 35 | styles[node.@id] = rules; |
|---|
| 36 | } |
|---|
| 37 | } |
|---|
| 38 | } |
|---|
| 39 | } |
|---|
| 40 | } |
|---|
| 41 | } |
|---|
| 42 | return styles; |
|---|
| 43 | }; |
|---|
| 44 | |
|---|
| 45 | |
|---|
| 46 | /** Parse captions from the TT XML, returning a list with {begin:Number,text:String} objects. **/ |
|---|
| 47 | public static function parseCaptions(data:XML):Array { |
|---|
| 48 | var arr:Array = new Array({begin:0,text:''}); |
|---|
| 49 | for each (var i:XML in data.children()) { |
|---|
| 50 | if (i.localName() == "body") { |
|---|
| 51 | for each (var j:XML in i.children()) { |
|---|
| 52 | for each (var k:XML in j.children()) { |
|---|
| 53 | // Paragraphs are single captions. They live inside dividers. |
|---|
| 54 | if (k.localName() == 'p') { |
|---|
| 55 | var obj:Object = TTParser.parseCaption(k); |
|---|
| 56 | arr.push(obj); |
|---|
| 57 | // End with a new, empty caption, accontig for duration or end set. |
|---|
| 58 | if (obj['end']) { |
|---|
| 59 | arr.push({begin:obj['end'],text:''}); |
|---|
| 60 | delete obj['end']; |
|---|
| 61 | } else if (obj['dur']) { |
|---|
| 62 | arr.push({begin:obj['begin']+obj['dur'],text:''}); |
|---|
| 63 | delete obj['dur']; |
|---|
| 64 | } |
|---|
| 65 | } |
|---|
| 66 | } |
|---|
| 67 | } |
|---|
| 68 | } |
|---|
| 69 | } |
|---|
| 70 | return arr; |
|---|
| 71 | }; |
|---|
| 72 | |
|---|
| 73 | |
|---|
| 74 | /** Parse a single captions entry. **/ |
|---|
| 75 | private static function parseCaption(dat:XML):Object { |
|---|
| 76 | var ptn:RegExp = /(\n<br.*>\n)+/; |
|---|
| 77 | var obj:Object = { |
|---|
| 78 | begin:Strings.seconds(dat.@begin), |
|---|
| 79 | dur:Strings.seconds(dat.@dur), |
|---|
| 80 | end:Strings.seconds(dat.@end), |
|---|
| 81 | style:dat.@style, |
|---|
| 82 | // Not sure what this does anymore, but looks like it should be cleaned up. |
|---|
| 83 | text:dat.children().toString().replace(ptn,'<br/>').replace(/\n</,' <').replace(/>\n/, '> ') |
|---|
| 84 | }; |
|---|
| 85 | return obj; |
|---|
| 86 | }; |
|---|
| 87 | |
|---|
| 88 | |
|---|
| 89 | /** Convert inline caption styling into HTML. */ |
|---|
| 90 | public static function parseSpans(text:String,styles:Object,defaults:Object):String { |
|---|
| 91 | while (text.indexOf("<span") > -1) { |
|---|
| 92 | text = parseSpan(text,styles,defaults); |
|---|
| 93 | } |
|---|
| 94 | return text; |
|---|
| 95 | }; |
|---|
| 96 | |
|---|
| 97 | |
|---|
| 98 | /** Convert a span entry into HTML. **/ |
|---|
| 99 | private static function parseSpan(text:String,styles:Object,defaults:Object):String { |
|---|
| 100 | var rules:Object = {}; |
|---|
| 101 | var newtext:String = ''; |
|---|
| 102 | // Find the span bounds and convert to XML. |
|---|
| 103 | var left:Number = text.indexOf("<span "); |
|---|
| 104 | var right:Number = text.indexOf("</span>",left); |
|---|
| 105 | if (left > -1 && right > -1) { |
|---|
| 106 | var span:XML = new XML(text.substring(left,right+7)); |
|---|
| 107 | // Use style if defined, else set defaults. |
|---|
| 108 | var style:String = span.@style; |
|---|
| 109 | if(style && styles[style]) { |
|---|
| 110 | for(var i:String in styles[style]) { rules[i] = styles[style][i]; } |
|---|
| 111 | } else { |
|---|
| 112 | for(var j:String in defaults) { rules[j] = defaults[j]; } |
|---|
| 113 | } |
|---|
| 114 | // Override style with inline declarations |
|---|
| 115 | for each (var attrib:XML in span.@*) { |
|---|
| 116 | var name:String = attrib.localName().toString(); |
|---|
| 117 | if (rules[name]) { |
|---|
| 118 | rules[name] = attrib.toString(); |
|---|
| 119 | } |
|---|
| 120 | } |
|---|
| 121 | // Wrap plain text with font and b/i/u tags. |
|---|
| 122 | newtext = '<font family="'+rules.fontFamily+'" size="'+rules.fontSize+'" color="'+rules.color+'">'; |
|---|
| 123 | newtext += text.substring(text.indexOf('>',left)+1, right); |
|---|
| 124 | newtext += "</font>"; |
|---|
| 125 | if(rules.fontWeight == 'bold') { newtext = '<b>'+newtext+'</b>'; } |
|---|
| 126 | if(rules.fontStyle == 'italic') { newtext = '<i>'+newtext+'</i>'; } |
|---|
| 127 | if(rules.textDecoration == 'underline') { newtext = '<u>'+newtext+'</u>'; } |
|---|
| 128 | } |
|---|
| 129 | return text.substr(0,left)+newtext+text.substr(right+7); |
|---|
| 130 | }; |
|---|
| 131 | |
|---|
| 132 | |
|---|
| 133 | } |
|---|
| 134 | |
|---|
| 135 | |
|---|
| 136 | } |
|---|