diff --git a/.gitignore b/.gitignore index 4117a54..9cc3ece 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,6 @@ /build/typescript/typescript.d.ts /build/typescript/*.js -/dist/ - -/lib/ !/lib/libjass.css /node_modules/ diff --git a/dist/CHANGELOG.md b/dist/CHANGELOG.md new file mode 100644 index 0000000..1fef252 --- /dev/null +++ b/dist/CHANGELOG.md @@ -0,0 +1,133 @@ +### v0.11.0 - 2016/01/24 +- BREAKING CHANGE - WebRenderer.resize(width, height) used to have a broken implementation of letterboxing to move the subs div right or down. Now it's WebRenderer.resize(width, height, left, top) and expects the caller to calculate letterboxing itself and supply left and top accordingly. DefaultRenderer does it using the video resolution and users of WebRenderer can do the same. +- BREAKING CHANGE - DefaultRenderer.resize() now ignores its parameters and always resizes to its video element's dimensions. It had already stopped resizing the video element when it was renamed from resizeVideo in v0.6.0, so it doesn't make sense to let it take a completely different width and height. +- BREAKING CHANGE - Removed fullscreen support in DefaultRenderer. It started out as a hack using max z-index and works on even fewer browsers now. It probably didn't work for you anyway so it should be no big loss. +- Implemented experimental support for \t +- Added RendererSettings.fallbackFonts to set the fallback fonts for all styles. Defaults to 'Arial, Helvetica, sans-serif, "Segoe UI Symbol"'. +- Better compatibility with loose ASS scripts - assume unnamed first section is Script Info, fall back to Default style for missing styles, recognize arbitrary-case property names, normalize asterisks in style names, etc. +- Various font size improvements - faster calculation, fix for incorrect size when line-height is overridden by site CSS, fix for incorrect scaled sizes for letterboxed subs, fix for incorrect metrics for web fonts, etc. The last one requires that all web fonts be specified in RendererSettings.fontMap to be rendered accurately. +- WebRenderer now supports using local() URLs in addition to url() in CSS font-face rules. +- Added RendererSettings.useAttachedFonts. If true, TTF fonts attached to the script will be used in addition to fonts specified in RendererSettings.fontMap. This setting is false by default, and should only be enabled on trusted fonts since it uses a very naive base64 and TTF parser to extract the font names from the attachment. It also requires ES6 typed arrays - ArrayBuffer, DataView, Uint8Array, etc. in the environment. +- Various pre-render, SVG filter and DOM perf improvements. +- Fixed \fscx and \fscy to not scale shadows. +- Fixed \fscx and \fscy to have optional values. +- Fixed \fs+ and \fs- to have required values. +- Fixed \r to use the target style's alpha values instead of 1. +- Fixed \fad subs to not flash after the fade-out ends with low-resolution clocks. +- Fixed outlines to not be darker than they should be. +- Fixed styles to not ignore the ScaleX and ScaleY properties in the script. +- Fixed lack of sufficient space between normal and italic text. +- Fixed SVG filters to interpolate in sRGB space instead of RGB. +- Fixed ASS parser to complain if a script doesn't have a Script Info section at all. +- The promise returned from ASS.from*() is now properly rejected due to errors from loading the script, instead of just remaining unresolved forever. +- Fixed SRT parser to swallow UTF-8 BOM just like the ASS parser. +- Fixed all clocks to suppress redundant ticks if the current timestamp hasn't change from the last tick. +- Fixed {AutoClock, VideoClock}.{setEnabled, toggle} methods to actually enable / disable the high-resolution timer. + + +### v0.10.0 - 2015/05/05 +- Implemented libjass.renderers.AutoClock, a clock that automatically ticks and generates clock events according to the state of an external driver. +- Implemented \k +- libjass.{Set, Map, Promise} can now be set to null to force the use of the polyfills, even if it defaulted to a runtime-provided implementation. +- Added ASS.fromReadableStream(), a function that can be used to parse ASS from a readable stream such as the response of window.fetch(). +- ASS.fromUrl() now tries to use window.fetch() if available instead of XMLHttpRequest. +- Fixed constant pausing and playing on Firefox triggered by how slowly it updates video.currentTime (wasn't noticeable but still undesirable). +- Fixed a dialogue's animation state not getting updated while seeking if the start and end times of the seek were within its start and end times. +- Fixed wrapping mode 1 (end-of-line wrapping) to actually wrap. +- Fixed parser to parse the time components of karaoke tags as centiseconds instead of seconds. +- Fixed parser to swallow leading BOM, if any. +- Fixed errors reported by webworker API were empty objects without message and stack properties. + + +### v0.9.0 - 2014/11/27 +- BREAKING CHANGE - ASS.fromString() now returns a Promise of an ASS object, not an ASS object directly. The synchronous ASS parser used by ASS.fromString() is no more. +- BACKWARD-COMPATIBLE CHANGE - WebRenderer constructor parameters order has changed from (ass, clock, settings, libjassSubsWrapper) to (ass, clock, libjassSubsWrapper, settings). The constructor will detect the old order and reorder accordingly. +- Added ASS.fromStream and ASS.fromXhr that read a stream and an XMLHttpRequest object's response respectively and return (a Promise of) an ASS object. Both of these parse the script asynchronously. +- Added RendererSettings.enableSvg that can be used to toggle the use of SVG filter effects for outlines and blur. +- libjass.js now has an AMD wrapper so that it can be used with RequireJS, etc. +- Settings parameter is now optional for WebRenderer and DefaultRenderer. +- Added support for clock rates apart from 1 to clocks and renderers. +- Added a parameter to libjass.createWorker to specify the path to libjass.js that will run in the worker. +- Fixed Style and Dialogue constructors not setting defaults for missing properties. +- Fixed color and alpha parser to support more formats. +- Fixed SRT parser to replace all HTML tags it finds, instead of just the first one. +- Fixed font size calculation to use the vertical scale instead of horizontal. +- Fixed line-height on newlines. +- Fixed missing perspective on X and Y rotations. + + +### v0.8.0 - 2014/08/16 +- Added web worker support. libjass.parse can now be offloaded to a web worker. +- Implemented \fs+ and \fs- +- Added ASS.addEvent() to add dialogue lines to an ASS object. +- Renamed ClockEvent.TimeUpdate to ClockEvent.Tick, and added ClockEvent.Stop +- Clock.enable() and .disable() now return a boolean to indicate whether the function had any effect. +- Added Clock.setEnabled() to force the enabled-state to the given value. +- Renamed ManualClock.timeUpdate() to ManualClock.tick() +- Moved WebRenderer.enable(), .disable() and .enabled to NullRenderer +- Fixed not being able to parse tags with default values. +- Fixed font preloader downloading the same font multiple times because it didn't filter for duplicates. +- Fixed min-width value not taking separate left and right margins into account. +- Fixed absolutely positioned subs were always left-aligned even if they had an alignment tag. +- Fixed blur and outlines getting truncated. + + +### v0.7.0 - 2014/05/15 +- Implemented \be +- Split a new renderer, WebRenderer, off DefaultRenderer that doesn't rely on a video element. +- All renderers now require a Clock to generate time events. VideoClock is a Clock backed by a video element, while ManualClock is a clock that can be used to generate arbitrary time events. + + +### v0.6.0 - 2014/03/24 +- All script properties and style properties are now parsed and stored in the ASS and Style objects. +- Basic SRT support, by passing in a libjass.Format argument to ASS.fromString() +- \clip and \iclip now have their drawing instructions parsed as an array of libjass.parts.drawing.Instruction's instead of just a string. +- Added DefaultRenderer.enable(), DefaultRenderer.disable() and DefaultRenderer.toggle() to change whether the renderer is displaying subtitles or not. +- DefaultRenderer.resizeVideo is now called DefaultRenderer.resize. Now it only resizes the subtitle wrapper div, not the video element. +- Replaced the 41ms setInterval-bsed timer with a requestAnimationFrame-based timer to reduce load on minimized or hidden browser tabs. +- DefaultRenderer now renders dialogues in the correct order according to the script. +- Fixed incorrect font sizes. +- Replaced jake with gulp. + + +### v0.5.0 - 2014/01/26 +- Removed preLoadFonts renderer setting. It was redundant with the actual fontMap setting since the presence or absence of that setting is enough to signal whether the user wants to preload fonts or not. +- Multiple renderers can now be used on the same page without conflicting with each other. +- Implemented \shad, \xshad, \yshad +- Fixed ASS draw scale being used incorrectly. +- ASS.resolutionX and ASS.resolutionY are now properties of ASS.properties, a ScriptProperties object. + + +### v0.4.0 - 2013/12/27 +- All parts moved from the libjass.tags namespace to the libjass.parts namespace. +- Replaced PEG.js parser with a hand-written one. This allows for parsing lines that are strictly invalid grammar but are parsed successfully by VSFilter or libass. +- All ASS tags are now supported by the parser. +- Removed the useHighResolutionTimer setting for DefaultRenderer. DefaultRenderer always uses the 41ms timer now. +- Implemented \move +- Implemented ASS draw +- Fixed subs overflowing the video dimensions still being visible. +- SVG filters are now used for outlines and blur. +- Delay parsing of dialogue lines till they need to be pre-rendered. As a side-effect, all fonts in the font map are preloaded now, not just the ones used in the current script. + + +### v0.3.0 - 2013/10/28 +- Moved libjass.DefaultRenderer to libjass.renderers.DefaultRenderer +- Added libjass.renderers.NullRenderer, a renderer that doesn't render anything. +- DefaultRenderer's fontMap setting is now a Map instead of an Object. It now supports more than one URL for each font name. +- DefaultRenderer now generates the subtitle wrapper div itself. +- DefaultRenderer now takes video letterboxing into account when resizing the subtitles. +- DefaultRenderer has a new setting useHighResolutionTimer that makes it use a 41ms timer instead of video.timeUpdate's 250ms timer. +- div IDs and CSS class names are now prefixed with "libjass-" to avoid collisions with other elements on the page. +- All numeric CSS property values are now truncated to three decimal places. +- Added ```jake watch``` that rebuilds and runs tests on changes to the source. +- Added ```jake doc``` that builds API documentation. +- Added Travis CI build. + + +### v0.2.0 - 2013/09/11 +- Added libjass.DefaultRenderer, a class that handles initializing the layer div's, preloading fonts, and drawing Dialogues based on the current video time. +- libjass.js can now be loaded in node. Only the parser can be used. +- Tests can now be run with ```jake test``` or ```npm test``` using Mocha. + +### v0.1.0 - 2013/08/29 +- First npm release. diff --git a/dist/LICENSE b/dist/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/dist/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/dist/README.md b/dist/README.md new file mode 100644 index 0000000..385b82b --- /dev/null +++ b/dist/README.md @@ -0,0 +1,171 @@ +[![Build Status](https://travis-ci.org/Arnavion/libjass.png?branch=master)](https://travis-ci.org/Arnavion/libjass) + +libjass is a JavaScript library written in TypeScript to render ASS subs in the browser. [Check out the demo.](http://arnavion.github.io/libjass/demo/index.xhtml) + + +### What's special about libjass? + +* libjass requires no tweaks to the ASS file from the original video. + +* libjass uses the browser's native CSS engine by converting the components of each line in the ASS script into a series of styled <div> and <span> elements. This allows all the layout and rendering to be handled by the browser instead of requiring complex and costly drawing and animation code. For example, libjass uses CSS3 animations to simulate tags such as \fad. While a canvas-drawing library would have to re-draw such a subtitle on the canvas for every frame of the video, libjass only renders it once and lets the browser render the fade effect. + +As a result, libjass is able to render subtitles with very low CPU usage. The downside to libjass's aproach is that it is hard (and potentially impossible) to map all effects possible in ASS (using \t, ASS draw) etc. into DOM elements. As of now, the subset of tags supported by libjass has no such problems. + + +### I want to use libjass for my website. What do I need to do? + +You can install the latest release of libjass + +* using npm with `npm install libjass` and load with `var libjass = require("libjass");` +* using bower with `bower install https://github.com/Arnavion/libjass/releases/download//libjass.zip` +* using jspm with `jspm install github:Arnavion/libjass` and load with `import libjass from "Arnavion/libjass";` + +Inside the package, you will find libjass.js and libjass.css, which you need to load on your website with your video. + +Alternatively, you can build libjass from source by cloning this repository and running `npm install`. This will install the dependencies and run the build. libjass.js and libjass.css will be found in the lib/ directory. + +Only libjass.js and libjass.css are needed to use libjass on your website. The other files are only used during the build process and you don't need to deploy them to your website. + + +### What are all these files? + +* The src/ directory contains the source of libjass. They are TypeScript files and get compiled into JavaScript for the browser using the TypeScript compiler. + +* build.js is the build script. The build command will use this script to build libjass.js. The build/ directory contains other files used for the build. + +* The tests/ directory contains unit and functional tests. + +* The lib/ directory contains libjass.js and libjass.css. You will need to deploy these to your website. + + +### How do I use libjass? + +The API documentation is linked in the Links section below. Here's an overview: + +* The [ASS.fromUrl()](http://arnavion.github.io/libjass/api.xhtml#libjass.ASS.fromUrl) function takes in a URL to an ASS script and returns a promise that resolves to an [ASS](http://arnavion.github.io/libjass/api.xhtml#libjass.ASS) object. This ASS object represents the script properties, the line styles and dialogue lines in it. Alternatively, you can use [ASS.fromString()](http://arnavion.github.io/libjass/api.xhtml#libjass.ASS.fromString) to convert a string of the script contents into an ASS object. + +* Next, you initialize a renderer to render the subtitles. libjass ships with an easy-to-use renderer, the [DefaultRenderer](http://arnavion.github.io/libjass/api.xhtml#libjass.renderers.DefaultRenderer). It uses information from the ASS object to build up a series of div elements around the video tag. There is a wrapper (.libjass-subs) containing div's corresponding to the layers in the ASS script, and each layer has div's corresponding to the 9 alignment directions. libjass.css contains styles for these div's to render them at the correct location. + +* The renderer uses [window.requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame) as a source of timer ticks. In each tick, it determines the set of dialogues to be shown at the current video time, renders each of them as a div, and appendChild's the div into the appropriate layer+alignment div. + +* The renderer can be told to dynamically change the size of the subtitles based on user input by calling [WebRenderer.resize()](http://arnavion.github.io/libjass/api.xhtml#libjass.renderers.WebRenderer.resize) + +* Lastly, the renderer contains an implementation of preloading fonts before playing the video. It uses a map of font names to URLs - this map can be conveniently created from a CSS file containing @font-face rules using [RendererSettings.makeFontMapFromStyleElement()](http://arnavion.github.io/libjass/api.xhtml#libjass.renderers.RendererSettings.makeFontMapFromStyleElement) + +* For an example of using libjass, check out [the demo.](http://arnavion.github.io/libjass/demo/index.xhtml) It has comments explaining basic usage and pointers to some advanced usage. + + +### What browser and JavaScript features does libjass need? + +* libjass uses some ES5 features like getters and setters (via Object.defineProperty), and assumptions like the behavior of parseInt with leading zeros. It cannot be used with an ES3 environment. + +* libjass will use ES6 Set, Map and Promise if they're available on the global object. If they're not present, it will use its own minimal internal implementations. If you have implementations of these that you would like libjass to use but don't want to register them on the global object, you can provide them to libjass specifically by setting the [libjass.Set](http://arnavion.github.io/libjass/api.xhtml#libjass.Set), [libjass.Map](http://arnavion.github.io/libjass/api.xhtml#libjass.Map) and [libjass.Promise](http://arnavion.github.io/libjass/api.xhtml#libjass.Promise) properties. + +* [AutoClock](http://arnavion.github.io/libjass/api.xhtml#libjass.renderers.AutoClock) and [VideoClock](http://arnavion.github.io/libjass/api.xhtml#libjass.renderers.VideoClock) use [window.requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) to generate clock ticks. + +* [WebRenderer](http://arnavion.github.io/libjass/api.xhtml#libjass.renderers.WebRenderer) and [DefaultRenderer](http://arnavion.github.io/libjass/api.xhtml#libjass.renderers.DefaultRenderer) use [SVG filter effects for HTML](http://caniuse.com/#feat=svg-html) to render outlines and blur. This feature is not available on all browsers, so you can tell them to fall back to more widely available CSS methods by setting the [RendererSettings.enableSvg](http://arnavion.github.io/libjass/api.xhtml#libjass.renderers.RendererSettings.enableSvg) property to false. + +* WebRenderer and DefaultRenderer use [CSS3 animations](http://caniuse.com/#feat=css-animation) for effects like \mov and \fad. + +* Using fonts attached to the script requires [ES6 typed arrays](http://caniuse.com/#feat=typedarrays) (ArrayBuffer, DataView, Uint8Array, etc). + + +### Can I use libjass in node? + +libjass's parser works in node. Entire scripts can be parsed via [ASS.fromString()](http://arnavion.github.io/libjass/api.xhtml#libjass.ASS.fromString) + +```javascript +> var libjass = require("libjass") +undefined +> var ass; libjass.ASS.fromString(fs.readFileSync("mysubs.ass", "utf8")).then(function (result) { ass = result; }) +{} +> ass.properties.resolutionX +1280 +> ass.dialogues.length +9 +> ass.dialogues[0].toString() +'#0 [646.460-652.130] {\\fad(200,0)}Sapien rhoncus, suscipit posuere in nunc pellentesque' +> var parts = ass.dialogues[0].parts +undefined +> parts.length +2 +> parts[0] instanceof libjass.parts.Fade +true +> parts[0].toString() +'Fade { start: 0.2, end: 0 }' +``` + +[libjass.parser.parse](http://arnavion.github.io/libjass/api.xhtml#libjass.parser.parse) parses the first parameter using the second parameter as the rule name. For example, the [dialogueParts](http://arnavion.github.io/libjass/api.xhtml#./parser/parse.ParserRun.parse_dialogueParts) rule can be used to get an array of [libjass.parts](http://arnavion.github.io/libjass/api.xhtml#libjass.parts) objects that represent the parts of an ASS dialogue line. + +```javascript +> var parts = libjass.parser.parse("{\\an8}Are {\\i1}you{\\i0} the one who stole the clock?!", "dialogueParts") +undefined +> parts.join(" ") +'Alignment { value: 8 } Text { value: Are } Italic { value: true } Text { value: you } Italic { value: false } Text { value: the one who stole the clock?! }' +> parts.length +6 +> parts[0].toString() +'Alignment { value: 8 }' +> parts[0] instanceof libjass.parts.Alignment +true +> parts[0].value +8 +``` + +The rule names are derived from the methods on the [ParserRun class](http://arnavion.github.io/libjass/api.xhtml#./parser/parse.ParserRun). + +See the tests, particularly the ones in tests/unit/miscellaneous.js, for examples. + + +### Can I contribute? + +Yes! Feature requests, suggestions, bug reports and pull requests are welcome! I'm especially looking for details and edge-cases of the ASS syntax that libjass doesn't support. + +You can also join the IRC channel in the links section below and ask any questions. + + +### Supported features + +* Styles: Italic, Bold, Underline, StrikeOut, FontName, FontSize, ScaleX, ScaleY, Spacing, PrimaryColor, OutlineColor, BackColor, Outline, Shadow, Alignment, MarginL, MarginR, MarginV +* Tags: \i, \b, \u, \s, \bord, \xbord, \ybord, \shad, \xshad, \yshad, \be, \blur, \fn, \fs, \fscx, \fscy, \fsp, \frx, \fry, \frz, \fr, \fax, \fay, \c, \1c, \3c, \4c, \alpha, \1a, \3a, \4a, \an, \a, \k, \q, \r, \pos, \move, \fad, \fade, \t (experimental), \p +* Custom fonts, using CSS web fonts. + + +### Known issues + +* Unsupported tags: \fe, \2c, \2a, \K, \kf, \ko, \org, \clip, \iclip +* \an4, \an5, \an6 aren't positioned correctly. +* Smart line wrapping is not supported. Such lines are rendered as [wrapping style 1 (end-of-line wrapping).](http://docs.aegisub.org/3.0/ASS_Tags/#wrapstyle) +* Lines with multiple rotations aren't rotated the same as VSFilter or libass. See [#14](https://github.com/Arnavion/libjass/issues/14) +- Desktop renderers include borders when calculating space between adjacent lines. libjass doesn't. + + +### Links + +* [GitHub](https://github.com/Arnavion/libjass/) +* IRC channel - #libjass on irc.rizon.net +* [API documentation](http://arnavion.github.io/libjass/api.xhtml) +* [Aegisub's documentation on ASS](http://docs.aegisub.org/3.0/ASS_Tags/) + + +### License + +``` +libjass + +https://github.com/Arnavion/libjass + +Copyright 2013 Arnav Singh + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/dist/libjass.css b/dist/libjass.css new file mode 100644 index 0000000..b94d7e5 --- /dev/null +++ b/dist/libjass.css @@ -0,0 +1,104 @@ +/** + * libjass + * + * https://github.com/Arnavion/libjass + * + * Copyright 2013 Arnav Singh + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.libjass-wrapper { + position: relative; + overflow: hidden; +} + +.libjass-subs { + position: absolute; + overflow: hidden; +} + +.libjass-subs, .libjass-subs * { + pointer-events: none; + -webkit-animation-fill-mode: both !important; + animation-fill-mode: both !important; +} + +.libjass-subs.paused * { + -webkit-animation-play-state: paused !important; + animation-play-state: paused !important; +} + +.libjass-subs .an { + position: absolute; +} + +.libjass-subs .an1, .libjass-subs .an2, .libjass-subs .an3 { + bottom: 0; +} + +.libjass-subs .an4, .libjass-subs .an5, .libjass-subs .an6 { + display: table; + width: 100%; + height: 100%; +} + +.libjass-subs .an4 > *, .libjass-subs .an5 > *, .libjass-subs .an6 > * { + display: table-cell; + vertical-align: middle; +} + +.libjass-subs .an7, .libjass-subs .an8, .libjass-subs .an9 { + top: 0; +} + +.libjass-subs .an1, .libjass-subs .an4, .libjass-subs .an7 { + text-align: left; +} + +.libjass-subs .an2, .libjass-subs .an5, .libjass-subs .an8 { + text-align: center; +} + +.libjass-subs .an3, .libjass-subs .an6, .libjass-subs .an9 { + text-align: right; +} + +.libjass-subs { + line-height: 0; +} + +/* Filter wrapper span */ +.libjass-subs div[data-dialogue-id] > span { + -webkit-perspective-origin: center; + -webkit-perspective: 400px; + perspective-origin: center; + perspective: 400px; +} + +.libjass-font-measure { + position: absolute; + visibility: hidden; + border: 0; + margin: 0; + padding: 0; + line-height: normal; +} + +.libjass-filters { + display: block; +} + +.libjass-filters * { + color-interpolation-filters: sRGB; +} diff --git a/dist/libjass.js b/dist/libjass.js new file mode 100644 index 0000000..329f0ce --- /dev/null +++ b/dist/libjass.js @@ -0,0 +1,9983 @@ +/** + * libjass + * + * https://github.com/Arnavion/libjass + * + * Copyright 2013 Arnav Singh + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @license + */ +(function (root, factory) { + var global = this; + if (typeof define === "function" && define.amd) { + define([], function () { + return factory(global); + }); + } else if (typeof exports === "object" && typeof module === "object") { + module.exports = factory(global); + } else if (typeof exports === "object") { + exports.libjass = factory(global); + } else { + root.libjass = factory(global); + } +})(this, function (global) { + "use strict"; + var registeredModules = Object.create(null); + var installedModules = Object.create(null); + function def(moduleId, deps, body) { + installedModules[moduleId] = { + deps: deps, + body: body + }; + } + function req(moduleId) { + if (moduleId in registeredModules) { + return registeredModules[moduleId]; + } + var exports = registeredModules[moduleId] = Object.create(null); + var deps = installedModules[moduleId].deps.map(req); + deps.push(exports); + installedModules[moduleId].body.apply(null, deps); + return exports; + } + var __decorate = function (decorators, target, key, desc) { + var c = arguments.length; + var r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc; + var d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __extends = function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + def("index", [ "settings", "settings", "utility/set", "utility/set", "utility/map", "utility/map", "utility/promise", "utility/promise", "webworker/index", "parts/index", "parser/index", "renderers/index", "serialization", "types/ass", "types/attachment", "types/dialogue", "types/script-properties", "types/style", "types/misc", "version" ], function (settings, settings_11, set, set_2, map, map_15, promise, promise_8, webworker, parts, parser, renderers, serialization_8, ass_2, attachment_3, dialogue_3, script_properties_2, style_3, misc_9, version_1, exports) { + exports.debugMode = settings_11.debugMode; + exports.verboseMode = settings_11.verboseMode; + exports.Set = set_2.Set; + exports.Map = map_15.Map; + exports.Promise = promise_8.Promise; + exports.DeferredPromise = promise_8.DeferredPromise; + exports.webworker = webworker; + exports.parts = parts; + exports.parser = parser; + exports.renderers = renderers; + exports.serialize = serialization_8.serialize; + exports.deserialize = serialization_8.deserialize; + exports.ASS = ass_2.ASS; + exports.Attachment = attachment_3.Attachment; + exports.AttachmentType = attachment_3.AttachmentType; + exports.Dialogue = dialogue_3.Dialogue; + exports.ScriptProperties = script_properties_2.ScriptProperties; + exports.Style = style_3.Style; + exports.BorderStyle = misc_9.BorderStyle; + exports.Format = misc_9.Format; + exports.WrappingStyle = misc_9.WrappingStyle; + exports.version = version_1.version; + /** + * Configures libjass with the given properties. + * + * @param {!*} newConfig + * @param {?boolean} newConfig["debugMode"] When true, libjass logs some debug messages. + * @param {?boolean} newConfig["verboseMode"] When true, libjass logs some more debug messages. This setting is independent of {@link libjass.debugMode} + * @param {?function(new:Set, !Array.=)} newConfig["Set"] Sets the Set implementation used by libjass to the provided one. If null, {@link ./utility/set.SimpleSet} is used. + * @param {?function(new:Map, !Array.>=)} newConfig["Map"] Sets the Map implementation used by libjass to the provided one. If null, {@link ./utility/map.SimpleMap} is used. + * @param {?function(new:Promise)} newConfig["Promise"] Sets the Promise implementation used by libjass to the provided one. If null, {@link ./utility/promise.SimplePromise} is used. + * + * @memberOf libjass + */ + function configure(newConfig) { + if (typeof newConfig.debugMode === "boolean") { + settings.setDebugMode(newConfig.debugMode); + } + if (typeof newConfig.verboseMode === "boolean") { + settings.setVerboseMode(newConfig.verboseMode); + } + if (typeof newConfig.Set === "function" || newConfig.Set === null) { + set.setImplementation(newConfig.Set); + } + if (typeof newConfig.Map === "function" || newConfig.Map === null) { + map.setImplementation(newConfig.Map); + } + if (typeof newConfig.Promise === "function" || newConfig.Promise === null) { + promise.setImplementation(newConfig.Promise); + } + } + exports.configure = configure; + // Getters below are to work around https://github.com/Microsoft/TypeScript/issues/6366 + Object.defineProperties(exports, { + debugMode: { + get: function () { + return settings.debugMode; + }, + set: function (value) { + console.warn("Setter `libjass.debugMode = value` has been deprecated. Use `libjass.configure({ debugMode: value })` instead."); + settings.setDebugMode(value); + } + }, + verboseMode: { + get: function () { + return settings.verboseMode; + }, + set: function (value) { + console.warn("Setter `libjass.verboseMode = value` has been deprecated. Use `libjass.configure({ verboseMode: value })` instead."); + settings.setVerboseMode(value); + } + }, + Set: { + get: function () { + return set.Set; + }, + set: function (value) { + console.warn("Setter `libjass.Set = value` has been deprecated. Use `libjass.configure({ Set: value })` instead."); + set.setImplementation(value); + } + }, + Map: { + get: function () { + return map.Map; + }, + set: function (value) { + console.warn("Setter `libjass.Map = value` has been deprecated. Use `libjass.configure({ Map: value })` instead."); + map.setImplementation(value); + } + }, + Promise: { + get: function () { + return promise.Promise; + }, + set: function (value) { + console.warn("Setter `libjass.Promise = value` has been deprecated. Use `libjass.configure({ Promise: value })` instead."); + promise.setImplementation(value); + } + } + }); + }); + def("version", [], function (exports) { + exports.version = [ "0.12.1", 0, 12, 1 ]; + }); + def("renderers/index", [ "renderers/clocks/base", "renderers/clocks/auto", "renderers/clocks/manual", "renderers/clocks/video", "renderers/default", "renderers/null", "renderers/web/renderer", "renderers/settings" ], function (base_4, auto_2, manual_2, video_2, default_1, null_2, renderer_2, settings_10, exports) { + exports.ClockEvent = base_4.ClockEvent; + exports.EventSource = base_4.EventSource; + exports.AutoClock = auto_2.AutoClock; + exports.ManualClock = manual_2.ManualClock; + exports.VideoClock = video_2.VideoClock; + exports.DefaultRenderer = default_1.DefaultRenderer; + exports.NullRenderer = null_2.NullRenderer; + exports.WebRenderer = renderer_2.WebRenderer; + exports.RendererSettings = settings_10.RendererSettings; + }); + def("renderers/default", [ "renderers/clocks/video", "renderers/web/renderer" ], function (video_1, renderer_1, exports) { + /** + * A default renderer implementation. + * + * @param {!HTMLVideoElement} video + * @param {!libjass.ASS} ass + * @param {libjass.renderers.RendererSettings} settings + * + * @constructor + * @extends {libjass.renderers.WebRenderer} + * @memberOf libjass.renderers + */ + var DefaultRenderer = function (_super) { + __extends(DefaultRenderer, _super); + function DefaultRenderer(video, ass, settings) { + _super.call(this, ass, new video_1.VideoClock(video), document.createElement("div"), settings); + this._video = video; + this._video.parentElement.replaceChild(this.libjassSubsWrapper, this._video); + this.libjassSubsWrapper.insertBefore(this._video, this.libjassSubsWrapper.firstElementChild); + } + /** + * Resize the subtitles to the dimensions of the video element. + * + * This method accounts for letterboxing if the video element's size is not the same ratio as the video resolution. + */ + DefaultRenderer.prototype.resize = function () { + // Handle letterboxing around the video. If the width or height are greater than the video can be, then consider that dead space. + var videoWidth = this._video.videoWidth; + var videoHeight = this._video.videoHeight; + var videoOffsetWidth = this._video.offsetWidth; + var videoOffsetHeight = this._video.offsetHeight; + var ratio = Math.min(videoOffsetWidth / videoWidth, videoOffsetHeight / videoHeight); + var subsWrapperWidth = videoWidth * ratio; + var subsWrapperHeight = videoHeight * ratio; + var subsWrapperLeft = (videoOffsetWidth - subsWrapperWidth) / 2; + var subsWrapperTop = (videoOffsetHeight - subsWrapperHeight) / 2; + _super.prototype.resize.call(this, subsWrapperWidth, subsWrapperHeight, subsWrapperLeft, subsWrapperTop); + }; + /** + * @deprecated + */ + DefaultRenderer.prototype.resizeVideo = function () { + console.warn("`DefaultRenderer.resizeVideo(width, height)` has been deprecated. Use `DefaultRenderer.resize()` instead."); + this.resize(); + }; + DefaultRenderer.prototype._ready = function () { + this.resize(); + _super.prototype._ready.call(this); + }; + return DefaultRenderer; + }(renderer_1.WebRenderer); + exports.DefaultRenderer = DefaultRenderer; + }); + def("renderers/web/renderer", [ "parser/ttf", "parts/index", "settings", "types/attachment", "types/misc", "utility/map", "utility/mixin", "utility/promise", "renderers/clocks/base", "renderers/null", "renderers/web/animation-collection", "renderers/web/drawing-styles", "renderers/web/font-size", "renderers/web/keyframe", "renderers/web/span-styles" ], function (ttf_1, parts, settings_9, attachment_2, misc_8, map_14, mixin_3, promise_7, base_3, null_1, animation_collection_1, drawing_styles_1, font_size_2, keyframe_1, span_styles_1, exports) { + var fontSrcUrlRegex = /^(url|local)\(["']?(.+?)["']?\)$/; + /** + * A renderer implementation that draws subtitles to the given
+ * + * After the renderer fires its ready event, {@link libjass.renderers.WebRenderer.resize} must be called to initialize its size before starting the clock. + * + * @param {!libjass.ASS} ass + * @param {!libjass.renderers.Clock} clock + * @param {!HTMLDivElement} libjassSubsWrapper Subtitles will be rendered to this
+ * @param {!libjass.renderers.RendererSettings} settings + * + * @constructor + * @extends {libjass.renderers.NullRenderer} + * @implements {libjass.renderers.EventSource.} + * @memberOf libjass.renderers + */ + var WebRenderer = function (_super) { + __extends(WebRenderer, _super); + function WebRenderer(ass, clock, libjassSubsWrapper, settings) { + var _this = this; + _super.call(this, ass, clock, function () { + if (!(libjassSubsWrapper instanceof HTMLDivElement)) { + var temp = settings; + settings = libjassSubsWrapper; + libjassSubsWrapper = temp; + console.warn("WebRenderer's constructor now takes libjassSubsWrapper as the third parameter and settings as the fourth parameter. Please update the caller."); + } + return settings; + }()); + this._libjassSubsWrapper = libjassSubsWrapper; + this._layerWrappers = []; + this._layerAlignmentWrappers = []; + this._fontMetricsCache = new map_14.Map(); + this._currentSubs = new map_14.Map(); + this._preRenderedSubs = new map_14.Map(); + // EventSource members + /** + * @type {!Map.>} + */ + this._eventListeners = new map_14.Map(); + this._libjassSubsWrapper.classList.add("libjass-wrapper"); + this._subsWrapper = document.createElement("div"); + this._libjassSubsWrapper.appendChild(this._subsWrapper); + this._subsWrapper.className = "libjass-subs"; + this._fontSizeElement = document.createElement("div"); + this._libjassSubsWrapper.appendChild(this._fontSizeElement); + this._fontSizeElement.className = "libjass-font-measure"; + this._fontSizeElement.appendChild(document.createTextNode("M")); + // Preload fonts + if (settings_9.debugMode) { + console.log("Preloading fonts..."); + } + var preloadFontPromises = []; + var fontFetchPromisesCache = new map_14.Map(); + var fontMap = this.settings.fontMap === null ? new map_14.Map() : this.settings.fontMap; + var attachedFontsMap = new map_14.Map(); + if (this.settings.useAttachedFonts) { + ass.attachments.forEach(function (attachment) { + if (attachment.type !== attachment_2.AttachmentType.Font) { + return; + } + var ttfNames; + try { + ttfNames = ttf_1.getTtfNames(attachment); + } catch (ex) { + console.error(ex); + return; + } + var attachmentUrl = "data:application/x-font-ttf;base64," + attachment.contents; + ttfNames.forEach(function (name) { + var correspondingFontMapEntry = fontMap.get(name); + if (correspondingFontMapEntry !== undefined) { + // Also defined in fontMap. + if (typeof correspondingFontMapEntry !== "string") { + // Entry in fontMap is an array. Append this URL to it. + correspondingFontMapEntry.push(attachmentUrl); + } else { + /* The entry in fontMap is a string. Don't append this URL to it. Instead, put it in attachedFontsMap now + * and it'll be merged with the entry from fontMap later. If it was added here, and later the string needed + * to be split on commas, then the commas in the data URI would break the result. + */ + var existingList = attachedFontsMap.get(name); + if (existingList === undefined) { + attachedFontsMap.set(name, existingList = []); + } + existingList.push(attachmentUrl); + } + } else { + // Not defined in fontMap. Add it there. + fontMap.set(name, [ attachmentUrl ]); + } + }); + }); + } + fontMap.forEach(function (srcs, fontFamily) { + var fontFamilyMetricsPromise; + if (global.document.fonts && global.document.fonts.add) { + // value should be string. If it's string[], combine it into string + var source_1 = typeof srcs === "string" ? srcs : srcs.map(function (src) { + return src.match(fontSrcUrlRegex) !== null ? src : 'url("' + src + '")'; + }).join(", "); + var attachedFontUrls = attachedFontsMap.get(fontFamily); + if (attachedFontUrls !== undefined) { + for (var _i = 0, attachedFontUrls_1 = attachedFontUrls; _i < attachedFontUrls_1.length; _i++) { + var url = attachedFontUrls_1[_i]; + source_1 += ', url("' + url + '")'; + } + } + var existingFontFaces_1 = []; + global.document.fonts.forEach(function (fontFace) { + if (fontFace.family === fontFamily || fontFace.family === '"' + fontFamily + '"') { + existingFontFaces_1.push(fontFace); + } + }); + var fontFetchPromise = void 0; + if (existingFontFaces_1.length === 0) { + var fontFace = new FontFace(fontFamily, source_1); + var quotedFontFace = new FontFace('"' + fontFamily + '"', source_1); + global.document.fonts.add(fontFace); + global.document.fonts.add(quotedFontFace); + fontFetchPromise = promise_7.any([ fontFace.load(), quotedFontFace.load() ]); + } else { + fontFetchPromise = promise_7.any(existingFontFaces_1.map(function (fontFace) { + return fontFace.load(); + })); + } + fontFamilyMetricsPromise = _this._calculateFontMetricsAfterFetch(fontFamily, fontFetchPromise.catch(function (reason) { + console.warn("Fetching fonts for " + fontFamily + " at " + source_1 + " failed: %o", reason); + return null; + })); + } else { + // value should be string[]. If it's string, split it into string[] + var urls_1 = (typeof srcs === "string" ? srcs.split(/,/) : srcs).map(function (url) { + return url.trim(); + }).map(function (url) { + var match = url.match(fontSrcUrlRegex); + if (match === null) { + // A URL + return url; + } + if (match[1] === "local") { + // A local() URL. Don't fetch it. + return null; + } + // A url() URL. Extract the raw URL. + return match[2]; + }).filter(function (url) { + return url !== null; + }); + var attachedFontUrls = attachedFontsMap.get(fontFamily); + if (attachedFontUrls !== undefined) { + urls_1 = urls_1.concat(attachedFontUrls); + } + var thisFontFamilysFetchPromises = urls_1.map(function (url) { + var fontFetchPromise = fontFetchPromisesCache.get(url); + if (fontFetchPromise === undefined) { + fontFetchPromise = new promise_7.Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.addEventListener("load", function () { + if (settings_9.debugMode) { + console.log("Preloaded " + url + "."); + } + resolve(null); + }); + xhr.addEventListener("error", function (err) { + reject(err); + }); + xhr.open("GET", url, true); + xhr.send(); + }); + fontFetchPromisesCache.set(url, fontFetchPromise); + } + return fontFetchPromise; + }); + var allFontsFetchedPromise = thisFontFamilysFetchPromises.length === 0 ? promise_7.Promise.resolve(null) : promise_7.first(thisFontFamilysFetchPromises).catch(function (reason) { + console.warn("Fetching fonts for " + fontFamily + " at " + urls_1.join(", ") + " failed: %o", reason); + return null; + }); + fontFamilyMetricsPromise = _this._calculateFontMetricsAfterFetch(fontFamily, allFontsFetchedPromise); + } + preloadFontPromises.push(fontFamilyMetricsPromise.then(function (metrics) { + return _this._fontMetricsCache.set(fontFamily, metrics); + })); + }); + promise_7.Promise.all(preloadFontPromises).then(function () { + if (settings_9.debugMode) { + console.log("All fonts have been preloaded."); + } + _this._ready(); + }); + } + Object.defineProperty(WebRenderer.prototype, "libjassSubsWrapper", { + /** + * @type {!HTMLDivElement} + */ + get: function () { + return this._libjassSubsWrapper; + }, + enumerable: true, + configurable: true + }); + /** + * Resize the subtitles to the given new dimensions. + * + * @param {number} width + * @param {number} height + * @param {number=0} left + * @param {number=0} top + */ + WebRenderer.prototype.resize = function (width, height, left, top) { + if (left === void 0) { + left = 0; + } + if (top === void 0) { + top = 0; + } + this._removeAllSubs(); + this._subsWrapper.style.width = width.toFixed(3) + "px"; + this._subsWrapper.style.height = height.toFixed(3) + "px"; + this._subsWrapper.style.left = left.toFixed(3) + "px"; + this._subsWrapper.style.top = top.toFixed(3) + "px"; + this._subsWrapperWidth = width; + this._scaleX = width / this.ass.properties.resolutionX; + this._scaleY = height / this.ass.properties.resolutionY; + // Any dialogues which have been pre-rendered will need to be pre-rendered again. + this._preRenderedSubs.clear(); + // this.currentTime will be -1 if resize() is called before the clock begins playing for the first time. In this situation, there is no need to force a redraw. + if (this.clock.currentTime !== -1) { + this._onClockTick(); + } + }; + /** + * The magic happens here. The subtitle div is rendered and stored. Call {@link libjass.renderers.WebRenderer.draw} to get a clone of the div to display. + * + * @param {!libjass.Dialogue} dialogue + * @return {PreRenderedSub} + */ + WebRenderer.prototype.preRender = function (dialogue) { + var _this = this; + var currentTimeRelativeToDialogueStart = this.clock.currentTime - dialogue.start; + if (dialogue.containsTransformTag && currentTimeRelativeToDialogueStart < 0) { + // draw() expects this function to always return non-null, but it only calls this function when currentTimeRelativeToDialogueStart would be >= 0 + return null; + } + var alreadyPreRenderedSub = this._preRenderedSubs.get(dialogue.id); + if (alreadyPreRenderedSub) { + return alreadyPreRenderedSub; + } + var sub = document.createElement("div"); + sub.style.marginLeft = (this._scaleX * dialogue.style.marginLeft).toFixed(3) + "px"; + sub.style.marginRight = (this._scaleX * dialogue.style.marginRight).toFixed(3) + "px"; + sub.style.marginTop = sub.style.marginBottom = (this._scaleY * dialogue.style.marginVertical).toFixed(3) + "px"; + sub.style.minWidth = (this._subsWrapperWidth - this._scaleX * (dialogue.style.marginLeft + dialogue.style.marginRight)).toFixed(3) + "px"; + var dialogueAnimationStylesElement = document.createElement("style"); + dialogueAnimationStylesElement.id = "libjass-animation-styles-" + this.id + "-" + dialogue.id; + dialogueAnimationStylesElement.type = "text/css"; + var dialogueAnimationCollection = new animation_collection_1.AnimationCollection(this, dialogueAnimationStylesElement); + var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + svgElement.setAttribute("version", "1.1"); + svgElement.setAttribute("class", "libjass-filters"); + svgElement.width.baseVal.valueAsString = "0"; + svgElement.height.baseVal.valueAsString = "0"; + var svgDefsElement = document.createElementNS("http://www.w3.org/2000/svg", "defs"); + svgElement.appendChild(svgDefsElement); + var currentSpan = null; + var currentSpanStyles = new span_styles_1.SpanStyles(this, dialogue, this._scaleX, this._scaleY, this.settings, this._fontSizeElement, svgDefsElement, this._fontMetricsCache); + var currentAnimationCollection = null; + var previousAddNewLine = false; + // If two or more \N's are encountered in sequence, then all but the first will be created using currentSpanStyles.makeNewLine() instead + var startNewSpan = function (addNewLine) { + if (currentSpan !== null && currentSpan.hasChildNodes()) { + sub.appendChild(currentSpanStyles.setStylesOnSpan(currentSpan, currentAnimationCollection)); + } + if (currentAnimationCollection !== null) { + currentAnimationCollection.animationDelays.forEach(function (delay, name) { + return dialogueAnimationCollection.animationDelays.set(name, delay); + }); + } + if (addNewLine) { + if (previousAddNewLine) { + sub.appendChild(currentSpanStyles.makeNewLine()); + } else { + sub.appendChild(document.createElement("br")); + } + } + currentSpan = document.createElement("span"); + currentAnimationCollection = new animation_collection_1.AnimationCollection(_this, dialogueAnimationStylesElement); + previousAddNewLine = addNewLine; + }; + startNewSpan(false); + var currentDrawingStyles = new drawing_styles_1.DrawingStyles(this._scaleX, this._scaleY); + var wrappingStyle = this.ass.properties.wrappingStyle; + var karaokeTimesAccumulator = 0; + for (var _i = 0, _a = dialogue.parts; _i < _a.length; _i++) { + var part = _a[_i]; + if (part instanceof parts.Italic) { + currentSpanStyles.italic = part.value; + } else if (part instanceof parts.Bold) { + currentSpanStyles.bold = part.value; + } else if (part instanceof parts.Underline) { + currentSpanStyles.underline = part.value; + } else if (part instanceof parts.StrikeThrough) { + currentSpanStyles.strikeThrough = part.value; + } else if (part instanceof parts.Border) { + currentSpanStyles.outlineWidth = part.value; + currentSpanStyles.outlineHeight = part.value; + } else if (part instanceof parts.BorderX) { + currentSpanStyles.outlineWidth = part.value; + } else if (part instanceof parts.BorderY) { + currentSpanStyles.outlineHeight = part.value; + } else if (part instanceof parts.Shadow) { + currentSpanStyles.shadowDepthX = part.value; + currentSpanStyles.shadowDepthY = part.value; + } else if (part instanceof parts.ShadowX) { + currentSpanStyles.shadowDepthX = part.value; + } else if (part instanceof parts.ShadowY) { + currentSpanStyles.shadowDepthY = part.value; + } else if (part instanceof parts.Blur) { + currentSpanStyles.blur = part.value; + } else if (part instanceof parts.GaussianBlur) { + currentSpanStyles.gaussianBlur = part.value; + } else if (part instanceof parts.FontName) { + currentSpanStyles.fontName = part.value; + } else if (part instanceof parts.FontSize) { + currentSpanStyles.fontSize = part.value; + } else if (part instanceof parts.FontSizePlus) { + currentSpanStyles.fontSize = currentSpanStyles.fontSize * (1 + part.value); + } else if (part instanceof parts.FontSizeMinus) { + currentSpanStyles.fontSize = currentSpanStyles.fontSize * (1 - part.value); + } else if (part instanceof parts.FontScaleX) { + currentSpanStyles.fontScaleX = part.value; + } else if (part instanceof parts.FontScaleY) { + currentSpanStyles.fontScaleY = part.value; + } else if (part instanceof parts.LetterSpacing) { + currentSpanStyles.letterSpacing = part.value; + } else if (part instanceof parts.RotateX) { + currentSpanStyles.rotationX = part.value; + } else if (part instanceof parts.RotateY) { + currentSpanStyles.rotationY = part.value; + } else if (part instanceof parts.RotateZ) { + currentSpanStyles.rotationZ = part.value; + } else if (part instanceof parts.SkewX) { + currentSpanStyles.skewX = part.value; + } else if (part instanceof parts.SkewY) { + currentSpanStyles.skewY = part.value; + } else if (part instanceof parts.PrimaryColor) { + currentSpanStyles.primaryColor = part.value; + } else if (part instanceof parts.SecondaryColor) { + currentSpanStyles.secondaryColor = part.value; + } else if (part instanceof parts.OutlineColor) { + currentSpanStyles.outlineColor = part.value; + } else if (part instanceof parts.ShadowColor) { + currentSpanStyles.shadowColor = part.value; + } else if (part instanceof parts.Alpha) { + currentSpanStyles.primaryAlpha = part.value; + currentSpanStyles.secondaryAlpha = part.value; + currentSpanStyles.outlineAlpha = part.value; + currentSpanStyles.shadowAlpha = part.value; + } else if (part instanceof parts.PrimaryAlpha) { + currentSpanStyles.primaryAlpha = part.value; + } else if (part instanceof parts.SecondaryAlpha) { + currentSpanStyles.secondaryAlpha = part.value; + } else if (part instanceof parts.OutlineAlpha) { + currentSpanStyles.outlineAlpha = part.value; + } else if (part instanceof parts.ShadowAlpha) { + currentSpanStyles.shadowAlpha = part.value; + } else if (part instanceof parts.Alignment) {} else if (part instanceof parts.ColorKaraoke) { + startNewSpan(false); + currentAnimationCollection.add("step-end", [ new keyframe_1.Keyframe(0, new map_14.Map([ [ "color", currentSpanStyles.secondaryColor.withAlpha(currentSpanStyles.secondaryAlpha).toString() ] ])), new keyframe_1.Keyframe(karaokeTimesAccumulator, new map_14.Map([ [ "color", currentSpanStyles.primaryColor.withAlpha(currentSpanStyles.primaryAlpha).toString() ] ])) ]); + karaokeTimesAccumulator += part.duration; + } else if (part instanceof parts.WrappingStyle) { + wrappingStyle = part.value; + } else if (part instanceof parts.Reset) { + var newStyleName = part.value; + if (newStyleName === null) { + currentSpanStyles.reset(null); + } else { + currentSpanStyles.reset(this.ass.styles.get(newStyleName)); + } + } else if (part instanceof parts.Position) { + sub.style.position = "absolute"; + sub.style.left = (this._scaleX * part.x).toFixed(3) + "px"; + sub.style.top = (this._scaleY * part.y).toFixed(3) + "px"; + } else if (part instanceof parts.Move) { + sub.style.position = "absolute"; + dialogueAnimationCollection.add("linear", [ new keyframe_1.Keyframe(0, new map_14.Map([ [ "left", (this._scaleX * part.x1).toFixed(3) + "px" ], [ "top", (this._scaleY * part.y1).toFixed(3) + "px" ] ])), new keyframe_1.Keyframe(part.t1, new map_14.Map([ [ "left", (this._scaleX * part.x1).toFixed(3) + "px" ], [ "top", (this._scaleY * part.y1).toFixed(3) + "px" ] ])), new keyframe_1.Keyframe(part.t2, new map_14.Map([ [ "left", (this._scaleX * part.x2).toFixed(3) + "px" ], [ "top", (this._scaleY * part.y2).toFixed(3) + "px" ] ])), new keyframe_1.Keyframe(dialogue.end - dialogue.start, new map_14.Map([ [ "left", (this._scaleX * part.x2).toFixed(3) + "px" ], [ "top", (this._scaleY * part.y2).toFixed(3) + "px" ] ])) ]); + } else if (part instanceof parts.Fade) { + dialogueAnimationCollection.add("linear", [ new keyframe_1.Keyframe(0, new map_14.Map([ [ "opacity", "0" ] ])), new keyframe_1.Keyframe(part.start, new map_14.Map([ [ "opacity", "1" ] ])), new keyframe_1.Keyframe(dialogue.end - dialogue.start - part.end, new map_14.Map([ [ "opacity", "1" ] ])), new keyframe_1.Keyframe(dialogue.end - dialogue.start, new map_14.Map([ [ "opacity", "0" ] ])) ]); + } else if (part instanceof parts.ComplexFade) { + dialogueAnimationCollection.add("linear", [ new keyframe_1.Keyframe(0, new map_14.Map([ [ "opacity", part.a1.toFixed(3) ] ])), new keyframe_1.Keyframe(part.t1, new map_14.Map([ [ "opacity", part.a1.toFixed(3) ] ])), new keyframe_1.Keyframe(part.t2, new map_14.Map([ [ "opacity", part.a2.toFixed(3) ] ])), new keyframe_1.Keyframe(part.t3, new map_14.Map([ [ "opacity", part.a2.toFixed(3) ] ])), new keyframe_1.Keyframe(part.t4, new map_14.Map([ [ "opacity", part.a3.toFixed(3) ] ])), new keyframe_1.Keyframe(dialogue.end - dialogue.start, new map_14.Map([ [ "opacity", part.a3.toFixed(3) ] ])) ]); + } else if (part instanceof parts.Transform) { + var progression = currentTimeRelativeToDialogueStart <= part.start ? 0 : currentTimeRelativeToDialogueStart >= part.end ? 1 : Math.pow((currentTimeRelativeToDialogueStart - part.start) / (part.end - part.start), part.accel); + for (var _b = 0, _c = part.tags; _b < _c.length; _b++) { + var tag = _c[_b]; + if (tag instanceof parts.Border) { + if (tag.value !== null) { + currentSpanStyles.outlineWidth += progression * (tag.value - currentSpanStyles.outlineWidth); + currentSpanStyles.outlineHeight += progression * (tag.value - currentSpanStyles.outlineHeight); + } else { + currentSpanStyles.outlineWidth = null; + currentSpanStyles.outlineHeight = null; + } + } else if (tag instanceof parts.BorderX) { + if (tag.value !== null) { + currentSpanStyles.outlineWidth += progression * (tag.value - currentSpanStyles.outlineWidth); + } else { + currentSpanStyles.outlineWidth = null; + } + } else if (tag instanceof parts.BorderY) { + if (tag.value !== null) { + currentSpanStyles.outlineHeight += progression * (tag.value - currentSpanStyles.outlineHeight); + } else { + currentSpanStyles.outlineHeight = null; + } + } else if (tag instanceof parts.Shadow) { + if (tag.value !== null) { + currentSpanStyles.shadowDepthX += progression * (tag.value - currentSpanStyles.shadowDepthX); + currentSpanStyles.shadowDepthY += progression * (tag.value - currentSpanStyles.shadowDepthY); + } else { + currentSpanStyles.shadowDepthX = null; + currentSpanStyles.shadowDepthY = null; + } + } else if (tag instanceof parts.ShadowX) { + if (tag.value !== null) { + currentSpanStyles.shadowDepthX += progression * (tag.value - currentSpanStyles.shadowDepthX); + } else { + currentSpanStyles.shadowDepthX = null; + } + } else if (tag instanceof parts.ShadowY) { + if (tag.value !== null) { + currentSpanStyles.shadowDepthY += progression * (tag.value - currentSpanStyles.shadowDepthY); + } else { + currentSpanStyles.shadowDepthY = null; + } + } else if (tag instanceof parts.Blur) { + if (tag.value !== null) { + currentSpanStyles.blur += progression * (tag.value - currentSpanStyles.blur); + } else { + currentSpanStyles.blur = null; + } + } else if (tag instanceof parts.GaussianBlur) { + if (tag.value !== null) { + currentSpanStyles.gaussianBlur += progression * (tag.value - currentSpanStyles.gaussianBlur); + } else { + currentSpanStyles.gaussianBlur = null; + } + } else if (tag instanceof parts.FontSize) { + if (tag.value !== null) { + currentSpanStyles.fontSize += progression * (tag.value - currentSpanStyles.fontSize); + } else { + currentSpanStyles.fontSize = null; + } + } else if (tag instanceof parts.FontSizePlus) { + currentSpanStyles.fontSize *= 1 + progression * tag.value; + } else if (tag instanceof parts.FontSizeMinus) { + currentSpanStyles.fontSize *= 1 - progression * tag.value; + } else if (tag instanceof parts.FontScaleX) { + if (tag.value !== null) { + currentSpanStyles.fontScaleX += progression * (tag.value - currentSpanStyles.fontScaleX); + } else { + currentSpanStyles.fontScaleX = null; + } + } else if (tag instanceof parts.FontScaleY) { + if (tag.value !== null) { + currentSpanStyles.fontScaleY += progression * (tag.value - currentSpanStyles.fontScaleY); + } else { + currentSpanStyles.fontScaleY = null; + } + } else if (tag instanceof parts.LetterSpacing) { + if (tag.value !== null) { + currentSpanStyles.letterSpacing += progression * (tag.value - currentSpanStyles.letterSpacing); + } else { + currentSpanStyles.letterSpacing = null; + } + } else if (tag instanceof parts.RotateX) { + if (tag.value !== null) { + currentSpanStyles.rotationX += progression * (tag.value - currentSpanStyles.rotationX); + } else { + currentSpanStyles.rotationX = null; + } + } else if (tag instanceof parts.RotateY) { + if (tag.value !== null) { + currentSpanStyles.rotationY += progression * (tag.value - currentSpanStyles.rotationY); + } else { + currentSpanStyles.rotationY = null; + } + } else if (tag instanceof parts.RotateZ) { + if (tag.value !== null) { + currentSpanStyles.rotationZ += progression * (tag.value - currentSpanStyles.rotationZ); + } else { + currentSpanStyles.rotationZ = null; + } + } else if (tag instanceof parts.SkewX) { + if (tag.value !== null) { + currentSpanStyles.skewX += progression * (tag.value - currentSpanStyles.skewX); + } else { + currentSpanStyles.skewX = null; + } + } else if (tag instanceof parts.SkewY) { + if (tag.value !== null) { + currentSpanStyles.skewY += progression * (tag.value - currentSpanStyles.skewY); + } else { + currentSpanStyles.skewY = null; + } + } else if (tag instanceof parts.PrimaryColor) { + if (tag.value !== null) { + currentSpanStyles.primaryColor = currentSpanStyles.primaryColor.interpolate(tag.value, progression); + } else { + currentSpanStyles.primaryColor = null; + } + } else if (tag instanceof parts.SecondaryColor) { + if (tag.value !== null) { + currentSpanStyles.secondaryColor = currentSpanStyles.secondaryColor.interpolate(tag.value, progression); + } else { + currentSpanStyles.secondaryColor = null; + } + } else if (tag instanceof parts.OutlineColor) { + if (tag.value !== null) { + currentSpanStyles.outlineColor = currentSpanStyles.outlineColor.interpolate(tag.value, progression); + } else { + currentSpanStyles.outlineColor = null; + } + } else if (tag instanceof parts.ShadowColor) { + if (tag.value !== null) { + currentSpanStyles.shadowColor = currentSpanStyles.shadowColor.interpolate(tag.value, progression); + } else { + currentSpanStyles.shadowColor = null; + } + } else if (tag instanceof parts.Alpha) { + if (tag.value !== null) { + currentSpanStyles.primaryAlpha += progression * (tag.value - currentSpanStyles.primaryAlpha); + currentSpanStyles.secondaryAlpha += progression * (tag.value - currentSpanStyles.secondaryAlpha); + currentSpanStyles.outlineAlpha += progression * (tag.value - currentSpanStyles.outlineAlpha); + currentSpanStyles.shadowAlpha += progression * (tag.value - currentSpanStyles.shadowAlpha); + } else { + currentSpanStyles.primaryAlpha = null; + currentSpanStyles.secondaryAlpha = null; + currentSpanStyles.outlineAlpha = null; + currentSpanStyles.shadowAlpha = null; + } + } else if (tag instanceof parts.PrimaryAlpha) { + if (tag.value !== null) { + currentSpanStyles.primaryAlpha += progression * (tag.value - currentSpanStyles.primaryAlpha); + } else { + currentSpanStyles.primaryAlpha = null; + } + } else if (tag instanceof parts.SecondaryAlpha) { + if (tag.value !== null) { + currentSpanStyles.secondaryAlpha += progression * (tag.value - currentSpanStyles.secondaryAlpha); + } else { + currentSpanStyles.secondaryAlpha = null; + } + } else if (tag instanceof parts.OutlineAlpha) { + if (tag.value !== null) { + currentSpanStyles.outlineAlpha += progression * (tag.value - currentSpanStyles.outlineAlpha); + } else { + currentSpanStyles.outlineAlpha = null; + } + } else if (tag instanceof parts.ShadowAlpha) { + if (tag.value !== null) { + currentSpanStyles.shadowAlpha += progression * (tag.value - currentSpanStyles.shadowAlpha); + } else { + currentSpanStyles.shadowAlpha = null; + } + } + } + } else if (part instanceof parts.DrawingMode) { + if (part.scale !== 0) { + currentDrawingStyles.scale = part.scale; + } + } else if (part instanceof parts.DrawingBaselineOffset) { + currentDrawingStyles.baselineOffset = part.value; + } else if (part instanceof parts.DrawingInstructions) { + currentSpan.appendChild(currentDrawingStyles.toSVG(part, currentSpanStyles.primaryColor.withAlpha(currentSpanStyles.primaryAlpha))); + startNewSpan(false); + } else if (part instanceof parts.Text) { + currentSpan.appendChild(document.createTextNode(part.value + "\u200c")); + startNewSpan(false); + } else if (settings_9.debugMode && part instanceof parts.Comment) { + currentSpan.appendChild(document.createTextNode(part.value)); + startNewSpan(false); + } else if (part instanceof parts.NewLine) { + startNewSpan(true); + } + } + var divTransformStyle = ""; + var transformOrigin = WebRenderer._transformOrigins[dialogue.alignment]; + var transformOriginString = transformOrigin[0] + "% " + transformOrigin[1] + "%"; + for (var _d = 0, _e = dialogue.parts; _d < _e.length; _d++) { + var part = _e[_d]; + if (part instanceof parts.Position || part instanceof parts.Move) { + divTransformStyle = "translate(" + -transformOrigin[0] + "%, " + -transformOrigin[1] + "%) translate(-" + sub.style.marginLeft + ", -" + sub.style.marginTop + ")"; + break; + } + } + if (currentSpanStyles.rotationY !== 0) { + divTransformStyle += " rotateY(" + currentSpanStyles.rotationY + "deg)"; + } + if (currentSpanStyles.rotationX !== 0) { + divTransformStyle += " rotateX(" + currentSpanStyles.rotationX + "deg)"; + } + if (currentSpanStyles.rotationZ !== 0) { + divTransformStyle += " rotateZ(" + -1 * currentSpanStyles.rotationZ + "deg)"; + } + sub.style.webkitTransform = divTransformStyle; + sub.style.transform = divTransformStyle; + sub.style.webkitTransformOrigin = transformOriginString; + sub.style.transformOrigin = transformOriginString; + switch (wrappingStyle) { + case misc_8.WrappingStyle.EndOfLineWrapping: + sub.style.whiteSpace = "pre-wrap"; + break; + + case misc_8.WrappingStyle.NoLineWrapping: + sub.style.whiteSpace = "pre"; + break; + + case misc_8.WrappingStyle.SmartWrappingWithWiderTopLine: + case misc_8.WrappingStyle.SmartWrappingWithWiderBottomLine: + /* Not supported. Treat the same as EndOfLineWrapping */ + sub.style.whiteSpace = "pre-wrap"; + break; + } + if (sub.style.position !== "") { + // Explicitly set text alignment on absolutely-positioned subs because they'll go in a .an0
and so won't get alignment CSS text-align. + switch (dialogue.alignment) { + case 1: + case 4: + case 7: + sub.style.textAlign = "left"; + break; + + case 2: + case 5: + case 8: + sub.style.textAlign = "center"; + break; + + case 3: + case 6: + case 9: + sub.style.textAlign = "right"; + break; + } + } + sub.style.webkitAnimation = dialogueAnimationCollection.animationStyle; + sub.style.animation = dialogueAnimationCollection.animationStyle; + sub.setAttribute("data-dialogue-id", this.id + "-" + dialogue.id); + if (dialogueAnimationStylesElement.textContent !== "") { + sub.appendChild(dialogueAnimationStylesElement); + } + if (svgDefsElement.hasChildNodes()) { + sub.appendChild(svgElement); + } + var result = { + sub: sub, + animationDelays: dialogueAnimationCollection.animationDelays + }; + if (!dialogue.containsTransformTag) { + this._preRenderedSubs.set(dialogue.id, result); + } + return result; + }; + /** + * Returns the subtitle div for display. The {@link libjass.renderers.Clock.currentTime} of the {@link libjass.renderers.NullRenderer.clock} is used to shift the + * animations appropriately, so that at the time the div is inserted into the DOM and the animations begin, they are in sync with the clock time. + * + * @param {!libjass.Dialogue} dialogue + */ + WebRenderer.prototype.draw = function (dialogue) { + var _this = this; + if (this._currentSubs.has(dialogue) && !dialogue.containsTransformTag) { + return; + } + if (settings_9.debugMode) { + console.log(dialogue.toString()); + } + var thePreRenderedSub = this._preRenderedSubs.get(dialogue.id); + if (thePreRenderedSub === undefined) { + thePreRenderedSub = this.preRender(dialogue); + if (settings_9.debugMode) { + console.log(dialogue.toString()); + } + } + var preRenderedSub = thePreRenderedSub; + var result = preRenderedSub.sub.cloneNode(true); + var applyAnimationDelays = function (node) { + var animationNames = node.style.animationName || node.style.webkitAnimationName || ""; + if (animationNames !== "") { + var animationDelays = animationNames.split(",").map(function (name) { + name = name.trim(); + var delay = preRenderedSub.animationDelays.get(name); + return ((delay + dialogue.start - _this.clock.currentTime) / _this.clock.rate).toFixed(3) + "s"; + }).join(", "); + node.style.webkitAnimationDelay = animationDelays; + node.style.animationDelay = animationDelays; + } + }; + applyAnimationDelays(result); + var animatedDescendants = result.querySelectorAll('[style*="animation:"]'); + for (var i = 0; i < animatedDescendants.length; i++) { + applyAnimationDelays(animatedDescendants[i]); + } + var layer = dialogue.layer; + var alignment = result.style.position === "absolute" ? 0 : dialogue.alignment; + // Alignment 0 is for absolutely-positioned subs + // Create the layer wrapper div and the alignment div inside it if not already created + if (this._layerWrappers[layer] === undefined) { + var layerWrapper = document.createElement("div"); + layerWrapper.className = "layer layer" + layer; + // Find the next greater layer div and insert this div before that one + var insertBeforeElement = null; + for (var insertBeforeLayer = layer + 1; insertBeforeLayer < this._layerWrappers.length && insertBeforeElement === null; insertBeforeLayer++) { + if (this._layerWrappers[insertBeforeLayer] !== undefined) { + insertBeforeElement = this._layerWrappers[insertBeforeLayer]; + } + } + this._subsWrapper.insertBefore(layerWrapper, insertBeforeElement); + this._layerWrappers[layer] = layerWrapper; + this._layerAlignmentWrappers[layer] = []; + } + var layerAlignmentWrapper = this._layerAlignmentWrappers[layer][alignment]; + if (layerAlignmentWrapper === undefined) { + layerAlignmentWrapper = this._layerAlignmentWrappers[layer][alignment] = document.createElement("div"); + layerAlignmentWrapper.className = "an an" + alignment; + // Find the next greater layer,alignment div and insert this div before that one + var layerWrapper = this._layerWrappers[layer]; + var insertBeforeElement = null; + for (var insertBeforeAlignment = alignment + 1; insertBeforeAlignment < this._layerAlignmentWrappers[layer].length && insertBeforeElement === null; insertBeforeAlignment++) { + if (this._layerAlignmentWrappers[layer][insertBeforeAlignment] !== undefined) { + insertBeforeElement = this._layerAlignmentWrappers[layer][insertBeforeAlignment]; + } + } + layerWrapper.insertBefore(layerAlignmentWrapper, insertBeforeElement); + } + if (alignment >= 1 && alignment <= 3) { + // New subs go above existing subs + layerAlignmentWrapper.insertBefore(result, layerAlignmentWrapper.firstElementChild); + } else { + // New subs go below existing subs + layerAlignmentWrapper.appendChild(result); + } + // Workaround for IE + var dialogueAnimationStylesElement = result.getElementsByTagName("style")[0]; + if (dialogueAnimationStylesElement !== undefined) { + var sheet = dialogueAnimationStylesElement.sheet; + if (sheet.cssRules.length === 0) { + sheet.cssText = dialogueAnimationStylesElement.textContent; + } + } + this._currentSubs.set(dialogue, result); + }; + WebRenderer.prototype._onClockPlay = function () { + _super.prototype._onClockPlay.call(this); + this._removeAllSubs(); + this._subsWrapper.style.display = ""; + this._subsWrapper.classList.remove("paused"); + }; + WebRenderer.prototype._onClockTick = function () { + // Remove dialogues that should be removed before adding new ones via super._onClockTick() + var _this = this; + var currentTime = this.clock.currentTime; + this._currentSubs.forEach(function (sub, dialogue) { + if (dialogue.start > currentTime || dialogue.end < currentTime || dialogue.containsTransformTag) { + _this._currentSubs.delete(dialogue); + _this._removeSub(sub); + } + }); + _super.prototype._onClockTick.call(this); + }; + WebRenderer.prototype._onClockPause = function () { + _super.prototype._onClockPause.call(this); + this._subsWrapper.classList.add("paused"); + }; + WebRenderer.prototype._onClockStop = function () { + _super.prototype._onClockStop.call(this); + this._subsWrapper.style.display = "none"; + }; + WebRenderer.prototype._onClockRateChange = function () { + _super.prototype._onClockRateChange.call(this); + // Any dialogues which have been pre-rendered will need to be pre-rendered again. + this._preRenderedSubs.clear(); + }; + WebRenderer.prototype._ready = function () { + this._dispatchEvent("ready", []); + }; + /** + * @param {string} fontFamily + * @param {!Promise.<*>} fontFetchPromise + * @return {!Promise.<[number, number]>} + * + * @private + */ + WebRenderer.prototype._calculateFontMetricsAfterFetch = function (fontFamily, fontFetchPromise) { + var _this = this; + return fontFetchPromise.then(function () { + var fontSizeElement = _this._fontSizeElement.cloneNode(true); + _this._libjassSubsWrapper.appendChild(fontSizeElement); + return promise_7.lastly(font_size_2.calculateFontMetrics(fontFamily, _this.settings.fallbackFonts, fontSizeElement), function () { + _this._libjassSubsWrapper.removeChild(fontSizeElement); + }); + }); + }; + /** + * @param {!HTMLDivElement} sub + * + * @private + */ + WebRenderer.prototype._removeSub = function (sub) { + sub.parentNode.removeChild(sub); + }; + WebRenderer.prototype._removeAllSubs = function () { + var _this = this; + this._currentSubs.forEach(function (sub) { + return _this._removeSub(sub); + }); + this._currentSubs.clear(); + }; + WebRenderer._transformOrigins = [ [], [ 0, 100 ], [ 50, 100 ], [ 100, 100 ], [ 0, 50 ], [ 50, 50 ], [ 100, 50 ], [ 0, 0 ], [ 50, 0 ], [ 100, 0 ] ]; + return WebRenderer; + }(null_1.NullRenderer); + exports.WebRenderer = WebRenderer; + mixin_3.mixin(WebRenderer, [ base_3.EventSource ]); + }); + def("renderers/web/span-styles", [ "renderers/web/font-size" ], function (font_size_1, exports) { + /** + * This class represents the style attribute of a span. + * As a Dialogue's div is rendered, individual parts are added to span's, and this class is used to maintain the style attribute of those. + * + * @param {!libjass.renderers.NullRenderer} renderer The renderer that this set of styles is associated with + * @param {!libjass.Dialogue} dialogue The Dialogue that this set of styles is associated with + * @param {number} scaleX The horizontal scaling of the subtitles + * @param {number} scaleY The vertical scaling of the subtitles + * @param {!libjass.renderers.RendererSettings} settings The renderer settings + * @param {!HTMLDivElement} fontSizeElement A
element to measure font sizes with + * @param {!SVGDefsElement} svgDefsElement An SVG element to append filter definitions to + * @param {!Map} fontMetricsCache Font metrics cache + * + * @constructor + */ + var SpanStyles = function () { + function SpanStyles(renderer, dialogue, scaleX, scaleY, settings, fontSizeElement, svgDefsElement, fontMetricsCache) { + this._scaleX = scaleX; + this._scaleY = scaleY; + this._settings = settings; + this._fontSizeElement = fontSizeElement; + this._svgDefsElement = svgDefsElement; + this._fontMetricsCache = fontMetricsCache; + this._nextFilterId = 0; + this._id = renderer.id + "-" + dialogue.id; + this._defaultStyle = dialogue.style; + this.reset(null); + } + /** + * Resets the styles to the defaults provided by the argument. + * + * @param {libjass.Style} newStyle The new defaults to reset the style to. If null, the styles are reset to the default style of the Dialogue. + */ + SpanStyles.prototype.reset = function (newStyle) { + if (newStyle === undefined || newStyle === null) { + newStyle = this._defaultStyle; + } + this.italic = newStyle.italic; + this.bold = newStyle.bold; + this.underline = newStyle.underline; + this.strikeThrough = newStyle.strikeThrough; + this.outlineWidth = newStyle.outlineThickness; + this.outlineHeight = newStyle.outlineThickness; + this.shadowDepthX = newStyle.shadowDepth; + this.shadowDepthY = newStyle.shadowDepth; + this.fontName = newStyle.fontName; + this.fontSize = newStyle.fontSize; + this.fontScaleX = newStyle.fontScaleX; + this.fontScaleY = newStyle.fontScaleY; + this.letterSpacing = newStyle.letterSpacing; + this.rotationX = 0; + this.rotationY = 0; + this.rotationZ = newStyle.rotationZ; + this.skewX = 0; + this.skewY = 0; + this.primaryColor = newStyle.primaryColor; + this.secondaryColor = newStyle.secondaryColor; + this.outlineColor = newStyle.outlineColor; + this.shadowColor = newStyle.shadowColor; + this.primaryAlpha = newStyle.primaryColor.alpha; + this.secondaryAlpha = newStyle.secondaryColor.alpha; + this.outlineAlpha = newStyle.outlineColor.alpha; + this.shadowAlpha = newStyle.shadowColor.alpha; + this.blur = null; + this.gaussianBlur = null; + }; + /** + * Sets the style attribute on the given span element. + * + * @param {!HTMLSpanElement} span + * @param {!AnimationCollection} animationCollection + * @return {!HTMLSpanElement} The resulting with the CSS styles applied. This may be a wrapper around the input if the styles were applied using SVG filters. + */ + SpanStyles.prototype.setStylesOnSpan = function (span, animationCollection) { + var isTextOnlySpan = span.childNodes[0] instanceof Text; + var fontStyleOrWeight = ""; + if (this._italic) { + fontStyleOrWeight += "italic "; + } + if (this._bold === true) { + fontStyleOrWeight += "bold "; + } else if (this._bold !== false) { + fontStyleOrWeight += this._bold.toFixed(0) + " "; + } + var lineHeight = this._scaleY * (isTextOnlySpan ? this._fontScaleX : 1) * this._fontSize; + var fontSize = font_size_1.fontSizeForLineHeight(this._fontName, lineHeight, this._settings.fallbackFonts, this._fontSizeElement, this._fontMetricsCache); + var fonts = this._fontName; + // Quote the font family unless it's a generic family, as those must never be quoted + switch (fonts) { + case "cursive": + case "fantasy": + case "monospace": + case "sans-serif": + case "serif": + break; + + default: + fonts = '"' + fonts + '"'; + break; + } + if (this._settings.fallbackFonts !== "") { + fonts += ", " + this._settings.fallbackFonts; + } + span.style.font = "" + fontStyleOrWeight + fontSize.toFixed(3) + "px/" + lineHeight.toFixed(3) + "px " + fonts; + var textDecoration = ""; + if (this._underline) { + textDecoration = "underline"; + } + if (this._strikeThrough) { + textDecoration += " line-through"; + } + span.style.textDecoration = textDecoration.trim(); + var transform = ""; + if (isTextOnlySpan) { + if (this._fontScaleY !== this._fontScaleX) { + transform += "scaleY(" + (this._fontScaleY / this._fontScaleX).toFixed(3) + ") "; + } + } else { + if (this._fontScaleX !== 1) { + transform += "scaleX(" + this._fontScaleX + ") "; + } + if (this._fontScaleY !== 1) { + transform += "scaleY(" + this._fontScaleY + ") "; + } + } + if (this._skewX !== 0 || this._skewY !== 0) { + transform += "matrix(1, " + this._skewY + ", " + this._skewX + ", 1, 0, 0) "; + } + if (transform !== "") { + span.style.webkitTransform = transform; + span.style.webkitTransformOrigin = "50% 50%"; + span.style.transform = transform; + span.style.transformOrigin = "50% 50%"; + span.style.display = "inline-block"; + } + span.style.letterSpacing = (this._scaleX * this._letterSpacing).toFixed(3) + "px"; + var outlineWidth = this._scaleX * this._outlineWidth; + var outlineHeight = this._scaleY * this._outlineHeight; + var shadowDepthX = this._scaleX * this._shadowDepthX; + var shadowDepthY = this._scaleY * this._shadowDepthY; + var primaryColor = this._primaryColor.withAlpha(this._primaryAlpha); + var outlineColor = this._outlineColor.withAlpha(this._outlineAlpha); + var shadowColor = this._shadowColor.withAlpha(this._shadowAlpha); + // If we're in non-SVG mode and all colors have the same alpha, then set all colors to alpha === 1 and set the common alpha as the span's opacity property instead + if (!this._settings.enableSvg && (outlineWidth === 0 && outlineHeight === 0 || outlineColor.alpha === primaryColor.alpha) && (this._shadowDepthX === 0 && this._shadowDepthY === 0 || shadowColor.alpha === primaryColor.alpha)) { + primaryColor = this._primaryColor.withAlpha(1); + outlineColor = this._outlineColor.withAlpha(1); + shadowColor = this._shadowColor.withAlpha(1); + span.style.opacity = this._primaryAlpha.toFixed(3); + } + span.style.color = primaryColor.toString(); + if (this._settings.enableSvg) { + this._svg(span, outlineWidth, outlineHeight, outlineColor, shadowDepthX, shadowDepthY, shadowColor); + } else { + this._textShadow(span, outlineWidth, outlineHeight, outlineColor, shadowDepthX, shadowDepthY, shadowColor); + } + if (this._rotationX !== 0 || this._rotationY !== 0) { + // Perspective needs to be set on a "transformable element" + span.style.display = "inline-block"; + } + span.style.webkitAnimation = animationCollection.animationStyle; + span.style.animation = animationCollection.animationStyle; + return span; + }; + /** + * @return {!HTMLBRElement} + */ + SpanStyles.prototype.makeNewLine = function () { + var result = document.createElement("br"); + result.style.lineHeight = (this._scaleY * this._fontSize).toFixed(3) + "px"; + return result; + }; + /** + * @param {!HTMLSpanElement} span + * @param {number} outlineWidth + * @param {number} outlineHeight + * @param {!libjass.parts.Color} outlineColor + * @param {number} shadowDepthX + * @param {number} shadowDepthY + * @param {!libjass.parts.Color} shadowColor + * + * @private + */ + SpanStyles.prototype._svg = function (span, outlineWidth, outlineHeight, outlineColor, shadowDepthX, shadowDepthY, shadowColor) { + var filterElement = document.createElementNS("http://www.w3.org/2000/svg", "filter"); + if (outlineWidth > 0 || outlineHeight > 0 || shadowDepthX > 0 || shadowDepthY > 0) { + // Start with SourceAlpha. Leave the alpha as 0 if it's 0, and set it to 1 if it's greater than 0 + var source = document.createElementNS("http://www.w3.org/2000/svg", "feComponentTransfer"); + filterElement.appendChild(source); + source.in1.baseVal = "SourceAlpha"; + source.result.baseVal = "source"; + var sourceAlphaTransferNode = document.createElementNS("http://www.w3.org/2000/svg", "feFuncA"); + source.appendChild(sourceAlphaTransferNode); + sourceAlphaTransferNode.type.baseVal = SVGComponentTransferFunctionElement.SVG_FECOMPONENTTRANSFER_TYPE_LINEAR; + sourceAlphaTransferNode.intercept.baseVal = 0; + /* The alphas of all colored pixels of the SourceAlpha should be made as close to 1 as possible. This way the summed outlines below will be uniformly dark. + * Multiply the pixels by 1 / primaryAlpha so that the primaryAlpha pixels become 1. A higher value would make the outline larger and too sharp, + * leading to jagged outer edge and transparent space around the inner edge between itself and the SourceGraphic. + */ + sourceAlphaTransferNode.slope.baseVal = this._primaryAlpha === 0 ? 1 : 1 / this._primaryAlpha; + /* Construct an elliptical border by merging together many rectangles. The border is creating using dilate morphology filters, but these only support + * generating rectangles. http://lists.w3.org/Archives/Public/public-fx/2012OctDec/0003.html + */ + // Merge the individual outlines + var mergedOutlines_1 = document.createElementNS("http://www.w3.org/2000/svg", "feMerge"); + filterElement.appendChild(mergedOutlines_1); + mergedOutlines_1.result.baseVal = "outline-alpha"; + var outlineNumber_1 = 0; + var increment_1 = !this._settings.preciseOutlines && this._gaussianBlur > 0 ? this._gaussianBlur : 1; + (function (addOutline) { + if (outlineWidth <= outlineHeight) { + if (outlineWidth > 0) { + var x = void 0; + for (x = 0; x <= outlineWidth; x += increment_1) { + addOutline(x, outlineHeight / outlineWidth * Math.sqrt(outlineWidth * outlineWidth - x * x)); + } + if (x !== outlineWidth + increment_1) { + addOutline(outlineWidth, 0); + } + } else { + addOutline(0, outlineHeight); + } + } else { + if (outlineHeight > 0) { + var y = void 0; + for (y = 0; y <= outlineHeight; y += increment_1) { + addOutline(outlineWidth / outlineHeight * Math.sqrt(outlineHeight * outlineHeight - y * y), y); + } + if (y !== outlineHeight + increment_1) { + addOutline(0, outlineHeight); + } + } else { + addOutline(outlineWidth, 0); + } + } + })(function (x, y) { + var outlineId = "outline" + outlineNumber_1; + var outlineFilter = document.createElementNS("http://www.w3.org/2000/svg", "feMorphology"); + filterElement.insertBefore(outlineFilter, mergedOutlines_1); + outlineFilter.in1.baseVal = "source"; + outlineFilter.operator.baseVal = SVGFEMorphologyElement.SVG_MORPHOLOGY_OPERATOR_DILATE; + outlineFilter.radiusX.baseVal = x; + outlineFilter.radiusY.baseVal = y; + outlineFilter.result.baseVal = outlineId; + var outlineReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode"); + mergedOutlines_1.appendChild(outlineReferenceNode); + outlineReferenceNode.in1.baseVal = outlineId; + outlineNumber_1++; + }); + (function (addOutline) { + if (outlineWidth % 1 > 0) { + addOutline(outlineWidth, 0); + addOutline(-outlineWidth, 0); + } + if (outlineHeight % 1 > 0) { + addOutline(0, outlineHeight); + addOutline(0, -outlineHeight); + } + })(function (x, y) { + var outlineId = "outline" + outlineNumber_1; + var outlineFilter = document.createElementNS("http://www.w3.org/2000/svg", "feOffset"); + filterElement.insertBefore(outlineFilter, mergedOutlines_1); + outlineFilter.in1.baseVal = "source"; + outlineFilter.dx.baseVal = x; + outlineFilter.dy.baseVal = y; + outlineFilter.result.baseVal = outlineId; + var outlineReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode"); + mergedOutlines_1.appendChild(outlineReferenceNode); + outlineReferenceNode.in1.baseVal = outlineId; + outlineNumber_1++; + }); + // Color it with the outline color + var coloredOutline = createComponentTransferFilter(outlineColor); + filterElement.appendChild(coloredOutline); + coloredOutline.in1.baseVal = "outline-alpha"; + // Blur the merged outline + if (this._gaussianBlur > 0) { + var gaussianBlurFilter = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur"); + filterElement.appendChild(gaussianBlurFilter); + // Don't use setStdDeviation - cloneNode() clears it in Chrome + gaussianBlurFilter.stdDeviationX.baseVal = this._gaussianBlur; + gaussianBlurFilter.stdDeviationY.baseVal = this._gaussianBlur; + } + for (var i = 0; i < this._blur; i++) { + var blurFilter = document.createElementNS("http://www.w3.org/2000/svg", "feConvolveMatrix"); + filterElement.appendChild(blurFilter); + blurFilter.setAttribute("kernelMatrix", "1 2 1 2 4 2 1 2 1"); + blurFilter.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE; + } + // Cut out the source, so only the exterior remains + var cutoutNode = document.createElementNS("http://www.w3.org/2000/svg", "feComposite"); + filterElement.appendChild(cutoutNode); + cutoutNode.in2.baseVal = "source"; + cutoutNode.operator.baseVal = SVGFECompositeElement.SVG_FECOMPOSITE_OPERATOR_OUT; + cutoutNode.result.baseVal = "outline-colored"; + if (shadowDepthX > 0 || shadowDepthY > 0) { + var shadowFilter = document.createElementNS("http://www.w3.org/2000/svg", "feOffset"); + filterElement.appendChild(shadowFilter); + shadowFilter.in1.baseVal = "outline-alpha"; + shadowFilter.dx.baseVal = shadowDepthX; + shadowFilter.dy.baseVal = shadowDepthY; + // Color it with the shadow color + var coloredShadow = createComponentTransferFilter(shadowColor); + filterElement.appendChild(coloredShadow); + var lastFilter = coloredShadow; + // Blur the shadow + if (this._gaussianBlur > 0) { + var gaussianBlurFilter = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur"); + filterElement.appendChild(gaussianBlurFilter); + // Don't use setStdDeviation - cloneNode() clears it in Chrome + gaussianBlurFilter.stdDeviationX.baseVal = this._gaussianBlur; + gaussianBlurFilter.stdDeviationY.baseVal = this._gaussianBlur; + lastFilter = gaussianBlurFilter; + } + for (var i = 0; i < this._blur; i++) { + var blurFilter = document.createElementNS("http://www.w3.org/2000/svg", "feConvolveMatrix"); + filterElement.appendChild(blurFilter); + blurFilter.setAttribute("kernelMatrix", "1 2 1 2 4 2 1 2 1"); + blurFilter.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE; + lastFilter = blurFilter; + } + lastFilter.result.baseVal = "shadow"; + } + // Merge the main text, outline and shadow + var mergedResult = document.createElementNS("http://www.w3.org/2000/svg", "feMerge"); + filterElement.appendChild(mergedResult); + if (shadowDepthX > 0 || shadowDepthY > 0) { + var shadowReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode"); + mergedResult.appendChild(shadowReferenceNode); + shadowReferenceNode.in1.baseVal = "shadow"; + } + var outlineReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode"); + mergedResult.appendChild(outlineReferenceNode); + outlineReferenceNode.in1.baseVal = "outline-colored"; + var sourceGraphicReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode"); + mergedResult.appendChild(sourceGraphicReferenceNode); + sourceGraphicReferenceNode.in1.baseVal = "SourceGraphic"; + } else { + // Blur the source graphic directly + if (this._gaussianBlur > 0) { + var gaussianBlurFilter = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur"); + filterElement.appendChild(gaussianBlurFilter); + // Don't use setStdDeviation - cloneNode() clears it in Chrome + gaussianBlurFilter.stdDeviationX.baseVal = this._gaussianBlur; + gaussianBlurFilter.stdDeviationY.baseVal = this._gaussianBlur; + } + for (var i = 0; i < this._blur; i++) { + var blurFilter = document.createElementNS("http://www.w3.org/2000/svg", "feConvolveMatrix"); + filterElement.appendChild(blurFilter); + blurFilter.setAttribute("kernelMatrix", "1 2 1 2 4 2 1 2 1"); + blurFilter.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE; + } + } + if (filterElement.childElementCount > 0) { + var filterId = "libjass-svg-filter-" + this._id + "-" + this._nextFilterId++; + this._svgDefsElement.appendChild(filterElement); + filterElement.id = filterId; + filterElement.x.baseVal.valueAsString = "-50%"; + filterElement.width.baseVal.valueAsString = "200%"; + filterElement.y.baseVal.valueAsString = "-50%"; + filterElement.height.baseVal.valueAsString = "200%"; + var filterProperty = 'url("#' + filterId + '")'; + span.style.webkitFilter = filterProperty; + span.style.filter = filterProperty; + } + }; + /** + * @param {!HTMLSpanElement} span + * @param {number} outlineWidth + * @param {number} outlineHeight + * @param {!libjass.parts.Color} outlineColor + * @param {number} shadowDepthX + * @param {number} shadowDepthY + * @param {!libjass.parts.Color} shadowColor + * + * @private + */ + SpanStyles.prototype._textShadow = function (span, outlineWidth, outlineHeight, outlineColor, shadowDepthX, shadowDepthY, shadowColor) { + var _this = this; + if (outlineWidth > 0 || outlineHeight > 0) { + var outlineCssString_1 = ""; + var shadowCssString_1 = ""; + (function (addOutline) { + for (var x = 0; x <= outlineWidth; x++) { + var maxY = outlineWidth === 0 ? outlineHeight : outlineHeight * Math.sqrt(1 - x * x / (outlineWidth * outlineWidth)); + for (var y = 0; y <= maxY; y++) { + addOutline(x, y); + if (x !== 0) { + addOutline(-x, y); + } + if (y !== 0) { + addOutline(x, -y); + } + if (x !== 0 && y !== 0) { + addOutline(-x, -y); + } + } + } + if (outlineWidth % 1 > 0) { + addOutline(outlineWidth, 0); + addOutline(-outlineWidth, 0); + } + if (outlineHeight % 1 > 0) { + addOutline(0, outlineHeight); + addOutline(0, -outlineHeight); + } + })(function (x, y) { + outlineCssString_1 += ", " + outlineColor.toString() + " " + x.toFixed(3) + "px " + y.toFixed(3) + "px " + _this._gaussianBlur.toFixed(3) + "px"; + if (_this._shadowDepthX !== 0 || _this._shadowDepthY !== 0) { + shadowCssString_1 += ", " + shadowColor.toString() + " " + (x + shadowDepthX).toFixed(3) + "px " + (y + shadowDepthY).toFixed(3) + "px " + _this._gaussianBlur.toFixed(3) + "px"; + } + }); + span.style.textShadow = (outlineCssString_1 + shadowCssString_1).substr(", ".length); + } else if (this._shadowDepthX !== 0 || this._shadowDepthY !== 0) { + var shadowCssString = shadowColor.toString() + " " + shadowDepthX.toFixed(3) + "px " + shadowDepthY.toFixed(3) + "px 0px"; + if (span.style.textShadow === "") { + span.style.textShadow = shadowCssString; + } else { + span.style.textShadow += ", " + shadowCssString; + } + } + }; + Object.defineProperty(SpanStyles.prototype, "italic", { + /** + * Sets the italic property. null defaults it to the default style's value. + * + * @type {?boolean} + */ + set: function (value) { + this._italic = valueOrDefault(value, this._defaultStyle.italic); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "bold", { + /** + * Sets the bold property. null defaults it to the default style's value. + * + * @type {(?boolean|?number)} + */ + set: function (value) { + this._bold = valueOrDefault(value, this._defaultStyle.bold); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "underline", { + /** + * Sets the underline property. null defaults it to the default style's value. + * + * @type {?boolean} + */ + set: function (value) { + this._underline = valueOrDefault(value, this._defaultStyle.underline); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "strikeThrough", { + /** + * Sets the strike-through property. null defaults it to the default style's value. + * + * @type {?boolean} + */ + set: function (value) { + this._strikeThrough = valueOrDefault(value, this._defaultStyle.strikeThrough); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "outlineWidth", { + /** + * Gets the outline width property. + * + * @type {number} + */ + get: function () { + return this._outlineWidth; + }, + /** + * Sets the outline width property. null defaults it to the style's original outline width value. + * + * @type {?number} + */ + set: function (value) { + this._outlineWidth = valueOrDefault(value, this._defaultStyle.outlineThickness); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "outlineHeight", { + /** + * Gets the outline height property. + * + * @type {number} + */ + get: function () { + return this._outlineHeight; + }, + /** + * Sets the outline height property. null defaults it to the style's original outline height value. + * + * @type {?number} + */ + set: function (value) { + this._outlineHeight = valueOrDefault(value, this._defaultStyle.outlineThickness); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "shadowDepthX", { + /** + * Gets the shadow width property. + * + * @type {number} + */ + get: function () { + return this._shadowDepthX; + }, + /** + * Sets the shadow width property. null defaults it to the style's original shadow depth value. + * + * @type {?number} + */ + set: function (value) { + this._shadowDepthX = valueOrDefault(value, this._defaultStyle.shadowDepth); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "shadowDepthY", { + /** + * Gets the shadow height property. + * + * @type {number} + */ + get: function () { + return this._shadowDepthY; + }, + /** + * Sets the shadow height property. null defaults it to the style's original shadow depth value. + * + * @type {?number} + */ + set: function (value) { + this._shadowDepthY = valueOrDefault(value, this._defaultStyle.shadowDepth); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "blur", { + /** + * Gets the blur property. + * + * @type {number} + */ + get: function () { + return this._blur; + }, + /** + * Sets the blur property. null defaults it to 0. + * + * @type {?number} + */ + set: function (value) { + this._blur = valueOrDefault(value, 0); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "gaussianBlur", { + /** + * Gets the Gaussian blur property. + * + * @type {number} + */ + get: function () { + return this._gaussianBlur; + }, + /** + * Sets the Gaussian blur property. null defaults it to 0. + * + * @type {?number} + */ + set: function (value) { + this._gaussianBlur = valueOrDefault(value, 0); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "fontName", { + /** + * Sets the font name property. null defaults it to the default style's value. + * + * @type {?string} + */ + set: function (value) { + this._fontName = valueOrDefault(value, this._defaultStyle.fontName); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "fontSize", { + /** + * Gets the font size property. + * + * @type {number} + */ + get: function () { + return this._fontSize; + }, + /** + * Sets the font size property. null defaults it to the default style's value. + * + * @type {?number} + */ + set: function (value) { + this._fontSize = valueOrDefault(value, this._defaultStyle.fontSize); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "fontScaleX", { + /** + * Gets the horizontal font scaling property. + * + * @type {number} + */ + get: function () { + return this._fontScaleX; + }, + /** + * Sets the horizontal font scaling property. null defaults it to the default style's value. + * + * @type {?number} + */ + set: function (value) { + this._fontScaleX = valueOrDefault(value, this._defaultStyle.fontScaleX); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "fontScaleY", { + /** + * Gets the vertical font scaling property. + * + * @type {number} + */ + get: function () { + return this._fontScaleY; + }, + /** + * Sets the vertical font scaling property. null defaults it to the default style's value. + * + * @type {?number} + */ + set: function (value) { + this._fontScaleY = valueOrDefault(value, this._defaultStyle.fontScaleY); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "letterSpacing", { + /** + * Gets the letter spacing property. + * + * @type {number} + */ + get: function () { + return this._letterSpacing; + }, + /** + * Sets the letter spacing property. null defaults it to the default style's value. + * + * @type {?number} + */ + set: function (value) { + this._letterSpacing = valueOrDefault(value, this._defaultStyle.letterSpacing); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "rotationX", { + /** + * Gets the X-axis rotation property. + * + * @type {number} + */ + get: function () { + return this._rotationX; + }, + /** + * Sets the X-axis rotation property. + * + * @type {?number} + */ + set: function (value) { + this._rotationX = valueOrDefault(value, 0); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "rotationY", { + /** + * Gets the Y-axis rotation property. + * + * @type {number} + */ + get: function () { + return this._rotationY; + }, + /** + * Sets the Y-axis rotation property. + * + * @type {?number} + */ + set: function (value) { + this._rotationY = valueOrDefault(value, 0); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "rotationZ", { + /** + * Gets the Z-axis rotation property. + * + * @type {number} + */ + get: function () { + return this._rotationZ; + }, + /** + * Sets the Z-axis rotation property. + * + * @type {?number} + */ + set: function (value) { + this._rotationZ = valueOrDefault(value, this._defaultStyle.rotationZ); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "skewX", { + /** + * Gets the X-axis skew property. + * + * @type {number} + */ + get: function () { + return this._skewX; + }, + /** + * Sets the X-axis skew property. + * + * @type {?number} + */ + set: function (value) { + this._skewX = valueOrDefault(value, 0); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "skewY", { + /** + * Gets the Y-axis skew property. + * + * @type {number} + */ + get: function () { + return this._skewY; + }, + /** + * Sets the Y-axis skew property. + * + * @type {?number} + */ + set: function (value) { + this._skewY = valueOrDefault(value, 0); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "primaryColor", { + /** + * Gets the primary color property. + * + * @type {!libjass.Color} + */ + get: function () { + return this._primaryColor; + }, + /** + * Sets the primary color property. null defaults it to the default style's value. + * + * @type {libjass.Color} + */ + set: function (value) { + this._primaryColor = valueOrDefault(value, this._defaultStyle.primaryColor); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "secondaryColor", { + /** + * Gets the secondary color property. + * + * @type {!libjass.Color} + */ + get: function () { + return this._secondaryColor; + }, + /** + * Sets the secondary color property. null defaults it to the default style's value. + * + * @type {libjass.Color} + */ + set: function (value) { + this._secondaryColor = valueOrDefault(value, this._defaultStyle.secondaryColor); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "outlineColor", { + /** + * Gets the outline color property. + * + * @type {!libjass.Color} + */ + get: function () { + return this._outlineColor; + }, + /** + * Sets the outline color property. null defaults it to the default style's value. + * + * @type {libjass.Color} + */ + set: function (value) { + this._outlineColor = valueOrDefault(value, this._defaultStyle.outlineColor); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "shadowColor", { + /** + * Gets the shadow color property. + * + * @type {!libjass.Color} + */ + get: function () { + return this._shadowColor; + }, + /** + * Sets the shadow color property. null defaults it to the default style's value. + * + * @type {libjass.Color} + */ + set: function (value) { + this._shadowColor = valueOrDefault(value, this._defaultStyle.shadowColor); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "primaryAlpha", { + /** + * Gets the primary alpha property. + * + * @type {number} + */ + get: function () { + return this._primaryAlpha; + }, + /** + * Sets the primary alpha property. + * + * @type {?number} + */ + set: function (value) { + this._primaryAlpha = valueOrDefault(value, this._defaultStyle.primaryColor.alpha); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "secondaryAlpha", { + /** + * Gets the secondary alpha property. + * + * @type {number} + */ + get: function () { + return this._secondaryAlpha; + }, + /** + * Sets the secondary alpha property. + * + * @type {?number} + */ + set: function (value) { + this._secondaryAlpha = valueOrDefault(value, this._defaultStyle.secondaryColor.alpha); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "outlineAlpha", { + /** + * Gets the outline alpha property. + * + * @type {number} + */ + get: function () { + return this._outlineAlpha; + }, + /** + * Sets the outline alpha property. + * + * @type {?number} + */ + set: function (value) { + this._outlineAlpha = valueOrDefault(value, this._defaultStyle.outlineColor.alpha); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(SpanStyles.prototype, "shadowAlpha", { + /** + * Gets the shadow alpha property. + * + * @type {number} + */ + get: function () { + return this._shadowAlpha; + }, + /** + * Sets the shadow alpha property. + * + * @type {?number} + */ + set: function (value) { + this._shadowAlpha = valueOrDefault(value, this._defaultStyle.shadowColor.alpha); + }, + enumerable: true, + configurable: true + }); + return SpanStyles; + }(); + exports.SpanStyles = SpanStyles; + /** + * @param {!libjass.parts.Color} color + * @return {!SVGFEComponentTransferElement} + * + * @private + */ + function createComponentTransferFilter(color) { + var result = document.createElementNS("http://www.w3.org/2000/svg", "feComponentTransfer"); + var redTransferNode = document.createElementNS("http://www.w3.org/2000/svg", "feFuncR"); + result.appendChild(redTransferNode); + redTransferNode.type.baseVal = SVGComponentTransferFunctionElement.SVG_FECOMPONENTTRANSFER_TYPE_LINEAR; + redTransferNode.slope.baseVal = 0; + redTransferNode.intercept.baseVal = color.red / 255; + var greenTransferNode = document.createElementNS("http://www.w3.org/2000/svg", "feFuncG"); + result.appendChild(greenTransferNode); + greenTransferNode.type.baseVal = SVGComponentTransferFunctionElement.SVG_FECOMPONENTTRANSFER_TYPE_LINEAR; + greenTransferNode.slope.baseVal = 0; + greenTransferNode.intercept.baseVal = color.green / 255; + var blueTransferNode = document.createElementNS("http://www.w3.org/2000/svg", "feFuncB"); + result.appendChild(blueTransferNode); + blueTransferNode.type.baseVal = SVGComponentTransferFunctionElement.SVG_FECOMPONENTTRANSFER_TYPE_LINEAR; + blueTransferNode.slope.baseVal = 0; + blueTransferNode.intercept.baseVal = color.blue / 255; + var alphaTransferNode = document.createElementNS("http://www.w3.org/2000/svg", "feFuncA"); + result.appendChild(alphaTransferNode); + alphaTransferNode.type.baseVal = SVGComponentTransferFunctionElement.SVG_FECOMPONENTTRANSFER_TYPE_LINEAR; + alphaTransferNode.slope.baseVal = color.alpha; + alphaTransferNode.intercept.baseVal = 0; + return result; + } + /** + * @param {?T} newValue + * @param {!T} defaultValue + * @return {!T} + * + * @template T + * @private + */ + function valueOrDefault(newValue, defaultValue) { + return newValue !== null ? newValue : defaultValue; + } + }); + def("renderers/web/font-size", [ "utility/promise" ], function (promise_6, exports) { + /** + * @param {string} fontFamily + * @param {number} fontSize + * @param {string} fallbackFonts + * @param {!HTMLDivElement} fontSizeElement + * + * @private + */ + function prepareFontSizeElement(fontFamily, fontSize, fallbackFonts, fontSizeElement) { + var fonts = '"' + fontFamily + '"'; + if (fallbackFonts !== "") { + fonts += ", " + fallbackFonts; + } + fontSizeElement.style.fontFamily = fonts; + fontSizeElement.style.fontSize = fontSize + "px"; + } + /** + * @param {string} fontFamily + * @param {number} fontSize + * @param {string} fallbackFonts + * @param {!HTMLDivElement} fontSizeElement + * @return {!Promise.} + * + * @private + */ + function lineHeightForFontSize(fontFamily, fontSize, fallbackFonts, fontSizeElement) { + prepareFontSizeElement(fontFamily, fontSize, fallbackFonts, fontSizeElement); + return new promise_6.Promise(function (resolve) { + return setTimeout(function () { + return resolve(fontSizeElement.offsetHeight); + }, 1e3); + }); + } + /** + * @param {string} fontFamily + * @param {number} fontSize + * @param {string} fallbackFonts + * @param {!HTMLDivElement} fontSizeElement + * @return {number} + * + * @private + */ + function lineHeightForFontSizeSync(fontFamily, fontSize, fallbackFonts, fontSizeElement) { + prepareFontSizeElement(fontFamily, fontSize, fallbackFonts, fontSizeElement); + return fontSizeElement.offsetHeight; + } + /** + * @param {number} lowerLineHeight + * @param {number} upperLineHeight + * @return {[number, number]} + * + * @private + */ + function fontMetricsFromLineHeights(lowerLineHeight, upperLineHeight) { + return [ lowerLineHeight, (360 - 180) / (upperLineHeight - lowerLineHeight) ]; + } + /** + * Calculates font metrics for the given font family. + * + * @param {string} fontFamily + * @param {string} fallbackFonts + * @param {!HTMLDivElement} fontSizeElement + * @return {!Promise.<[number, number]>} + */ + function calculateFontMetrics(fontFamily, fallbackFonts, fontSizeElement) { + return lineHeightForFontSize(fontFamily, 180, fallbackFonts, fontSizeElement).then(function (lowerLineHeight) { + return lineHeightForFontSize(fontFamily, 360, fallbackFonts, fontSizeElement).then(function (upperLineHeight) { + return fontMetricsFromLineHeights(lowerLineHeight, upperLineHeight); + }); + }); + } + exports.calculateFontMetrics = calculateFontMetrics; + /** + * @param {number} lineHeight + * @param {number} lowerLineHeight + * @param {number} factor + * @return {number} + * + * @private + */ + function fontSizeFromMetrics(lineHeight, lowerLineHeight, factor) { + return 180 + (lineHeight - lowerLineHeight) * factor; + } + /** + * Uses linear interpolation to calculate the CSS font size that would give the specified line height for the specified font family. + * + * WARNING: If fontMetricsCache doesn't already contain a cached value for this font family, and it is not a font already installed on the user's device, then this function + * may return wrong values. To avoid this, make sure to preload the font using the {@link libjass.renderers.RendererSettings.fontMap} property when constructing the renderer. + * + * @param {string} fontFamily + * @param {number} lineHeight + * @param {string} fallbackFonts + * @param {!HTMLDivElement} fontSizeElement + * @param {!Map.} fontMetricsCache + * @return {number} + */ + function fontSizeForLineHeight(fontFamily, lineHeight, fallbackFonts, fontSizeElement, fontMetricsCache) { + var existingMetrics = fontMetricsCache.get(fontFamily); + if (existingMetrics === undefined) { + var lowerLineHeight_1 = lineHeightForFontSizeSync(fontFamily, 180, fallbackFonts, fontSizeElement); + var upperLineHeight = lineHeightForFontSizeSync(fontFamily, 360, fallbackFonts, fontSizeElement); + fontMetricsCache.set(fontFamily, existingMetrics = fontMetricsFromLineHeights(lowerLineHeight_1, upperLineHeight)); + } + var lowerLineHeight = existingMetrics[0]; + var factor = existingMetrics[1]; + return fontSizeFromMetrics(lineHeight, lowerLineHeight, factor); + } + exports.fontSizeForLineHeight = fontSizeForLineHeight; + }); + def("renderers/web/drawing-styles", [ "parts/index" ], function (parts, exports) { + /** + * This class represents an ASS drawing - a set of drawing instructions between {\p} tags. + * + * @param {number} outputScaleX + * @param {number} outputScaleY + * + * @constructor + */ + var DrawingStyles = function () { + function DrawingStyles(outputScaleX, outputScaleY) { + this._outputScaleX = outputScaleX; + this._outputScaleY = outputScaleY; + this._scale = 1; + this._baselineOffset = 0; + } + Object.defineProperty(DrawingStyles.prototype, "scale", { + /** + * @type {number} + */ + set: function (value) { + this._scale = value; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(DrawingStyles.prototype, "baselineOffset", { + /** + * @type {number} + */ + set: function (value) { + this._baselineOffset = value; + }, + enumerable: true, + configurable: true + }); + /** + * Converts this drawing to an element. + * + * @param {!libjass.parts.DrawingInstructions} drawingInstructions + * @param {!libjass.parts.Color} fillColor + * @return {!SVGSVGElement} + */ + DrawingStyles.prototype.toSVG = function (drawingInstructions, fillColor) { + var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + svg.setAttribute("version", "1.1"); + if (drawingInstructions.instructions.length === 0) { + return svg; + } + var scaleFactor = Math.pow(2, this._scale - 1); + var scaleX = this._outputScaleX / scaleFactor; + var scaleY = this._outputScaleY / scaleFactor; + var path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + var pathString = ""; + var bboxMinX = Infinity; + var bboxMaxX = -Infinity; + var bboxMinY = Infinity; + var bboxMaxY = -Infinity; + for (var _i = 0, _a = drawingInstructions.instructions; _i < _a.length; _i++) { + var instruction = _a[_i]; + if (instruction instanceof parts.drawing.MoveInstruction) { + pathString += " M " + instruction.x.toFixed(3) + " " + (instruction.y + this._baselineOffset).toFixed(3); + bboxMinX = Math.min(bboxMinX, instruction.x); + bboxMaxX = Math.max(bboxMaxX, instruction.x); + bboxMinY = Math.min(bboxMinY, instruction.y + this._baselineOffset); + bboxMaxY = Math.max(bboxMaxY, instruction.y + this._baselineOffset); + } else if (instruction instanceof parts.drawing.LineInstruction) { + pathString += " L " + instruction.x.toFixed(3) + " " + (instruction.y + this._baselineOffset).toFixed(3); + bboxMinX = Math.min(bboxMinX, instruction.x); + bboxMaxX = Math.max(bboxMaxX, instruction.x); + bboxMinY = Math.min(bboxMinY, instruction.y + this._baselineOffset); + bboxMaxY = Math.max(bboxMaxY, instruction.y + this._baselineOffset); + } else if (instruction instanceof parts.drawing.CubicBezierCurveInstruction) { + pathString += " C " + instruction.x1.toFixed(3) + " " + (instruction.y1 + this._baselineOffset).toFixed(3) + " " + instruction.x2.toFixed(3) + " " + (instruction.y2 + this._baselineOffset).toFixed(3) + " " + instruction.x3.toFixed(3) + " " + (instruction.y3 + this._baselineOffset).toFixed(3); + bboxMinX = Math.min(bboxMinX, instruction.x1, instruction.x2, instruction.x3); + bboxMaxX = Math.max(bboxMaxX, instruction.x1, instruction.x2, instruction.x3); + bboxMinY = Math.min(bboxMinY, instruction.y1 + this._baselineOffset, instruction.y2 + this._baselineOffset, instruction.y3 + this._baselineOffset); + bboxMaxY = Math.max(bboxMaxY, instruction.y1 + this._baselineOffset, instruction.y2 + this._baselineOffset, instruction.y3 + this._baselineOffset); + } + } + bboxMinX *= scaleX; + bboxMaxX *= scaleX; + bboxMinY *= scaleY; + bboxMaxY *= scaleY; + var bboxWidth = bboxMaxX - bboxMinX; + var bboxHeight = bboxMaxY - bboxMinY; + svg.width.baseVal.valueAsString = bboxWidth.toFixed(3) + "px"; + svg.height.baseVal.valueAsString = bboxHeight.toFixed(3) + "px"; + // svg.viewBox.baseVal is null in atleast FF. See https://bugzilla.mozilla.org/show_bug.cgi?id=888307 which justifies it with SVG 1.2 spec. + svg.setAttribute("viewBox", bboxMinX + " " + bboxMinY + " " + bboxWidth + " " + bboxHeight); + svg.style.position = "relative"; + svg.style.left = bboxMinX.toFixed(3) + "px"; + svg.style.top = bboxMinY.toFixed(3) + "px"; + var g = document.createElementNS("http://www.w3.org/2000/svg", "g"); + svg.appendChild(g); + g.setAttribute("transform", "scale(" + scaleX.toFixed(3) + " " + scaleY.toFixed(3) + ")"); + g.appendChild(path); + path.setAttribute("d", pathString); + path.setAttribute("fill", fillColor.toString()); + return svg; + }; + return DrawingStyles; + }(); + exports.DrawingStyles = DrawingStyles; + }); + def("renderers/web/animation-collection", [ "utility/map" ], function (map_13, exports) { + /** + * This class represents a collection of animations. Each animation contains one or more keyframes. + * The collection can then be converted to a CSS3 representation. + * + * @param {!libjass.renderers.NullRenderer} renderer The renderer that this collection is associated with + * @param {!HTMLStyleElement} style A /g, "{\\b1}").replace(/\{b\}/g, "{\\b1}").replace(/<\/b>/g, "{\\b0}").replace(/\{\/b\}/g, "{\\b0}").replace(//g, "{\\i1}").replace(/\{i\}/g, "{\\i1}").replace(/<\/i>/g, "{\\i0}").replace(/\{\/i\}/g, "{\\i0}").replace(//g, "{\\u1}").replace(/\{u\}/g, "{\\u1}").replace(/<\/u>/g, "{\\u0}").replace(/\{\/u\}/g, "{\\u0}").replace(//g, function (/* ujs:unreferenced */ substring, red, green, blue) { + return "{c&H" + blue + green + red + "&}"; + }).replace(/<\/font>/g, "{\\c}"); + if (this._currentDialogueText !== null) { + this._currentDialogueText += "\\N" + line; + } else { + this._currentDialogueText = line; + } + } + } + this._stream.nextLine().then(function (line) { + return _this._onNextLine(line); + }, function (reason) { + _this._deferred.reject(reason); + }); + }; + return SrtStreamParser; + }(); + exports.SrtStreamParser = SrtStreamParser; + /** + * Converts a uuencoded string to a base64 string. + * + * @param {string} str + * @return {string} + * + * @private + */ + function uuencodedToBase64(str) { + var result = ""; + for (var i = 0; i < str.length; i++) { + var charCode = str.charCodeAt(i) - 33; + if (charCode < 0 || charCode > 63) { + throw new Error("Out-of-range character code " + charCode + " at index " + i + " in string " + str); + } + if (charCode < 26) { + result += String.fromCharCode("A".charCodeAt(0) + charCode); + } else if (charCode < 52) { + result += String.fromCharCode("a".charCodeAt(0) + charCode - 26); + } else if (charCode < 62) { + result += String.fromCharCode("0".charCodeAt(0) + charCode - 52); + } else if (charCode === 62) { + result += "+"; + } else { + result += "/"; + } + } + return result; + } + }); + def("types/ass", [ "parser/index", "parser/misc", "serialization", "settings", "utility/map", "utility/promise", "types/dialogue", "types/misc", "types/script-properties", "types/style" ], function (parser, misc_5, serialization_7, settings_3, map_8, promise_4, dialogue_1, misc_6, script_properties_1, style_1, exports) { + /** + * This class represents an ASS script. It contains the {@link libjass.ScriptProperties}, an array of {@link libjass.Style}s, and an array of {@link libjass.Dialogue}s. + * + * @constructor + * @memberOf libjass + */ + var ASS = function () { + function ASS() { + this._properties = new script_properties_1.ScriptProperties(); + this._styles = new map_8.Map(); + this._dialogues = []; + this._attachments = []; + this._stylesFormatSpecifier = null; + this._dialoguesFormatSpecifier = null; + // Deprecated constructor argument + if (arguments.length === 1) { + throw new Error("Constructor `new ASS(rawASS)` has been deprecated. Use `ASS.fromString(rawASS)` instead."); + } + this._styles.set("Default", new style_1.Style(new map_8.Map([ [ "Name", "Default" ] ]))); + } + /** + * Creates an ASS object from the raw text of an ASS script. + * + * @param {string} raw The raw text of the script. + * @param {(number|string)=0} type The type of the script. One of the {@link libjass.Format} constants, or one of the strings "ass" and "srt". + * @return {!Promise.} + * + * @static + */ + ASS.fromString = function (raw, type) { + if (type === void 0) { + type = misc_6.Format.ASS; + } + return ASS.fromStream(new parser.StringStream(raw), type); + }; + /** + * Creates an ASS object from the given {@link libjass.parser.Stream}. + * + * @param {!libjass.parser.Stream} stream The stream to parse the script from + * @param {(number|string)=0} type The type of the script. One of the {@link libjass.Format} constants, or one of the strings "ass" and "srt". + * @return {!Promise.} A promise that will be resolved with the ASS object when it has been fully parsed + * + * @static + */ + ASS.fromStream = function (stream, type) { + if (type === void 0) { + type = misc_6.Format.ASS; + } + switch (type) { + case misc_6.Format.ASS: + case "ass": + return new parser.StreamParser(stream).ass; + + case misc_6.Format.SRT: + case "srt": + return new parser.SrtStreamParser(stream).ass; + + default: + throw new Error("Invalid value of type: " + type); + } + }; + /** + * Creates an ASS object from the given URL. + * + * @param {string} url The URL of the script. + * @param {(number|string)=0} type The type of the script. One of the {@link libjass.Format} constants, or one of the strings "ass" and "srt". + * @return {!Promise.} A promise that will be resolved with the ASS object when it has been fully parsed + * + * @static + */ + ASS.fromUrl = function (url, type) { + if (type === void 0) { + type = misc_6.Format.ASS; + } + var fetchPromise; + if (typeof global.fetch === "function" && typeof global.ReadableStream === "function" && typeof global.ReadableStream.prototype.getReader === "function" && typeof global.TextDecoder === "function") { + fetchPromise = global.fetch(url).then(function (response) { + if (response.ok === false || response.ok === undefined && (response.status === undefined || response.status < 200 || response.status > 299)) { + throw new Error("HTTP request for " + url + " failed with status code " + response.status); + } + return ASS.fromReadableStream(response.body, "utf-8", type); + }); + } else { + fetchPromise = promise_4.Promise.reject(new Error("Not supported.")); + } + return fetchPromise.catch(function (reason) { + if (settings_3.debugMode) { + console.log("fetch() failed, falling back to XHR: %o", reason); + } + var xhr = new XMLHttpRequest(); + var result = ASS.fromStream(new parser.XhrStream(xhr), type); + xhr.open("GET", url, true); + xhr.send(); + return result; + }); + }; + /** + * Creates an ASS object from the given ReadableStream. + * + * @param {!ReadableStream} stream + * @param {string="utf-8"} encoding + * @param {(number|string)=0} type The type of the script. One of the {@link libjass.Format} constants, or one of the strings "ass" and "srt". + * @return {!Promise.} A promise that will be resolved with the ASS object when it has been fully parsed + * + * @static + */ + ASS.fromReadableStream = function (stream, encoding, type) { + if (encoding === void 0) { + encoding = "utf-8"; + } + if (type === void 0) { + type = misc_6.Format.ASS; + } + return ASS.fromStream(new parser.BrowserReadableStream(stream, encoding), type); + }; + /** + * Custom deserialization for ASS objects. + * + * @param {!*} obj + * @return {!libjass.ASS} + * + * @static + */ + ASS.fromJSON = function (obj) { + var result = Object.create(ASS.prototype); + result._properties = obj._properties; + result._styles = new map_8.Map(); + for (var _i = 0, _a = Object.keys(obj._styles); _i < _a.length; _i++) { + var name_1 = _a[_i]; + var style = obj._styles[name_1]; + result._styles.set(name_1, style); + } + result._dialogues = obj._dialogues; + result._attachments = obj._attachments; + result._stylesFormatSpecifier = obj._stylesFormatSpecifier; + result._dialoguesFormatSpecifier = obj._dialoguesFormatSpecifier; + return result; + }; + Object.defineProperty(ASS.prototype, "properties", { + /** + * The properties of this script. + * + * @type {!libjass.ScriptProperties} + */ + get: function () { + return this._properties; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ASS.prototype, "styles", { + /** + * The styles in this script. + * + * @type {!Map.} + */ + get: function () { + return this._styles; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ASS.prototype, "dialogues", { + /** + * The dialogues in this script. + * + * @type {!Array.} + */ + get: function () { + return this._dialogues; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ASS.prototype, "attachments", { + /** + * The attachments of this script. + * + * @type {!Array.} + */ + get: function () { + return this._attachments; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ASS.prototype, "stylesFormatSpecifier", { + /** + * The format specifier for the styles section. + * + * @type {Array.} + */ + get: function () { + return this._stylesFormatSpecifier; + }, + /** + * The format specifier for the events section. + * + * @type {Array.} + */ + set: function (value) { + this._stylesFormatSpecifier = value; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ASS.prototype, "dialoguesFormatSpecifier", { + /** + * The format specifier for the styles section. + * + * @type {Array.} + */ + get: function () { + return this._dialoguesFormatSpecifier; + }, + /** + * The format specifier for the events section. + * + * @type {Array.} + */ + set: function (value) { + this._dialoguesFormatSpecifier = value; + }, + enumerable: true, + configurable: true + }); + /** + * Add a style to this ASS script. + * + * @param {string} line The line from the script that contains the new style. + */ + ASS.prototype.addStyle = function (line) { + if (this._stylesFormatSpecifier === null) { + throw new Error("stylesFormatSpecifier is not set."); + } + var styleLine = misc_5.parseLineIntoTypedTemplate(line, this._stylesFormatSpecifier); + if (styleLine === null || styleLine.type !== "Style") { + return; + } + var styleTemplate = styleLine.template; + if (settings_3.verboseMode) { + var repr_1 = ""; + styleTemplate.forEach(function (value, key) { + return repr_1 += key + " = " + value + ", "; + }); + console.log("Read style: " + repr_1); + } + // Create the dialogue and add it to the dialogues array + var style = new style_1.Style(styleTemplate); + this._styles.set(style.name, style); + }; + /** + * Add an event to this ASS script. + * + * @param {string} line The line from the script that contains the new event. + */ + ASS.prototype.addEvent = function (line) { + if (this._dialoguesFormatSpecifier === null) { + throw new Error("dialoguesFormatSpecifier is not set."); + } + var dialogueLine = misc_5.parseLineIntoTypedTemplate(line, this._dialoguesFormatSpecifier); + if (dialogueLine === null || dialogueLine.type !== "Dialogue") { + return; + } + var dialogueTemplate = dialogueLine.template; + if (settings_3.verboseMode) { + var repr_2 = ""; + dialogueTemplate.forEach(function (value, key) { + return repr_2 += key + " = " + value + ", "; + }); + console.log("Read dialogue: " + repr_2); + } + // Create the dialogue and add it to the dialogues array + this.dialogues.push(new dialogue_1.Dialogue(dialogueTemplate, this)); + }; + /** + * Add an attachment to this ASS script. + * + * @param {!libjass.Attachment} attachment + */ + ASS.prototype.addAttachment = function (attachment) { + this._attachments.push(attachment); + }; + /** + * Custom JSON serialization for ASS objects. + * + * @return {!*} + */ + ASS.prototype.toJSON = function () { + var result = Object.create(null); + result._properties = this._properties; + result._styles = Object.create(null); + this._styles.forEach(function (style, name) { + return result._styles[name] = style; + }); + result._dialogues = this._dialogues; + result._attachments = this._attachments; + result._stylesFormatSpecifier = this._stylesFormatSpecifier; + result._dialoguesFormatSpecifier = this._dialoguesFormatSpecifier; + result._classTag = ASS.prototype._classTag; + return result; + }; + ASS = __decorate([ serialization_7.registerClass ], ASS); + return ASS; + }(); + exports.ASS = ASS; + }); + def("types/script-properties", [ "serialization" ], function (serialization_6, exports) { + /** + * This class represents the properties of a {@link libjass.ASS} script. + * + * @constructor + * @memberOf libjass + */ + var ScriptProperties = function () { + function ScriptProperties() {} + Object.defineProperty(ScriptProperties.prototype, "resolutionX", { + /** + * The horizontal script resolution. + * + * @type {number} + */ + get: function () { + return this._resolutionX; + }, + /** + * The horizontal script resolution. + * + * @type {number} + */ + set: function (value) { + this._resolutionX = value; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ScriptProperties.prototype, "resolutionY", { + /** + * The vertical script resolution. + * + * @type {number} + */ + get: function () { + return this._resolutionY; + }, + /** + * The vertical script resolution. + * + * @type {number} + */ + set: function (value) { + this._resolutionY = value; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ScriptProperties.prototype, "wrappingStyle", { + /** + * The wrap style. One of the {@link libjass.WrappingStyle} constants. + * + * @type {number} + */ + get: function () { + return this._wrappingStyle; + }, + /** + * The wrap style. One of the {@link libjass.WrappingStyle} constants. + * + * @type {number} + */ + set: function (value) { + this._wrappingStyle = value; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ScriptProperties.prototype, "scaleBorderAndShadow", { + /** + * Whether to scale outline widths and shadow depths from script resolution to video resolution or not. If true, widths and depths are scaled. + * + * @type {boolean} + */ + get: function () { + return this._scaleBorderAndShadow; + }, + /** + * Whether to scale outline widths and shadow depths from script resolution to video resolution or not. If true, widths and depths are scaled. + * + * @type {boolean} + */ + set: function (value) { + this._scaleBorderAndShadow = value; + }, + enumerable: true, + configurable: true + }); + ScriptProperties = __decorate([ serialization_6.registerClass ], ScriptProperties); + return ScriptProperties; + }(); + exports.ScriptProperties = ScriptProperties; + }); + def("types/dialogue", [ "types/misc", "parser/parse", "parts/index", "serialization", "settings", "utility/map" ], function (misc_4, parse_2, parts, serialization_5, settings_2, map_7, exports) { + /** + * This class represents a dialogue in a {@link libjass.ASS} script. + * + * @param {!Map.} template The template object that contains the dialogue's properties. It is a map of the string values read from the ASS file. + * @param {string} template["Style"] The name of the default style of this dialogue + * @param {string} template["Start"] The start time + * @param {string} template["End"] The end time + * @param {string} template["Layer"] The layer number + * @param {string} template["Text"] The text of this dialogue + * @param {!libjass.ASS} ass The ASS object to which this dialogue belongs + * + * @constructor + * @memberOf libjass + */ + var Dialogue = function () { + function Dialogue(template, ass) { + this._parts = null; + this._containsTransformTag = false; + { + var normalizedTemplate_2 = new map_7.Map(); + template.forEach(function (value, key) { + normalizedTemplate_2.set(key.toLowerCase(), value); + }); + template = normalizedTemplate_2; + } + this._id = ++Dialogue._lastDialogueId; + var styleName = template.get("style"); + if (typeof styleName === "string") { + styleName = styleName.replace(/^\*+/, ""); + if (styleName.match(/^Default$/i) !== null) { + styleName = "Default"; + } + } + var style = styleName !== undefined ? ass.styles.get(styleName) : undefined; + if (style === undefined) { + if (settings_2.debugMode) { + console.warn("Unrecognized style " + styleName + '. Falling back to "Default"'); + } + style = ass.styles.get("Default"); + if (style === undefined) { + throw new Error("Unrecognized style " + styleName + '. Could not fall back to "Default" style since it doesn\'t exist.'); + } + } + this._style = style; + var start = template.get("start"); + if (typeof start !== "string") { + throw new Error("Dialogue start time " + start + " is not a string."); + } + this._start = toTime(start); + var end = template.get("end"); + if (typeof end !== "string") { + throw new Error("Dialogue end time " + end + " is not a string."); + } + this._end = toTime(end); + this._layer = Math.max(misc_4.valueOrDefault(template, "layer", parseInt, function (value) { + return !isNaN(value); + }, "0"), 0); + var text = template.get("text"); + if (typeof text !== "string") { + throw new Error("Dialogue text " + text + " is not a string."); + } + this._rawPartsString = text; + } + Object.defineProperty(Dialogue.prototype, "id", { + /** + * The unique ID of this dialogue. Auto-generated. + * + * @type {number} + */ + get: function () { + return this._id; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Dialogue.prototype, "start", { + /** + * The start time of this dialogue. + * + * @type {number} + */ + get: function () { + return this._start; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Dialogue.prototype, "end", { + /** + * The end time of this dialogue. + * + * @type {number} + */ + get: function () { + return this._end; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Dialogue.prototype, "style", { + /** + * The default style of this dialogue. + * + * @type {!libjass.Style} + */ + get: function () { + return this._style; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Dialogue.prototype, "alignment", { + /** + * The alignment number of this dialogue. + * + * @type {number} + */ + get: function () { + if (this._parts === null) { + this._parsePartsString(); + } + return this._alignment; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Dialogue.prototype, "layer", { + /** + * The layer number of this dialogue. + * + * @type {number} + */ + get: function () { + return this._layer; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Dialogue.prototype, "parts", { + /** + * The {@link libjass.parts} of this dialogue. + * + * @type {!Array.} + */ + get: function () { + if (this._parts === null) { + this._parsePartsString(); + } + return this._parts; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Dialogue.prototype, "containsTransformTag", { + /** + * Convenience getter for whether this dialogue contains a {\t} tag. + * + * @type {boolean} + */ + get: function () { + if (this._parts === null) { + this._parsePartsString(); + } + return this._containsTransformTag; + }, + enumerable: true, + configurable: true + }); + /** + * @return {string} A simple representation of this dialogue's properties and parts. + */ + Dialogue.prototype.toString = function () { + return "#" + this._id + " [" + this._start.toFixed(3) + "-" + this._end.toFixed(3) + "] " + (this._parts !== null ? this._parts.join(", ") : this._rawPartsString); + }; + /** + * Parses this dialogue's parts from the raw parts string. + * + * @private + */ + Dialogue.prototype._parsePartsString = function () { + var _this = this; + this._parts = parse_2.parse(this._rawPartsString, "dialogueParts"); + this._alignment = this._style.alignment; + this._parts.forEach(function (part, index) { + if (part instanceof parts.Alignment) { + _this._alignment = part.value; + } else if (part instanceof parts.Move) { + if (part.t1 === null || part.t2 === null) { + _this._parts[index] = new parts.Move(part.x1, part.y1, part.x2, part.y2, 0, _this._end - _this._start); + } + } else if (part instanceof parts.Transform) { + if (part.start === null || part.end === null || part.accel === null) { + _this._parts[index] = new parts.Transform(part.start === null ? 0 : part.start, part.end === null ? _this._end - _this._start : part.end, part.accel === null ? 1 : part.accel, part.tags); + } + _this._containsTransformTag = true; + } + }); + if (settings_2.debugMode) { + var possiblyIncorrectParses = this._parts.filter(function (part) { + return part instanceof parts.Comment && part.value.indexOf("\\") !== -1; + }); + if (possiblyIncorrectParses.length > 0) { + console.warn("Possible incorrect parse:\n" + this._rawPartsString + "\nwas parsed as\n" + this.toString() + "\nThe possibly incorrect parses are:\n" + possiblyIncorrectParses.join("\n")); + } + } + }; + Dialogue._lastDialogueId = -1; + Dialogue = __decorate([ serialization_5.registerClass ], Dialogue); + return Dialogue; + }(); + exports.Dialogue = Dialogue; + /** + * Converts this string into the number of seconds it represents. This string must be in the form of hh:mm:ss.MMM + * + * @param {string} str + * @return {number} + * + * @private + */ + function toTime(str) { + return str.split(":").reduce(function (previousValue, currentValue) { + return previousValue * 60 + parseFloat(currentValue); + }, 0); + } + }); + def("types/style", [ "parser/parse", "serialization", "utility/map", "types/misc" ], function (parse_1, serialization_4, map_6, misc_3, exports) { + /** + * This class represents a single global style declaration in a {@link libjass.ASS} script. The styles can be obtained via the {@link libjass.ASS.styles} property. + * + * @param {!Map.} template The template object that contains the style's properties. It is a map of the string values read from the ASS file. + * @param {string} template["Name"] The name of the style + * @param {string} template["Italic"] -1 if the style is italicized + * @param {string} template["Bold"] -1 if the style is bold + * @param {string} template["Underline"] -1 if the style is underlined + * @param {string} template["StrikeOut"] -1 if the style is struck-through + * @param {string} template["Fontname"] The name of the font + * @param {string} template["Fontsize"] The size of the font + * @param {string} template["ScaleX"] The horizontal scaling of the font + * @param {string} template["ScaleY"] The vertical scaling of the font + * @param {string} template["Spacing"] The letter spacing of the font + * @param {string} template["PrimaryColour"] The primary color + * @param {string} template["OutlineColour"] The outline color + * @param {string} template["BackColour"] The shadow color + * @param {string} template["Outline"] The outline thickness + * @param {string} template["Shadow"] The shadow depth + * @param {string} template["Alignment"] The alignment number + * @param {string} template["MarginL"] The left margin + * @param {string} template["MarginR"] The right margin + * @param {string} template["MarginV"] The vertical margin + * + * @constructor + * @memberOf libjass + */ + var Style = function () { + function Style(template) { + { + var normalizedTemplate_1 = new map_6.Map(); + template.forEach(function (value, key) { + normalizedTemplate_1.set(key.toLowerCase(), value); + }); + template = normalizedTemplate_1; + } + var name = template.get("name"); + if (typeof name !== "string") { + throw new Error("Style name " + name + " is not a string."); + } + this._name = name.replace(/^\*+/, ""); + this._italic = !!misc_3.valueOrDefault(template, "italic", parseFloat, function (value) { + return !isNaN(value); + }, "0"); + this._bold = !!misc_3.valueOrDefault(template, "bold", parseFloat, function (value) { + return !isNaN(value); + }, "0"); + this._underline = !!misc_3.valueOrDefault(template, "underline", parseFloat, function (value) { + return !isNaN(value); + }, "0"); + this._strikeThrough = !!misc_3.valueOrDefault(template, "strikeout", parseFloat, function (value) { + return !isNaN(value); + }, "0"); + this._fontName = misc_3.valueOrDefault(template, "fontname", function (str) { + return str; + }, function (value) { + return value.constructor === String; + }, "sans-serif"); + this._fontSize = misc_3.valueOrDefault(template, "fontsize", parseFloat, function (value) { + return !isNaN(value); + }, "18"); + this._fontScaleX = misc_3.valueOrDefault(template, "scalex", parseFloat, function (value) { + return value >= 0; + }, "100") / 100; + this._fontScaleY = misc_3.valueOrDefault(template, "scaley", parseFloat, function (value) { + return value >= 0; + }, "100") / 100; + this._letterSpacing = misc_3.valueOrDefault(template, "spacing", parseFloat, function (value) { + return value >= 0; + }, "0"); + this._rotationZ = misc_3.valueOrDefault(template, "angle", parseFloat, function (value) { + return !isNaN(value); + }, "0"); + this._primaryColor = misc_3.valueOrDefault(template, "primarycolour", function (str) { + return parse_1.parse(str, "colorWithAlpha"); + }, null, "&H00FFFFFF"); + this._secondaryColor = misc_3.valueOrDefault(template, "secondarycolour", function (str) { + return parse_1.parse(str, "colorWithAlpha"); + }, null, "&H00FFFF00"); + this._outlineColor = misc_3.valueOrDefault(template, "outlinecolour", function (str) { + return parse_1.parse(str, "colorWithAlpha"); + }, null, "&H00000000"); + this._shadowColor = misc_3.valueOrDefault(template, "backcolour", function (str) { + return parse_1.parse(str, "colorWithAlpha"); + }, null, "&H80000000"); + this._outlineThickness = misc_3.valueOrDefault(template, "outline", parseFloat, function (value) { + return value >= 0; + }, "2"); + this._borderStyle = misc_3.valueOrDefault(template, "borderstyle", parseInt, function (value) { + return misc_3.BorderStyle[misc_3.BorderStyle[value]] === value; + }, "1"); + this._shadowDepth = misc_3.valueOrDefault(template, "shadow", parseFloat, function (value) { + return value >= 0; + }, "3"); + this._alignment = misc_3.valueOrDefault(template, "alignment", parseInt, function (value) { + return value >= 1 && value <= 9; + }, "2"); + this._marginLeft = misc_3.valueOrDefault(template, "marginl", parseFloat, function (value) { + return !isNaN(value); + }, "20"); + this._marginRight = misc_3.valueOrDefault(template, "marginr", parseFloat, function (value) { + return !isNaN(value); + }, "20"); + this._marginVertical = misc_3.valueOrDefault(template, "marginv", parseFloat, function (value) { + return !isNaN(value); + }, "20"); + } + Object.defineProperty(Style.prototype, "name", { + /** + * The name of this style. + * + * @type {string} + */ + get: function () { + return this._name; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "italic", { + /** + * Whether this style is italicized or not. + * + * @type {string} + */ + get: function () { + return this._italic; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "bold", { + /** + * Whether this style is bold or not. + * + * @type {boolean} + */ + get: function () { + return this._bold; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "underline", { + /** + * Whether this style is underlined or not. + * + * @type {boolean} + */ + get: function () { + return this._underline; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "strikeThrough", { + /** + * Whether this style is struck-through or not. + * + * @type {boolean} + */ + get: function () { + return this._strikeThrough; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "fontName", { + /** + * The name of this style's font. + * + * @type {string} + */ + get: function () { + return this._fontName; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "fontSize", { + /** + * The size of this style's font. + * + * @type {number} + */ + get: function () { + return this._fontSize; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "fontScaleX", { + /** + * The horizontal scaling of this style's font. + * + * @type {number} + */ + get: function () { + return this._fontScaleX; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "fontScaleY", { + /** + * The vertical scaling of this style's font. + * + * @type {number} + */ + get: function () { + return this._fontScaleY; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "letterSpacing", { + /** + * The letter spacing scaling of this style's font. + * + * @type {number} + */ + get: function () { + return this._letterSpacing; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "rotationZ", { + /** + * The default Z-rotation of this style. + * + * @type {number} + */ + get: function () { + return this._rotationZ; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "primaryColor", { + /** + * The color of this style's font. + * + * @type {!libjass.parts.Color} + */ + get: function () { + return this._primaryColor; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "secondaryColor", { + /** + * The alternate color of this style's font, used in karaoke. + * + * @type {!libjass.parts.Color} + */ + get: function () { + return this._secondaryColor; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "outlineColor", { + /** + * The color of this style's outline. + * + * @type {!libjass.parts.Color} + */ + get: function () { + return this._outlineColor; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "shadowColor", { + /** + * The color of this style's shadow. + * + * @type {!libjass.parts.Color} + */ + get: function () { + return this._shadowColor; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "outlineThickness", { + /** + * The thickness of this style's outline. + * + * @type {number} + */ + get: function () { + return this._outlineThickness; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "borderStyle", { + /** + * The border style of this style. + * + * @type {number} + */ + get: function () { + return this._borderStyle; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "shadowDepth", { + /** + * The depth of this style's shadow. + * + * @type {number} + */ + get: function () { + return this._shadowDepth; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "alignment", { + /** + * The alignment of dialogues of this style. + * + * @type {number} + */ + get: function () { + return this._alignment; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "marginLeft", { + /** + * The left margin of dialogues of this style. + * + * @type {number} + */ + get: function () { + return this._marginLeft; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "marginRight", { + /** + * The right margin of dialogues of this style. + * + * @type {number} + */ + get: function () { + return this._marginRight; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Style.prototype, "marginVertical", { + /** + * The vertical margin of dialogues of this style. + * + * @type {number} + */ + get: function () { + return this._marginVertical; + }, + enumerable: true, + configurable: true + }); + Style = __decorate([ serialization_4.registerClass ], Style); + return Style; + }(); + exports.Style = Style; + }); + def("types/attachment", [ "serialization" ], function (serialization_3, exports) { + /** + * The type of an attachment. + * + * @enum + * @memberOf libjass + */ + (function (AttachmentType) { + AttachmentType[AttachmentType["Font"] = 0] = "Font"; + AttachmentType[AttachmentType["Graphic"] = 1] = "Graphic"; + })(exports.AttachmentType || (exports.AttachmentType = {})); + /** + * This class represents an attachment in a {@link libjass.ASS} script. + * + * @param {string} filename The filename of this attachment. + * @param {number} type The type of this attachment. + * + * @constructor + * @memberOf libjass + */ + var Attachment = function () { + function Attachment(filename, type) { + this._filename = filename; + this._type = type; + this._contents = ""; + } + Object.defineProperty(Attachment.prototype, "filename", { + /** + * The filename of this attachment. + * + * @type {number} + */ + get: function () { + return this._filename; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Attachment.prototype, "type", { + /** + * The type of this attachment. + * + * @type {number} + */ + get: function () { + return this._type; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Attachment.prototype, "contents", { + /** + * The contents of this attachment in base64 encoding. + * + * @type {number} + */ + get: function () { + return this._contents; + }, + /** + * The contents of this attachment in base64 encoding. + * + * @type {number} + */ + set: function (value) { + this._contents = value; + }, + enumerable: true, + configurable: true + }); + Attachment = __decorate([ serialization_3.registerClass ], Attachment); + return Attachment; + }(); + exports.Attachment = Attachment; + }); + def("parser/misc", [ "utility/map" ], function (map_5, exports) { + /** + * Parses a line into a {@link ./types/misc.Property}. + * + * @param {string} line + * @return {Property} + */ + function parseLineIntoProperty(line) { + var colonPos = line.indexOf(":"); + if (colonPos === -1) { + return null; + } + var name = line.substr(0, colonPos); + var value = line.substr(colonPos + 1).replace(/^\s+/, ""); + return { + name: name, + value: value + }; + } + exports.parseLineIntoProperty = parseLineIntoProperty; + /** + * Parses a line into a {@link ./types/misc.TypedTemplate} according to the given format specifier. + * + * @param {string} line + * @param {!Array.} formatSpecifier + * @return {TypedTemplate} + */ + function parseLineIntoTypedTemplate(line, formatSpecifier) { + var property = parseLineIntoProperty(line); + if (property === null) { + return null; + } + var value = property.value.split(","); + if (value.length > formatSpecifier.length) { + value[formatSpecifier.length - 1] = value.slice(formatSpecifier.length - 1).join(","); + } + var template = new map_5.Map(); + formatSpecifier.forEach(function (formatKey, index) { + template.set(formatKey, value[index]); + }); + return { + type: property.name, + template: template + }; + } + exports.parseLineIntoTypedTemplate = parseLineIntoTypedTemplate; + }); + def("parser/streams", [ "utility/promise" ], function (promise_3, exports) { + /** + * A {@link libjass.parser.Stream} that reads from a string in memory. + * + * @param {string} str The string + * + * @constructor + * @implements {libjass.parser.Stream} + * @memberOf libjass.parser + */ + var StringStream = function () { + function StringStream(str) { + this._str = str; + this._readTill = 0; + } + /** + * @return {!Promise.} A promise that will be resolved with the next line, or null if the string has been completely read. + */ + StringStream.prototype.nextLine = function () { + var result; + if (this._readTill < this._str.length) { + var nextNewLinePos = this._str.indexOf("\n", this._readTill); + if (nextNewLinePos !== -1) { + result = promise_3.Promise.resolve(this._str.substring(this._readTill, nextNewLinePos)); + this._readTill = nextNewLinePos + 1; + } else { + result = promise_3.Promise.resolve(this._str.substr(this._readTill)); + this._readTill = this._str.length; + } + } else { + result = promise_3.Promise.resolve(null); + } + return result; + }; + return StringStream; + }(); + exports.StringStream = StringStream; + /** + * A {@link libjass.parser.Stream} that reads from an XMLHttpRequest object. + * + * @param {!XMLHttpRequest} xhr The XMLHttpRequest object. Make sure to not call .open() on this object before passing it in here, + * since event handlers cannot be registered after open() has been called. + * + * @constructor + * @implements {libjass.parser.Stream} + * @memberOf libjass.parser + */ + var XhrStream = function () { + function XhrStream(xhr) { + var _this = this; + this._xhr = xhr; + this._readTill = 0; + this._pendingDeferred = null; + this._failedError = null; + xhr.addEventListener("progress", function () { + return _this._onXhrProgress(); + }, false); + xhr.addEventListener("load", function () { + return _this._onXhrLoad(); + }, false); + xhr.addEventListener("error", function (event) { + return _this._onXhrError(event); + }, false); + } + /** + * @return {!Promise.} A promise that will be resolved with the next line, or null if the stream is exhausted. + */ + XhrStream.prototype.nextLine = function () { + if (this._pendingDeferred !== null) { + throw new Error("XhrStream only supports one pending unfulfilled read at a time."); + } + var deferred = this._pendingDeferred = new promise_3.DeferredPromise(); + this._tryResolveNextLine(); + return deferred.promise; + }; + /** + * @private + */ + XhrStream.prototype._onXhrProgress = function () { + if (this._pendingDeferred === null) { + return; + } + if (this._xhr.readyState === XMLHttpRequest.DONE) { + /* Suppress resolving next line here. Let the "load" or "error" event handlers do it. + * + * This is required because a failed XHR fires the progress event with readyState === DONE before it fires the error event. + * This would confuse _tryResolveNextLine() into thinking the request succeeded with no data if it was called here. + */ + return; + } + this._tryResolveNextLine(); + }; + /** + * @private + */ + XhrStream.prototype._onXhrLoad = function () { + if (this._pendingDeferred === null) { + return; + } + this._tryResolveNextLine(); + }; + /** + * @param {!ErrorEvent} event + * + * @private + */ + XhrStream.prototype._onXhrError = function (event) { + this._failedError = event; + if (this._pendingDeferred === null) { + return; + } + this._tryResolveNextLine(); + }; + /** + * @private + */ + XhrStream.prototype._tryResolveNextLine = function () { + if (this._failedError !== null) { + this._pendingDeferred.reject(this._failedError); + return; + } + var response = this._xhr.responseText; + var nextNewLinePos = response.indexOf("\n", this._readTill); + if (nextNewLinePos !== -1) { + this._pendingDeferred.resolve(response.substring(this._readTill, nextNewLinePos)); + this._readTill = nextNewLinePos + 1; + this._pendingDeferred = null; + } else if (this._xhr.readyState === XMLHttpRequest.DONE) { + if (this._failedError !== null) { + this._pendingDeferred.reject(this._failedError); + } else if (this._readTill < response.length) { + this._pendingDeferred.resolve(response.substr(this._readTill)); + this._readTill = response.length; + } else { + this._pendingDeferred.resolve(null); + } + this._pendingDeferred = null; + } + }; + return XhrStream; + }(); + exports.XhrStream = XhrStream; + /** + * A {@link libjass.parser.Stream} that reads from a ReadableStream object. + * + * @param {!ReadableStream} stream + * @param {string} encoding + * + * @constructor + * @implements {libjass.parser.Stream} + * @memberOf libjass.parser + */ + var BrowserReadableStream = function () { + function BrowserReadableStream(stream, encoding) { + this._buffer = ""; + this._pendingDeferred = null; + this._reader = stream.getReader(); + this._decoder = new global.TextDecoder(encoding, { + ignoreBOM: true + }); + } + /** + * @return {!Promise.} A promise that will be resolved with the next line, or null if the stream is exhausted. + */ + BrowserReadableStream.prototype.nextLine = function () { + if (this._pendingDeferred !== null) { + throw new Error("BrowserReadableStream only supports one pending unfulfilled read at a time."); + } + var deferred = this._pendingDeferred = new promise_3.DeferredPromise(); + this._tryResolveNextLine(); + return deferred.promise; + }; + /** + * @private + */ + BrowserReadableStream.prototype._tryResolveNextLine = function () { + var _this = this; + var nextNewLinePos = this._buffer.indexOf("\n"); + if (nextNewLinePos !== -1) { + this._pendingDeferred.resolve(this._buffer.substr(0, nextNewLinePos)); + this._buffer = this._buffer.substr(nextNewLinePos + 1); + this._pendingDeferred = null; + } else { + this._reader.read().then(function (next) { + var value = next.value; + var done = next.done; + if (!done) { + _this._buffer += _this._decoder.decode(value, { + stream: true + }); + _this._tryResolveNextLine(); + } else { + // No more data. + if (_this._buffer.length === 0) { + _this._pendingDeferred.resolve(null); + } else { + _this._pendingDeferred.resolve(_this._buffer); + _this._buffer = ""; + } + _this._pendingDeferred = null; + } + }); + } + }; + return BrowserReadableStream; + }(); + exports.BrowserReadableStream = BrowserReadableStream; + }); + def("parser/parse", [ "parts/index", "settings", "utility/map", "utility/promise", "webworker/commands", "webworker/misc" ], function (parts, settings_1, map_4, promise_2, commands_3, misc_2, exports) { + var rules = new map_4.Map(); + /** + * Parses a given string with the specified rule. + * + * @param {string} input The string to be parsed. + * @param {string} rule The rule to parse the string with + * @return {*} The value returned depends on the rule used. + * + * @memberOf libjass.parser + */ + function parse(input, rule) { + var result = new ParserRun(input, rule).result; + if (result === null || result.end !== input.length) { + if (settings_1.debugMode) { + console.error("Parse failed. %s %s %o", rule, input, result); + } + throw new Error("Parse failed."); + } + return result.value; + } + exports.parse = parse; + /** + * This class represents a single run of the parser. + * + * @param {string} input + * @param {string} rule + * + * @constructor + * @private + */ + var ParserRun = function () { + function ParserRun(input, rule) { + this._input = input; + var ruleFunction = rules.get(rule); + if (ruleFunction === undefined) { + throw new Error("Could not find parser rule named " + rule); + } + this._parseTree = new ParseNode(null); + this._result = ruleFunction.call(this, this._parseTree); + } + Object.defineProperty(ParserRun.prototype, "result", { + /** + * @type {ParseNode} + */ + get: function () { + return this._result; + }, + enumerable: true, + configurable: true + }); + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_dialogueParts = function (parent) { + var _a; + var current = new ParseNode(parent); + current.value = []; + while (this._haveMore()) { + var enclosedTagsNode = this.parse_enclosedTags(current); + if (enclosedTagsNode !== null) { + (_a = current.value).push.apply(_a, enclosedTagsNode.value); + } else { + var whiteSpaceOrTextNode = this.parse_newline(current) || this.parse_hardspace(current) || this.parse_text(current); + if (whiteSpaceOrTextNode.value instanceof parts.Text && current.value[current.value.length - 1] instanceof parts.Text) { + // Merge consecutive text parts into one part + var previousTextPart = current.value[current.value.length - 1]; + current.value[current.value.length - 1] = new parts.Text(previousTextPart.value + whiteSpaceOrTextNode.value.value); + } else { + current.value.push(whiteSpaceOrTextNode.value); + } + } + } + var inDrawingMode = false; + current.value.forEach(function (part, i) { + if (part instanceof parts.DrawingMode) { + inDrawingMode = part.scale !== 0; + } else if (part instanceof parts.Text && inDrawingMode) { + current.value[i] = new parts.DrawingInstructions(parse(part.value, "drawingInstructions")); + } + }); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_enclosedTags = function (parent) { + var current = new ParseNode(parent); + current.value = []; + if (this.read(current, "{") === null) { + parent.pop(); + return null; + } + for (var next = this._peek(); this._haveMore() && next !== "}"; next = this._peek()) { + var childNode = null; + if (this.read(current, "\\") !== null) { + childNode = this.parse_tag_alpha(current) || this.parse_tag_iclip(current) || this.parse_tag_xbord(current) || this.parse_tag_ybord(current) || this.parse_tag_xshad(current) || this.parse_tag_yshad(current) || this.parse_tag_blur(current) || this.parse_tag_bord(current) || this.parse_tag_clip(current) || this.parse_tag_fade(current) || this.parse_tag_fscx(current) || this.parse_tag_fscy(current) || this.parse_tag_move(current) || this.parse_tag_shad(current) || this.parse_tag_fad(current) || this.parse_tag_fax(current) || this.parse_tag_fay(current) || this.parse_tag_frx(current) || this.parse_tag_fry(current) || this.parse_tag_frz(current) || this.parse_tag_fsp(current) || this.parse_tag_fsplus(current) || this.parse_tag_fsminus(current) || this.parse_tag_org(current) || this.parse_tag_pbo(current) || this.parse_tag_pos(current) || this.parse_tag_an(current) || this.parse_tag_be(current) || this.parse_tag_fn(current) || this.parse_tag_fr(current) || this.parse_tag_fs(current) || this.parse_tag_kf(current) || this.parse_tag_ko(current) || this.parse_tag_1a(current) || this.parse_tag_1c(current) || this.parse_tag_2a(current) || this.parse_tag_2c(current) || this.parse_tag_3a(current) || this.parse_tag_3c(current) || this.parse_tag_4a(current) || this.parse_tag_4c(current) || this.parse_tag_a(current) || this.parse_tag_b(current) || this.parse_tag_c(current) || this.parse_tag_i(current) || this.parse_tag_k(current) || this.parse_tag_K(current) || this.parse_tag_p(current) || this.parse_tag_q(current) || this.parse_tag_r(current) || this.parse_tag_s(current) || this.parse_tag_t(current) || this.parse_tag_u(current); + if (childNode === null) { + current.pop(); + } + } + if (childNode === null) { + childNode = this.parse_comment(current); + } + if (childNode !== null) { + if (childNode.value instanceof parts.Comment && current.value[current.value.length - 1] instanceof parts.Comment) { + // Merge consecutive comment parts into one part + current.value[current.value.length - 1] = new parts.Comment(current.value[current.value.length - 1].value + childNode.value.value); + } else { + current.value.push(childNode.value); + } + } else { + parent.pop(); + return null; + } + } + if (this.read(current, "}") === null) { + parent.pop(); + return null; + } + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_newline = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "\\N") === null && this.read(current, "\\n") === null) { + parent.pop(); + return null; + } + current.value = new parts.NewLine(); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_hardspace = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "\\h") === null) { + parent.pop(); + return null; + } + current.value = new parts.Text("\xa0"); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_text = function (parent) { + var value = this._peek(); + var current = new ParseNode(parent); + var valueNode = new ParseNode(current, value); + current.value = new parts.Text(valueNode.value); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_comment = function (parent) { + var value = this._peek(); + var current = new ParseNode(parent); + var valueNode = new ParseNode(current, value); + current.value = new parts.Comment(valueNode.value); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_a = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "a") === null) { + parent.pop(); + return null; + } + var next = this._peek(); + switch (next) { + case "1": + var next2 = this._peek(2); + switch (next2) { + case "10": + case "11": + next = next2; + break; + } + break; + + case "2": + case "3": + case "5": + case "6": + case "7": + case "9": + break; + + default: + parent.pop(); + return null; + } + var valueNode = new ParseNode(current, next); + var value = -1; + switch (valueNode.value) { + case "1": + value = 1; + break; + + case "2": + value = 2; + break; + + case "3": + value = 3; + break; + + case "5": + value = 7; + break; + + case "6": + value = 8; + break; + + case "7": + value = 9; + break; + + case "9": + value = 4; + break; + + case "10": + value = 5; + break; + + case "11": + value = 6; + break; + } + current.value = new parts.Alignment(value); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_alpha = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_an = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "an") === null) { + parent.pop(); + return null; + } + var next = this._peek(); + if (next < "1" || next > "9") { + parent.pop(); + return null; + } + var valueNode = new ParseNode(current, next); + current.value = new parts.Alignment(parseInt(valueNode.value)); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_b = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "b") === null) { + parent.pop(); + return null; + } + var valueNode = null; + var next = this._peek(); + if (next >= "1" && next <= "9") { + next = this._peek(3); + if (next.substr(1) === "00") { + valueNode = new ParseNode(current, next); + valueNode.value = parseInt(valueNode.value); + } + } + if (valueNode === null) { + valueNode = this.parse_enableDisable(current); + } + if (valueNode !== null) { + current.value = new parts.Bold(valueNode.value); + } else { + current.value = new parts.Bold(null); + } + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_be = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_blur = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_bord = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_c = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_clip = function (parent) { + return this._parse_tag_clip_or_iclip("clip", parent); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fad = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "fad") === null) { + parent.pop(); + return null; + } + if (this.read(current, "(") === null) { + parent.pop(); + return null; + } + var startNode = this.parse_decimal(current); + if (startNode === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var endNode = this.parse_decimal(current); + if (endNode === null) { + parent.pop(); + return null; + } + if (this.read(current, ")") === null) { + parent.pop(); + return null; + } + current.value = new parts.Fade(startNode.value / 1e3, endNode.value / 1e3); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fade = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "fade") === null) { + parent.pop(); + return null; + } + if (this.read(current, "(") === null) { + parent.pop(); + return null; + } + var a1Node = this.parse_decimal(current); + if (a1Node === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var a2Node = this.parse_decimal(current); + if (a2Node === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var a3Node = this.parse_decimal(current); + if (a3Node === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var t1Node = this.parse_decimal(current); + if (t1Node === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var t2Node = this.parse_decimal(current); + if (t2Node === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var t3Node = this.parse_decimal(current); + if (t3Node === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var t4Node = this.parse_decimal(current); + if (t4Node === null) { + parent.pop(); + return null; + } + if (this.read(current, ")") === null) { + parent.pop(); + return null; + } + current.value = new parts.ComplexFade(1 - a1Node.value / 255, 1 - a2Node.value / 255, 1 - a3Node.value / 255, t1Node.value / 1e3, t2Node.value / 1e3, t3Node.value / 1e3, t4Node.value / 1e3); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fax = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fay = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fn = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "fn") === null) { + parent.pop(); + return null; + } + var valueNode = new ParseNode(current, ""); + for (var next = this._peek(); this._haveMore() && next !== "\\" && next !== "}"; next = this._peek()) { + valueNode.value += next; + } + if (valueNode.value.length > 0) { + current.value = new parts.FontName(valueNode.value); + } else { + current.value = new parts.FontName(null); + } + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fr = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_frx = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fry = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_frz = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fs = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fsplus = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "fs+") === null) { + parent.pop(); + return null; + } + var valueNode = this.parse_decimal(current); + if (valueNode === null) { + parent.pop(); + return null; + } + current.value = new parts.FontSizePlus(valueNode.value / 10); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fsminus = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "fs-") === null) { + parent.pop(); + return null; + } + var valueNode = this.parse_decimal(current); + if (valueNode === null) { + parent.pop(); + return null; + } + current.value = new parts.FontSizeMinus(valueNode.value / 10); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fscx = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "fscx") === null) { + parent.pop(); + return null; + } + var valueNode = this.parse_decimal(current); + if (valueNode !== null) { + current.value = new parts.FontScaleX(valueNode.value / 100); + } else { + current.value = new parts.FontScaleX(null); + } + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fscy = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "fscy") === null) { + parent.pop(); + return null; + } + var valueNode = this.parse_decimal(current); + if (valueNode !== null) { + current.value = new parts.FontScaleY(valueNode.value / 100); + } else { + current.value = new parts.FontScaleY(null); + } + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_fsp = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_i = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_iclip = function (parent) { + return this._parse_tag_clip_or_iclip("iclip", parent); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_k = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "k") === null) { + parent.pop(); + return null; + } + var valueNode = this.parse_decimal(current); + if (valueNode === null) { + parent.pop(); + return null; + } + current.value = new parts.ColorKaraoke(valueNode.value / 100); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_K = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "K") === null) { + parent.pop(); + return null; + } + var valueNode = this.parse_decimal(current); + if (valueNode === null) { + parent.pop(); + return null; + } + current.value = new parts.SweepingColorKaraoke(valueNode.value / 100); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_kf = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "kf") === null) { + parent.pop(); + return null; + } + var valueNode = this.parse_decimal(current); + if (valueNode === null) { + parent.pop(); + return null; + } + current.value = new parts.SweepingColorKaraoke(valueNode.value / 100); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_ko = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "ko") === null) { + parent.pop(); + return null; + } + var valueNode = this.parse_decimal(current); + if (valueNode === null) { + parent.pop(); + return null; + } + current.value = new parts.OutlineKaraoke(valueNode.value / 100); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_move = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "move") === null) { + parent.pop(); + return null; + } + if (this.read(current, "(") === null) { + parent.pop(); + return null; + } + var x1Node = this.parse_decimal(current); + if (x1Node === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var y1Node = this.parse_decimal(current); + if (y1Node === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var x2Node = this.parse_decimal(current); + if (x2Node === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var y2Node = this.parse_decimal(current); + if (y2Node === null) { + parent.pop(); + return null; + } + var t1Node = null; + var t2Node = null; + if (this.read(current, ",") !== null) { + t1Node = this.parse_decimal(current); + if (t1Node === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + t2Node = this.parse_decimal(current); + if (t2Node === null) { + parent.pop(); + return null; + } + } + if (this.read(current, ")") === null) { + parent.pop(); + return null; + } + current.value = new parts.Move(x1Node.value, y1Node.value, x2Node.value, y2Node.value, t1Node !== null ? t1Node.value / 1e3 : null, t2Node !== null ? t2Node.value / 1e3 : null); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_org = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "org") === null) { + parent.pop(); + return null; + } + if (this.read(current, "(") === null) { + parent.pop(); + return null; + } + var xNode = this.parse_decimal(current); + if (xNode === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var yNode = this.parse_decimal(current); + if (yNode === null) { + parent.pop(); + return null; + } + if (this.read(current, ")") === null) { + parent.pop(); + return null; + } + current.value = new parts.RotationOrigin(xNode.value, yNode.value); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_p = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_pbo = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_pos = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "pos") === null) { + parent.pop(); + return null; + } + if (this.read(current, "(") === null) { + parent.pop(); + return null; + } + var xNode = this.parse_decimal(current); + if (xNode === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var yNode = this.parse_decimal(current); + if (yNode === null) { + parent.pop(); + return null; + } + if (this.read(current, ")") === null) { + parent.pop(); + return null; + } + current.value = new parts.Position(xNode.value, yNode.value); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_q = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "q") === null) { + parent.pop(); + return null; + } + var next = this._peek(); + if (next < "0" || next > "3") { + parent.pop(); + return null; + } + var valueNode = new ParseNode(current, next); + current.value = new parts.WrappingStyle(parseInt(valueNode.value)); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_r = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "r") === null) { + parent.pop(); + return null; + } + var valueNode = new ParseNode(current, ""); + for (var next = this._peek(); this._haveMore() && next !== "\\" && next !== "}"; next = this._peek()) { + valueNode.value += next; + } + if (valueNode.value.length > 0) { + current.value = new parts.Reset(valueNode.value); + } else { + current.value = new parts.Reset(null); + } + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_s = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_shad = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_t = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, "t") === null) { + parent.pop(); + return null; + } + if (this.read(current, "(") === null) { + parent.pop(); + return null; + } + var startNode = null; + var endNode = null; + var accelNode = null; + var firstNode = this.parse_decimal(current); + if (firstNode !== null) { + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var secondNode = this.parse_decimal(current); + if (secondNode !== null) { + startNode = firstNode; + endNode = secondNode; + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var thirdNode = this.parse_decimal(current); + if (thirdNode !== null) { + accelNode = thirdNode; + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + } + } else { + accelNode = firstNode; + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + } + } + var transformTags = []; + for (var next = this._peek(); this._haveMore() && next !== ")" && next !== "}"; next = this._peek()) { + var childNode = null; + if (this.read(current, "\\") !== null) { + childNode = this.parse_tag_alpha(current) || this.parse_tag_iclip(current) || this.parse_tag_xbord(current) || this.parse_tag_ybord(current) || this.parse_tag_xshad(current) || this.parse_tag_yshad(current) || this.parse_tag_blur(current) || this.parse_tag_bord(current) || this.parse_tag_clip(current) || this.parse_tag_fscx(current) || this.parse_tag_fscy(current) || this.parse_tag_shad(current) || this.parse_tag_fax(current) || this.parse_tag_fay(current) || this.parse_tag_frx(current) || this.parse_tag_fry(current) || this.parse_tag_frz(current) || this.parse_tag_fsp(current) || this.parse_tag_fsplus(current) || this.parse_tag_fsminus(current) || this.parse_tag_be(current) || this.parse_tag_fr(current) || this.parse_tag_fs(current) || this.parse_tag_1a(current) || this.parse_tag_1c(current) || this.parse_tag_2a(current) || this.parse_tag_2c(current) || this.parse_tag_3a(current) || this.parse_tag_3c(current) || this.parse_tag_4a(current) || this.parse_tag_4c(current) || this.parse_tag_c(current); + if (childNode === null) { + current.pop(); + } + } + if (childNode === null) { + childNode = this.parse_comment(current); + } + if (childNode !== null) { + if (childNode.value instanceof parts.Comment && transformTags[transformTags.length - 1] instanceof parts.Comment) { + // Merge consecutive comment parts into one part + transformTags[transformTags.length - 1] = new parts.Comment(transformTags[transformTags.length - 1].value + childNode.value.value); + } else { + transformTags.push(childNode.value); + } + } else { + parent.pop(); + return null; + } + } + this.read(current, ")"); + current.value = new parts.Transform(startNode !== null ? startNode.value / 1e3 : null, endNode !== null ? endNode.value / 1e3 : null, accelNode !== null ? accelNode.value / 1e3 : null, transformTags); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_u = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_xbord = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_xshad = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_ybord = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_yshad = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_1a = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_1c = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_2a = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_2c = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_3a = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_3c = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_4a = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_tag_4c = function () { + throw new Error("Method not implemented."); + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_drawingInstructions = function (parent) { + var current = new ParseNode(parent); + var currentType = null; + var numberParts = []; + current.value = []; + while (this._haveMore()) { + while (this.read(current, " ") !== null) {} + if (!this._haveMore()) { + break; + } + if (currentType !== null) { + var numberPart = this.parse_decimal(current); + if (numberPart !== null) { + numberParts.push(numberPart); + if (currentType === "m" && numberParts.length === 2) { + current.value.push(new parts.drawing.MoveInstruction(numberParts[0].value, numberParts[1].value)); + numberParts.splice(0, numberParts.length); + } else if (currentType === "l" && numberParts.length === 2) { + current.value.push(new parts.drawing.LineInstruction(numberParts[0].value, numberParts[1].value)); + numberParts.splice(0, numberParts.length); + } else if (currentType === "b" && numberParts.length === 6) { + current.value.push(new parts.drawing.CubicBezierCurveInstruction(numberParts[0].value, numberParts[1].value, numberParts[2].value, numberParts[3].value, numberParts[4].value, numberParts[5].value)); + numberParts.splice(0, numberParts.length); + } + continue; + } + } + var typePart = this.parse_text(current); + var newType = typePart.value.value; + switch (newType) { + case "m": + case "l": + case "b": + currentType = newType; + numberParts.splice(0, numberParts.length); + break; + } + } + while (this.read(current, " ") !== null) {} + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_decimalInt32 = function (parent) { + var current = new ParseNode(parent); + var isNegative = this.read(current, "-") !== null; + var numberNode = new ParseNode(current, ""); + for (var next = this._peek(); this._haveMore() && next >= "0" && next <= "9"; next = this._peek()) { + numberNode.value += next; + } + if (numberNode.value.length === 0) { + parent.pop(); + return null; + } + var value = parseInt(numberNode.value); + if (value >= 4294967295) { + value = 4294967295; + } else if (isNegative) { + value = -value; + } + current.value = value; + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_hexInt32 = function (parent) { + var current = new ParseNode(parent); + var isNegative = this.read(current, "-") !== null; + var numberNode = new ParseNode(current, ""); + for (var next = this._peek(); this._haveMore() && (next >= "0" && next <= "9" || next >= "a" && next <= "f" || next >= "A" && next <= "F"); next = this._peek()) { + numberNode.value += next; + } + if (numberNode.value.length === 0) { + parent.pop(); + return null; + } + var value = parseInt(numberNode.value, 16); + if (value >= 4294967295) { + value = 4294967295; + } else if (isNegative) { + value = -value; + } + current.value = value; + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_decimalOrHexInt32 = function (parent) { + var current = new ParseNode(parent); + var valueNode = this.read(current, "&H") !== null || this.read(current, "&h") !== null ? this.parse_hexInt32(current) : this.parse_decimalInt32(current); + if (valueNode === null) { + parent.pop(); + return null; + } + current.value = valueNode.value; + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_decimal = function (parent) { + var current = new ParseNode(parent); + var negative = this.read(current, "-") !== null; + var numericalPart = this.parse_unsignedDecimal(current); + if (numericalPart === null) { + parent.pop(); + return null; + } + current.value = numericalPart.value; + if (negative) { + current.value = -current.value; + } + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_unsignedDecimal = function (parent) { + var current = new ParseNode(parent); + var characteristicNode = new ParseNode(current, ""); + var mantissaNode = null; + for (var next = this._peek(); this._haveMore() && next >= "0" && next <= "9"; next = this._peek()) { + characteristicNode.value += next; + } + if (characteristicNode.value.length === 0) { + parent.pop(); + return null; + } + if (this.read(current, ".") !== null) { + mantissaNode = new ParseNode(current, ""); + for (var next = this._peek(); this._haveMore() && next >= "0" && next <= "9"; next = this._peek()) { + mantissaNode.value += next; + } + if (mantissaNode.value.length === 0) { + parent.pop(); + return null; + } + } + current.value = parseFloat(characteristicNode.value + (mantissaNode !== null ? "." + mantissaNode.value : "")); + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_enableDisable = function (parent) { + var next = this._peek(); + if (next === "0" || next === "1") { + var result = new ParseNode(parent, next); + result.value = result.value === "1"; + return result; + } + return null; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_color = function (parent) { + var current = new ParseNode(parent); + while (this.read(current, "&") !== null || this.read(current, "H") !== null) {} + var valueNode = this.parse_hexInt32(current); + if (valueNode === null) { + parent.pop(); + return null; + } + var value = valueNode.value; + current.value = new parts.Color(value & 255, value >> 8 & 255, value >> 16 & 255); + while (this.read(current, "&") !== null || this.read(current, "H") !== null) {} + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_alpha = function (parent) { + var current = new ParseNode(parent); + while (this.read(current, "&") !== null || this.read(current, "H") !== null) {} + var valueNode = this.parse_hexInt32(current); + if (valueNode === null) { + parent.pop(); + return null; + } + var value = valueNode.value; + current.value = 1 - (value & 255) / 255; + while (this.read(current, "&") !== null || this.read(current, "H") !== null) {} + return current; + }; + /** + * @param {!ParseNode} parent + * @return {ParseNode} + */ + ParserRun.prototype.parse_colorWithAlpha = function (parent) { + var current = new ParseNode(parent); + var valueNode = this.parse_decimalOrHexInt32(current); + if (valueNode === null) { + parent.pop(); + return null; + } + var value = valueNode.value; + current.value = new parts.Color(value & 255, value >> 8 & 255, value >> 16 & 255, 1 - (value >> 24 & 255) / 255); + return current; + }; + /** + * @param {!ParseNode} parent + * @param {string} next + * @return {ParseNode} + */ + ParserRun.prototype.read = function (parent, next) { + if (this._peek(next.length) !== next) { + return null; + } + return new ParseNode(parent, next); + }; + /** + * @param {number=1} count + * @return {string} + * + * @private + */ + ParserRun.prototype._peek = function (count) { + if (count === void 0) { + count = 1; + } + // Fastpath for count === 1. http://jsperf.com/substr-vs-indexer + if (count === 1) { + return this._input[this._parseTree.end]; + } + return this._input.substr(this._parseTree.end, count); + }; + /** + * @return {boolean} + * + * @private + */ + ParserRun.prototype._haveMore = function () { + return this._parseTree.end < this._input.length; + }; + /** + * @param {string} tagName One of "clip" and "iclip" + * @param {!ParseNode} parent + * @return {ParseNode} + * + * @private + */ + ParserRun.prototype._parse_tag_clip_or_iclip = function (tagName, parent) { + var current = new ParseNode(parent); + if (this.read(current, tagName) === null) { + parent.pop(); + return null; + } + if (this.read(current, "(") === null) { + parent.pop(); + return null; + } + var x1Node = null; + var x2Node = null; + var y1Node = null; + var y2Node = null; + var scaleNode = null; + var commandsNode = null; + var firstNode = this.parse_decimal(current); + if (firstNode !== null) { + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + var secondNode = this.parse_decimal(current); + if (secondNode !== null) { + x1Node = firstNode; + y1Node = secondNode; + } else { + scaleNode = firstNode; + } + } + if (x1Node !== null && y1Node !== null) { + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + x2Node = this.parse_decimal(current); + if (x2Node === null) { + parent.pop(); + return null; + } + if (this.read(current, ",") === null) { + parent.pop(); + return null; + } + y2Node = this.parse_decimal(current); + if (y2Node === null) { + parent.pop(); + return null; + } + current.value = new parts.RectangularClip(x1Node.value, y1Node.value, x2Node.value, y2Node.value, tagName === "clip"); + } else { + commandsNode = new ParseNode(current, ""); + for (var next = this._peek(); this._haveMore() && next !== ")" && next !== "}"; next = this._peek()) { + commandsNode.value += next; + } + current.value = new parts.VectorClip(scaleNode !== null ? scaleNode.value : 1, parse(commandsNode.value, "drawingInstructions"), tagName === "clip"); + } + if (this.read(current, ")") === null) { + parent.pop(); + return null; + } + return current; + }; + return ParserRun; + }(); + /** + * Constructs a simple tag parser function and sets it on the prototype of the {@link ./parser/parse.ParserRun} class. + * + * @param {string} tagName The name of the tag to generate the parser function for + * @param {function(new: !libjass.parts.Part, *)} tagConstructor The type of tag to be returned by the generated parser function + * @param {function(!ParseNode): ParseNode} valueParser The parser for the tag's value + * @param {boolean} required Whether the tag's value is required or optional + * + * @private + */ + function makeTagParserFunction(tagName, tagConstructor, valueParser, required) { + ParserRun.prototype["parse_tag_" + tagName] = function (parent) { + var current = new ParseNode(parent); + if (this.read(current, tagName) === null) { + parent.pop(); + return null; + } + var valueNode = valueParser.call(this, current); + if (valueNode !== null) { + current.value = new tagConstructor(valueNode.value); + } else if (!required) { + current.value = new tagConstructor(null); + } else { + parent.pop(); + return null; + } + return current; + }; + } + makeTagParserFunction("alpha", parts.Alpha, ParserRun.prototype.parse_alpha, false); + makeTagParserFunction("be", parts.Blur, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("blur", parts.GaussianBlur, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("bord", parts.Border, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("c", parts.PrimaryColor, ParserRun.prototype.parse_color, false); + makeTagParserFunction("fax", parts.SkewX, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("fay", parts.SkewY, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("fr", parts.RotateZ, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("frx", parts.RotateX, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("fry", parts.RotateY, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("frz", parts.RotateZ, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("fs", parts.FontSize, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("fsp", parts.LetterSpacing, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("i", parts.Italic, ParserRun.prototype.parse_enableDisable, false); + makeTagParserFunction("p", parts.DrawingMode, ParserRun.prototype.parse_decimal, true); + makeTagParserFunction("pbo", parts.DrawingBaselineOffset, ParserRun.prototype.parse_decimal, true); + makeTagParserFunction("s", parts.StrikeThrough, ParserRun.prototype.parse_enableDisable, false); + makeTagParserFunction("shad", parts.Shadow, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("u", parts.Underline, ParserRun.prototype.parse_enableDisable, false); + makeTagParserFunction("xbord", parts.BorderX, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("xshad", parts.ShadowX, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("ybord", parts.BorderY, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("yshad", parts.ShadowY, ParserRun.prototype.parse_decimal, false); + makeTagParserFunction("1a", parts.PrimaryAlpha, ParserRun.prototype.parse_alpha, false); + makeTagParserFunction("1c", parts.PrimaryColor, ParserRun.prototype.parse_color, false); + makeTagParserFunction("2a", parts.SecondaryAlpha, ParserRun.prototype.parse_alpha, false); + makeTagParserFunction("2c", parts.SecondaryColor, ParserRun.prototype.parse_color, false); + makeTagParserFunction("3a", parts.OutlineAlpha, ParserRun.prototype.parse_alpha, false); + makeTagParserFunction("3c", parts.OutlineColor, ParserRun.prototype.parse_color, false); + makeTagParserFunction("4a", parts.ShadowAlpha, ParserRun.prototype.parse_alpha, false); + makeTagParserFunction("4c", parts.ShadowColor, ParserRun.prototype.parse_color, false); + for (var _d = 0, _e = Object.keys(ParserRun.prototype); _d < _e.length; _d++) { + var key = _e[_d]; + if (key.indexOf("parse_") === 0 && typeof ParserRun.prototype[key] === "function") { + rules.set(key.substr("parse_".length), ParserRun.prototype[key]); + } + } + /** + * This class represents a single parse node. It has a start and end position, and an optional value object. + * + * @param {ParseNode} parent The parent of this parse node. + * @param {*=null} value If provided, it is assigned as the value of the node. + * + * @constructor + * @private + */ + var ParseNode = function () { + function ParseNode(parent, value) { + if (value === void 0) { + value = null; + } + this._parent = parent; + this._children = []; + if (parent !== null) { + parent.children.push(this); + } + this._start = parent !== null ? parent.end : 0; + this._end = this._start; + this.value = value; + } + Object.defineProperty(ParseNode.prototype, "start", { + /** + * The start position of this parse node. + * + * @type {number} + */ + get: function () { + return this._start; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ParseNode.prototype, "end", { + /** + * The end position of this parse node. + * + * @type {number} + */ + get: function () { + return this._end; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ParseNode.prototype, "parent", { + /** + * @type {ParseNode} + */ + get: function () { + return this._parent; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ParseNode.prototype, "children", { + /** + * @type {!Array.} + */ + get: function () { + return this._children; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ParseNode.prototype, "value", { + /** + * An optional object associated with this parse node. + * + * @type {*} + */ + get: function () { + return this._value; + }, + /** + * An optional object associated with this parse node. + * + * If the value is a string, then the end property is updated to be the length of the string. + * + * @type {*} + */ + set: function (newValue) { + this._value = newValue; + if (this._value !== null && this._value.constructor === String && this._children.length === 0) { + this._setEnd(this._start + this._value.length); + } + }, + enumerable: true, + configurable: true + }); + /** + * Removes the last child of this node and updates the end position to be end position of the new last child. + */ + ParseNode.prototype.pop = function () { + this._children.splice(this._children.length - 1, 1); + if (this._children.length > 0) { + this._setEnd(this._children[this._children.length - 1].end); + } else { + this._setEnd(this.start); + } + }; + /** + * Updates the end property of this node and its parent recursively to the root node. + * + * @param {number} newEnd + * + * @private + */ + ParseNode.prototype._setEnd = function (newEnd) { + this._end = newEnd; + if (this._parent !== null && this._parent.end !== this._end) { + this._parent._setEnd(this._end); + } + }; + return ParseNode; + }(); + misc_2.registerWorkerCommand(commands_3.WorkerCommands.Parse, function (parameters) { + return new promise_2.Promise(function (resolve) { + resolve(parse(parameters.input, parameters.rule)); + }); + }); + }); + def("parts/index", [ "parts/drawing", "serialization" ], function (drawing, serialization_2, exports) { + exports.drawing = drawing; + /** + * Represents a CSS color with red, green, blue and alpha components. + * + * Instances of this class are immutable. + * + * @param {number} red + * @param {number} green + * @param {number} blue + * @param {number=1} alpha + * + * @constructor + * @memberOf libjass.parts + */ + var Color = function () { + function Color(red, green, blue, alpha) { + if (alpha === void 0) { + alpha = 1; + } + this._red = red; + this._green = green; + this._blue = blue; + this._alpha = alpha; + } + Object.defineProperty(Color.prototype, "red", { + /** + * The red component of this color as a number between 0 and 255. + * + * @type {number} + */ + get: function () { + return this._red; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Color.prototype, "green", { + /** + * The green component of this color as a number between 0 and 255. + * + * @type {number} + */ + get: function () { + return this._green; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Color.prototype, "blue", { + /** + * The blue component of this color as a number between 0 and 255. + * + * @type {number} + */ + get: function () { + return this._blue; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Color.prototype, "alpha", { + /** + * The alpha component of this color as a number between 0 and 1, where 0 means transparent and 1 means opaque. + * + * @type {number} + */ + get: function () { + return this._alpha; + }, + enumerable: true, + configurable: true + }); + /** + * @param {?number} value The new alpha. If null, the existing alpha is used. + * @return {!libjass.parts.Color} Returns a new Color instance with the same color but the provided alpha. + */ + Color.prototype.withAlpha = function (value) { + if (value !== null) { + return new Color(this._red, this._green, this._blue, value); + } + return this; + }; + /** + * @return {string} The CSS representation "rgba(...)" of this color. + */ + Color.prototype.toString = function () { + return "rgba(" + this._red + ", " + this._green + ", " + this._blue + ", " + this._alpha.toFixed(3) + ")"; + }; + /** + * Returns a new Color by interpolating the current color to the final color by the given progression. + * + * @param {!libjass.parts.Color} final + * @param {number} progression + * @return {!libjass.parts.Color} + */ + Color.prototype.interpolate = function (final, progression) { + return new Color(this._red + progression * (final.red - this._red), this._green + progression * (final.green - this._green), this._blue + progression * (final.blue - this._blue), this._alpha + progression * (final.alpha - this._alpha)); + }; + return Color; + }(); + exports.Color = Color; + /** + * A comment, i.e., any text enclosed in {} that is not understood as an ASS tag. + * + * @param {string} value The text of this comment + * + * @constructor + * @memberOf libjass.parts + */ + var Comment = function () { + function Comment(value) { + this._value = value; + } + Object.defineProperty(Comment.prototype, "value", { + /** + * The value of this comment. + * + * @type {string} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return Comment; + }(); + exports.Comment = Comment; + /** + * A block of text, i.e., any text not enclosed in {}. Also includes \h. + * + * @param {string} value The content of this block of text + * + * @constructor + * @memberOf libjass.parts + */ + var Text = function () { + function Text(value) { + this._value = value; + } + Object.defineProperty(Text.prototype, "value", { + /** + * The value of this text part. + * + * @type {string} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + /** + * @return {string} + */ + Text.prototype.toString = function () { + return "Text { value: " + this._value.replace(/\u00A0/g, "\\h") + " }"; + }; + return Text; + }(); + exports.Text = Text; + /** + * A newline character \N. + * + * @constructor + * @memberOf libjass.parts + */ + var NewLine = function () { + function NewLine() {} + return NewLine; + }(); + exports.NewLine = NewLine; + /** + * An italic tag {\i} + * + * @param {?boolean} value {\i1} -> true, {\i0} -> false, {\i} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var Italic = function () { + function Italic(value) { + this._value = value; + } + Object.defineProperty(Italic.prototype, "value", { + /** + * The value of this italic tag. + * + * @type {?boolean} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return Italic; + }(); + exports.Italic = Italic; + /** + * A bold tag {\b} + * + * @param {?boolean|?number} value {\b1} -> true, {\b0} -> false, {\b###} -> weight of the bold (number), {\b} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var Bold = function () { + function Bold(value) { + this._value = value; + } + Object.defineProperty(Bold.prototype, "value", { + /** + * The value of this bold tag. + * + * @type {?boolean|?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return Bold; + }(); + exports.Bold = Bold; + /** + * An underline tag {\u} + * + * @param {?boolean} value {\u1} -> true, {\u0} -> false, {\u} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var Underline = function () { + function Underline(value) { + this._value = value; + } + Object.defineProperty(Underline.prototype, "value", { + /** + * The value of this underline tag. + * + * @type {?boolean} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return Underline; + }(); + exports.Underline = Underline; + /** + * A strike-through tag {\s} + * + * @param {?boolean} value {\s1} -> true, {\s0} -> false, {\s} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var StrikeThrough = function () { + function StrikeThrough(value) { + this._value = value; + } + Object.defineProperty(StrikeThrough.prototype, "value", { + /** + * The value of this strike-through tag. + * + * @type {?boolean} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return StrikeThrough; + }(); + exports.StrikeThrough = StrikeThrough; + /** + * A border tag {\bord} + * + * @param {?number} value {\bord###} -> width (number), {\bord} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var Border = function () { + function Border(value) { + this._value = value; + } + Object.defineProperty(Border.prototype, "value", { + /** + * The value of this border tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return Border; + }(); + exports.Border = Border; + /** + * A horizontal border tag {\xbord} + * + * @param {?number} value {\xbord###} -> width (number), {\xbord} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var BorderX = function () { + function BorderX(value) { + this._value = value; + } + Object.defineProperty(BorderX.prototype, "value", { + /** + * The value of this horizontal border tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return BorderX; + }(); + exports.BorderX = BorderX; + /** + * A vertical border tag {\ybord} + * + * @param {?number} value {\ybord###} -> height (number), {\ybord} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var BorderY = function () { + function BorderY(value) { + this._value = value; + } + Object.defineProperty(BorderY.prototype, "value", { + /** + * The value of this vertical border tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return BorderY; + }(); + exports.BorderY = BorderY; + /** + * A shadow tag {\shad} + * + * @param {?number} value {\shad###} -> depth (number), {\shad} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var Shadow = function () { + function Shadow(value) { + this._value = value; + } + Object.defineProperty(Shadow.prototype, "value", { + /** + * The value of this shadow tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return Shadow; + }(); + exports.Shadow = Shadow; + /** + * A horizontal shadow tag {\xshad} + * + * @param {?number} value {\xshad###} -> depth (number), {\xshad} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var ShadowX = function () { + function ShadowX(value) { + this._value = value; + } + Object.defineProperty(ShadowX.prototype, "value", { + /** + * The value of this horizontal shadow tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return ShadowX; + }(); + exports.ShadowX = ShadowX; + /** + * A vertical shadow tag {\yshad} + * + * @param {?number} value {\yshad###} -> depth (number), {\yshad} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var ShadowY = function () { + function ShadowY(value) { + this._value = value; + } + Object.defineProperty(ShadowY.prototype, "value", { + /** + * The value of this vertical shadow tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return ShadowY; + }(); + exports.ShadowY = ShadowY; + /** + * A blur tag {\be} + * + * @param {?number} value {\be###} -> strength (number), {\be} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var Blur = function () { + function Blur(value) { + this._value = value; + } + Object.defineProperty(Blur.prototype, "value", { + /** + * The value of this blur tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return Blur; + }(); + exports.Blur = Blur; + /** + * A Gaussian blur tag {\blur} + * + * @param {?number} value {\blur###} -> strength (number), {\blur} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var GaussianBlur = function () { + function GaussianBlur(value) { + this._value = value; + } + Object.defineProperty(GaussianBlur.prototype, "value", { + /** + * The value of this Gaussian blur tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return GaussianBlur; + }(); + exports.GaussianBlur = GaussianBlur; + /** + * A font name tag {\fn} + * + * @param {?string} value {\fn###} -> name (string), {\fn} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var FontName = function () { + function FontName(value) { + this._value = value; + } + Object.defineProperty(FontName.prototype, "value", { + /** + * The value of this font name tag. + * + * @type {?string} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return FontName; + }(); + exports.FontName = FontName; + /** + * A font size tag {\fs} + * + * @param {?number} value {\fs###} -> size (number), {\fs} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var FontSize = function () { + function FontSize(value) { + this._value = value; + } + Object.defineProperty(FontSize.prototype, "value", { + /** + * The value of this font size tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return FontSize; + }(); + exports.FontSize = FontSize; + /** + * A font size increase tag {\fs+} + * + * @param {number} value {\fs+###} -> relative difference (number, percentage) + * + * @constructor + * @memberOf libjass.parts + */ + var FontSizePlus = function () { + function FontSizePlus(value) { + this._value = value; + } + Object.defineProperty(FontSizePlus.prototype, "value", { + /** + * The value of this font size increase tag. + * + * @type {number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return FontSizePlus; + }(); + exports.FontSizePlus = FontSizePlus; + /** + * A font size decrease tag {\fs-} + * + * @param {number} value {\fs-###} -> relative difference (number, percentage) + * + * @constructor + * @memberOf libjass.parts + */ + var FontSizeMinus = function () { + function FontSizeMinus(value) { + this._value = value; + } + Object.defineProperty(FontSizeMinus.prototype, "value", { + /** + * The value of this font size decrease tag. + * + * @type {number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return FontSizeMinus; + }(); + exports.FontSizeMinus = FontSizeMinus; + /** + * A horizontal font scaling tag {\fscx} + * + * @param {?number} value {\fscx###} -> scale (number), {\fscx} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var FontScaleX = function () { + function FontScaleX(value) { + this._value = value; + } + Object.defineProperty(FontScaleX.prototype, "value", { + /** + * The value of this horizontal font scaling tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return FontScaleX; + }(); + exports.FontScaleX = FontScaleX; + /** + * A vertical font scaling tag {\fscy} + * + * @param {?number} value {\fscy###} -> scale (number), {\fscy} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var FontScaleY = function () { + function FontScaleY(value) { + this._value = value; + } + Object.defineProperty(FontScaleY.prototype, "value", { + /** + * The value of this vertical font scaling tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return FontScaleY; + }(); + exports.FontScaleY = FontScaleY; + /** + * A letter-spacing tag {\fsp} + * + * @param {?number} value {\fsp###} -> spacing (number), {\fsp} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var LetterSpacing = function () { + function LetterSpacing(value) { + this._value = value; + } + Object.defineProperty(LetterSpacing.prototype, "value", { + /** + * The value of this letter-spacing tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return LetterSpacing; + }(); + exports.LetterSpacing = LetterSpacing; + /** + * An X-axis rotation tag {\frx} + * + * @param {?number} value {\frx###} -> angle (number), {\frx} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var RotateX = function () { + function RotateX(value) { + this._value = value; + } + Object.defineProperty(RotateX.prototype, "value", { + /** + * The value of this X-axis rotation tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return RotateX; + }(); + exports.RotateX = RotateX; + /** + * A Y-axis rotation tag {\fry} + * + * @param {?number} value {\fry###} -> angle (number), {\fry} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var RotateY = function () { + function RotateY(value) { + this._value = value; + } + Object.defineProperty(RotateY.prototype, "value", { + /** + * The value of this Y-axis rotation tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return RotateY; + }(); + exports.RotateY = RotateY; + /** + * A Z-axis rotation tag {\fr} or {\frz} + * + * @param {?number} value {\frz###} -> angle (number), {\frz} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var RotateZ = function () { + function RotateZ(value) { + this._value = value; + } + Object.defineProperty(RotateZ.prototype, "value", { + /** + * The value of this Z-axis rotation tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return RotateZ; + }(); + exports.RotateZ = RotateZ; + /** + * An X-axis shearing tag {\fax} + * + * @param {?number} value {\fax###} -> angle (number), {\fax} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var SkewX = function () { + function SkewX(value) { + this._value = value; + } + Object.defineProperty(SkewX.prototype, "value", { + /** + * The value of this X-axis shearing tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return SkewX; + }(); + exports.SkewX = SkewX; + /** + * A Y-axis shearing tag {\fay} + * + * @param {?number} value {\fay###} -> angle (number), {\fay} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var SkewY = function () { + function SkewY(value) { + this._value = value; + } + Object.defineProperty(SkewY.prototype, "value", { + /** + * The value of this Y-axis shearing tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return SkewY; + }(); + exports.SkewY = SkewY; + /** + * A primary color tag {\c} or {\1c} + * + * @param {libjass.parts.Color} value {\1c###} -> color (Color), {\1c} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var PrimaryColor = function () { + function PrimaryColor(value) { + this._value = value; + } + Object.defineProperty(PrimaryColor.prototype, "value", { + /** + * The value of this primary color tag. + * + * @type {libjass.parts.Color} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return PrimaryColor; + }(); + exports.PrimaryColor = PrimaryColor; + /** + * A secondary color tag {\2c} + * + * @param {libjass.parts.Color} value {\2c###} -> color (Color), {\2c} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var SecondaryColor = function () { + function SecondaryColor(value) { + this._value = value; + } + Object.defineProperty(SecondaryColor.prototype, "value", { + /** + * The value of this secondary color tag. + * + * @type {libjass.parts.Color} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return SecondaryColor; + }(); + exports.SecondaryColor = SecondaryColor; + /** + * An outline color tag {\3c} + * + * @param {libjass.parts.Color} value {\3c###} -> color (Color), {\3c} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var OutlineColor = function () { + function OutlineColor(value) { + this._value = value; + } + Object.defineProperty(OutlineColor.prototype, "value", { + /** + * The value of this outline color tag. + * + * @type {libjass.parts.Color} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return OutlineColor; + }(); + exports.OutlineColor = OutlineColor; + /** + * A shadow color tag {\4c} + * + * @param {libjass.parts.Color} value {\4c###} -> color (Color), {\4c} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var ShadowColor = function () { + function ShadowColor(value) { + this._value = value; + } + Object.defineProperty(ShadowColor.prototype, "value", { + /** + * The value of this shadow color tag. + * + * @type {libjass.parts.Color} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return ShadowColor; + }(); + exports.ShadowColor = ShadowColor; + /** + * An alpha tag {\alpha} + * + * @param {?number} value {\alpha###} -> alpha (number), {\alpha} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var Alpha = function () { + function Alpha(value) { + this._value = value; + } + Object.defineProperty(Alpha.prototype, "value", { + /** + * The value of this alpha tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return Alpha; + }(); + exports.Alpha = Alpha; + /** + * A primary alpha tag {\1a} + * + * @param {?number} value {\1a###} -> alpha (number), {\1a} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var PrimaryAlpha = function () { + function PrimaryAlpha(value) { + this._value = value; + } + Object.defineProperty(PrimaryAlpha.prototype, "value", { + /** + * The value of this primary alpha tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return PrimaryAlpha; + }(); + exports.PrimaryAlpha = PrimaryAlpha; + /** + * A secondary alpha tag {\2a} + * + * @param {?number} value {\2a###} -> alpha (number), {\2a} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var SecondaryAlpha = function () { + function SecondaryAlpha(value) { + this._value = value; + } + Object.defineProperty(SecondaryAlpha.prototype, "value", { + /** + * The value of this secondary alpha tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return SecondaryAlpha; + }(); + exports.SecondaryAlpha = SecondaryAlpha; + /** + * An outline alpha tag {\3a} + * + * @param {?number} value {\3a###} -> alpha (number), {\3a} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var OutlineAlpha = function () { + function OutlineAlpha(value) { + this._value = value; + } + Object.defineProperty(OutlineAlpha.prototype, "value", { + /** + * The value of this outline alpha tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return OutlineAlpha; + }(); + exports.OutlineAlpha = OutlineAlpha; + /** + * A shadow alpha tag {\4a} + * + * @param {?number} value {\4a###} -> alpha (number), {\4a} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var ShadowAlpha = function () { + function ShadowAlpha(value) { + this._value = value; + } + Object.defineProperty(ShadowAlpha.prototype, "value", { + /** + * The value of this shadow alpha tag. + * + * @type {?number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return ShadowAlpha; + }(); + exports.ShadowAlpha = ShadowAlpha; + /** + * An alignment tag {\an} or {\a} + * + * @param {number} value {\an###} -> alignment (number) + * + * @constructor + * @memberOf libjass.parts + */ + var Alignment = function () { + function Alignment(value) { + this._value = value; + } + Object.defineProperty(Alignment.prototype, "value", { + /** + * The value of this alignment tag. + * + * @type {number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return Alignment; + }(); + exports.Alignment = Alignment; + /** + * A color karaoke tag {\k} + * + * @param {number} duration {\k###} -> duration (number) + * + * @constructor + * @memberOf libjass.parts + */ + var ColorKaraoke = function () { + function ColorKaraoke(duration) { + this._duration = duration; + } + Object.defineProperty(ColorKaraoke.prototype, "duration", { + /** + * The duration of this color karaoke tag. + * + * @type {number} + */ + get: function () { + return this._duration; + }, + enumerable: true, + configurable: true + }); + return ColorKaraoke; + }(); + exports.ColorKaraoke = ColorKaraoke; + /** + * A sweeping color karaoke tag {\K} or {\kf} + * + * @param {number} duration {\kf###} -> duration (number) + * + * @constructor + * @memberOf libjass.parts + */ + var SweepingColorKaraoke = function () { + function SweepingColorKaraoke(duration) { + this._duration = duration; + } + Object.defineProperty(SweepingColorKaraoke.prototype, "duration", { + /** + * The duration of this sweeping color karaoke tag. + * + * @type {number} + */ + get: function () { + return this._duration; + }, + enumerable: true, + configurable: true + }); + return SweepingColorKaraoke; + }(); + exports.SweepingColorKaraoke = SweepingColorKaraoke; + /** + * An outline karaoke tag {\ko} + * + * @param {number} duration {\ko###} -> duration (number) + * + * @constructor + * @memberOf libjass.parts + */ + var OutlineKaraoke = function () { + function OutlineKaraoke(duration) { + this._duration = duration; + } + Object.defineProperty(OutlineKaraoke.prototype, "duration", { + /** + * The duration of this outline karaoke tag. + * + * @type {number} + */ + get: function () { + return this._duration; + }, + enumerable: true, + configurable: true + }); + return OutlineKaraoke; + }(); + exports.OutlineKaraoke = OutlineKaraoke; + /** + * A wrapping style tag {\q} + * + * @param {number} value {\q###} -> style (number) + * + * @constructor + * @memberOf libjass.parts + */ + var WrappingStyle = function () { + function WrappingStyle(value) { + this._value = value; + } + Object.defineProperty(WrappingStyle.prototype, "value", { + /** + * The value of this wrapping style tag. + * + * @type {number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return WrappingStyle; + }(); + exports.WrappingStyle = WrappingStyle; + /** + * A style reset tag {\r} + * + * @param {?string} value {\r###} -> style name (string), {\r} -> null + * + * @constructor + * @memberOf libjass.parts + */ + var Reset = function () { + function Reset(value) { + this._value = value; + } + Object.defineProperty(Reset.prototype, "value", { + /** + * The value of this style reset tag. + * + * @type {?string} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return Reset; + }(); + exports.Reset = Reset; + /** + * A position tag {\pos} + * + * @param {number} x + * @param {number} y + * + * @constructor + * @memberOf libjass.parts + */ + var Position = function () { + function Position(x, y) { + this._x = x; + this._y = y; + } + Object.defineProperty(Position.prototype, "x", { + /** + * The x value of this position tag. + * + * @type {number} + */ + get: function () { + return this._x; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Position.prototype, "y", { + /** + * The y value of this position tag. + * + * @type {number} + */ + get: function () { + return this._y; + }, + enumerable: true, + configurable: true + }); + return Position; + }(); + exports.Position = Position; + /** + * A movement tag {\move} + * + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {?number} t1 + * @param {?number} t2 + * + * @constructor + * @memberOf libjass.parts + */ + var Move = function () { + function Move(x1, y1, x2, y2, t1, t2) { + this._x1 = x1; + this._y1 = y1; + this._x2 = x2; + this._y2 = y2; + this._t1 = t1; + this._t2 = t2; + } + Object.defineProperty(Move.prototype, "x1", { + /** + * The starting x value of this move tag. + * + * @type {number} + */ + get: function () { + return this._x1; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Move.prototype, "y1", { + /** + * The starting y value of this move tag. + * + * @type {number} + */ + get: function () { + return this._y1; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Move.prototype, "x2", { + /** + * The ending x value of this move tag. + * + * @type {number} + */ + get: function () { + return this._x2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Move.prototype, "y2", { + /** + * The ending y value of this move tag. + * + * @type {number} + */ + get: function () { + return this._y2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Move.prototype, "t1", { + /** + * The start time of this move tag. + * + * @type {?number} + */ + get: function () { + return this._t1; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Move.prototype, "t2", { + /** + * The end time value of this move tag. + * + * @type {?number} + */ + get: function () { + return this._t2; + }, + enumerable: true, + configurable: true + }); + return Move; + }(); + exports.Move = Move; + /** + * A rotation origin tag {\org} + * + * @param {number} x + * @param {number} y + * + * @constructor + * @memberOf libjass.parts + */ + var RotationOrigin = function () { + function RotationOrigin(x, y) { + this._x = x; + this._y = y; + } + Object.defineProperty(RotationOrigin.prototype, "x", { + /** + * The x value of this rotation origin tag. + * + * @type {number} + */ + get: function () { + return this._x; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(RotationOrigin.prototype, "y", { + /** + * The y value of this rotation origin tag. + * + * @type {number} + */ + get: function () { + return this._y; + }, + enumerable: true, + configurable: true + }); + return RotationOrigin; + }(); + exports.RotationOrigin = RotationOrigin; + /** + * A simple fade tag {\fad} + * + * @param {number} start + * @param {number} end + * + * @constructor + * @memberOf libjass.parts + */ + var Fade = function () { + function Fade(start, end) { + this._start = start; + this._end = end; + } + Object.defineProperty(Fade.prototype, "start", { + /** + * The start time of this fade tag. + * + * @type {number} + */ + get: function () { + return this._start; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Fade.prototype, "end", { + /** + * The end time of this fade tag. + * + * @type {number} + */ + get: function () { + return this._end; + }, + enumerable: true, + configurable: true + }); + return Fade; + }(); + exports.Fade = Fade; + /** + * A complex fade tag {\fade} + * + * @param {number} a1 + * @param {number} a2 + * @param {number} a3 + * @param {number} t1 + * @param {number} t2 + * @param {number} t3 + * @param {number} t4 + * + * @constructor + * @memberOf libjass.parts + */ + var ComplexFade = function () { + function ComplexFade(a1, a2, a3, t1, t2, t3, t4) { + this._a1 = a1; + this._a2 = a2; + this._a3 = a3; + this._t1 = t1; + this._t2 = t2; + this._t3 = t3; + this._t4 = t4; + } + Object.defineProperty(ComplexFade.prototype, "a1", { + /** + * The alpha value of this complex fade tag at time t2. + * + * @type {number} + */ + get: function () { + return this._a1; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ComplexFade.prototype, "a2", { + /** + * The alpha value of this complex fade tag at time t3. + * + * @type {number} + */ + get: function () { + return this._a2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ComplexFade.prototype, "a3", { + /** + * The alpha value of this complex fade tag at time t4. + * + * @type {number} + */ + get: function () { + return this._a3; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ComplexFade.prototype, "t1", { + /** + * The starting time of this complex fade tag. + * + * @type {number} + */ + get: function () { + return this._t1; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ComplexFade.prototype, "t2", { + /** + * The first intermediate time of this complex fade tag. + * + * @type {number} + */ + get: function () { + return this._t2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ComplexFade.prototype, "t3", { + /** + * The second intermediate time of this complex fade tag. + * + * @type {number} + */ + get: function () { + return this._t3; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ComplexFade.prototype, "t4", { + /** + * The ending time of this complex fade tag. + * + * @type {number} + */ + get: function () { + return this._t4; + }, + enumerable: true, + configurable: true + }); + return ComplexFade; + }(); + exports.ComplexFade = ComplexFade; + /** + * A transform tag {\t} + * + * @param {?number} start + * @param {?number} end + * @param {?number} accel + * @param {!Array.} tags + * + * @constructor + * @memberOf libjass.parts + */ + var Transform = function () { + function Transform(start, end, accel, tags) { + this._start = start; + this._end = end; + this._accel = accel; + this._tags = tags; + } + Object.defineProperty(Transform.prototype, "start", { + /** + * The starting time of this transform tag. + * + * @type {?number} + */ + get: function () { + return this._start; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "end", { + /** + * The ending time of this transform tag. + * + * @type {?number} + */ + get: function () { + return this._end; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "accel", { + /** + * The acceleration of this transform tag. + * + * @type {?number} + */ + get: function () { + return this._accel; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "tags", { + /** + * The tags animated by this transform tag. + * + * @type {!Array.} + */ + get: function () { + return this._tags; + }, + enumerable: true, + configurable: true + }); + return Transform; + }(); + exports.Transform = Transform; + /** + * A rectangular clip tag {\clip} or {\iclip} + * + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {boolean} inside + * + * @constructor + * @memberOf libjass.parts + */ + var RectangularClip = function () { + function RectangularClip(x1, y1, x2, y2, inside) { + this._x1 = x1; + this._y1 = y1; + this._x2 = x2; + this._y2 = y2; + this._inside = inside; + } + Object.defineProperty(RectangularClip.prototype, "x1", { + /** + * The X coordinate of the starting position of this rectangular clip tag. + * + * @type {number} + */ + get: function () { + return this._x1; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(RectangularClip.prototype, "y1", { + /** + * The Y coordinate of the starting position of this rectangular clip tag. + * + * @type {number} + */ + get: function () { + return this._y1; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(RectangularClip.prototype, "x2", { + /** + * The X coordinate of the ending position of this rectangular clip tag. + * + * @type {number} + */ + get: function () { + return this._x2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(RectangularClip.prototype, "y2", { + /** + * The Y coordinate of the ending position of this rectangular clip tag. + * + * @type {number} + */ + get: function () { + return this._y2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(RectangularClip.prototype, "inside", { + /** + * Whether this rectangular clip tag clips the region it encloses or the region it excludes. + * + * @type {boolean} + */ + get: function () { + return this._inside; + }, + enumerable: true, + configurable: true + }); + return RectangularClip; + }(); + exports.RectangularClip = RectangularClip; + /** + * A vector clip tag {\clip} or {\iclip} + * + * @param {number} scale + * @param {!Array.} instructions + * @param {boolean} inside + * + * @constructor + * @memberOf libjass.parts + */ + var VectorClip = function () { + function VectorClip(scale, instructions, inside) { + this._scale = scale; + this._instructions = instructions; + this._inside = inside; + } + Object.defineProperty(VectorClip.prototype, "scale", { + /** + * The scale of this vector clip tag. + * + * @type {number} + */ + get: function () { + return this._scale; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(VectorClip.prototype, "instructions", { + /** + * The clip commands of this vector clip tag. + * + * @type {string} + */ + get: function () { + return this._instructions; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(VectorClip.prototype, "inside", { + /** + * Whether this vector clip tag clips the region it encloses or the region it excludes. + * + * @type {boolean} + */ + get: function () { + return this._inside; + }, + enumerable: true, + configurable: true + }); + return VectorClip; + }(); + exports.VectorClip = VectorClip; + /** + * A drawing mode tag {\p} + * + * @param {number} scale + * + * @constructor + * @memberOf libjass.parts + */ + var DrawingMode = function () { + function DrawingMode(scale) { + this._scale = scale; + } + Object.defineProperty(DrawingMode.prototype, "scale", { + /** + * The scale of this drawing mode tag. + * + * @type {number} + */ + get: function () { + return this._scale; + }, + enumerable: true, + configurable: true + }); + return DrawingMode; + }(); + exports.DrawingMode = DrawingMode; + /** + * A drawing mode baseline offset tag {\pbo} + * + * @param {number} value + * + * @constructor + * @memberOf libjass.parts + */ + var DrawingBaselineOffset = function () { + function DrawingBaselineOffset(value) { + this._value = value; + } + Object.defineProperty(DrawingBaselineOffset.prototype, "value", { + /** + * The value of this drawing mode baseline offset tag. + * + * @type {number} + */ + get: function () { + return this._value; + }, + enumerable: true, + configurable: true + }); + return DrawingBaselineOffset; + }(); + exports.DrawingBaselineOffset = DrawingBaselineOffset; + /** + * A pseudo-part representing text interpreted as drawing instructions + * + * @param {!Array.} instructions + * + * @constructor + * @memberOf libjass.parts + */ + var DrawingInstructions = function () { + function DrawingInstructions(instructions) { + this._instructions = instructions; + } + Object.defineProperty(DrawingInstructions.prototype, "instructions", { + /** + * The instructions contained in this drawing instructions part. + * + * @type {!Array.} + */ + get: function () { + return this._instructions; + }, + enumerable: true, + configurable: true + }); + return DrawingInstructions; + }(); + exports.DrawingInstructions = DrawingInstructions; + var addToString = function (ctor, ctorName) { + if (!ctor.prototype.hasOwnProperty("toString")) { + var propertyNames_1 = Object.getOwnPropertyNames(ctor.prototype).filter(function (property) { + return property !== "constructor"; + }); + ctor.prototype.toString = function () { + var _this = this; + return ctorName + " { " + propertyNames_1.map(function (name) { + return name + ": " + _this[name]; + }).join(", ") + (propertyNames_1.length > 0 ? " " : "") + "}"; + }; + } + }; + for (var _i = 0, _a = Object.keys(exports); _i < _a.length; _i++) { + var key = _a[_i]; + var value = exports[key]; + if (value instanceof Function) { + addToString(value, key); + serialization_2.registerClass(value); + } + } + for (var _b = 0, _c = Object.keys(drawing); _b < _c.length; _b++) { + var key = _c[_b]; + var value = drawing[key]; + if (value instanceof Function) { + addToString(value, "Drawing" + key); + serialization_2.registerClass(value); + } + } + }); + def("parts/drawing", [], function (exports) { + /** + * An instruction to move to a particular position. + * + * @param {number} x + * @param {number} y + * + * @constructor + * @implements {libjass.parts.drawing.Instruction} + * @memberOf libjass.parts.drawing + */ + var MoveInstruction = function () { + function MoveInstruction(x, y) { + this._x = x; + this._y = y; + } + Object.defineProperty(MoveInstruction.prototype, "x", { + /** + * The X position of this move instruction. + * + * @type {number} + */ + get: function () { + return this._x; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(MoveInstruction.prototype, "y", { + /** + * The Y position of this move instruction. + * + * @type {number} + */ + get: function () { + return this._y; + }, + enumerable: true, + configurable: true + }); + return MoveInstruction; + }(); + exports.MoveInstruction = MoveInstruction; + /** + * An instruction to draw a line to a particular position. + * + * @param {number} x + * @param {number} y + * + * @constructor + * @implements {libjass.parts.drawing.Instruction} + * @memberOf libjass.parts.drawing + */ + var LineInstruction = function () { + function LineInstruction(x, y) { + this._x = x; + this._y = y; + } + Object.defineProperty(LineInstruction.prototype, "x", { + /** + * The X position of this line instruction. + * + * @type {number} + */ + get: function () { + return this._x; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(LineInstruction.prototype, "y", { + /** + * The Y position of this line instruction. + * + * @type {number} + */ + get: function () { + return this._y; + }, + enumerable: true, + configurable: true + }); + return LineInstruction; + }(); + exports.LineInstruction = LineInstruction; + /** + * An instruction to draw a cubic bezier curve to a particular position, with two given control points. + * + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * + * @constructor + * @implements {libjass.parts.drawing.Instruction} + * @memberOf libjass.parts.drawing + */ + var CubicBezierCurveInstruction = function () { + function CubicBezierCurveInstruction(x1, y1, x2, y2, x3, y3) { + this._x1 = x1; + this._y1 = y1; + this._x2 = x2; + this._y2 = y2; + this._x3 = x3; + this._y3 = y3; + } + Object.defineProperty(CubicBezierCurveInstruction.prototype, "x1", { + /** + * The X position of the first control point of this cubic bezier curve instruction. + * + * @type {number} + */ + get: function () { + return this._x1; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CubicBezierCurveInstruction.prototype, "y1", { + /** + * The Y position of the first control point of this cubic bezier curve instruction. + * + * @type {number} + */ + get: function () { + return this._y1; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CubicBezierCurveInstruction.prototype, "x2", { + /** + * The X position of the second control point of this cubic bezier curve instruction. + * + * @type {number} + */ + get: function () { + return this._x2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CubicBezierCurveInstruction.prototype, "y2", { + /** + * The Y position of the second control point of this cubic bezier curve instruction. + * + * @type {number} + */ + get: function () { + return this._y2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CubicBezierCurveInstruction.prototype, "x3", { + /** + * The ending X position of this cubic bezier curve instruction. + * + * @type {number} + */ + get: function () { + return this._x3; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CubicBezierCurveInstruction.prototype, "y3", { + /** + * The ending Y position of this cubic bezier curve instruction. + * + * @type {number} + */ + get: function () { + return this._y3; + }, + enumerable: true, + configurable: true + }); + return CubicBezierCurveInstruction; + }(); + exports.CubicBezierCurveInstruction = CubicBezierCurveInstruction; + }); + def("types/misc", [], function (exports) { + /** + * The format of the string passed to {@link libjass.ASS.fromString} + * + * @enum + * @memberOf libjass + */ + (function (Format) { + Format[Format["ASS"] = 0] = "ASS"; + Format[Format["SRT"] = 1] = "SRT"; + })(exports.Format || (exports.Format = {})); + /** + * The wrapping style defined in the {@link libjass.ScriptProperties} + * + * @enum + * @memberOf libjass + */ + (function (WrappingStyle) { + WrappingStyle[WrappingStyle["SmartWrappingWithWiderTopLine"] = 0] = "SmartWrappingWithWiderTopLine"; + WrappingStyle[WrappingStyle["SmartWrappingWithWiderBottomLine"] = 3] = "SmartWrappingWithWiderBottomLine"; + WrappingStyle[WrappingStyle["EndOfLineWrapping"] = 1] = "EndOfLineWrapping"; + WrappingStyle[WrappingStyle["NoLineWrapping"] = 2] = "NoLineWrapping"; + })(exports.WrappingStyle || (exports.WrappingStyle = {})); + /** + * The border style defined in the {@link libjass.Style} properties. + * + * @enum + * @memberOf libjass + */ + (function (BorderStyle) { + BorderStyle[BorderStyle["Outline"] = 1] = "Outline"; + BorderStyle[BorderStyle["OpaqueBox"] = 3] = "OpaqueBox"; + })(exports.BorderStyle || (exports.BorderStyle = {})); + /** + * @param {!Map.} template + * @param {string} key + * @param {function(string):T} converter + * @param {?function(T):boolean} validator + * @param {T} defaultValue + * @return {T} + * + * @template T + */ + function valueOrDefault(template, key, converter, validator, defaultValue) { + var value = template.get(key); + if (value === undefined) { + return converter(defaultValue); + } + try { + var result = converter(value); + if (validator !== null && !validator(result)) { + throw new Error("Validation failed."); + } + return result; + } catch (ex) { + throw new Error("Property " + key + " has invalid value " + value + " - " + ex.stack); + } + } + exports.valueOrDefault = valueOrDefault; + }); + def("webworker/index", [ "webworker/channel", "webworker/commands" ], function (channel_1, commands_2, exports) { + exports.WorkerCommands = commands_2.WorkerCommands; + /** + * Indicates whether web workers are supposed in this environment or not. + * + * @type {boolean} + * + * @memberOf libjass.webworker + */ + exports.supported = typeof Worker !== "undefined"; + var _scriptNode = typeof document !== "undefined" && document.currentScript !== undefined ? document.currentScript : null; + /** + * Create a new web worker and returns a {@link libjass.webworker.WorkerChannel} to it. + * + * @param {string=} scriptPath The path to libjass.js to be loaded in the web worker. If the browser supports document.currentScript, the parameter is optional and, if not provided, + * the path will be determined from the src attribute of the