content/media/webvtt/vtt.jsm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/webvtt/vtt.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1425 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +this.EXPORTED_SYMBOLS = ["WebVTT"];
     1.9 +
    1.10 +/**
    1.11 + * Code below is vtt.js the JS WebVTT implementation.
    1.12 + * Current source code can be found at http://github.com/mozilla/vtt.js
    1.13 + *
    1.14 + * Code taken from commit 65ae2daaf6ec7e710f591214893bb03d8b7a94b5
    1.15 + */
    1.16 +/**
    1.17 + * Copyright 2013 vtt.js Contributors
    1.18 + *
    1.19 + * Licensed under the Apache License, Version 2.0 (the "License");
    1.20 + * you may not use this file except in compliance with the License.
    1.21 + * You may obtain a copy of the License at
    1.22 + *
    1.23 + *   http://www.apache.org/licenses/LICENSE-2.0
    1.24 + *
    1.25 + * Unless required by applicable law or agreed to in writing, software
    1.26 + * distributed under the License is distributed on an "AS IS" BASIS,
    1.27 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    1.28 + * See the License for the specific language governing permissions and
    1.29 + * limitations under the License.
    1.30 + */
    1.31 +
    1.32 +
    1.33 +(function(global) {
    1.34 +
    1.35 +  _objCreate = Object.create || (function() {
    1.36 +    function F() {}
    1.37 +    return function(o) {
    1.38 +      if (arguments.length !== 1) {
    1.39 +        throw new Error('Object.create shim only accepts one parameter.');
    1.40 +      }
    1.41 +      F.prototype = o;
    1.42 +      return new F();
    1.43 +    };
    1.44 +  })();
    1.45 +
    1.46 +  // Creates a new ParserError object from an errorData object. The errorData
    1.47 +  // object should have default code and message properties. The default message
    1.48 +  // property can be overriden by passing in a message parameter.
    1.49 +  // See ParsingError.Errors below for acceptable errors.
    1.50 +  function ParsingError(errorData, message) {
    1.51 +    this.name = "ParsingError";
    1.52 +    this.code = errorData.code;
    1.53 +    this.message = message || errorData.message;
    1.54 +  }
    1.55 +  ParsingError.prototype = _objCreate(Error.prototype);
    1.56 +  ParsingError.prototype.constructor = ParsingError;
    1.57 +
    1.58 +  // ParsingError metadata for acceptable ParsingErrors.
    1.59 +  ParsingError.Errors = {
    1.60 +    BadSignature: {
    1.61 +      code: 0,
    1.62 +      message: "Malformed WebVTT signature."
    1.63 +    },
    1.64 +    BadTimeStamp: {
    1.65 +      code: 1,
    1.66 +      message: "Malformed time stamp."
    1.67 +    }
    1.68 +  };
    1.69 +
    1.70 +  // Try to parse input as a time stamp.
    1.71 +  function parseTimeStamp(input) {
    1.72 +
    1.73 +    function computeSeconds(h, m, s, f) {
    1.74 +      return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
    1.75 +    }
    1.76 +
    1.77 +    var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
    1.78 +    if (!m) {
    1.79 +      return null;
    1.80 +    }
    1.81 +
    1.82 +    if (m[3]) {
    1.83 +      // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
    1.84 +      return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]);
    1.85 +    } else if (m[1] > 59) {
    1.86 +      // Timestamp takes the form of [hours]:[minutes].[milliseconds]
    1.87 +      // First position is hours as it's over 59.
    1.88 +      return computeSeconds(m[1], m[2], 0,  m[4]);
    1.89 +    } else {
    1.90 +      // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
    1.91 +      return computeSeconds(0, m[1], m[2], m[4]);
    1.92 +    }
    1.93 +  }
    1.94 +
    1.95 +  // A settings object holds key/value pairs and will ignore anything but the first
    1.96 +  // assignment to a specific key.
    1.97 +  function Settings() {
    1.98 +    this.values = _objCreate(null);
    1.99 +  }
   1.100 +
   1.101 +  Settings.prototype = {
   1.102 +    // Only accept the first assignment to any key.
   1.103 +    set: function(k, v) {
   1.104 +      if (!this.get(k) && v !== "") {
   1.105 +        this.values[k] = v;
   1.106 +      }
   1.107 +    },
   1.108 +    // Return the value for a key, or a default value.
   1.109 +    // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
   1.110 +    // a number of possible default values as properties where 'defaultKey' is
   1.111 +    // the key of the property that will be chosen; otherwise it's assumed to be
   1.112 +    // a single value.
   1.113 +    get: function(k, dflt, defaultKey) {
   1.114 +      if (defaultKey) {
   1.115 +        return this.has(k) ? this.values[k] : dflt[defaultKey];
   1.116 +      }
   1.117 +      return this.has(k) ? this.values[k] : dflt;
   1.118 +    },
   1.119 +    // Check whether we have a value for a key.
   1.120 +    has: function(k) {
   1.121 +      return k in this.values;
   1.122 +    },
   1.123 +    // Accept a setting if its one of the given alternatives.
   1.124 +    alt: function(k, v, a) {
   1.125 +      for (var n = 0; n < a.length; ++n) {
   1.126 +        if (v === a[n]) {
   1.127 +          this.set(k, v);
   1.128 +          break;
   1.129 +        }
   1.130 +      }
   1.131 +    },
   1.132 +    // Accept a setting if its a valid (signed) integer.
   1.133 +    integer: function(k, v) {
   1.134 +      if (/^-?\d+$/.test(v)) { // integer
   1.135 +        this.set(k, parseInt(v, 10));
   1.136 +      }
   1.137 +    },
   1.138 +    // Accept a setting if its a valid percentage.
   1.139 +    percent: function(k, v) {
   1.140 +      var m;
   1.141 +      if ((m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/))) {
   1.142 +        v = parseFloat(v);
   1.143 +        if (v >= 0 && v <= 100) {
   1.144 +          this.set(k, v);
   1.145 +          return true;
   1.146 +        }
   1.147 +      }
   1.148 +      return false;
   1.149 +    }
   1.150 +  };
   1.151 +
   1.152 +  // Helper function to parse input into groups separated by 'groupDelim', and
   1.153 +  // interprete each group as a key/value pair separated by 'keyValueDelim'.
   1.154 +  function parseOptions(input, callback, keyValueDelim, groupDelim) {
   1.155 +    var groups = groupDelim ? input.split(groupDelim) : [input];
   1.156 +    for (var i in groups) {
   1.157 +      if (typeof groups[i] !== "string") {
   1.158 +        continue;
   1.159 +      }
   1.160 +      var kv = groups[i].split(keyValueDelim);
   1.161 +      if (kv.length !== 2) {
   1.162 +        continue;
   1.163 +      }
   1.164 +      var k = kv[0];
   1.165 +      var v = kv[1];
   1.166 +      callback(k, v);
   1.167 +    }
   1.168 +  }
   1.169 +
   1.170 +  function parseCue(input, cue, regionList) {
   1.171 +    // 4.1 WebVTT timestamp
   1.172 +    function consumeTimeStamp() {
   1.173 +      var ts = parseTimeStamp(input);
   1.174 +      if (ts === null) {
   1.175 +        throw new ParsingError(ParsingError.Errors.BadTimeStamp);
   1.176 +      }
   1.177 +      // Remove time stamp from input.
   1.178 +      input = input.replace(/^[^\sa-zA-Z-]+/, "");
   1.179 +      return ts;
   1.180 +    }
   1.181 +
   1.182 +    // 4.4.2 WebVTT cue settings
   1.183 +    function consumeCueSettings(input, cue) {
   1.184 +      var settings = new Settings();
   1.185 +
   1.186 +      parseOptions(input, function (k, v) {
   1.187 +        switch (k) {
   1.188 +        case "region":
   1.189 +          // Find the last region we parsed with the same region id.
   1.190 +          for (var i = regionList.length - 1; i >= 0; i--) {
   1.191 +            if (regionList[i].id === v) {
   1.192 +              settings.set(k, regionList[i].region);
   1.193 +              break;
   1.194 +            }
   1.195 +          }
   1.196 +          break;
   1.197 +        case "vertical":
   1.198 +          settings.alt(k, v, ["rl", "lr"]);
   1.199 +          break;
   1.200 +        case "line":
   1.201 +          var vals = v.split(","),
   1.202 +              vals0 = vals[0];
   1.203 +          settings.integer(k, vals0);
   1.204 +          settings.percent(k, vals0) ? settings.set("snapToLines", false) : null;
   1.205 +          settings.alt(k, vals0, ["auto"]);
   1.206 +          if (vals.length === 2) {
   1.207 +            settings.alt("lineAlign", vals[1], ["start", "middle", "end"]);
   1.208 +          }
   1.209 +          break;
   1.210 +        case "position":
   1.211 +          vals = v.split(",");
   1.212 +          settings.percent(k, vals[0]);
   1.213 +          if (vals.length === 2) {
   1.214 +            settings.alt("positionAlign", vals[1], ["start", "middle", "end"]);
   1.215 +          }
   1.216 +          break;
   1.217 +        case "size":
   1.218 +          settings.percent(k, v);
   1.219 +          break;
   1.220 +        case "align":
   1.221 +          settings.alt(k, v, ["start", "middle", "end", "left", "right"]);
   1.222 +          break;
   1.223 +        }
   1.224 +      }, /:/, /\s/);
   1.225 +
   1.226 +      // Apply default values for any missing fields.
   1.227 +      cue.region = settings.get("region", null);
   1.228 +      cue.vertical = settings.get("vertical", "");
   1.229 +      cue.line = settings.get("line", "auto");
   1.230 +      cue.lineAlign = settings.get("lineAlign", "start");
   1.231 +      cue.snapToLines = settings.get("snapToLines", true);
   1.232 +      cue.size = settings.get("size", 100);
   1.233 +      cue.align = settings.get("align", "middle");
   1.234 +      cue.position = settings.get("position", {
   1.235 +        start: 0,
   1.236 +        left: 0,
   1.237 +        middle: 50,
   1.238 +        end: 100,
   1.239 +        right: 100
   1.240 +      }, cue.align);
   1.241 +      cue.positionAlign = settings.get("positionAlign", {
   1.242 +        start: "start",
   1.243 +        left: "start",
   1.244 +        middle: "middle",
   1.245 +        end: "end",
   1.246 +        right: "end"
   1.247 +      }, cue.align);
   1.248 +    }
   1.249 +
   1.250 +    function skipWhitespace() {
   1.251 +      input = input.replace(/^\s+/, "");
   1.252 +    }
   1.253 +
   1.254 +    // 4.1 WebVTT cue timings.
   1.255 +    skipWhitespace();
   1.256 +    cue.startTime = consumeTimeStamp();   // (1) collect cue start time
   1.257 +    skipWhitespace();
   1.258 +    if (input.substr(0, 3) !== "-->") {     // (3) next characters must match "-->"
   1.259 +      throw new ParsingError(ParsingError.Errors.BadTimeStamp,
   1.260 +                             "Malformed time stamp (time stamps must be separated by '-->').");
   1.261 +    }
   1.262 +    input = input.substr(3);
   1.263 +    skipWhitespace();
   1.264 +    cue.endTime = consumeTimeStamp();     // (5) collect cue end time
   1.265 +
   1.266 +    // 4.1 WebVTT cue settings list.
   1.267 +    skipWhitespace();
   1.268 +    consumeCueSettings(input, cue);
   1.269 +  }
   1.270 +
   1.271 +  var ESCAPE = {
   1.272 +    "&amp;": "&",
   1.273 +    "&lt;": "<",
   1.274 +    "&gt;": ">",
   1.275 +    "&lrm;": "\u200e",
   1.276 +    "&rlm;": "\u200f",
   1.277 +    "&nbsp;": "\u00a0"
   1.278 +  };
   1.279 +
   1.280 +  var TAG_NAME = {
   1.281 +    c: "span",
   1.282 +    i: "i",
   1.283 +    b: "b",
   1.284 +    u: "u",
   1.285 +    ruby: "ruby",
   1.286 +    rt: "rt",
   1.287 +    v: "span",
   1.288 +    lang: "span"
   1.289 +  };
   1.290 +
   1.291 +  var TAG_ANNOTATION = {
   1.292 +    v: "title",
   1.293 +    lang: "lang"
   1.294 +  };
   1.295 +
   1.296 +  var NEEDS_PARENT = {
   1.297 +    rt: "ruby"
   1.298 +  };
   1.299 +
   1.300 +  // Parse content into a document fragment.
   1.301 +  function parseContent(window, input) {
   1.302 +    function nextToken() {
   1.303 +      // Check for end-of-string.
   1.304 +      if (!input) {
   1.305 +        return null;
   1.306 +      }
   1.307 +
   1.308 +      // Consume 'n' characters from the input.
   1.309 +      function consume(result) {
   1.310 +        input = input.substr(result.length);
   1.311 +        return result;
   1.312 +      }
   1.313 +
   1.314 +      var m = input.match(/^([^<]*)(<[^>]+>?)?/);
   1.315 +      // If there is some text before the next tag, return it, otherwise return
   1.316 +      // the tag.
   1.317 +      return consume(m[1] ? m[1] : m[2]);
   1.318 +    }
   1.319 +
   1.320 +    // Unescape a string 's'.
   1.321 +    function unescape1(e) {
   1.322 +      return ESCAPE[e];
   1.323 +    }
   1.324 +    function unescape(s) {
   1.325 +      while ((m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/))) {
   1.326 +        s = s.replace(m[0], unescape1);
   1.327 +      }
   1.328 +      return s;
   1.329 +    }
   1.330 +
   1.331 +    function shouldAdd(current, element) {
   1.332 +      return !NEEDS_PARENT[element.localName] ||
   1.333 +             NEEDS_PARENT[element.localName] === current.localName;
   1.334 +    }
   1.335 +
   1.336 +    // Create an element for this tag.
   1.337 +    function createElement(type, annotation) {
   1.338 +      var tagName = TAG_NAME[type];
   1.339 +      if (!tagName) {
   1.340 +        return null;
   1.341 +      }
   1.342 +      var element = window.document.createElement(tagName);
   1.343 +      element.localName = tagName;
   1.344 +      var name = TAG_ANNOTATION[type];
   1.345 +      if (name && annotation) {
   1.346 +        element[name] = annotation.trim();
   1.347 +      }
   1.348 +      return element;
   1.349 +    }
   1.350 +
   1.351 +    var rootDiv = window.document.createElement("div"),
   1.352 +        current = rootDiv,
   1.353 +        t,
   1.354 +        tagStack = [];
   1.355 +
   1.356 +    while ((t = nextToken()) !== null) {
   1.357 +      if (t[0] === '<') {
   1.358 +        if (t[1] === "/") {
   1.359 +          // If the closing tag matches, move back up to the parent node.
   1.360 +          if (tagStack.length &&
   1.361 +              tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) {
   1.362 +            tagStack.pop();
   1.363 +            current = current.parentNode;
   1.364 +          }
   1.365 +          // Otherwise just ignore the end tag.
   1.366 +          continue;
   1.367 +        }
   1.368 +        var ts = parseTimeStamp(t.substr(1, t.length - 2));
   1.369 +        var node;
   1.370 +        if (ts) {
   1.371 +          // Timestamps are lead nodes as well.
   1.372 +          node = window.document.createProcessingInstruction("timestamp", ts);
   1.373 +          current.appendChild(node);
   1.374 +          continue;
   1.375 +        }
   1.376 +        var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/);
   1.377 +        // If we can't parse the tag, skip to the next tag.
   1.378 +        if (!m) {
   1.379 +          continue;
   1.380 +        }
   1.381 +        // Try to construct an element, and ignore the tag if we couldn't.
   1.382 +        node = createElement(m[1], m[3]);
   1.383 +        if (!node) {
   1.384 +          continue;
   1.385 +        }
   1.386 +        // Determine if the tag should be added based on the context of where it
   1.387 +        // is placed in the cuetext.
   1.388 +        if (!shouldAdd(current, node)) {
   1.389 +          continue;
   1.390 +        }
   1.391 +        // Set the class list (as a list of classes, separated by space).
   1.392 +        if (m[2]) {
   1.393 +          node.className = m[2].substr(1).replace('.', ' ');
   1.394 +        }
   1.395 +        // Append the node to the current node, and enter the scope of the new
   1.396 +        // node.
   1.397 +        tagStack.push(m[1]);
   1.398 +        current.appendChild(node);
   1.399 +        current = node;
   1.400 +        continue;
   1.401 +      }
   1.402 +
   1.403 +      // Text nodes are leaf nodes.
   1.404 +      current.appendChild(window.document.createTextNode(unescape(t)));
   1.405 +    }
   1.406 +
   1.407 +    return rootDiv;
   1.408 +  }
   1.409 +
   1.410 +  // This is a list of all the Unicode characters that have a strong
   1.411 +  // right-to-left category. What this means is that these characters are
   1.412 +  // written right-to-left for sure. It was generated by pulling all the strong
   1.413 +  // right-to-left characters out of the Unicode data table. That table can
   1.414 +  // found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
   1.415 +  var strongRTLChars = [0x05BE, 0x05C0, 0x05C3, 0x05C6, 0x05D0, 0x05D1,
   1.416 +      0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA,
   1.417 +      0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3,
   1.418 +      0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x05F0, 0x05F1,
   1.419 +      0x05F2, 0x05F3, 0x05F4, 0x0608, 0x060B, 0x060D, 0x061B, 0x061E, 0x061F,
   1.420 +      0x0620, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628,
   1.421 +      0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631,
   1.422 +      0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A,
   1.423 +      0x063B, 0x063C, 0x063D, 0x063E, 0x063F, 0x0640, 0x0641, 0x0642, 0x0643,
   1.424 +      0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x066D, 0x066E,
   1.425 +      0x066F, 0x0671, 0x0672, 0x0673, 0x0674, 0x0675, 0x0676, 0x0677, 0x0678,
   1.426 +      0x0679, 0x067A, 0x067B, 0x067C, 0x067D, 0x067E, 0x067F, 0x0680, 0x0681,
   1.427 +      0x0682, 0x0683, 0x0684, 0x0685, 0x0686, 0x0687, 0x0688, 0x0689, 0x068A,
   1.428 +      0x068B, 0x068C, 0x068D, 0x068E, 0x068F, 0x0690, 0x0691, 0x0692, 0x0693,
   1.429 +      0x0694, 0x0695, 0x0696, 0x0697, 0x0698, 0x0699, 0x069A, 0x069B, 0x069C,
   1.430 +      0x069D, 0x069E, 0x069F, 0x06A0, 0x06A1, 0x06A2, 0x06A3, 0x06A4, 0x06A5,
   1.431 +      0x06A6, 0x06A7, 0x06A8, 0x06A9, 0x06AA, 0x06AB, 0x06AC, 0x06AD, 0x06AE,
   1.432 +      0x06AF, 0x06B0, 0x06B1, 0x06B2, 0x06B3, 0x06B4, 0x06B5, 0x06B6, 0x06B7,
   1.433 +      0x06B8, 0x06B9, 0x06BA, 0x06BB, 0x06BC, 0x06BD, 0x06BE, 0x06BF, 0x06C0,
   1.434 +      0x06C1, 0x06C2, 0x06C3, 0x06C4, 0x06C5, 0x06C6, 0x06C7, 0x06C8, 0x06C9,
   1.435 +      0x06CA, 0x06CB, 0x06CC, 0x06CD, 0x06CE, 0x06CF, 0x06D0, 0x06D1, 0x06D2,
   1.436 +      0x06D3, 0x06D4, 0x06D5, 0x06E5, 0x06E6, 0x06EE, 0x06EF, 0x06FA, 0x06FB,
   1.437 +      0x06FC, 0x06FD, 0x06FE, 0x06FF, 0x0700, 0x0701, 0x0702, 0x0703, 0x0704,
   1.438 +      0x0705, 0x0706, 0x0707, 0x0708, 0x0709, 0x070A, 0x070B, 0x070C, 0x070D,
   1.439 +      0x070F, 0x0710, 0x0712, 0x0713, 0x0714, 0x0715, 0x0716, 0x0717, 0x0718,
   1.440 +      0x0719, 0x071A, 0x071B, 0x071C, 0x071D, 0x071E, 0x071F, 0x0720, 0x0721,
   1.441 +      0x0722, 0x0723, 0x0724, 0x0725, 0x0726, 0x0727, 0x0728, 0x0729, 0x072A,
   1.442 +      0x072B, 0x072C, 0x072D, 0x072E, 0x072F, 0x074D, 0x074E, 0x074F, 0x0750,
   1.443 +      0x0751, 0x0752, 0x0753, 0x0754, 0x0755, 0x0756, 0x0757, 0x0758, 0x0759,
   1.444 +      0x075A, 0x075B, 0x075C, 0x075D, 0x075E, 0x075F, 0x0760, 0x0761, 0x0762,
   1.445 +      0x0763, 0x0764, 0x0765, 0x0766, 0x0767, 0x0768, 0x0769, 0x076A, 0x076B,
   1.446 +      0x076C, 0x076D, 0x076E, 0x076F, 0x0770, 0x0771, 0x0772, 0x0773, 0x0774,
   1.447 +      0x0775, 0x0776, 0x0777, 0x0778, 0x0779, 0x077A, 0x077B, 0x077C, 0x077D,
   1.448 +      0x077E, 0x077F, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0786,
   1.449 +      0x0787, 0x0788, 0x0789, 0x078A, 0x078B, 0x078C, 0x078D, 0x078E, 0x078F,
   1.450 +      0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0796, 0x0797, 0x0798,
   1.451 +      0x0799, 0x079A, 0x079B, 0x079C, 0x079D, 0x079E, 0x079F, 0x07A0, 0x07A1,
   1.452 +      0x07A2, 0x07A3, 0x07A4, 0x07A5, 0x07B1, 0x07C0, 0x07C1, 0x07C2, 0x07C3,
   1.453 +      0x07C4, 0x07C5, 0x07C6, 0x07C7, 0x07C8, 0x07C9, 0x07CA, 0x07CB, 0x07CC,
   1.454 +      0x07CD, 0x07CE, 0x07CF, 0x07D0, 0x07D1, 0x07D2, 0x07D3, 0x07D4, 0x07D5,
   1.455 +      0x07D6, 0x07D7, 0x07D8, 0x07D9, 0x07DA, 0x07DB, 0x07DC, 0x07DD, 0x07DE,
   1.456 +      0x07DF, 0x07E0, 0x07E1, 0x07E2, 0x07E3, 0x07E4, 0x07E5, 0x07E6, 0x07E7,
   1.457 +      0x07E8, 0x07E9, 0x07EA, 0x07F4, 0x07F5, 0x07FA, 0x0800, 0x0801, 0x0802,
   1.458 +      0x0803, 0x0804, 0x0805, 0x0806, 0x0807, 0x0808, 0x0809, 0x080A, 0x080B,
   1.459 +      0x080C, 0x080D, 0x080E, 0x080F, 0x0810, 0x0811, 0x0812, 0x0813, 0x0814,
   1.460 +      0x0815, 0x081A, 0x0824, 0x0828, 0x0830, 0x0831, 0x0832, 0x0833, 0x0834,
   1.461 +      0x0835, 0x0836, 0x0837, 0x0838, 0x0839, 0x083A, 0x083B, 0x083C, 0x083D,
   1.462 +      0x083E, 0x0840, 0x0841, 0x0842, 0x0843, 0x0844, 0x0845, 0x0846, 0x0847,
   1.463 +      0x0848, 0x0849, 0x084A, 0x084B, 0x084C, 0x084D, 0x084E, 0x084F, 0x0850,
   1.464 +      0x0851, 0x0852, 0x0853, 0x0854, 0x0855, 0x0856, 0x0857, 0x0858, 0x085E,
   1.465 +      0x08A0, 0x08A2, 0x08A3, 0x08A4, 0x08A5, 0x08A6, 0x08A7, 0x08A8, 0x08A9,
   1.466 +      0x08AA, 0x08AB, 0x08AC, 0x200F, 0xFB1D, 0xFB1F, 0xFB20, 0xFB21, 0xFB22,
   1.467 +      0xFB23, 0xFB24, 0xFB25, 0xFB26, 0xFB27, 0xFB28, 0xFB2A, 0xFB2B, 0xFB2C,
   1.468 +      0xFB2D, 0xFB2E, 0xFB2F, 0xFB30, 0xFB31, 0xFB32, 0xFB33, 0xFB34, 0xFB35,
   1.469 +      0xFB36, 0xFB38, 0xFB39, 0xFB3A, 0xFB3B, 0xFB3C, 0xFB3E, 0xFB40, 0xFB41,
   1.470 +      0xFB43, 0xFB44, 0xFB46, 0xFB47, 0xFB48, 0xFB49, 0xFB4A, 0xFB4B, 0xFB4C,
   1.471 +      0xFB4D, 0xFB4E, 0xFB4F, 0xFB50, 0xFB51, 0xFB52, 0xFB53, 0xFB54, 0xFB55,
   1.472 +      0xFB56, 0xFB57, 0xFB58, 0xFB59, 0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D, 0xFB5E,
   1.473 +      0xFB5F, 0xFB60, 0xFB61, 0xFB62, 0xFB63, 0xFB64, 0xFB65, 0xFB66, 0xFB67,
   1.474 +      0xFB68, 0xFB69, 0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D, 0xFB6E, 0xFB6F, 0xFB70,
   1.475 +      0xFB71, 0xFB72, 0xFB73, 0xFB74, 0xFB75, 0xFB76, 0xFB77, 0xFB78, 0xFB79,
   1.476 +      0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D, 0xFB7E, 0xFB7F, 0xFB80, 0xFB81, 0xFB82,
   1.477 +      0xFB83, 0xFB84, 0xFB85, 0xFB86, 0xFB87, 0xFB88, 0xFB89, 0xFB8A, 0xFB8B,
   1.478 +      0xFB8C, 0xFB8D, 0xFB8E, 0xFB8F, 0xFB90, 0xFB91, 0xFB92, 0xFB93, 0xFB94,
   1.479 +      0xFB95, 0xFB96, 0xFB97, 0xFB98, 0xFB99, 0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D,
   1.480 +      0xFB9E, 0xFB9F, 0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3, 0xFBA4, 0xFBA5, 0xFBA6,
   1.481 +      0xFBA7, 0xFBA8, 0xFBA9, 0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD, 0xFBAE, 0xFBAF,
   1.482 +      0xFBB0, 0xFBB1, 0xFBB2, 0xFBB3, 0xFBB4, 0xFBB5, 0xFBB6, 0xFBB7, 0xFBB8,
   1.483 +      0xFBB9, 0xFBBA, 0xFBBB, 0xFBBC, 0xFBBD, 0xFBBE, 0xFBBF, 0xFBC0, 0xFBC1,
   1.484 +      0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6, 0xFBD7, 0xFBD8, 0xFBD9, 0xFBDA, 0xFBDB,
   1.485 +      0xFBDC, 0xFBDD, 0xFBDE, 0xFBDF, 0xFBE0, 0xFBE1, 0xFBE2, 0xFBE3, 0xFBE4,
   1.486 +      0xFBE5, 0xFBE6, 0xFBE7, 0xFBE8, 0xFBE9, 0xFBEA, 0xFBEB, 0xFBEC, 0xFBED,
   1.487 +      0xFBEE, 0xFBEF, 0xFBF0, 0xFBF1, 0xFBF2, 0xFBF3, 0xFBF4, 0xFBF5, 0xFBF6,
   1.488 +      0xFBF7, 0xFBF8, 0xFBF9, 0xFBFA, 0xFBFB, 0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF,
   1.489 +      0xFC00, 0xFC01, 0xFC02, 0xFC03, 0xFC04, 0xFC05, 0xFC06, 0xFC07, 0xFC08,
   1.490 +      0xFC09, 0xFC0A, 0xFC0B, 0xFC0C, 0xFC0D, 0xFC0E, 0xFC0F, 0xFC10, 0xFC11,
   1.491 +      0xFC12, 0xFC13, 0xFC14, 0xFC15, 0xFC16, 0xFC17, 0xFC18, 0xFC19, 0xFC1A,
   1.492 +      0xFC1B, 0xFC1C, 0xFC1D, 0xFC1E, 0xFC1F, 0xFC20, 0xFC21, 0xFC22, 0xFC23,
   1.493 +      0xFC24, 0xFC25, 0xFC26, 0xFC27, 0xFC28, 0xFC29, 0xFC2A, 0xFC2B, 0xFC2C,
   1.494 +      0xFC2D, 0xFC2E, 0xFC2F, 0xFC30, 0xFC31, 0xFC32, 0xFC33, 0xFC34, 0xFC35,
   1.495 +      0xFC36, 0xFC37, 0xFC38, 0xFC39, 0xFC3A, 0xFC3B, 0xFC3C, 0xFC3D, 0xFC3E,
   1.496 +      0xFC3F, 0xFC40, 0xFC41, 0xFC42, 0xFC43, 0xFC44, 0xFC45, 0xFC46, 0xFC47,
   1.497 +      0xFC48, 0xFC49, 0xFC4A, 0xFC4B, 0xFC4C, 0xFC4D, 0xFC4E, 0xFC4F, 0xFC50,
   1.498 +      0xFC51, 0xFC52, 0xFC53, 0xFC54, 0xFC55, 0xFC56, 0xFC57, 0xFC58, 0xFC59,
   1.499 +      0xFC5A, 0xFC5B, 0xFC5C, 0xFC5D, 0xFC5E, 0xFC5F, 0xFC60, 0xFC61, 0xFC62,
   1.500 +      0xFC63, 0xFC64, 0xFC65, 0xFC66, 0xFC67, 0xFC68, 0xFC69, 0xFC6A, 0xFC6B,
   1.501 +      0xFC6C, 0xFC6D, 0xFC6E, 0xFC6F, 0xFC70, 0xFC71, 0xFC72, 0xFC73, 0xFC74,
   1.502 +      0xFC75, 0xFC76, 0xFC77, 0xFC78, 0xFC79, 0xFC7A, 0xFC7B, 0xFC7C, 0xFC7D,
   1.503 +      0xFC7E, 0xFC7F, 0xFC80, 0xFC81, 0xFC82, 0xFC83, 0xFC84, 0xFC85, 0xFC86,
   1.504 +      0xFC87, 0xFC88, 0xFC89, 0xFC8A, 0xFC8B, 0xFC8C, 0xFC8D, 0xFC8E, 0xFC8F,
   1.505 +      0xFC90, 0xFC91, 0xFC92, 0xFC93, 0xFC94, 0xFC95, 0xFC96, 0xFC97, 0xFC98,
   1.506 +      0xFC99, 0xFC9A, 0xFC9B, 0xFC9C, 0xFC9D, 0xFC9E, 0xFC9F, 0xFCA0, 0xFCA1,
   1.507 +      0xFCA2, 0xFCA3, 0xFCA4, 0xFCA5, 0xFCA6, 0xFCA7, 0xFCA8, 0xFCA9, 0xFCAA,
   1.508 +      0xFCAB, 0xFCAC, 0xFCAD, 0xFCAE, 0xFCAF, 0xFCB0, 0xFCB1, 0xFCB2, 0xFCB3,
   1.509 +      0xFCB4, 0xFCB5, 0xFCB6, 0xFCB7, 0xFCB8, 0xFCB9, 0xFCBA, 0xFCBB, 0xFCBC,
   1.510 +      0xFCBD, 0xFCBE, 0xFCBF, 0xFCC0, 0xFCC1, 0xFCC2, 0xFCC3, 0xFCC4, 0xFCC5,
   1.511 +      0xFCC6, 0xFCC7, 0xFCC8, 0xFCC9, 0xFCCA, 0xFCCB, 0xFCCC, 0xFCCD, 0xFCCE,
   1.512 +      0xFCCF, 0xFCD0, 0xFCD1, 0xFCD2, 0xFCD3, 0xFCD4, 0xFCD5, 0xFCD6, 0xFCD7,
   1.513 +      0xFCD8, 0xFCD9, 0xFCDA, 0xFCDB, 0xFCDC, 0xFCDD, 0xFCDE, 0xFCDF, 0xFCE0,
   1.514 +      0xFCE1, 0xFCE2, 0xFCE3, 0xFCE4, 0xFCE5, 0xFCE6, 0xFCE7, 0xFCE8, 0xFCE9,
   1.515 +      0xFCEA, 0xFCEB, 0xFCEC, 0xFCED, 0xFCEE, 0xFCEF, 0xFCF0, 0xFCF1, 0xFCF2,
   1.516 +      0xFCF3, 0xFCF4, 0xFCF5, 0xFCF6, 0xFCF7, 0xFCF8, 0xFCF9, 0xFCFA, 0xFCFB,
   1.517 +      0xFCFC, 0xFCFD, 0xFCFE, 0xFCFF, 0xFD00, 0xFD01, 0xFD02, 0xFD03, 0xFD04,
   1.518 +      0xFD05, 0xFD06, 0xFD07, 0xFD08, 0xFD09, 0xFD0A, 0xFD0B, 0xFD0C, 0xFD0D,
   1.519 +      0xFD0E, 0xFD0F, 0xFD10, 0xFD11, 0xFD12, 0xFD13, 0xFD14, 0xFD15, 0xFD16,
   1.520 +      0xFD17, 0xFD18, 0xFD19, 0xFD1A, 0xFD1B, 0xFD1C, 0xFD1D, 0xFD1E, 0xFD1F,
   1.521 +      0xFD20, 0xFD21, 0xFD22, 0xFD23, 0xFD24, 0xFD25, 0xFD26, 0xFD27, 0xFD28,
   1.522 +      0xFD29, 0xFD2A, 0xFD2B, 0xFD2C, 0xFD2D, 0xFD2E, 0xFD2F, 0xFD30, 0xFD31,
   1.523 +      0xFD32, 0xFD33, 0xFD34, 0xFD35, 0xFD36, 0xFD37, 0xFD38, 0xFD39, 0xFD3A,
   1.524 +      0xFD3B, 0xFD3C, 0xFD3D, 0xFD50, 0xFD51, 0xFD52, 0xFD53, 0xFD54, 0xFD55,
   1.525 +      0xFD56, 0xFD57, 0xFD58, 0xFD59, 0xFD5A, 0xFD5B, 0xFD5C, 0xFD5D, 0xFD5E,
   1.526 +      0xFD5F, 0xFD60, 0xFD61, 0xFD62, 0xFD63, 0xFD64, 0xFD65, 0xFD66, 0xFD67,
   1.527 +      0xFD68, 0xFD69, 0xFD6A, 0xFD6B, 0xFD6C, 0xFD6D, 0xFD6E, 0xFD6F, 0xFD70,
   1.528 +      0xFD71, 0xFD72, 0xFD73, 0xFD74, 0xFD75, 0xFD76, 0xFD77, 0xFD78, 0xFD79,
   1.529 +      0xFD7A, 0xFD7B, 0xFD7C, 0xFD7D, 0xFD7E, 0xFD7F, 0xFD80, 0xFD81, 0xFD82,
   1.530 +      0xFD83, 0xFD84, 0xFD85, 0xFD86, 0xFD87, 0xFD88, 0xFD89, 0xFD8A, 0xFD8B,
   1.531 +      0xFD8C, 0xFD8D, 0xFD8E, 0xFD8F, 0xFD92, 0xFD93, 0xFD94, 0xFD95, 0xFD96,
   1.532 +      0xFD97, 0xFD98, 0xFD99, 0xFD9A, 0xFD9B, 0xFD9C, 0xFD9D, 0xFD9E, 0xFD9F,
   1.533 +      0xFDA0, 0xFDA1, 0xFDA2, 0xFDA3, 0xFDA4, 0xFDA5, 0xFDA6, 0xFDA7, 0xFDA8,
   1.534 +      0xFDA9, 0xFDAA, 0xFDAB, 0xFDAC, 0xFDAD, 0xFDAE, 0xFDAF, 0xFDB0, 0xFDB1,
   1.535 +      0xFDB2, 0xFDB3, 0xFDB4, 0xFDB5, 0xFDB6, 0xFDB7, 0xFDB8, 0xFDB9, 0xFDBA,
   1.536 +      0xFDBB, 0xFDBC, 0xFDBD, 0xFDBE, 0xFDBF, 0xFDC0, 0xFDC1, 0xFDC2, 0xFDC3,
   1.537 +      0xFDC4, 0xFDC5, 0xFDC6, 0xFDC7, 0xFDF0, 0xFDF1, 0xFDF2, 0xFDF3, 0xFDF4,
   1.538 +      0xFDF5, 0xFDF6, 0xFDF7, 0xFDF8, 0xFDF9, 0xFDFA, 0xFDFB, 0xFDFC, 0xFE70,
   1.539 +      0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE76, 0xFE77, 0xFE78, 0xFE79, 0xFE7A,
   1.540 +      0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F, 0xFE80, 0xFE81, 0xFE82, 0xFE83,
   1.541 +      0xFE84, 0xFE85, 0xFE86, 0xFE87, 0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C,
   1.542 +      0xFE8D, 0xFE8E, 0xFE8F, 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95,
   1.543 +      0xFE96, 0xFE97, 0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E,
   1.544 +      0xFE9F, 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7,
   1.545 +      0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF, 0xFEB0,
   1.546 +      0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8, 0xFEB9,
   1.547 +      0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0, 0xFEC1, 0xFEC2,
   1.548 +      0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8, 0xFEC9, 0xFECA, 0xFECB,
   1.549 +      0xFECC, 0xFECD, 0xFECE, 0xFECF, 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4,
   1.550 +      0xFED5, 0xFED6, 0xFED7, 0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD,
   1.551 +      0xFEDE, 0xFEDF, 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6,
   1.552 +      0xFEE7, 0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF,
   1.553 +      0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7, 0xFEF8,
   1.554 +      0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0x10800, 0x10801, 0x10802, 0x10803,
   1.555 +      0x10804, 0x10805, 0x10808, 0x1080A, 0x1080B, 0x1080C, 0x1080D, 0x1080E,
   1.556 +      0x1080F, 0x10810, 0x10811, 0x10812, 0x10813, 0x10814, 0x10815, 0x10816,
   1.557 +      0x10817, 0x10818, 0x10819, 0x1081A, 0x1081B, 0x1081C, 0x1081D, 0x1081E,
   1.558 +      0x1081F, 0x10820, 0x10821, 0x10822, 0x10823, 0x10824, 0x10825, 0x10826,
   1.559 +      0x10827, 0x10828, 0x10829, 0x1082A, 0x1082B, 0x1082C, 0x1082D, 0x1082E,
   1.560 +      0x1082F, 0x10830, 0x10831, 0x10832, 0x10833, 0x10834, 0x10835, 0x10837,
   1.561 +      0x10838, 0x1083C, 0x1083F, 0x10840, 0x10841, 0x10842, 0x10843, 0x10844,
   1.562 +      0x10845, 0x10846, 0x10847, 0x10848, 0x10849, 0x1084A, 0x1084B, 0x1084C,
   1.563 +      0x1084D, 0x1084E, 0x1084F, 0x10850, 0x10851, 0x10852, 0x10853, 0x10854,
   1.564 +      0x10855, 0x10857, 0x10858, 0x10859, 0x1085A, 0x1085B, 0x1085C, 0x1085D,
   1.565 +      0x1085E, 0x1085F, 0x10900, 0x10901, 0x10902, 0x10903, 0x10904, 0x10905,
   1.566 +      0x10906, 0x10907, 0x10908, 0x10909, 0x1090A, 0x1090B, 0x1090C, 0x1090D,
   1.567 +      0x1090E, 0x1090F, 0x10910, 0x10911, 0x10912, 0x10913, 0x10914, 0x10915,
   1.568 +      0x10916, 0x10917, 0x10918, 0x10919, 0x1091A, 0x1091B, 0x10920, 0x10921,
   1.569 +      0x10922, 0x10923, 0x10924, 0x10925, 0x10926, 0x10927, 0x10928, 0x10929,
   1.570 +      0x1092A, 0x1092B, 0x1092C, 0x1092D, 0x1092E, 0x1092F, 0x10930, 0x10931,
   1.571 +      0x10932, 0x10933, 0x10934, 0x10935, 0x10936, 0x10937, 0x10938, 0x10939,
   1.572 +      0x1093F, 0x10980, 0x10981, 0x10982, 0x10983, 0x10984, 0x10985, 0x10986,
   1.573 +      0x10987, 0x10988, 0x10989, 0x1098A, 0x1098B, 0x1098C, 0x1098D, 0x1098E,
   1.574 +      0x1098F, 0x10990, 0x10991, 0x10992, 0x10993, 0x10994, 0x10995, 0x10996,
   1.575 +      0x10997, 0x10998, 0x10999, 0x1099A, 0x1099B, 0x1099C, 0x1099D, 0x1099E,
   1.576 +      0x1099F, 0x109A0, 0x109A1, 0x109A2, 0x109A3, 0x109A4, 0x109A5, 0x109A6,
   1.577 +      0x109A7, 0x109A8, 0x109A9, 0x109AA, 0x109AB, 0x109AC, 0x109AD, 0x109AE,
   1.578 +      0x109AF, 0x109B0, 0x109B1, 0x109B2, 0x109B3, 0x109B4, 0x109B5, 0x109B6,
   1.579 +      0x109B7, 0x109BE, 0x109BF, 0x10A00, 0x10A10, 0x10A11, 0x10A12, 0x10A13,
   1.580 +      0x10A15, 0x10A16, 0x10A17, 0x10A19, 0x10A1A, 0x10A1B, 0x10A1C, 0x10A1D,
   1.581 +      0x10A1E, 0x10A1F, 0x10A20, 0x10A21, 0x10A22, 0x10A23, 0x10A24, 0x10A25,
   1.582 +      0x10A26, 0x10A27, 0x10A28, 0x10A29, 0x10A2A, 0x10A2B, 0x10A2C, 0x10A2D,
   1.583 +      0x10A2E, 0x10A2F, 0x10A30, 0x10A31, 0x10A32, 0x10A33, 0x10A40, 0x10A41,
   1.584 +      0x10A42, 0x10A43, 0x10A44, 0x10A45, 0x10A46, 0x10A47, 0x10A50, 0x10A51,
   1.585 +      0x10A52, 0x10A53, 0x10A54, 0x10A55, 0x10A56, 0x10A57, 0x10A58, 0x10A60,
   1.586 +      0x10A61, 0x10A62, 0x10A63, 0x10A64, 0x10A65, 0x10A66, 0x10A67, 0x10A68,
   1.587 +      0x10A69, 0x10A6A, 0x10A6B, 0x10A6C, 0x10A6D, 0x10A6E, 0x10A6F, 0x10A70,
   1.588 +      0x10A71, 0x10A72, 0x10A73, 0x10A74, 0x10A75, 0x10A76, 0x10A77, 0x10A78,
   1.589 +      0x10A79, 0x10A7A, 0x10A7B, 0x10A7C, 0x10A7D, 0x10A7E, 0x10A7F, 0x10B00,
   1.590 +      0x10B01, 0x10B02, 0x10B03, 0x10B04, 0x10B05, 0x10B06, 0x10B07, 0x10B08,
   1.591 +      0x10B09, 0x10B0A, 0x10B0B, 0x10B0C, 0x10B0D, 0x10B0E, 0x10B0F, 0x10B10,
   1.592 +      0x10B11, 0x10B12, 0x10B13, 0x10B14, 0x10B15, 0x10B16, 0x10B17, 0x10B18,
   1.593 +      0x10B19, 0x10B1A, 0x10B1B, 0x10B1C, 0x10B1D, 0x10B1E, 0x10B1F, 0x10B20,
   1.594 +      0x10B21, 0x10B22, 0x10B23, 0x10B24, 0x10B25, 0x10B26, 0x10B27, 0x10B28,
   1.595 +      0x10B29, 0x10B2A, 0x10B2B, 0x10B2C, 0x10B2D, 0x10B2E, 0x10B2F, 0x10B30,
   1.596 +      0x10B31, 0x10B32, 0x10B33, 0x10B34, 0x10B35, 0x10B40, 0x10B41, 0x10B42,
   1.597 +      0x10B43, 0x10B44, 0x10B45, 0x10B46, 0x10B47, 0x10B48, 0x10B49, 0x10B4A,
   1.598 +      0x10B4B, 0x10B4C, 0x10B4D, 0x10B4E, 0x10B4F, 0x10B50, 0x10B51, 0x10B52,
   1.599 +      0x10B53, 0x10B54, 0x10B55, 0x10B58, 0x10B59, 0x10B5A, 0x10B5B, 0x10B5C,
   1.600 +      0x10B5D, 0x10B5E, 0x10B5F, 0x10B60, 0x10B61, 0x10B62, 0x10B63, 0x10B64,
   1.601 +      0x10B65, 0x10B66, 0x10B67, 0x10B68, 0x10B69, 0x10B6A, 0x10B6B, 0x10B6C,
   1.602 +      0x10B6D, 0x10B6E, 0x10B6F, 0x10B70, 0x10B71, 0x10B72, 0x10B78, 0x10B79,
   1.603 +      0x10B7A, 0x10B7B, 0x10B7C, 0x10B7D, 0x10B7E, 0x10B7F, 0x10C00, 0x10C01,
   1.604 +      0x10C02, 0x10C03, 0x10C04, 0x10C05, 0x10C06, 0x10C07, 0x10C08, 0x10C09,
   1.605 +      0x10C0A, 0x10C0B, 0x10C0C, 0x10C0D, 0x10C0E, 0x10C0F, 0x10C10, 0x10C11,
   1.606 +      0x10C12, 0x10C13, 0x10C14, 0x10C15, 0x10C16, 0x10C17, 0x10C18, 0x10C19,
   1.607 +      0x10C1A, 0x10C1B, 0x10C1C, 0x10C1D, 0x10C1E, 0x10C1F, 0x10C20, 0x10C21,
   1.608 +      0x10C22, 0x10C23, 0x10C24, 0x10C25, 0x10C26, 0x10C27, 0x10C28, 0x10C29,
   1.609 +      0x10C2A, 0x10C2B, 0x10C2C, 0x10C2D, 0x10C2E, 0x10C2F, 0x10C30, 0x10C31,
   1.610 +      0x10C32, 0x10C33, 0x10C34, 0x10C35, 0x10C36, 0x10C37, 0x10C38, 0x10C39,
   1.611 +      0x10C3A, 0x10C3B, 0x10C3C, 0x10C3D, 0x10C3E, 0x10C3F, 0x10C40, 0x10C41,
   1.612 +      0x10C42, 0x10C43, 0x10C44, 0x10C45, 0x10C46, 0x10C47, 0x10C48, 0x1EE00,
   1.613 +      0x1EE01, 0x1EE02, 0x1EE03, 0x1EE05, 0x1EE06, 0x1EE07, 0x1EE08, 0x1EE09,
   1.614 +      0x1EE0A, 0x1EE0B, 0x1EE0C, 0x1EE0D, 0x1EE0E, 0x1EE0F, 0x1EE10, 0x1EE11,
   1.615 +      0x1EE12, 0x1EE13, 0x1EE14, 0x1EE15, 0x1EE16, 0x1EE17, 0x1EE18, 0x1EE19,
   1.616 +      0x1EE1A, 0x1EE1B, 0x1EE1C, 0x1EE1D, 0x1EE1E, 0x1EE1F, 0x1EE21, 0x1EE22,
   1.617 +      0x1EE24, 0x1EE27, 0x1EE29, 0x1EE2A, 0x1EE2B, 0x1EE2C, 0x1EE2D, 0x1EE2E,
   1.618 +      0x1EE2F, 0x1EE30, 0x1EE31, 0x1EE32, 0x1EE34, 0x1EE35, 0x1EE36, 0x1EE37,
   1.619 +      0x1EE39, 0x1EE3B, 0x1EE42, 0x1EE47, 0x1EE49, 0x1EE4B, 0x1EE4D, 0x1EE4E,
   1.620 +      0x1EE4F, 0x1EE51, 0x1EE52, 0x1EE54, 0x1EE57, 0x1EE59, 0x1EE5B, 0x1EE5D,
   1.621 +      0x1EE5F, 0x1EE61, 0x1EE62, 0x1EE64, 0x1EE67, 0x1EE68, 0x1EE69, 0x1EE6A,
   1.622 +      0x1EE6C, 0x1EE6D, 0x1EE6E, 0x1EE6F, 0x1EE70, 0x1EE71, 0x1EE72, 0x1EE74,
   1.623 +      0x1EE75, 0x1EE76, 0x1EE77, 0x1EE79, 0x1EE7A, 0x1EE7B, 0x1EE7C, 0x1EE7E,
   1.624 +      0x1EE80, 0x1EE81, 0x1EE82, 0x1EE83, 0x1EE84, 0x1EE85, 0x1EE86, 0x1EE87,
   1.625 +      0x1EE88, 0x1EE89, 0x1EE8B, 0x1EE8C, 0x1EE8D, 0x1EE8E, 0x1EE8F, 0x1EE90,
   1.626 +      0x1EE91, 0x1EE92, 0x1EE93, 0x1EE94, 0x1EE95, 0x1EE96, 0x1EE97, 0x1EE98,
   1.627 +      0x1EE99, 0x1EE9A, 0x1EE9B, 0x1EEA1, 0x1EEA2, 0x1EEA3, 0x1EEA5, 0x1EEA6,
   1.628 +      0x1EEA7, 0x1EEA8, 0x1EEA9, 0x1EEAB, 0x1EEAC, 0x1EEAD, 0x1EEAE, 0x1EEAF,
   1.629 +      0x1EEB0, 0x1EEB1, 0x1EEB2, 0x1EEB3, 0x1EEB4, 0x1EEB5, 0x1EEB6, 0x1EEB7,
   1.630 +      0x1EEB8, 0x1EEB9, 0x1EEBA, 0x1EEBB, 0x10FFFD];
   1.631 +
   1.632 +  function determineBidi(cueDiv) {
   1.633 +    var nodeStack = [],
   1.634 +        text = "",
   1.635 +        charCode;
   1.636 +
   1.637 +    if (!cueDiv || !cueDiv.childNodes) {
   1.638 +      return "ltr";
   1.639 +    }
   1.640 +
   1.641 +    function pushNodes(nodeStack, node) {
   1.642 +      for (var i = node.childNodes.length - 1; i >= 0; i--) {
   1.643 +        nodeStack.push(node.childNodes[i]);
   1.644 +      }
   1.645 +    }
   1.646 +
   1.647 +    function nextTextNode(nodeStack) {
   1.648 +      if (!nodeStack || !nodeStack.length) {
   1.649 +        return null;
   1.650 +      }
   1.651 +
   1.652 +      var node = nodeStack.pop(),
   1.653 +          text = node.textContent || node.innerText;
   1.654 +      if (text) {
   1.655 +        // TODO: This should match all unicode type B characters (paragraph
   1.656 +        // separator characters). See issue #115.
   1.657 +        var m = text.match(/^.*(\n|\r)/);
   1.658 +        if (m) {
   1.659 +          nodeStack.length = 0;
   1.660 +          return m[0];
   1.661 +        }
   1.662 +        return text;
   1.663 +      }
   1.664 +      if (node.tagName === "ruby") {
   1.665 +        return nextTextNode(nodeStack);
   1.666 +      }
   1.667 +      if (node.childNodes) {
   1.668 +        pushNodes(nodeStack, node);
   1.669 +        return nextTextNode(nodeStack);
   1.670 +      }
   1.671 +    }
   1.672 +
   1.673 +    pushNodes(nodeStack, cueDiv);
   1.674 +    while ((text = nextTextNode(nodeStack))) {
   1.675 +      for (var i = 0; i < text.length; i++) {
   1.676 +        charCode = text.charCodeAt(i);
   1.677 +        for (var j = 0; j < strongRTLChars.length; j++) {
   1.678 +          if (strongRTLChars[j] === charCode) {
   1.679 +            return "rtl";
   1.680 +          }
   1.681 +        }
   1.682 +      }
   1.683 +    }
   1.684 +    return "ltr";
   1.685 +  }
   1.686 +
   1.687 +  function computeLinePos(cue) {
   1.688 +    if (typeof cue.line === "number" &&
   1.689 +        (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) {
   1.690 +      return cue.line;
   1.691 +    }
   1.692 +    if (!cue.track || !cue.track.textTrackList ||
   1.693 +        !cue.track.textTrackList.mediaElement) {
   1.694 +      return -1;
   1.695 +    }
   1.696 +    var track = cue.track,
   1.697 +        trackList = track.textTrackList,
   1.698 +        count = 0;
   1.699 +    for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
   1.700 +      if (trackList[i].mode === "showing") {
   1.701 +        count++;
   1.702 +      }
   1.703 +    }
   1.704 +    return ++count * -1;
   1.705 +  }
   1.706 +
   1.707 +  function StyleBox() {
   1.708 +  }
   1.709 +
   1.710 +  // Apply styles to a div. If there is no div passed then it defaults to the
   1.711 +  // div on 'this'.
   1.712 +  StyleBox.prototype.applyStyles = function(styles, div) {
   1.713 +    div = div || this.div;
   1.714 +    for (var prop in styles) {
   1.715 +      if (styles.hasOwnProperty(prop)) {
   1.716 +        div.style[prop] = styles[prop];
   1.717 +      }
   1.718 +    }
   1.719 +  };
   1.720 +
   1.721 +  StyleBox.prototype.formatStyle = function(val, unit) {
   1.722 +    return val === 0 ? 0 : val + unit;
   1.723 +  };
   1.724 +
   1.725 +  // Constructs the computed display state of the cue (a div). Places the div
   1.726 +  // into the overlay which should be a block level element (usually a div).
   1.727 +  function CueStyleBox(window, cue, styleOptions) {
   1.728 +    StyleBox.call(this);
   1.729 +    this.cue = cue;
   1.730 +
   1.731 +    // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
   1.732 +    // have inline positioning and will function as the cue background box.
   1.733 +    this.cueDiv = parseContent(window, cue.text);
   1.734 +    this.applyStyles({
   1.735 +      color: "rgba(255, 255, 255, 1)",
   1.736 +      backgroundColor: "rgba(0, 0, 0, 0.8)",
   1.737 +      position: "relative",
   1.738 +      left: 0,
   1.739 +      right: 0,
   1.740 +      top: 0,
   1.741 +      bottom: 0,
   1.742 +      display: "inline"
   1.743 +    }, this.cueDiv);
   1.744 +
   1.745 +    // Create an absolutely positioned div that will be used to position the cue
   1.746 +    // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
   1.747 +    // mirrors of them except "middle" which is "center" in CSS.
   1.748 +    this.div = window.document.createElement("div");
   1.749 +    this.applyStyles({
   1.750 +      textAlign: cue.align === "middle" ? "center" : cue.align,
   1.751 +      direction: determineBidi(this.cueDiv),
   1.752 +      writingMode: cue.vertical === "" ? "horizontal-tb"
   1.753 +                                       : cue.vertical === "lr" ? "vertical-lr"
   1.754 +                                                               : "vertical-rl",
   1.755 +      unicodeBidi: "plaintext",
   1.756 +      font: styleOptions.font,
   1.757 +      whiteSpace: "pre-line",
   1.758 +      position: "absolute"
   1.759 +    });
   1.760 +
   1.761 +    this.div.appendChild(this.cueDiv);
   1.762 +
   1.763 +    // Calculate the distance from the reference edge of the viewport to the text
   1.764 +    // position of the cue box. The reference edge will be resolved later when
   1.765 +    // the box orientation styles are applied.
   1.766 +    var textPos = 0;
   1.767 +    switch (cue.positionAlign) {
   1.768 +    case "start":
   1.769 +      textPos = cue.position;
   1.770 +      break;
   1.771 +    case "middle":
   1.772 +      textPos = cue.position - (cue.size / 2);
   1.773 +      break;
   1.774 +    case "end":
   1.775 +      textPos = cue.position - cue.size;
   1.776 +      break;
   1.777 +    }
   1.778 +
   1.779 +    // Horizontal box orientation; textPos is the distance from the left edge of the
   1.780 +    // area to the left edge of the box and cue.size is the distance extending to
   1.781 +    // the right from there.
   1.782 +    if (cue.vertical === "") {
   1.783 +      this.applyStyles({
   1.784 +        left:  this.formatStyle(textPos, "%"),
   1.785 +        width: this.formatStyle(cue.size, "%"),
   1.786 +      });
   1.787 +    // Vertical box orientation; textPos is the distance from the top edge of the
   1.788 +    // area to the top edge of the box and cue.size is the height extending
   1.789 +    // downwards from there.
   1.790 +    } else {
   1.791 +      this.applyStyles({
   1.792 +        top: this.formatStyle(textPos, "%"),
   1.793 +        height: this.formatStyle(cue.size, "%")
   1.794 +      });
   1.795 +    }
   1.796 +
   1.797 +    this.move = function(box) {
   1.798 +      this.applyStyles({
   1.799 +        top: this.formatStyle(box.top, "px"),
   1.800 +        bottom: this.formatStyle(box.bottom, "px"),
   1.801 +        left: this.formatStyle(box.left, "px"),
   1.802 +        right: this.formatStyle(box.right, "px"),
   1.803 +        height: this.formatStyle(box.height, "px"),
   1.804 +        width: this.formatStyle(box.width, "px"),
   1.805 +      });
   1.806 +    };
   1.807 +  }
   1.808 +  CueStyleBox.prototype = _objCreate(StyleBox.prototype);
   1.809 +  CueStyleBox.prototype.constructor = CueStyleBox;
   1.810 +
   1.811 +  // Represents the co-ordinates of an Element in a way that we can easily
   1.812 +  // compute things with such as if it overlaps or intersects with another Element.
   1.813 +  // Can initialize it with either a StyleBox or another BoxPosition.
   1.814 +  function BoxPosition(obj) {
   1.815 +    // Either a BoxPosition was passed in and we need to copy it, or a StyleBox
   1.816 +    // was passed in and we need to copy the results of 'getBoundingClientRect'
   1.817 +    // as the object returned is readonly. All co-ordinate values are in reference
   1.818 +    // to the viewport origin (top left).
   1.819 +    var lh;
   1.820 +    if (obj.div) {
   1.821 +      var rects = (rects = obj.div.childNodes) && (rects = rects[0]) &&
   1.822 +                  rects.getClientRects && rects.getClientRects();
   1.823 +      obj = obj.div.getBoundingClientRect();
   1.824 +      // In certain cases the outter div will be slightly larger then the sum of
   1.825 +      // the inner div's lines. This could be due to bold text, etc, on some platforms.
   1.826 +      // In this case we should get the average line height and use that. This will
   1.827 +      // result in the desired behaviour.
   1.828 +      lh = rects ? Math.max((rects[0] && rects[0].height) || 0, obj.height / rects.length)
   1.829 +                 : 0;
   1.830 +    }
   1.831 +    this.left = obj.left;
   1.832 +    this.right = obj.right;
   1.833 +    this.top = obj.top;
   1.834 +    this.height = obj.height;
   1.835 +    this.bottom = obj.bottom;
   1.836 +    this.width = obj.width;
   1.837 +    this.lineHeight = lh !== undefined ? lh : obj.lineHeight;
   1.838 +  }
   1.839 +
   1.840 +  // Move the box along a particular axis. Optionally pass in an amount to move
   1.841 +  // the box. If no amount is passed then the default is the line height of the
   1.842 +  // box.
   1.843 +  BoxPosition.prototype.move = function(axis, toMove) {
   1.844 +    toMove = toMove !== undefined ? toMove : this.lineHeight;
   1.845 +    switch (axis) {
   1.846 +    case "+x":
   1.847 +      this.left += toMove;
   1.848 +      this.right += toMove;
   1.849 +      break;
   1.850 +    case "-x":
   1.851 +      this.left -= toMove;
   1.852 +      this.right -= toMove;
   1.853 +      break;
   1.854 +    case "+y":
   1.855 +      this.top += toMove;
   1.856 +      this.bottom += toMove;
   1.857 +      break;
   1.858 +    case "-y":
   1.859 +      this.top -= toMove;
   1.860 +      this.bottom -= toMove;
   1.861 +      break;
   1.862 +    }
   1.863 +  };
   1.864 +
   1.865 +  // Check if this box overlaps another box, b2.
   1.866 +  BoxPosition.prototype.overlaps = function(b2) {
   1.867 +    return this.left < b2.right &&
   1.868 +           this.right > b2.left &&
   1.869 +           this.top < b2.bottom &&
   1.870 +           this.bottom > b2.top;
   1.871 +  };
   1.872 +
   1.873 +  // Check if this box overlaps any other boxes in boxes.
   1.874 +  BoxPosition.prototype.overlapsAny = function(boxes) {
   1.875 +    for (var i = 0; i < boxes.length; i++) {
   1.876 +      if (this.overlaps(boxes[i])) {
   1.877 +        return true;
   1.878 +      }
   1.879 +    }
   1.880 +    return false;
   1.881 +  };
   1.882 +
   1.883 +  // Check if this box is within another box.
   1.884 +  BoxPosition.prototype.within = function(container) {
   1.885 +    return this.top >= container.top &&
   1.886 +           this.bottom <= container.bottom &&
   1.887 +           this.left >= container.left &&
   1.888 +           this.right <= container.right;
   1.889 +  };
   1.890 +
   1.891 +  // Check if this box is entirely within the container or it is overlapping
   1.892 +  // on the edge opposite of the axis direction passed. For example, if "+x" is
   1.893 +  // passed and the box is overlapping on the left edge of the container, then
   1.894 +  // return true.
   1.895 +  BoxPosition.prototype.overlapsOppositeAxis = function(container, axis) {
   1.896 +    switch (axis) {
   1.897 +    case "+x":
   1.898 +      return this.left < container.left;
   1.899 +    case "-x":
   1.900 +      return this.right > container.right;
   1.901 +    case "+y":
   1.902 +      return this.top < container.top;
   1.903 +    case "-y":
   1.904 +      return this.bottom > container.bottom;
   1.905 +    }
   1.906 +  };
   1.907 +
   1.908 +  // Find the percentage of the area that this box is overlapping with another
   1.909 +  // box.
   1.910 +  BoxPosition.prototype.intersectPercentage = function(b2) {
   1.911 +    var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),
   1.912 +        y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),
   1.913 +        intersectArea = x * y;
   1.914 +    return intersectArea / (this.height * this.width);
   1.915 +  };
   1.916 +
   1.917 +  // Convert the positions from this box to CSS compatible positions using
   1.918 +  // the reference container's positions. This has to be done because this
   1.919 +  // box's positions are in reference to the viewport origin, whereas, CSS
   1.920 +  // values are in referecne to their respective edges.
   1.921 +  BoxPosition.prototype.toCSSCompatValues = function(reference) {
   1.922 +    return {
   1.923 +      top: this.top - reference.top,
   1.924 +      bottom: reference.bottom - this.bottom,
   1.925 +      left: this.left - reference.left,
   1.926 +      right: reference.right - this.right,
   1.927 +      height: this.height,
   1.928 +      width: this.width
   1.929 +    };
   1.930 +  };
   1.931 +
   1.932 +  // Get an object that represents the box's position without anything extra.
   1.933 +  // Can pass a StyleBox, HTMLElement, or another BoxPositon.
   1.934 +  BoxPosition.getSimpleBoxPosition = function(obj) {
   1.935 +    obj = obj.div ? obj.div.getBoundingClientRect() :
   1.936 +                  obj.tagName ? obj.getBoundingClientRect() : obj;
   1.937 +    return {
   1.938 +      left: obj.left,
   1.939 +      right: obj.right,
   1.940 +      top: obj.top,
   1.941 +      height: obj.height,
   1.942 +      bottom: obj.bottom,
   1.943 +      width: obj.width
   1.944 +    };
   1.945 +  };
   1.946 +
   1.947 +  // Move a StyleBox to its specified, or next best, position. The containerBox
   1.948 +  // is the box that contains the StyleBox, such as a div. boxPositions are
   1.949 +  // a list of other boxes that the styleBox can't overlap with.
   1.950 +  function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {
   1.951 +
   1.952 +    // Find the best position for a cue box, b, on the video. The axis parameter
   1.953 +    // is a list of axis, the order of which, it will move the box along. For example:
   1.954 +    // Passing ["+x", "-x"] will move the box first along the x axis in the positive
   1.955 +    // direction. If it doesn't find a good position for it there it will then move
   1.956 +    // it along the x axis in the negative direction.
   1.957 +    function findBestPosition(b, axis) {
   1.958 +      var bestPosition,
   1.959 +          specifiedPosition = new BoxPosition(b),
   1.960 +          percentage = 1; // Highest possible so the first thing we get is better.
   1.961 +
   1.962 +      for (var i = 0; i < axis.length; i++) {
   1.963 +        while (b.overlapsOppositeAxis(containerBox, axis[i]) ||
   1.964 +               (b.within(containerBox) && b.overlapsAny(boxPositions))) {
   1.965 +          b.move(axis[i]);
   1.966 +        }
   1.967 +        // We found a spot where we aren't overlapping anything. This is our
   1.968 +        // best position.
   1.969 +        if (b.within(containerBox)) {
   1.970 +          return b;
   1.971 +        }
   1.972 +        var p = b.intersectPercentage(containerBox);
   1.973 +        // If we're outside the container box less then we were on our last try
   1.974 +        // then remember this position as the best position.
   1.975 +        if (percentage > p) {
   1.976 +          bestPosition = new BoxPosition(b);
   1.977 +          percentage = p;
   1.978 +        }
   1.979 +        // Reset the box position to the specified position.
   1.980 +        b = new BoxPosition(specifiedPosition);
   1.981 +      }
   1.982 +      return bestPosition || specifiedPosition;
   1.983 +    }
   1.984 +
   1.985 +    var boxPosition = new BoxPosition(styleBox),
   1.986 +        cue = styleBox.cue,
   1.987 +        linePos = computeLinePos(cue),
   1.988 +        axis = [];
   1.989 +
   1.990 +    // If we have a line number to align the cue to.
   1.991 +    if (cue.snapToLines) {
   1.992 +      var size;
   1.993 +      switch (cue.vertical) {
   1.994 +      case "":
   1.995 +        axis = [ "+y", "-y" ];
   1.996 +        size = "height";
   1.997 +        break;
   1.998 +      case "rl":
   1.999 +        axis = [ "+x", "-x" ];
  1.1000 +        size = "width";
  1.1001 +        break;
  1.1002 +      case "lr":
  1.1003 +        axis = [ "-x", "+x" ];
  1.1004 +        size = "width";
  1.1005 +        break;
  1.1006 +      }
  1.1007 +
  1.1008 +      var step = boxPosition.lineHeight,
  1.1009 +          position = step * Math.round(linePos),
  1.1010 +          maxPosition = containerBox[size] + step,
  1.1011 +          initialAxis = axis[0];
  1.1012 +
  1.1013 +      // If the specified intial position is greater then the max position then
  1.1014 +      // clamp the box to the amount of steps it would take for the box to
  1.1015 +      // reach the max position.
  1.1016 +      if (Math.abs(position) > maxPosition) {
  1.1017 +        position = position < 0 ? -1 : 1;
  1.1018 +        position *= Math.ceil(maxPosition / step) * step;
  1.1019 +      }
  1.1020 +
  1.1021 +      // If computed line position returns negative then line numbers are
  1.1022 +      // relative to the bottom of the video instead of the top. Therefore, we
  1.1023 +      // need to increase our initial position by the length or width of the
  1.1024 +      // video, depending on the writing direction, and reverse our axis directions.
  1.1025 +      if (linePos < 0) {
  1.1026 +        position += cue.vertical === "" ? containerBox.height : containerBox.width;
  1.1027 +        axis = axis.reverse();
  1.1028 +      }
  1.1029 +
  1.1030 +      // Move the box to the specified position. This may not be its best
  1.1031 +      // position.
  1.1032 +      boxPosition.move(initialAxis, position);
  1.1033 +
  1.1034 +    } else {
  1.1035 +      // If we have a percentage line value for the cue.
  1.1036 +      var calculatedPercentage = (boxPosition.lineHeight / containerBox.height) * 100;
  1.1037 +
  1.1038 +      switch (cue.lineAlign) {
  1.1039 +      case "middle":
  1.1040 +        linePos -= (calculatedPercentage / 2);
  1.1041 +        break;
  1.1042 +      case "end":
  1.1043 +        linePos -= calculatedPercentage;
  1.1044 +        break;
  1.1045 +      }
  1.1046 +
  1.1047 +      // Apply initial line position to the cue box.
  1.1048 +      switch (cue.vertical) {
  1.1049 +      case "":
  1.1050 +        styleBox.applyStyles({
  1.1051 +          top: styleBox.formatStyle(linePos, "%")
  1.1052 +        });
  1.1053 +        break;
  1.1054 +      case "rl":
  1.1055 +        styleBox.applyStyles({
  1.1056 +          left: styleBox.formatStyle(linePos, "%")
  1.1057 +        });
  1.1058 +        break;
  1.1059 +      case "lr":
  1.1060 +        styleBox.applyStyles({
  1.1061 +          right: styleBox.formatStyle(linePos, "%")
  1.1062 +        });
  1.1063 +        break;
  1.1064 +      }
  1.1065 +
  1.1066 +      axis = [ "+y", "-x", "+x", "-y" ];
  1.1067 +
  1.1068 +      // Get the box position again after we've applied the specified positioning
  1.1069 +      // to it.
  1.1070 +      boxPosition = new BoxPosition(styleBox);
  1.1071 +    }
  1.1072 +
  1.1073 +    var bestPosition = findBestPosition(boxPosition, axis);
  1.1074 +    styleBox.move(bestPosition.toCSSCompatValues(containerBox));
  1.1075 +  }
  1.1076 +
  1.1077 +  function WebVTT() {
  1.1078 +    // Nothing
  1.1079 +  }
  1.1080 +
  1.1081 +  // Helper to allow strings to be decoded instead of the default binary utf8 data.
  1.1082 +  WebVTT.StringDecoder = function() {
  1.1083 +    return {
  1.1084 +      decode: function(data) {
  1.1085 +        if (!data) {
  1.1086 +          return "";
  1.1087 +        }
  1.1088 +        if (typeof data !== "string") {
  1.1089 +          throw new Error("Error - expected string data.");
  1.1090 +        }
  1.1091 +        return decodeURIComponent(encodeURIComponent(data));
  1.1092 +      }
  1.1093 +    };
  1.1094 +  };
  1.1095 +
  1.1096 +  WebVTT.convertCueToDOMTree = function(window, cuetext) {
  1.1097 +    if (!window || !cuetext) {
  1.1098 +      return null;
  1.1099 +    }
  1.1100 +    return parseContent(window, cuetext);
  1.1101 +  };
  1.1102 +
  1.1103 +  var FONT_SIZE_PERCENT = 0.05;
  1.1104 +  var FONT_STYLE = "sans-serif";
  1.1105 +  var CUE_BACKGROUND_PADDING = "1.5%";
  1.1106 +
  1.1107 +  // Runs the processing model over the cues and regions passed to it.
  1.1108 +  // @param overlay A block level element (usually a div) that the computed cues
  1.1109 +  //                and regions will be placed into.
  1.1110 +  WebVTT.processCues = function(window, cues, overlay) {
  1.1111 +    if (!window || !cues || !overlay) {
  1.1112 +      return null;
  1.1113 +    }
  1.1114 +
  1.1115 +    // Remove all previous children.
  1.1116 +    while (overlay.firstChild) {
  1.1117 +      overlay.removeChild(overlay.firstChild);
  1.1118 +    }
  1.1119 +
  1.1120 +    var paddedOverlay = window.document.createElement("div");
  1.1121 +    paddedOverlay.style.position = "absolute";
  1.1122 +    paddedOverlay.style.left = "0";
  1.1123 +    paddedOverlay.style.right = "0";
  1.1124 +    paddedOverlay.style.top = "0";
  1.1125 +    paddedOverlay.style.bottom = "0";
  1.1126 +    paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
  1.1127 +    overlay.appendChild(paddedOverlay);
  1.1128 +
  1.1129 +    // Determine if we need to compute the display states of the cues. This could
  1.1130 +    // be the case if a cue's state has been changed since the last computation or
  1.1131 +    // if it has not been computed yet.
  1.1132 +    function shouldCompute(cues) {
  1.1133 +      for (var i = 0; i < cues.length; i++) {
  1.1134 +        if (cues[i].hasBeenReset || !cues[i].displayState) {
  1.1135 +          return true;
  1.1136 +        }
  1.1137 +      }
  1.1138 +      return false;
  1.1139 +    }
  1.1140 +
  1.1141 +    // We don't need to recompute the cues' display states. Just reuse them.
  1.1142 +    if (!shouldCompute(cues)) {
  1.1143 +      cues.forEach(function(cue) {
  1.1144 +        paddedOverlay.appendChild(cue.displayState);
  1.1145 +      });
  1.1146 +      return;
  1.1147 +    }
  1.1148 +
  1.1149 +    var boxPositions = [],
  1.1150 +        containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),
  1.1151 +        fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100;
  1.1152 +    var styleOptions = {
  1.1153 +      font: fontSize + "px " + FONT_STYLE
  1.1154 +    };
  1.1155 +
  1.1156 +    cues.forEach(function(cue) {
  1.1157 +      // Compute the intial position and styles of the cue div.
  1.1158 +      var styleBox = new CueStyleBox(window, cue, styleOptions);
  1.1159 +      paddedOverlay.appendChild(styleBox.div);
  1.1160 +
  1.1161 +      // Move the cue div to it's correct line position.
  1.1162 +      moveBoxToLinePosition(window, styleBox, containerBox, boxPositions);
  1.1163 +
  1.1164 +      // Remember the computed div so that we don't have to recompute it later
  1.1165 +      // if we don't have too.
  1.1166 +      cue.displayState = styleBox.div;
  1.1167 +
  1.1168 +      boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
  1.1169 +    });
  1.1170 +  };
  1.1171 +
  1.1172 +  WebVTT.Parser = function(window, decoder) {
  1.1173 +    this.window = window;
  1.1174 +    this.state = "INITIAL";
  1.1175 +    this.buffer = "";
  1.1176 +    this.decoder = decoder || new TextDecoder("utf8");
  1.1177 +    this.regionList = [];
  1.1178 +  };
  1.1179 +
  1.1180 +  WebVTT.Parser.prototype = {
  1.1181 +    // If the error is a ParsingError then report it to the consumer if
  1.1182 +    // possible. If it's not a ParsingError then throw it like normal.
  1.1183 +    reportOrThrowError: function(e) {
  1.1184 +      if (e instanceof ParsingError) {
  1.1185 +        this.onparsingerror && this.onparsingerror(e);
  1.1186 +      } else {
  1.1187 +        throw e;
  1.1188 +      }
  1.1189 +    },
  1.1190 +    parse: function (data) {
  1.1191 +      var self = this;
  1.1192 +
  1.1193 +      // If there is no data then we won't decode it, but will just try to parse
  1.1194 +      // whatever is in buffer already. This may occur in circumstances, for
  1.1195 +      // example when flush() is called.
  1.1196 +      if (data) {
  1.1197 +        // Try to decode the data that we received.
  1.1198 +        self.buffer += self.decoder.decode(data, {stream: true});
  1.1199 +      }
  1.1200 +
  1.1201 +      function collectNextLine() {
  1.1202 +        var buffer = self.buffer;
  1.1203 +        var pos = 0;
  1.1204 +        while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
  1.1205 +          ++pos;
  1.1206 +        }
  1.1207 +        var line = buffer.substr(0, pos);
  1.1208 +        // Advance the buffer early in case we fail below.
  1.1209 +        if (buffer[pos] === '\r') {
  1.1210 +          ++pos;
  1.1211 +        }
  1.1212 +        if (buffer[pos] === '\n') {
  1.1213 +          ++pos;
  1.1214 +        }
  1.1215 +        self.buffer = buffer.substr(pos);
  1.1216 +        return line;
  1.1217 +      }
  1.1218 +
  1.1219 +      // 3.4 WebVTT region and WebVTT region settings syntax
  1.1220 +      function parseRegion(input) {
  1.1221 +        var settings = new Settings();
  1.1222 +
  1.1223 +        parseOptions(input, function (k, v) {
  1.1224 +          switch (k) {
  1.1225 +          case "id":
  1.1226 +            settings.set(k, v);
  1.1227 +            break;
  1.1228 +          case "width":
  1.1229 +            settings.percent(k, v);
  1.1230 +            break;
  1.1231 +          case "lines":
  1.1232 +            settings.integer(k, v);
  1.1233 +            break;
  1.1234 +          case "regionanchor":
  1.1235 +          case "viewportanchor":
  1.1236 +            var xy = v.split(',');
  1.1237 +            if (xy.length !== 2) {
  1.1238 +              break;
  1.1239 +            }
  1.1240 +            // We have to make sure both x and y parse, so use a temporary
  1.1241 +            // settings object here.
  1.1242 +            var anchor = new Settings();
  1.1243 +            anchor.percent("x", xy[0]);
  1.1244 +            anchor.percent("y", xy[1]);
  1.1245 +            if (!anchor.has("x") || !anchor.has("y")) {
  1.1246 +              break;
  1.1247 +            }
  1.1248 +            settings.set(k + "X", anchor.get("x"));
  1.1249 +            settings.set(k + "Y", anchor.get("y"));
  1.1250 +            break;
  1.1251 +          case "scroll":
  1.1252 +            settings.alt(k, v, ["up"]);
  1.1253 +            break;
  1.1254 +          }
  1.1255 +        }, /=/, /\s/);
  1.1256 +
  1.1257 +        // Create the region, using default values for any values that were not
  1.1258 +        // specified.
  1.1259 +        if (settings.has("id")) {
  1.1260 +          var region = new self.window.VTTRegion();
  1.1261 +          region.width = settings.get("width", 100);
  1.1262 +          region.lines = settings.get("lines", 3);
  1.1263 +          region.regionAnchorX = settings.get("regionanchorX", 0);
  1.1264 +          region.regionAnchorY = settings.get("regionanchorY", 100);
  1.1265 +          region.viewportAnchorX = settings.get("viewportanchorX", 0);
  1.1266 +          region.viewportAnchorY = settings.get("viewportanchorY", 100);
  1.1267 +          region.scroll = settings.get("scroll", "");
  1.1268 +          // Register the region.
  1.1269 +          self.onregion && self.onregion(region);
  1.1270 +          // Remember the VTTRegion for later in case we parse any VTTCues that
  1.1271 +          // reference it.
  1.1272 +          self.regionList.push({
  1.1273 +            id: settings.get("id"),
  1.1274 +            region: region
  1.1275 +          });
  1.1276 +        }
  1.1277 +      }
  1.1278 +
  1.1279 +      // 3.2 WebVTT metadata header syntax
  1.1280 +      function parseHeader(input) {
  1.1281 +        parseOptions(input, function (k, v) {
  1.1282 +          switch (k) {
  1.1283 +          case "Region":
  1.1284 +            // 3.3 WebVTT region metadata header syntax
  1.1285 +            parseRegion(v);
  1.1286 +            break;
  1.1287 +          }
  1.1288 +        }, /:/);
  1.1289 +      }
  1.1290 +
  1.1291 +      // 5.1 WebVTT file parsing.
  1.1292 +      try {
  1.1293 +        var line;
  1.1294 +        if (self.state === "INITIAL") {
  1.1295 +          // We can't start parsing until we have the first line.
  1.1296 +          if (!/\r\n|\n/.test(self.buffer)) {
  1.1297 +            return this;
  1.1298 +          }
  1.1299 +
  1.1300 +          line = collectNextLine();
  1.1301 +
  1.1302 +          var m = line.match(/^WEBVTT([ \t].*)?$/);
  1.1303 +          if (!m || !m[0]) {
  1.1304 +            throw new ParsingError(ParsingError.Errors.BadSignature);
  1.1305 +          }
  1.1306 +
  1.1307 +          self.state = "HEADER";
  1.1308 +        }
  1.1309 +
  1.1310 +        while (self.buffer) {
  1.1311 +          // We can't parse a line until we have the full line.
  1.1312 +          if (!/\r\n|\n/.test(self.buffer)) {
  1.1313 +            return this;
  1.1314 +          }
  1.1315 +
  1.1316 +          line = collectNextLine();
  1.1317 +
  1.1318 +          switch (self.state) {
  1.1319 +          case "HEADER":
  1.1320 +            // 13-18 - Allow a header (metadata) under the WEBVTT line.
  1.1321 +            if (/:/.test(line)) {
  1.1322 +              parseHeader(line);
  1.1323 +            } else if (!line) {
  1.1324 +              // An empty line terminates the header and starts the body (cues).
  1.1325 +              self.state = "ID";
  1.1326 +            }
  1.1327 +            continue;
  1.1328 +          case "NOTE":
  1.1329 +            // Ignore NOTE blocks.
  1.1330 +            if (!line) {
  1.1331 +              self.state = "ID";
  1.1332 +            }
  1.1333 +            continue;
  1.1334 +          case "ID":
  1.1335 +            // Check for the start of NOTE blocks.
  1.1336 +            if (/^NOTE($|[ \t])/.test(line)) {
  1.1337 +              self.state = "NOTE";
  1.1338 +              break;
  1.1339 +            }
  1.1340 +            // 19-29 - Allow any number of line terminators, then initialize new cue values.
  1.1341 +            if (!line) {
  1.1342 +              continue;
  1.1343 +            }
  1.1344 +            self.cue = new self.window.VTTCue(0, 0, "");
  1.1345 +            self.state = "CUE";
  1.1346 +            // 30-39 - Check if self line contains an optional identifier or timing data.
  1.1347 +            if (line.indexOf("-->") === -1) {
  1.1348 +              self.cue.id = line;
  1.1349 +              continue;
  1.1350 +            }
  1.1351 +            // Process line as start of a cue.
  1.1352 +            /*falls through*/
  1.1353 +          case "CUE":
  1.1354 +            // 40 - Collect cue timings and settings.
  1.1355 +            try {
  1.1356 +              parseCue(line, self.cue, self.regionList);
  1.1357 +            } catch (e) {
  1.1358 +              self.reportOrThrowError(e);
  1.1359 +              // In case of an error ignore rest of the cue.
  1.1360 +              self.cue = null;
  1.1361 +              self.state = "BADCUE";
  1.1362 +              continue;
  1.1363 +            }
  1.1364 +            self.state = "CUETEXT";
  1.1365 +            continue;
  1.1366 +          case "CUETEXT":
  1.1367 +            // 41-53 - Collect the cue text, create a cue, and add it to the output.
  1.1368 +            if (!line) {
  1.1369 +              // We are done parsing self cue.
  1.1370 +              self.oncue && self.oncue(self.cue);
  1.1371 +              self.cue = null;
  1.1372 +              self.state = "ID";
  1.1373 +              continue;
  1.1374 +            }
  1.1375 +            if (self.cue.text) {
  1.1376 +              self.cue.text += "\n";
  1.1377 +            }
  1.1378 +            self.cue.text += line;
  1.1379 +            continue;
  1.1380 +          case "BADCUE": // BADCUE
  1.1381 +            // 54-62 - Collect and discard the remaining cue.
  1.1382 +            if (!line) {
  1.1383 +              self.state = "ID";
  1.1384 +            }
  1.1385 +            continue;
  1.1386 +          }
  1.1387 +        }
  1.1388 +      } catch (e) {
  1.1389 +        self.reportOrThrowError(e);
  1.1390 +
  1.1391 +        // If we are currently parsing a cue, report what we have.
  1.1392 +        if (self.state === "CUETEXT" && self.cue && self.oncue) {
  1.1393 +          self.oncue(self.cue);
  1.1394 +        }
  1.1395 +        self.cue = null;
  1.1396 +        // Enter BADWEBVTT state if header was not parsed correctly otherwise
  1.1397 +        // another exception occurred so enter BADCUE state.
  1.1398 +        self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
  1.1399 +      }
  1.1400 +      return this;
  1.1401 +    },
  1.1402 +    flush: function () {
  1.1403 +      var self = this;
  1.1404 +      try {
  1.1405 +        // Finish decoding the stream.
  1.1406 +        self.buffer += self.decoder.decode();
  1.1407 +        // Synthesize the end of the current cue or region.
  1.1408 +        if (self.cue || self.state === "HEADER") {
  1.1409 +          self.buffer += "\n\n";
  1.1410 +          self.parse();
  1.1411 +        }
  1.1412 +        // If we've flushed, parsed, and we're still on the INITIAL state then
  1.1413 +        // that means we don't have enough of the stream to parse the first
  1.1414 +        // line.
  1.1415 +        if (self.state === "INITIAL") {
  1.1416 +          throw new ParsingError(ParsingError.Errors.BadSignature);
  1.1417 +        }
  1.1418 +      } catch(e) {
  1.1419 +        self.reportOrThrowError(e);
  1.1420 +      }
  1.1421 +      self.onflush && self.onflush();
  1.1422 +      return this;
  1.1423 +    }
  1.1424 +  };
  1.1425 +
  1.1426 +  global.WebVTT = WebVTT;
  1.1427 +
  1.1428 +}(this));

mercurial