Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9c3ee5edf | |||
| 41d8516449 | |||
| 8b7ef6201e | |||
| 5fb5a4d73d | |||
| 86971d1902 | |||
| 9071332e1e | |||
| ae62c0c89f | |||
| 027a8d69f5 | |||
| 4c4ef34e8b | |||
| 4d519fd6c9 | |||
| 4ffa5499a4 | |||
| 6e7e7a005e | |||
| 20a0d8ee64 | |||
| 56fbd7a9b2 | |||
| 293617aa22 | |||
| 7ae40e7164 | |||
| 361bc707d3 | |||
| 04dc60f128 | |||
| 70537168a0 | |||
| 6e33697c5c | |||
| 8680e884a1 | |||
| ec693190b5 | |||
| 4dfdaf6010 | |||
| 9540b2c00a | |||
| 9445d04efa | |||
| 7a7ee02905 | |||
| a6605064c1 | |||
| 4438caae92 | |||
| 212f376b77 | |||
| 35310a5980 | |||
| 4ccb76f53c | |||
| 1ac4fb24aa | |||
| 1aef4ef1c9 | |||
| c421cb10b6 | |||
| 739ae86ffe | |||
| 3dc4a78812 | |||
| 97c0691994 | |||
| 79c9b73e0d | |||
| 1e20e498c4 | |||
| 5e6f3c8ba4 | |||
| c15bd7391d | |||
| 66f8ffc4c2 | |||
| 93c2428626 | |||
| c04d0b6631 | |||
| 05fc742588 | |||
| d590984eb6 | |||
| a67ec2948f | |||
| c432f33149 | |||
| 1bc6e63d18 | |||
| d5831e65a1 | |||
| 15d13e2fa2 | |||
| 5e100e88fb | |||
| 18b5d88f07 | |||
| 201fc044ee | |||
| bb28a85d97 | |||
| 414e48c022 | |||
| 7a5957cb27 | |||
| 2ae4ba0a09 | |||
| 40ac799134 | |||
| 990ca04065 | |||
| efa2fa7ade | |||
| 1de86c435a | |||
| df3ddb214e | |||
| fa3467c649 | |||
| bba93dad9f |
@@ -1,12 +1,14 @@
|
||||
build/doc.js
|
||||
build/typescript/typescript.d.ts
|
||||
build/typescript/*.js
|
||||
/build/doc.js
|
||||
/build/typescript/typescript.d.ts
|
||||
/build/typescript/*.js
|
||||
|
||||
dist/
|
||||
/dist/
|
||||
|
||||
lib/
|
||||
!lib/libjass.css
|
||||
/lib/
|
||||
!/lib/libjass.css
|
||||
|
||||
node_modules/
|
||||
/node_modules/
|
||||
|
||||
npm-debug.log
|
||||
/npm-debug.log
|
||||
|
||||
/src/version.ts
|
||||
|
||||
@@ -4,6 +4,7 @@ node_js:
|
||||
- "0.12"
|
||||
- "4"
|
||||
- "5"
|
||||
- "6"
|
||||
before_script:
|
||||
- "node ./build.js doc"
|
||||
sudo: false
|
||||
|
||||
@@ -50,7 +50,7 @@ The API documentation is linked in the Links section below. Here's an overview:
|
||||
|
||||
* 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 [libjass.renderers.makeFontMapFromStyleElement()](http://arnavion.github.io/libjass/api.xhtml#libjass.renderers.makeFontMapFromStyleElement)
|
||||
* 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.
|
||||
|
||||
@@ -137,6 +137,7 @@ You can also join the IRC channel in the links section below and ask any questio
|
||||
* \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
|
||||
|
||||
@@ -67,7 +67,43 @@ task("build-tools", function (callback) {
|
||||
|
||||
task("default", ["libjass.js", "libjass.min.js"]);
|
||||
|
||||
task("libjass.js", ["build-tools"], function (callback) {
|
||||
task("version.ts", function (callback) {
|
||||
fs.exists("./src/version.ts", function (exists) {
|
||||
if (exists) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
fs.readFile.bind(fs, "./package.json"),
|
||||
function (buffer, callback) {
|
||||
try {
|
||||
var packageJson = JSON.parse(buffer);
|
||||
var versionString = packageJson.version;
|
||||
var versionParts = versionString.split(".").map(function (num) { return parseInt(num); });
|
||||
var versionFileContents =
|
||||
"/**\n" +
|
||||
" * The version of libjass. An array like\n" +
|
||||
" *\n" +
|
||||
" * [\"0.12.0\", 0, 12, 0]\n" +
|
||||
" *\n" +
|
||||
" * @type {!Array.<string|number>}\n" +
|
||||
" */\n" +
|
||||
"export const version = " + JSON.stringify([versionString].concat(versionParts)) + ";\n";
|
||||
callback(null, versionFileContents);
|
||||
}
|
||||
catch (ex) {
|
||||
callback(ex);
|
||||
}
|
||||
},
|
||||
function (contents, callback) {
|
||||
fs.writeFile("./src/version.ts", contents, "utf8", callback);
|
||||
}
|
||||
], callback);
|
||||
});
|
||||
});
|
||||
|
||||
task("libjass.js", ["build-tools", "version.ts"], function (callback) {
|
||||
fs.exists("./lib/libjass.js", function (exists) {
|
||||
if (exists) {
|
||||
callback();
|
||||
@@ -76,7 +112,7 @@ task("libjass.js", ["build-tools"], function (callback) {
|
||||
|
||||
callback(null, task.src("./src/tsconfig.json")
|
||||
.pipe(TypeScript.build("./src/index.ts", "libjass"))
|
||||
.pipe(UglifyJS.build("./src/index", "libjass", ["AttachmentType", "BorderStyle", "ClockEvent", "Format", "WorkerCommands", "WrappingStyle"]))
|
||||
.pipe(UglifyJS.build("libjass", ["AttachmentType", "BorderStyle", "ClockEvent", "Format", "WorkerCommands", "WrappingStyle"]))
|
||||
.pipe(task.dest("./lib")));
|
||||
});
|
||||
});
|
||||
@@ -94,9 +130,9 @@ task("libjass.min.js", ["libjass.js"], function (callback) {
|
||||
});
|
||||
});
|
||||
|
||||
task("clean", task.clean(["./lib/libjass.js", "./lib/libjass.js.map", "./lib/libjass.min.js", "./lib/libjass.min.js.map"]));
|
||||
task("clean", task.clean(["./src/version.ts", "./lib/libjass.js", "./lib/libjass.js.map", "./lib/libjass.min.js", "./lib/libjass.min.js.map"]));
|
||||
|
||||
task("watch", ["build-tools"], function (callback) {
|
||||
task("watch", ["build-tools", "version.ts"], function (callback) {
|
||||
npm.load(function () {
|
||||
var testRunning = false;
|
||||
var rerunTest = false;
|
||||
@@ -125,7 +161,7 @@ task("watch", ["build-tools"], function (callback) {
|
||||
|
||||
task.src("./src/tsconfig.json")
|
||||
.pipe(TypeScript.watch("./src/index.ts", "libjass"))
|
||||
.pipe(UglifyJS.watch("./src/index", "libjass", ["BorderStyle", "ClockEvent", "Format", "WorkerCommands", "WrappingStyle"]))
|
||||
.pipe(UglifyJS.watch("libjass", ["BorderStyle", "ClockEvent", "Format", "WorkerCommands", "WrappingStyle"]))
|
||||
.pipe(task.dest("./lib"))
|
||||
.pipe(new task.FileTransform(function (file) {
|
||||
if (file.path === "libjass.js") {
|
||||
@@ -165,7 +201,7 @@ task("demo", ["libjass.js"], function () {
|
||||
|
||||
task("doc", ["make-doc", "test-doc"]);
|
||||
|
||||
task("make-doc", ["build-tools"], function () {
|
||||
task("make-doc", ["build-tools", "version.ts"], function () {
|
||||
var Doc = require("./build/doc.js");
|
||||
|
||||
return task.src("./src/tsconfig.json")
|
||||
|
||||
@@ -18,18 +18,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { Transform } from "stream";
|
||||
|
||||
import { File, FileTransform } from "async-build";
|
||||
import { FileTransform } from "async-build";
|
||||
|
||||
import * as AST from "./typescript/ast";
|
||||
import { Compiler } from "./typescript/compiler";
|
||||
import { walk } from "./typescript/walker";
|
||||
|
||||
function flatten<T>(arr: T[][]): T[] {
|
||||
var result: T[] = [];
|
||||
let result: T[] = [];
|
||||
|
||||
for (const a of arr) {
|
||||
result = result.concat(a);
|
||||
@@ -38,7 +34,7 @@ function flatten<T>(arr: T[][]): T[] {
|
||||
return result;
|
||||
}
|
||||
|
||||
var sorter = (() => {
|
||||
const sorter = (() => {
|
||||
function visibilitySorter(value1: { isPrivate?: boolean; isProtected?: boolean; }, value2: { isPrivate?: boolean; isProtected?: boolean; }) {
|
||||
if (value1.isPrivate === value2.isPrivate && value1.isProtected === value2.isProtected) {
|
||||
return 0;
|
||||
@@ -63,10 +59,10 @@ var sorter = (() => {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var types = [AST.Property, AST.Function, AST.Interface, AST.Class, AST.Enum];
|
||||
const types = [AST.Property, AST.Function, AST.Interface, AST.Class, AST.Enum];
|
||||
function typeSorter(value1: AST.ModuleMember | AST.NamespaceMember, value2: AST.ModuleMember | AST.NamespaceMember) {
|
||||
var type1Index = -1;
|
||||
var type2Index = -1;
|
||||
let type1Index = -1;
|
||||
let type2Index = -1;
|
||||
|
||||
types.every((type, index) => {
|
||||
if (value1 instanceof type) {
|
||||
@@ -85,11 +81,11 @@ var sorter = (() => {
|
||||
return value1.name.localeCompare(value2.name);
|
||||
}
|
||||
|
||||
var sorters: ((value1: AST.ModuleMember, value2: AST.ModuleMember) => number)[] = [visibilitySorter, typeSorter, nameSorter];
|
||||
const sorters: ((value1: AST.ModuleMember, value2: AST.ModuleMember) => number)[] = [visibilitySorter, typeSorter, nameSorter];
|
||||
|
||||
return (value1: AST.ModuleMember, value2: AST.ModuleMember) => {
|
||||
for (var i = 0; i < sorters.length; i++) {
|
||||
var result = sorters[i](value1, value2);
|
||||
for (const sorter of sorters) {
|
||||
const result = sorter(value1, value2);
|
||||
|
||||
if (result !== 0) {
|
||||
return result;
|
||||
@@ -111,10 +107,10 @@ function sanitize(str: string) {
|
||||
function toVariableName(item: { name: string }) {
|
||||
// TODO: Handle non-letters (are both their toLowerCase() and toUpperCase())
|
||||
|
||||
var name = item.name;
|
||||
var result = "";
|
||||
const name = item.name;
|
||||
let result = "";
|
||||
|
||||
for (var i = 0; i < name.length; i++) {
|
||||
for (let i = 0; i < name.length; i++) {
|
||||
if (name[i] === name[i].toLowerCase()) {
|
||||
// This is lower case. Write it as lower case.
|
||||
result += name[i];
|
||||
@@ -169,15 +165,15 @@ function toUsageName(item: AST.Class | AST.Interface | AST.Function | AST.Proper
|
||||
}
|
||||
|
||||
if (item.parent instanceof AST.Namespace) {
|
||||
if ((<AST.Class | AST.Interface | AST.Function | AST.Enum>item).isPrivate) {
|
||||
if ((item as AST.CanBePrivate).isPrivate) {
|
||||
return item.name;
|
||||
}
|
||||
|
||||
return item.fullName;
|
||||
}
|
||||
|
||||
if ((<AST.Function>item).isStatic) {
|
||||
return toUsageName(<AST.Class | AST.Interface>item.parent) + '.' + item.name;
|
||||
if ((item as AST.CanBeStatic).isStatic) {
|
||||
return toUsageName(item.parent as AST.Class | AST.Interface) + '.' + item.name;
|
||||
}
|
||||
|
||||
return toVariableName(item.parent) + '.' + item.name;
|
||||
@@ -188,13 +184,12 @@ function toId(item: { fullName?: string; name: string; }): string {
|
||||
}
|
||||
|
||||
function toLink(item: AST.ModuleMember | AST.EnumMember | AST.TypeReference): string {
|
||||
var result = `<a href="#${ toId(item) }">${ sanitize(item.name) }`;
|
||||
let result = `<a href="#${ toId(item) }">${ sanitize(item.name) }`;
|
||||
|
||||
var itemWithGenerics = <AST.HasGenerics>item;
|
||||
if (itemWithGenerics.generics !== undefined && itemWithGenerics.generics.length > 0) {
|
||||
var generics = <(string | AST.TypeReference | AST.IntrinsicTypeReference)[]>itemWithGenerics.generics;
|
||||
if (AST.hasGenerics(item) && item.generics.length > 0) {
|
||||
const generics = item.generics as (string | AST.TypeReference | AST.IntrinsicTypeReference)[];
|
||||
result += sanitize(`.<${ generics.map(generic =>
|
||||
(generic instanceof AST.TypeReference || generic instanceof AST.IntrinsicTypeReference) ? generic.name : <string>generic
|
||||
(generic instanceof AST.TypeReference || generic instanceof AST.IntrinsicTypeReference) ? generic.name : generic
|
||||
).join(', ') }>`);
|
||||
}
|
||||
|
||||
@@ -204,9 +199,9 @@ function toLink(item: AST.ModuleMember | AST.EnumMember | AST.TypeReference): st
|
||||
}
|
||||
|
||||
function writeDescription(text: string): string {
|
||||
var result = sanitize(text).replace(/\{@link ([^} ]+)\}/g, (substring, linkTarget) => `<a href="#${ linkTarget }">${ linkTarget }</a>`);
|
||||
let result = sanitize(text).replace(/\{@link ([^} ]+)\}/g, (substring, linkTarget) => `<a href="#${ linkTarget }">${ linkTarget }</a>`);
|
||||
|
||||
var inCodeBlock = false;
|
||||
let inCodeBlock = false;
|
||||
result = result.split("\n").map(line => {
|
||||
if (line.substr(0, " ".length) === " ") {
|
||||
line = line.substr(" ".length);
|
||||
@@ -289,7 +284,7 @@ function functionToHtml(func: AST.Function): string[] {
|
||||
}
|
||||
|
||||
function interfaceToHtml(interfase: AST.Interface): string[] {
|
||||
var members: AST.InterfaceMember[] = [];
|
||||
const members: AST.InterfaceMember[] = [];
|
||||
Object.keys(interfase.members).forEach(memberName => members.push(interfase.members[memberName]));
|
||||
|
||||
members.sort(sorter);
|
||||
@@ -311,7 +306,7 @@ function interfaceToHtml(interfase: AST.Interface): string[] {
|
||||
return functionToHtml(member).map(indenter(2));
|
||||
}
|
||||
else {
|
||||
throw new Error(`Unrecognized member type: ${ (<any>member.constructor).name }`);
|
||||
throw new Error(`Unrecognized member type: ${ (member as any).constructor.name }`);
|
||||
}
|
||||
}))).concat([
|
||||
' </dd>',
|
||||
@@ -321,7 +316,7 @@ function interfaceToHtml(interfase: AST.Interface): string[] {
|
||||
}
|
||||
|
||||
function classToHtml(clazz: AST.Class): string[] {
|
||||
var members: AST.InterfaceMember[] = [];
|
||||
const members: AST.InterfaceMember[] = [];
|
||||
Object.keys(clazz.members).forEach(memberName => members.push(clazz.members[memberName]));
|
||||
|
||||
members.sort(sorter);
|
||||
@@ -331,7 +326,7 @@ function classToHtml(clazz: AST.Class): string[] {
|
||||
clazz.isAbstract ? ' abstract' : ''}${
|
||||
clazz.isPrivate ? ' private' : ''}">`,
|
||||
` <dt class="name">class ${ toLink(clazz) }${
|
||||
(clazz.baseType !== null) ? ` extends ${ (clazz.baseType instanceof AST.TypeReference) ? toLink(<AST.TypeReference>clazz.baseType) : clazz.baseType.name }` : '' }${
|
||||
(clazz.baseType !== null) ? ` extends ${ (clazz.baseType instanceof AST.TypeReference) ? toLink(clazz.baseType) : clazz.baseType.name }` : '' }${
|
||||
(clazz.interfaces.length > 0) ? ` implements ${ clazz.interfaces.map(interfase => interfase instanceof AST.TypeReference ? toLink(interfase) : interfase.name).join(', ') }` : ''}</dt>`,
|
||||
' <dd class="description">',
|
||||
` ${ writeDescription(clazz.description) }`,
|
||||
@@ -351,7 +346,7 @@ function classToHtml(clazz: AST.Class): string[] {
|
||||
return functionToHtml(member).map(indenter(2));
|
||||
}
|
||||
else {
|
||||
throw new Error(`Unrecognized member type: ${ (<any>member.constructor).name }`);
|
||||
throw new Error(`Unrecognized member type: ${ (member as any).constructor.name }`);
|
||||
}
|
||||
}))).concat([
|
||||
' </dd>',
|
||||
@@ -407,30 +402,26 @@ function propertyToHtml(property: AST.Property): string[] {
|
||||
}
|
||||
|
||||
export function build(outputFilePath: string, root: string, rootNamespaceName: string): FileTransform {
|
||||
var compiler = new Compiler();
|
||||
|
||||
return new FileTransform(function (file: File): void {
|
||||
var self: FileTransform = this;
|
||||
const compiler = new Compiler();
|
||||
|
||||
return new FileTransform(function (file): void {
|
||||
// Compile
|
||||
compiler.compile(file);
|
||||
|
||||
// Walk
|
||||
var walkResult = walk(compiler, root, rootNamespaceName);
|
||||
var namespaces = walkResult.namespaces;
|
||||
var modules = walkResult.modules;
|
||||
const walkResult = walk(compiler, root, rootNamespaceName);
|
||||
const namespaces = walkResult.namespaces;
|
||||
const modules = walkResult.modules;
|
||||
|
||||
// Make HTML
|
||||
|
||||
var namespaceNames = Object.keys(namespaces)
|
||||
const namespaceNames = Object.keys(namespaces)
|
||||
.filter(namespaceName => namespaceName.substr(0, rootNamespaceName.length) === rootNamespaceName)
|
||||
.sort((ns1, ns2) => ns1.localeCompare(ns2));
|
||||
|
||||
var moduleNames = Object.keys(modules).sort((ns1, ns2) => ns1.localeCompare(ns2));
|
||||
const moduleNames = Object.keys(modules).sort((ns1, ns2) => ns1.localeCompare(ns2)).filter(moduleName => Object.keys(modules[moduleName].members).length > 0);
|
||||
|
||||
moduleNames = moduleNames.filter(moduleName => Object.keys(modules[moduleName].members).length > 0);
|
||||
|
||||
self.push({
|
||||
this.push({
|
||||
path: outputFilePath,
|
||||
contents: Buffer.concat([new Buffer(
|
||||
`<?xml version="1.0" encoding="utf-8" ?>
|
||||
@@ -590,9 +581,9 @@ export function build(outputFilePath: string, root: string, rootNamespaceName: s
|
||||
<label><input type="checkbox" id="show-private" />Show private</label>
|
||||
`
|
||||
)].concat(namespaceNames.map(namespaceName => {
|
||||
var namespace = namespaces[namespaceName];
|
||||
const namespace = namespaces[namespaceName];
|
||||
|
||||
var namespaceMembers: AST.NamespaceMember[] = [];
|
||||
const namespaceMembers: AST.NamespaceMember[] = [];
|
||||
for (const memberName of Object.keys(namespace.members)) {
|
||||
namespaceMembers.push(namespace.members[memberName]);
|
||||
}
|
||||
@@ -614,13 +605,13 @@ export function build(outputFilePath: string, root: string, rootNamespaceName: s
|
||||
`
|
||||
)]));
|
||||
})).concat(moduleNames.map(moduleName => {
|
||||
var module = modules[moduleName];
|
||||
const module = modules[moduleName];
|
||||
|
||||
var moduleMembers: AST.ModuleMemberWithoutReference[] = [];
|
||||
const moduleMembers: AST.ModuleMemberWithoutReference[] = [];
|
||||
for (const memberName of Object.keys(module.members)) {
|
||||
var member = module.members[memberName];
|
||||
if ((<AST.HasParent><any>member).parent === module) {
|
||||
moduleMembers.push(<AST.ModuleMemberWithoutReference>member);
|
||||
const member = module.members[memberName];
|
||||
if ((member as AST.HasParent).parent === module) {
|
||||
moduleMembers.push(member as AST.ModuleMemberWithoutReference);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -650,22 +641,22 @@ export function build(outputFilePath: string, root: string, rootNamespaceName: s
|
||||
<div class="content">
|
||||
`
|
||||
)]).concat(flatten(namespaceNames.map(namespaceName => {
|
||||
var namespace = namespaces[namespaceName];
|
||||
const namespace = namespaces[namespaceName];
|
||||
|
||||
var namespaceMembers: AST.NamespaceMember[] = [];
|
||||
const namespaceMembers: AST.NamespaceMember[] = [];
|
||||
for (const memberName of Object.keys(namespace.members)) {
|
||||
namespaceMembers.push(namespace.members[memberName]);
|
||||
}
|
||||
|
||||
namespaceMembers.sort(sorter);
|
||||
|
||||
var properties = <AST.Property[]>namespaceMembers.filter(member => member instanceof AST.Property);
|
||||
var functions = <AST.Function[]>namespaceMembers.filter(member => member instanceof AST.Function);
|
||||
var interfaces = <AST.Interface[]>namespaceMembers.filter(member => member instanceof AST.Interface);
|
||||
var classes = <AST.Class[]>namespaceMembers.filter(member => member instanceof AST.Class);
|
||||
var enums = <AST.Enum[]>namespaceMembers.filter(member => member instanceof AST.Enum);
|
||||
const properties = namespaceMembers.filter(member => member instanceof AST.Property) as AST.Property[];
|
||||
const functions = namespaceMembers.filter(member => member instanceof AST.Function) as AST.Function[];
|
||||
const interfaces = namespaceMembers.filter(member => member instanceof AST.Interface) as AST.Interface[];
|
||||
const classes = namespaceMembers.filter(member => member instanceof AST.Class) as AST.Class[];
|
||||
const enums = namespaceMembers.filter(member => member instanceof AST.Enum) as AST.Enum[];
|
||||
|
||||
var result = [new Buffer(
|
||||
const result = [new Buffer(
|
||||
` <section class="namespace">
|
||||
<h1 id="${ sanitize(namespaceName) }">Namespace ${ sanitize(namespaceName) }</h1>
|
||||
`
|
||||
@@ -763,13 +754,13 @@ export function build(outputFilePath: string, root: string, rootNamespaceName: s
|
||||
|
||||
return result;
|
||||
}))).concat(flatten(moduleNames.map(moduleName => {
|
||||
var module = modules[moduleName];
|
||||
const module = modules[moduleName];
|
||||
|
||||
var moduleMembers: AST.ModuleMember[] = [];
|
||||
const moduleMembers: AST.ModuleMember[] = [];
|
||||
for (const memberName of Object.keys(module.members)) {
|
||||
var member = module.members[memberName];
|
||||
if ((<AST.HasParent><any>member).parent === module) {
|
||||
moduleMembers.push(<AST.ModuleMember>member);
|
||||
const member = module.members[memberName];
|
||||
if ((member as AST.HasParent).parent === module) {
|
||||
moduleMembers.push(member);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -779,13 +770,13 @@ export function build(outputFilePath: string, root: string, rootNamespaceName: s
|
||||
|
||||
moduleMembers.sort(sorter);
|
||||
|
||||
var properties = <AST.Property[]>moduleMembers.filter(member => member instanceof AST.Property);
|
||||
var functions = <AST.Function[]>moduleMembers.filter(member => member instanceof AST.Function);
|
||||
var interfaces = <AST.Interface[]>moduleMembers.filter(member => member instanceof AST.Interface);
|
||||
var classes = <AST.Class[]>moduleMembers.filter(member => member instanceof AST.Class);
|
||||
var enums = <AST.Enum[]>moduleMembers.filter(member => member instanceof AST.Enum);
|
||||
const properties = moduleMembers.filter(member => member instanceof AST.Property) as AST.Property[];
|
||||
const functions = moduleMembers.filter(member => member instanceof AST.Function) as AST.Function[];
|
||||
const interfaces = moduleMembers.filter(member => member instanceof AST.Interface) as AST.Interface[];
|
||||
const classes = moduleMembers.filter(member => member instanceof AST.Class) as AST.Class[];
|
||||
const enums = moduleMembers.filter(member => member instanceof AST.Enum) as AST.Enum[];
|
||||
|
||||
var result = [new Buffer(
|
||||
const result = [new Buffer(
|
||||
` <section class="module">
|
||||
<h1 id="${ sanitize(moduleName) }">Module ${ sanitize(moduleName) }</h1>
|
||||
`
|
||||
|
||||
@@ -7,6 +7,12 @@ declare var require: {
|
||||
resolve(id: string): string;
|
||||
};
|
||||
|
||||
interface BufferConstructor {
|
||||
new (str: string): Buffer;
|
||||
prototype: Buffer;
|
||||
concat(list: Buffer[]): Buffer;
|
||||
}
|
||||
|
||||
declare module "fs" {
|
||||
export function existsSync(filename: string): boolean;
|
||||
export function readFileSync(filename: string, options: { encoding: string }): string;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": false,
|
||||
"strictNullChecks": false,
|
||||
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "classic",
|
||||
"noImplicitUseStrict": false
|
||||
},
|
||||
|
||||
"files": [
|
||||
"./typescript/index.ts",
|
||||
"./doc.ts",
|
||||
"./node.d.ts",
|
||||
"./typescript/typescript.d.ts",
|
||||
"../node_modules/async-build/typings.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as ts from "typescript";
|
||||
import ts = require("typescript");
|
||||
|
||||
export class HasParent {
|
||||
public parent: HasParent = null;
|
||||
@@ -30,7 +30,7 @@ export class HasParent {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
var parent = this.parent;
|
||||
const parent = this.parent;
|
||||
if (parent instanceof Namespace) {
|
||||
return parent.getMemberFullName(this);
|
||||
}
|
||||
@@ -188,7 +188,17 @@ export class UnresolvedType {
|
||||
}
|
||||
|
||||
export type HasStringGenerics = Class | Interface | Function;
|
||||
|
||||
export function hasStringGenerics(item: NamespaceMember): item is HasStringGenerics {
|
||||
return (item as HasGenerics).generics !== undefined;
|
||||
}
|
||||
|
||||
export type HasGenerics = HasStringGenerics | TypeReference;
|
||||
|
||||
export function hasGenerics(item: ModuleMember | EnumMember | TypeReference): item is HasGenerics {
|
||||
return (item as HasGenerics).generics !== undefined;
|
||||
}
|
||||
|
||||
export type CanBePrivate = Class | Interface | Function | Getter | Setter | Enum | Reference;
|
||||
export type CanBeProtected = Function;
|
||||
export type CanBeStatic = Function;
|
||||
|
||||
@@ -18,46 +18,70 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { Transform } from "stream";
|
||||
import * as ts from "typescript";
|
||||
import path = require("path");
|
||||
import ts = require("typescript");
|
||||
|
||||
import { File, FileTransform, FileWatcher } from "async-build";
|
||||
import { File, FileTransform } from "async-build";
|
||||
|
||||
import * as AST from "./ast";
|
||||
import { walk } from "./walker";
|
||||
|
||||
export interface StreamingCompilerHost extends ts.CompilerHost, ts.ParseConfigHost {
|
||||
export interface StreamingCompilerHost extends ts.CompilerHost {
|
||||
setOutputStream(outputStream: FileTransform): void;
|
||||
}
|
||||
|
||||
function createCompilerHost(options: ts.CompilerOptions): StreamingCompilerHost {
|
||||
const host = ts.createCompilerHost(options) as StreamingCompilerHost;
|
||||
|
||||
let _outputStream: FileTransform = null;
|
||||
host.setOutputStream = outputStream => _outputStream = outputStream;
|
||||
|
||||
host.writeFile = (fileName, data, writeByteOrderMark, onError?, sourceFiles?): void => {
|
||||
_outputStream.push({
|
||||
path: fileName,
|
||||
contents: new Buffer(data)
|
||||
});
|
||||
};
|
||||
|
||||
host.useCaseSensitiveFileNames = () => true;
|
||||
|
||||
host.getNewLine = () => "\n";
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
export class Compiler {
|
||||
private _projectRoot: string = null;
|
||||
private _host: StreamingCompilerHost;
|
||||
private _program: ts.Program = null;
|
||||
|
||||
constructor(private _host: StreamingCompilerHost = new CompilerHost()) { }
|
||||
|
||||
compile(projectConfigFile: File) {
|
||||
this._projectRoot = path.dirname(projectConfigFile.path);
|
||||
|
||||
var projectConfig = ts.parseJsonConfigFileContent(JSON.parse(projectConfigFile.contents.toString()), this._host, this._projectRoot);
|
||||
const projectConfig = ts.parseJsonConfigFileContent(JSON.parse(projectConfigFile.contents.toString()), ts.sys, this._projectRoot);
|
||||
|
||||
this._host = createCompilerHost(projectConfig.options);
|
||||
this._program = ts.createProgram(projectConfig.fileNames, projectConfig.options, this._host);
|
||||
|
||||
var syntacticDiagnostics = this._program.getSyntacticDiagnostics();
|
||||
const syntacticDiagnostics = this._program.getSyntacticDiagnostics();
|
||||
if (syntacticDiagnostics.length > 0) {
|
||||
this._reportDiagnostics(syntacticDiagnostics);
|
||||
throw new Error("There were one or more syntactic diagnostics.");
|
||||
}
|
||||
|
||||
var globalDiagnostics = this._program.getGlobalDiagnostics();
|
||||
const optionsDiagnostics = this._program.getOptionsDiagnostics();
|
||||
if (optionsDiagnostics.length > 0) {
|
||||
this._reportDiagnostics(optionsDiagnostics);
|
||||
throw new Error("There were one or more options diagnostics.");
|
||||
}
|
||||
|
||||
const globalDiagnostics = this._program.getGlobalDiagnostics();
|
||||
if (globalDiagnostics.length > 0) {
|
||||
this._reportDiagnostics(globalDiagnostics);
|
||||
throw new Error("There were one or more global diagnostics.");
|
||||
}
|
||||
|
||||
var semanticDiagnostics = this._program.getSemanticDiagnostics();
|
||||
const semanticDiagnostics = this._program.getSemanticDiagnostics();
|
||||
if (semanticDiagnostics.length > 0) {
|
||||
this._reportDiagnostics(semanticDiagnostics);
|
||||
throw new Error("There were one or more semantic diagnostics.");
|
||||
@@ -67,7 +91,7 @@ export class Compiler {
|
||||
writeFiles(outputStream: FileTransform) {
|
||||
this._host.setOutputStream(outputStream);
|
||||
|
||||
var emitDiagnostics = this._program.emit().diagnostics;
|
||||
const emitDiagnostics = this._program.emit().diagnostics;
|
||||
if (emitDiagnostics.length > 0) {
|
||||
this._reportDiagnostics(emitDiagnostics);
|
||||
throw new Error("There were one or more emit diagnostics.");
|
||||
@@ -88,10 +112,10 @@ export class Compiler {
|
||||
|
||||
private _reportDiagnostics(diagnostics: ts.Diagnostic[]) {
|
||||
for (const diagnostic of diagnostics) {
|
||||
var message = "";
|
||||
let message = "";
|
||||
|
||||
if (diagnostic.file) {
|
||||
var location = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
|
||||
const location = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
|
||||
message = `${ diagnostic.file.fileName }(${ location.line + 1 },${ location.character }): `;
|
||||
}
|
||||
|
||||
@@ -105,168 +129,23 @@ export class Compiler {
|
||||
};
|
||||
}
|
||||
|
||||
const typeScriptModulePath = path.dirname(require.resolve("typescript"));
|
||||
|
||||
class CompilerHost implements StreamingCompilerHost {
|
||||
protected _sourceFiles = Object.create(null);
|
||||
|
||||
private _outputStream: FileTransform = null;
|
||||
|
||||
setOutputStream(outputStream: FileTransform): void {
|
||||
this._outputStream = outputStream;
|
||||
}
|
||||
|
||||
// ts.ModuleResolutionHost members
|
||||
|
||||
fileExists(fileName: string): boolean {
|
||||
return fs.existsSync(fileName);
|
||||
}
|
||||
|
||||
readFile(fileName: string): string {
|
||||
if (!this.fileExists(fileName)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return fs.readFileSync(fileName, { encoding: "utf8" });
|
||||
}
|
||||
|
||||
// ts.CompilerHost members
|
||||
|
||||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError: (message: string) => void): ts.SourceFile {
|
||||
if (fileName in this._sourceFiles) {
|
||||
return this._sourceFiles[fileName];
|
||||
}
|
||||
|
||||
try {
|
||||
var text = fs.readFileSync(fileName, { encoding: "utf8" });
|
||||
var result = ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5);
|
||||
this._sourceFiles[fileName] = result;
|
||||
}
|
||||
catch (ex) {
|
||||
if (onError) {
|
||||
onError(ex.message);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getDefaultLibFileName(): string {
|
||||
return path.join(typeScriptModulePath, "lib.dom.d.ts");
|
||||
}
|
||||
|
||||
writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError: (message?: string) => void): void {
|
||||
this._outputStream.push({
|
||||
path: fileName,
|
||||
contents: new Buffer(data)
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentDirectory(): string {
|
||||
return path.resolve(".");
|
||||
}
|
||||
|
||||
getCanonicalFileName(fileName: string): string {
|
||||
return ts.normalizeSlashes(path.resolve(fileName));
|
||||
}
|
||||
|
||||
useCaseSensitiveFileNames(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNewLine(): string {
|
||||
return "\n";
|
||||
}
|
||||
|
||||
// ts.ParseConfigHost members
|
||||
|
||||
readDirectory(rootDir: string, extension: string, exclude: string[]): string[] {
|
||||
return ts.sys.readDirectory(rootDir, extension, exclude).map(fileName => this.getCanonicalFileName(fileName));
|
||||
}
|
||||
}
|
||||
|
||||
class WatchCompilerHost extends CompilerHost {
|
||||
private _fileWatcher = new FileWatcher(fileNames => this._onFilesChanged(fileNames));
|
||||
|
||||
private _filesChangedSinceLast: string[] = [];
|
||||
|
||||
constructor(private _onChangeCallback: () => void) {
|
||||
super();
|
||||
}
|
||||
|
||||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError: (message: string) => void): ts.SourceFile {
|
||||
var result = super.getSourceFile(fileName, languageVersion, onError);
|
||||
if (result !== undefined) {
|
||||
this._fileWatcher.watchFile(fileName);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
private _onFilesChanged(fileNames: string[]) {
|
||||
for (const fileName of fileNames) {
|
||||
delete this._sourceFiles[fileName];
|
||||
}
|
||||
|
||||
this._onChangeCallback();
|
||||
}
|
||||
}
|
||||
|
||||
export function build(root: string, rootNamespaceName: string): FileTransform {
|
||||
var compiler = new Compiler();
|
||||
|
||||
return new FileTransform(function (projectConfigFile: File): void {
|
||||
var self: FileTransform = this;
|
||||
const compiler = new Compiler();
|
||||
|
||||
return new FileTransform(function (projectConfigFile): void {
|
||||
console.log("Compiling " + projectConfigFile.path + "...");
|
||||
|
||||
compiler.compile(projectConfigFile);
|
||||
|
||||
var walkResult = walk(compiler, root, rootNamespaceName);
|
||||
const walkResult = walk(compiler, root, rootNamespaceName);
|
||||
addJSDocComments(walkResult.modules);
|
||||
|
||||
compiler.writeFiles(self);
|
||||
compiler.writeFiles(this);
|
||||
|
||||
console.log("Compile succeeded.");
|
||||
});
|
||||
}
|
||||
|
||||
export function watch(root: string, rootNamespaceName: string): FileTransform {
|
||||
return new FileTransform(function (projectConfigFile: File): void {
|
||||
var self: FileTransform = this;
|
||||
|
||||
function compile() {
|
||||
console.log("Compiling " + projectConfigFile.path + "...");
|
||||
|
||||
compiler.compile(projectConfigFile);
|
||||
|
||||
compiler.writeFiles(self);
|
||||
|
||||
console.log("Compile succeeded.");
|
||||
|
||||
self.push({
|
||||
path: "END",
|
||||
contents: ""
|
||||
});
|
||||
};
|
||||
|
||||
var compilerHost = new WatchCompilerHost(() => {
|
||||
try {
|
||||
compile();
|
||||
}
|
||||
catch (ex) {
|
||||
console.error("Compile failed." + ex.stack);
|
||||
}
|
||||
});
|
||||
|
||||
var compiler = new Compiler(compilerHost);
|
||||
|
||||
compile();
|
||||
|
||||
console.log("Listening for changes...");
|
||||
}, callback => { });
|
||||
}
|
||||
|
||||
function addJSDocComments(modules: { [name: string]: AST.Module }): void {
|
||||
function visitor(current: AST.Module | AST.ModuleMember | AST.InterfaceMember) {
|
||||
if (current instanceof AST.Module) {
|
||||
@@ -277,18 +156,18 @@ function addJSDocComments(modules: { [name: string]: AST.Module }): void {
|
||||
return;
|
||||
}
|
||||
|
||||
var newComments: string[] = [];
|
||||
const newComments: string[] = [];
|
||||
|
||||
if (current instanceof AST.Class) {
|
||||
newComments.push("@constructor");
|
||||
|
||||
if (current.baseType !== null) {
|
||||
var baseType = current.baseType;
|
||||
const baseType = current.baseType;
|
||||
newComments.push(
|
||||
"@extends {" +
|
||||
baseType.fullName + (
|
||||
(baseType instanceof AST.TypeReference && baseType.generics.length) > 0 ?
|
||||
(".<" + (<AST.TypeReference>baseType).generics.map(generic => generic.fullName).join(", ") + ">") :
|
||||
(".<" + (baseType as AST.TypeReference).generics.map(generic => generic.fullName).join(", ") + ">") :
|
||||
""
|
||||
) +
|
||||
"}"
|
||||
@@ -318,37 +197,37 @@ function addJSDocComments(modules: { [name: string]: AST.Module }): void {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((<AST.HasParent><any>current).parent instanceof AST.Namespace) {
|
||||
newComments.push("@memberOf " + (<AST.HasParent><any>current).parent.fullName);
|
||||
if (current.parent instanceof AST.Namespace) {
|
||||
newComments.push("@memberOf " + current.parent.fullName);
|
||||
}
|
||||
|
||||
if ((<AST.HasStringGenerics>current).generics !== undefined && (<AST.HasStringGenerics>current).generics.length > 0) {
|
||||
newComments.push("@template " + (<AST.HasStringGenerics>current).generics.join(", "));
|
||||
if (AST.hasStringGenerics(current) && current.generics.length > 0) {
|
||||
newComments.push("@template " + current.generics.join(", "));
|
||||
}
|
||||
|
||||
if ((<AST.CanBePrivate><any>current).isPrivate) {
|
||||
if ((current as AST.CanBePrivate).isPrivate) {
|
||||
newComments.push("@private");
|
||||
}
|
||||
|
||||
if ((<AST.CanBeProtected>current).isProtected) {
|
||||
if ((current as AST.CanBeProtected).isProtected) {
|
||||
newComments.push("@protected");
|
||||
}
|
||||
|
||||
if ((<AST.CanBeStatic>current).isStatic) {
|
||||
if ((current as AST.CanBeStatic).isStatic) {
|
||||
newComments.push("@static");
|
||||
}
|
||||
|
||||
if (newComments.length > 0) {
|
||||
if (current instanceof AST.Property) {
|
||||
var nodes: ts.Node[] = [];
|
||||
const nodes: ts.Node[] = [];
|
||||
if (current.getter !== null) { nodes.push(current.getter.astNode); }
|
||||
if (current.setter !== null && nodes[0] !== current.setter.astNode) { nodes.push(current.setter.astNode); }
|
||||
for (const node of nodes) {
|
||||
(<any>node)["typescript-new-comment"] = newComments;
|
||||
(node as any)["typescript-new-comment"] = newComments;
|
||||
}
|
||||
}
|
||||
else {
|
||||
(<any>(<AST.Class | AST.Interface | AST.Function | AST.Enum>current).astNode)["typescript-new-comment"] = newComments;
|
||||
(current.astNode as any)["typescript-new-comment"] = newComments;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -358,6 +237,7 @@ function addJSDocComments(modules: { [name: string]: AST.Module }): void {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
class FakeSourceFile {
|
||||
public text: string;
|
||||
public lineMap: number[];
|
||||
@@ -398,15 +278,17 @@ class FakeSourceFile {
|
||||
}
|
||||
|
||||
var fakeSourceFiles: { [name: string]: FakeSourceFile } = Object.create(null);
|
||||
*/
|
||||
|
||||
export var oldGetLeadingCommentRangesOfNode: typeof ts.getLeadingCommentRangesOfNode = ts.getLeadingCommentRangesOfNode.bind(ts);
|
||||
ts.getLeadingCommentRangesOfNode = (node: ts.Node, sourceFileOfNode: ts.SourceFile) => {
|
||||
sourceFileOfNode = sourceFileOfNode || ts.getSourceFileOfNode(node);
|
||||
export const oldGetLeadingCommentRangesOfNodeFromText: typeof ts.getLeadingCommentRangesOfNodeFromText = ts.getLeadingCommentRangesOfNodeFromText.bind(ts);
|
||||
|
||||
var originalComments = oldGetLeadingCommentRangesOfNode(node, sourceFileOfNode);
|
||||
/*
|
||||
ts.getLeadingCommentRangesOfNodeFromText = (node: ts.Node, text: string) => {
|
||||
const originalComments = oldGetLeadingCommentRangesOfNodeFromText(node, text);
|
||||
|
||||
if (originalComments !== undefined && (<any>node)["typescript-new-comment"] !== undefined) {
|
||||
var fakeSourceFile = fakeSourceFiles[sourceFileOfNode.fileName];
|
||||
const sourceFileOfNode = ts.getSourceFileOfNode(node);
|
||||
let fakeSourceFile = fakeSourceFiles[sourceFileOfNode.fileName];
|
||||
if (fakeSourceFile === undefined) {
|
||||
fakeSourceFile = fakeSourceFiles[sourceFileOfNode.fileName] = new FakeSourceFile(sourceFileOfNode);
|
||||
}
|
||||
@@ -418,10 +300,13 @@ ts.getLeadingCommentRangesOfNode = (node: ts.Node, sourceFileOfNode: ts.SourceFi
|
||||
};
|
||||
|
||||
var oldWriteCommentRange: typeof ts.writeCommentRange = ts.writeCommentRange.bind(ts);
|
||||
ts.writeCommentRange = (currentSourceFile: ts.SourceFile, writer: ts.EmitTextWriter, comment: ts.CommentRange, newLine: string) => {
|
||||
ts.writeCommentRange = (text: string, lineMap: number[], writer: ts.EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) => {
|
||||
if ((<{ sourceFile: ts.SourceFile }><any>comment).sourceFile) {
|
||||
currentSourceFile = (<{ sourceFile: ts.SourceFile }><any>comment).sourceFile;
|
||||
const currentSourceFile = (<{ sourceFile: ts.SourceFile }><any>comment).sourceFile;
|
||||
text = currentSourceFile.text;
|
||||
lineMap = currentSourceFile.lineMap;
|
||||
}
|
||||
|
||||
return oldWriteCommentRange(currentSourceFile, writer, comment, newLine);
|
||||
return oldWriteCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
|
||||
};
|
||||
*/
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
declare namespace ts {
|
||||
export interface EmitTextWriter { }
|
||||
interface EmitTextWriter { }
|
||||
|
||||
export interface IntrinsicType extends Type {
|
||||
interface IntrinsicType extends Type {
|
||||
intrinsicName: string;
|
||||
}
|
||||
|
||||
export function forEachValue<T, U>(map: Map<T>, callback: (value: T) => U): U;
|
||||
export function getClassExtendsHeritageClauseElement(node: ClassLikeDeclaration): ExpressionWithTypeArguments;
|
||||
export function getClassImplementsHeritageClauseElements(node: ClassDeclaration): NodeArray<ExpressionWithTypeArguments>;
|
||||
export function getInterfaceBaseTypeNodes(node: InterfaceDeclaration): NodeArray<ExpressionWithTypeArguments>;
|
||||
export function getLeadingCommentRangesOfNode(node: Node, sourceFileOfNode: SourceFile): CommentRange[];
|
||||
export function getLineStarts(sourceFile: SourceFile): number[];
|
||||
export function getSourceFileOfNode(node: Node): SourceFile;
|
||||
export function getTextOfNode(node: Node, includeTrivia?: boolean): string;
|
||||
export function normalizeSlashes(path: string): string;
|
||||
export function writeCommentRange(currentSourceFile: SourceFile, writer: EmitTextWriter, comment: CommentRange, newLine: string): void;
|
||||
interface SourceFile {
|
||||
lineMap: number[];
|
||||
}
|
||||
|
||||
function forEachProperty<T, U>(map: Map<T>, callback: (value: T, key: string) => U): U;
|
||||
function getClassExtendsHeritageClauseElement(node: ClassLikeDeclaration | InterfaceDeclaration): ExpressionWithTypeArguments;
|
||||
function getClassImplementsHeritageClauseElements(node: ClassLikeDeclaration): ExpressionWithTypeArguments[];
|
||||
function getInterfaceBaseTypeNodes(node: InterfaceDeclaration): ExpressionWithTypeArguments[];
|
||||
function getLeadingCommentRangesOfNodeFromText(node: Node, text: string): CommentRange[];
|
||||
function getLineStarts(sourceFile: SourceFile): number[];
|
||||
function getSourceFileOfNode(node: Node): SourceFile;
|
||||
function getTextOfNode(node: Node, includeTrivia?: boolean): string;
|
||||
function normalizeSlashes(path: string): string;
|
||||
function writeCommentRange(text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string): void;
|
||||
function hasModifier(node: Node, flags: ModifierFlags): boolean;
|
||||
}
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { build, watch } from "./compiler";
|
||||
export { build } from "./compiler";
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as path from "path";
|
||||
import * as ts from "typescript";
|
||||
import path = require("path");
|
||||
import ts = require("typescript");
|
||||
|
||||
import { Compiler, oldGetLeadingCommentRangesOfNode } from "./compiler";
|
||||
import { Compiler, oldGetLeadingCommentRangesOfNodeFromText } from "./compiler";
|
||||
|
||||
import * as AST from "./ast";
|
||||
|
||||
@@ -68,13 +68,13 @@ class Walker {
|
||||
}
|
||||
|
||||
walk(sourceFile: ts.SourceFile): void {
|
||||
var moduleName = this._moduleNameFromFileName(sourceFile.fileName);
|
||||
const moduleName = this._moduleNameFromFileName(sourceFile.fileName);
|
||||
|
||||
if (!(moduleName in this.modules)) {
|
||||
this.modules[moduleName] = new AST.Module(moduleName);
|
||||
}
|
||||
|
||||
var module = this._scope.enter(this.modules[moduleName]);
|
||||
const module = this._scope.enter(this.modules[moduleName]);
|
||||
this._currentSourceFile = sourceFile;
|
||||
|
||||
for (const statement of sourceFile.statements) {
|
||||
@@ -87,31 +87,31 @@ class Walker {
|
||||
private _walk(node: ts.Node, parent: AST.Module): void {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.VariableStatement:
|
||||
this._visitVariableStatement(<ts.VariableStatement>node, parent);
|
||||
this._visitVariableStatement(node as ts.VariableStatement, parent);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.FunctionDeclaration:
|
||||
this._visitFunctionDeclaration(<ts.FunctionDeclaration>node, parent);
|
||||
this._visitFunctionDeclaration(node as ts.FunctionDeclaration, parent);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.ClassDeclaration:
|
||||
this._visitClassDeclaration(<ts.ClassDeclaration>node, parent);
|
||||
this._visitClassDeclaration(node as ts.ClassDeclaration, parent);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.InterfaceDeclaration:
|
||||
this._visitInterfaceDeclaration(<ts.InterfaceDeclaration>node, parent);
|
||||
this._visitInterfaceDeclaration(node as ts.InterfaceDeclaration, parent);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.EnumDeclaration:
|
||||
this._visitEnumDeclaration(<ts.EnumDeclaration>node, parent);
|
||||
this._visitEnumDeclaration(node as ts.EnumDeclaration, parent);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.ImportDeclaration:
|
||||
this._visitImportDeclaration(<ts.ImportDeclaration>node, parent);
|
||||
this._visitImportDeclaration(node as ts.ImportDeclaration, parent);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.ExportDeclaration:
|
||||
this._visitExportDeclaration(<ts.ExportDeclaration>node, parent);
|
||||
this._visitExportDeclaration(node as ts.ExportDeclaration, parent);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.ExpressionStatement:
|
||||
@@ -121,7 +121,7 @@ class Walker {
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error(node.kind, (<any>ts).SyntaxKind[node.kind], node);
|
||||
console.error(node.kind, ts.SyntaxKind[node.kind], node);
|
||||
throw new Error("Unrecognized node.");
|
||||
}
|
||||
}
|
||||
@@ -130,20 +130,20 @@ class Walker {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.PropertySignature:
|
||||
case ts.SyntaxKind.PropertyDeclaration:
|
||||
this._visitProperty(<ts.PropertyDeclaration>node, clazz);
|
||||
this._visitProperty(node as ts.PropertyDeclaration, clazz);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.MethodSignature:
|
||||
case ts.SyntaxKind.MethodDeclaration:
|
||||
this._visitMethod(<ts.MethodDeclaration>node, clazz);
|
||||
this._visitMethod(node as ts.MethodDeclaration, clazz);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.GetAccessor:
|
||||
this._visitGetAccessor(<ts.AccessorDeclaration>node, clazz);
|
||||
this._visitGetAccessor(node as ts.AccessorDeclaration, clazz);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.SetAccessor:
|
||||
this._visitSetAccessor(<ts.AccessorDeclaration>node, clazz);
|
||||
this._visitSetAccessor(node as ts.AccessorDeclaration, clazz);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.TypeParameter:
|
||||
@@ -152,7 +152,7 @@ class Walker {
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error(node.kind, (<any>ts).SyntaxKind[node.kind], node);
|
||||
console.error(node.kind, ts.SyntaxKind[node.kind], node);
|
||||
throw new Error("Unrecognized node.");
|
||||
}
|
||||
}
|
||||
@@ -161,12 +161,12 @@ class Walker {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.PropertySignature:
|
||||
case ts.SyntaxKind.PropertyDeclaration:
|
||||
this._visitProperty(<ts.PropertyDeclaration>node, interfase);
|
||||
this._visitProperty(node as ts.PropertyDeclaration, interfase);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.MethodSignature:
|
||||
case ts.SyntaxKind.MethodDeclaration:
|
||||
this._visitMethod(<ts.MethodDeclaration>node, interfase);
|
||||
this._visitMethod(node as ts.MethodDeclaration, interfase);
|
||||
break;
|
||||
|
||||
case ts.SyntaxKind.TypeParameter:
|
||||
@@ -176,24 +176,24 @@ class Walker {
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error(node.kind, (<any>ts).SyntaxKind[node.kind], node);
|
||||
console.error(node.kind, ts.SyntaxKind[node.kind], node);
|
||||
throw new Error("Unrecognized node.");
|
||||
}
|
||||
}
|
||||
|
||||
private _visitProperty(node: ts.PropertyDeclaration, parent: AST.Class | AST.Interface) {
|
||||
if ((node.flags & ts.NodeFlags.Private) === ts.NodeFlags.Private) {
|
||||
if (ts.hasModifier(node, ts.ModifierFlags.Private)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var jsDoc = this._parseJSDoc(node);
|
||||
const jsDoc = this._parseJSDoc(node);
|
||||
|
||||
if (jsDoc.typeAnnotation === null) {
|
||||
this._notifyIncorrectJsDoc(`Field ${ ts.getTextOfNode(node.name) } has no @type annotation.`);
|
||||
jsDoc.typeAnnotation = "*";
|
||||
}
|
||||
|
||||
var property = this._scope.enter(new AST.Property(ts.getTextOfNode(node.name)));
|
||||
const property = this._scope.enter(new AST.Property(ts.getTextOfNode(node.name)));
|
||||
parent.members[property.name] = property;
|
||||
property.getter = new AST.Getter(node, jsDoc.description, jsDoc.typeAnnotation, false);
|
||||
property.setter = new AST.Setter(node, jsDoc.description, jsDoc.typeAnnotation, false);
|
||||
@@ -201,9 +201,9 @@ class Walker {
|
||||
}
|
||||
|
||||
private _visitMethod(node: ts.MethodDeclaration, parent: AST.Class | AST.Interface) {
|
||||
var jsDoc = this._parseJSDoc(node);
|
||||
const jsDoc = this._parseJSDoc(node);
|
||||
|
||||
var parameters = this._connectParameters(node.parameters, jsDoc.parameters,
|
||||
const parameters = this._connectParameters(node.parameters, jsDoc.parameters,
|
||||
parameterName => `Could not find @param annotation for ${ parameterName } on method ${ ts.getTextOfNode(node.name) }`
|
||||
);
|
||||
|
||||
@@ -212,25 +212,25 @@ class Walker {
|
||||
jsDoc.returnType = new AST.ReturnType("", "*");
|
||||
}
|
||||
|
||||
var isPrivate = (node.flags & ts.NodeFlags.Private) === ts.NodeFlags.Private;
|
||||
var isProtected = (node.flags & ts.NodeFlags.Protected) === ts.NodeFlags.Protected;
|
||||
var isStatic = (node.flags & ts.NodeFlags.Static) === ts.NodeFlags.Static;
|
||||
const isPrivate = ts.hasModifier(node, ts.ModifierFlags.Private);
|
||||
const isProtected = ts.hasModifier(node, ts.ModifierFlags.Protected);
|
||||
const isStatic = ts.hasModifier(node, ts.ModifierFlags.Static);
|
||||
|
||||
var generics = this._getGenericsOfSignatureDeclaration(node);
|
||||
const generics = this._getGenericsOfSignatureDeclaration(node);
|
||||
|
||||
var method = this._scope.enter(new AST.Function(ts.getTextOfNode(node.name), node, jsDoc.description, generics, parameters, jsDoc.returnType, jsDoc.isAbstract, isPrivate, isProtected, isStatic));
|
||||
const method = this._scope.enter(new AST.Function(ts.getTextOfNode(node.name), node, jsDoc.description, generics, parameters, jsDoc.returnType, jsDoc.isAbstract, isPrivate, isProtected, isStatic));
|
||||
parent.members[method.name] = method;
|
||||
this._scope.leave();
|
||||
}
|
||||
|
||||
private _visitGetAccessor(node: ts.AccessorDeclaration, clazz: AST.Class): void {
|
||||
var jsDoc = this._parseJSDoc(node);
|
||||
const jsDoc = this._parseJSDoc(node);
|
||||
|
||||
var name = ts.getTextOfNode(node.name);
|
||||
const name = ts.getTextOfNode(node.name);
|
||||
|
||||
var isPrivate = (node.flags & ts.NodeFlags.Private) === ts.NodeFlags.Private;
|
||||
const isPrivate = ts.hasModifier(node, ts.ModifierFlags.Private);
|
||||
|
||||
var property = <AST.Property>clazz.members[name];
|
||||
let property = clazz.members[name] as AST.Property;
|
||||
if (property === undefined) {
|
||||
this._scope.enter(property = new AST.Property(name));
|
||||
|
||||
@@ -247,13 +247,13 @@ class Walker {
|
||||
}
|
||||
|
||||
private _visitSetAccessor(node: ts.AccessorDeclaration, clazz: AST.Class): void {
|
||||
var jsDoc = this._parseJSDoc(node);
|
||||
const jsDoc = this._parseJSDoc(node);
|
||||
|
||||
var name = ts.getTextOfNode(node.name);
|
||||
const name = ts.getTextOfNode(node.name);
|
||||
|
||||
var isPrivate = (node.flags & ts.NodeFlags.Private) === ts.NodeFlags.Private;
|
||||
const isPrivate = ts.hasModifier(node, ts.ModifierFlags.Private);
|
||||
|
||||
var property = <AST.Property>clazz.members[name];
|
||||
let property = clazz.members[name] as AST.Property;
|
||||
if (property === undefined) {
|
||||
this._scope.enter(property = new AST.Property(name));
|
||||
|
||||
@@ -274,19 +274,18 @@ class Walker {
|
||||
return;
|
||||
}
|
||||
|
||||
var declaration = node.declarationList.declarations[0];
|
||||
if ((declaration.flags & ts.NodeFlags.Ambient) === ts.NodeFlags.Ambient) {
|
||||
const declaration = node.declarationList.declarations[0];
|
||||
if (ts.hasModifier(declaration, ts.ModifierFlags.Ambient)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var jsDoc = this._parseJSDoc(node);
|
||||
const jsDoc = this._parseJSDoc(node);
|
||||
if (jsDoc.typeAnnotation === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var property = this._scope.enter(new AST.Property(ts.getTextOfNode(declaration.name)));
|
||||
const property = this._scope.enter(new AST.Property(ts.getTextOfNode(declaration.name)));
|
||||
property.getter = new AST.Getter(node, jsDoc.description, jsDoc.typeAnnotation, false);
|
||||
property.setter = new AST.Setter(node, jsDoc.description, jsDoc.typeAnnotation, false);
|
||||
|
||||
parent.members[property.name] = property;
|
||||
|
||||
@@ -294,13 +293,13 @@ class Walker {
|
||||
}
|
||||
|
||||
private _visitFunctionDeclaration(node: ts.FunctionDeclaration, parent: AST.Module): void {
|
||||
var jsDoc = this._parseJSDoc(node);
|
||||
const jsDoc = this._parseJSDoc(node);
|
||||
|
||||
var isPrivate = (node.flags & ts.NodeFlags.Export) !== ts.NodeFlags.Export;
|
||||
const isPrivate = !ts.hasModifier(node, ts.ModifierFlags.Export);
|
||||
|
||||
var generics = this._getGenericsOfSignatureDeclaration(node);
|
||||
const generics = this._getGenericsOfSignatureDeclaration(node);
|
||||
|
||||
var parameters = this._connectParameters(node.parameters, jsDoc.parameters,
|
||||
const parameters = this._connectParameters(node.parameters, jsDoc.parameters,
|
||||
parameterName => `Could not find @param annotation for ${ parameterName } on function ${ node.name.text }`
|
||||
);
|
||||
|
||||
@@ -313,7 +312,7 @@ class Walker {
|
||||
jsDoc.returnType = new AST.ReturnType("", "*");
|
||||
}
|
||||
|
||||
var freeFunction = this._scope.enter(new AST.Function(node.name.text, node, jsDoc.description, generics, parameters, jsDoc.returnType, jsDoc.isAbstract, isPrivate, false, false));
|
||||
const freeFunction = this._scope.enter(new AST.Function(node.name.text, node, jsDoc.description, generics, parameters, jsDoc.returnType, jsDoc.isAbstract, isPrivate, false, false));
|
||||
|
||||
parent.members[freeFunction.name] = freeFunction;
|
||||
|
||||
@@ -321,32 +320,32 @@ class Walker {
|
||||
}
|
||||
|
||||
private _visitClassDeclaration(node: ts.ClassDeclaration, parent: AST.Module): void {
|
||||
var jsDoc = this._parseJSDoc(node);
|
||||
const jsDoc = this._parseJSDoc(node);
|
||||
|
||||
var baseTypeHeritageClauseElement = ts.getClassExtendsHeritageClauseElement(node) || null;
|
||||
var baseType: AST.UnresolvedType = null;
|
||||
const type = this._typeChecker.getTypeAtLocation(node) as ts.InterfaceType;
|
||||
|
||||
const generics = this._getGenericsOfInterfaceType(type);
|
||||
|
||||
const baseTypeHeritageClauseElement = ts.getClassExtendsHeritageClauseElement(node) || null;
|
||||
let baseType: AST.UnresolvedType = null;
|
||||
if (baseTypeHeritageClauseElement !== null) {
|
||||
baseType = new AST.UnresolvedType(
|
||||
this._typeChecker.getTypeAtLocation(baseTypeHeritageClauseElement).symbol,
|
||||
this._getGenericsOfTypeReferenceNode(baseTypeHeritageClauseElement)
|
||||
this._getGenericsOfTypeReferenceNode(baseTypeHeritageClauseElement, generics)
|
||||
);
|
||||
}
|
||||
|
||||
var interfaces = (ts.getClassImplementsHeritageClauseElements(node) || []).map(type => new AST.UnresolvedType(
|
||||
const interfaces = (ts.getClassImplementsHeritageClauseElements(node) || []).map(type => new AST.UnresolvedType(
|
||||
this._typeChecker.getTypeAtLocation(type).symbol,
|
||||
this._getGenericsOfTypeReferenceNode(type)
|
||||
this._getGenericsOfTypeReferenceNode(type, generics)
|
||||
));
|
||||
|
||||
var isPrivate = (node.flags & ts.NodeFlags.Export) !== ts.NodeFlags.Export;
|
||||
const isPrivate = !ts.hasModifier(node, ts.ModifierFlags.Export);
|
||||
|
||||
var type = <ts.InterfaceType>this._typeChecker.getTypeAtLocation(node);
|
||||
|
||||
var generics = this._getGenericsOfInterfaceType(type);
|
||||
|
||||
var parameters: AST.Parameter[] = [];
|
||||
let parameters: AST.Parameter[] = [];
|
||||
|
||||
if (type.symbol.members["__constructor"] !== undefined) {
|
||||
parameters = this._connectParameters((<ts.ConstructorDeclaration>type.symbol.members["__constructor"].declarations[0]).parameters, jsDoc.parameters,
|
||||
parameters = this._connectParameters((type.symbol.members["__constructor"].declarations[0] as ts.ConstructorDeclaration).parameters, jsDoc.parameters,
|
||||
parameterName => `Could not find @param annotation for ${ parameterName } on constructor in class ${ node.name.text }`
|
||||
);
|
||||
}
|
||||
@@ -354,11 +353,11 @@ class Walker {
|
||||
this._notifyIncorrectJsDoc("There are @param annotations on this class but it has no constructors.");
|
||||
}
|
||||
|
||||
var clazz = this._scope.enter(new AST.Class(node.name.text, node, jsDoc.description, generics, parameters, baseType, interfaces, jsDoc.isAbstract, isPrivate));
|
||||
const clazz = this._scope.enter(new AST.Class(node.name.text, node, jsDoc.description, generics, parameters, baseType, interfaces, jsDoc.isAbstract, isPrivate));
|
||||
|
||||
parent.members[clazz.name] = clazz;
|
||||
|
||||
ts.forEachValue(type.symbol.exports, symbol => {
|
||||
ts.forEachProperty(type.symbol.exports, symbol => {
|
||||
if (symbol.name === "prototype") {
|
||||
return;
|
||||
}
|
||||
@@ -368,7 +367,7 @@ class Walker {
|
||||
}
|
||||
});
|
||||
|
||||
ts.forEachValue(type.symbol.members, symbol => {
|
||||
ts.forEachProperty(type.symbol.members, symbol => {
|
||||
for (const declaration of symbol.declarations) {
|
||||
this._walkClassMember(declaration, clazz);
|
||||
}
|
||||
@@ -378,28 +377,28 @@ class Walker {
|
||||
}
|
||||
|
||||
private _visitInterfaceDeclaration(node: ts.InterfaceDeclaration, parent: AST.Module): void {
|
||||
var jsDoc = this._parseJSDoc(node);
|
||||
const jsDoc = this._parseJSDoc(node);
|
||||
|
||||
var baseTypes = (ts.getInterfaceBaseTypeNodes(node) || []).map(type => new AST.UnresolvedType(
|
||||
const type = this._typeChecker.getTypeAtLocation(node) as ts.InterfaceType;
|
||||
|
||||
const generics = this._getGenericsOfInterfaceType(type);
|
||||
|
||||
const baseTypes = (ts.getInterfaceBaseTypeNodes(node) || []).map(type => new AST.UnresolvedType(
|
||||
this._typeChecker.getTypeAtLocation(type).symbol,
|
||||
this._getGenericsOfTypeReferenceNode(type)
|
||||
this._getGenericsOfTypeReferenceNode(type, generics)
|
||||
));
|
||||
|
||||
var existingInterfaceType = parent.members[node.name.text];
|
||||
const existingInterfaceType = parent.members[node.name.text];
|
||||
if (existingInterfaceType !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var isPrivate = (node.flags & ts.NodeFlags.Export) !== ts.NodeFlags.Export;
|
||||
const isPrivate = !ts.hasModifier(node, ts.ModifierFlags.Export);
|
||||
|
||||
var type = <ts.InterfaceType>this._typeChecker.getTypeAtLocation(node);
|
||||
|
||||
var generics = this._getGenericsOfInterfaceType(type);
|
||||
|
||||
var interfase = this._scope.enter(new AST.Interface(node.name.text, node, jsDoc.description, generics, baseTypes, isPrivate));
|
||||
const interfase = this._scope.enter(new AST.Interface(node.name.text, node, jsDoc.description, generics, baseTypes, isPrivate));
|
||||
parent.members[interfase.name] = interfase;
|
||||
|
||||
ts.forEachValue(type.symbol.members, symbol => {
|
||||
ts.forEachProperty(type.symbol.members, symbol => {
|
||||
for (const declaration of symbol.declarations) {
|
||||
this._walkInterfaceMember(declaration, interfase);
|
||||
}
|
||||
@@ -409,33 +408,33 @@ class Walker {
|
||||
}
|
||||
|
||||
private _visitEnumDeclaration(node: ts.EnumDeclaration, parent: AST.Module): void {
|
||||
var jsDoc = this._parseJSDoc(node);
|
||||
const jsDoc = this._parseJSDoc(node);
|
||||
|
||||
var existingEnumType = parent.members[node.name.text];
|
||||
const existingEnumType = parent.members[node.name.text];
|
||||
if (existingEnumType !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var isPrivate = (node.flags & ts.NodeFlags.Export) !== ts.NodeFlags.Export;
|
||||
const isPrivate = !ts.hasModifier(node, ts.ModifierFlags.Export);
|
||||
|
||||
var type = this._typeChecker.getTypeAtLocation(node);
|
||||
const type = this._typeChecker.getTypeAtLocation(node);
|
||||
|
||||
var enumType = this._scope.enter(new AST.Enum(node.name.text, node, jsDoc.description, isPrivate));
|
||||
const enumType = this._scope.enter(new AST.Enum(node.name.text, node, jsDoc.description, isPrivate));
|
||||
parent.members[enumType.name] = enumType;
|
||||
|
||||
ts.forEachValue(type.symbol.exports, symbol => {
|
||||
this._visitEnumMember(<ts.EnumMember>symbol.declarations[0], enumType);
|
||||
ts.forEachProperty(type.symbol.exports, symbol => {
|
||||
this._visitEnumMember(symbol.declarations[0] as ts.EnumMember, enumType);
|
||||
});
|
||||
|
||||
this._scope.leave();
|
||||
}
|
||||
|
||||
private _visitEnumMember(node: ts.EnumMember, parent: AST.Enum): void {
|
||||
var jsDoc = this._parseJSDoc(node);
|
||||
const jsDoc = this._parseJSDoc(node);
|
||||
|
||||
var value = (node.initializer === undefined) ? null : parseInt((<ts.LiteralExpression>node.initializer).text);
|
||||
const value = (node.initializer === undefined) ? null : parseInt((node.initializer as ts.LiteralExpression).text);
|
||||
|
||||
var enumMember = this._scope.enter(new AST.EnumMember(ts.getTextOfNode(node.name), (jsDoc === null) ? "" : jsDoc.description, value));
|
||||
const enumMember = this._scope.enter(new AST.EnumMember(ts.getTextOfNode(node.name), (jsDoc === null) ? "" : jsDoc.description, value));
|
||||
|
||||
parent.members.push(enumMember);
|
||||
|
||||
@@ -452,16 +451,16 @@ class Walker {
|
||||
throw new Error("Default import is not supported.");
|
||||
}
|
||||
|
||||
var moduleName = this._resolve((<ts.LiteralExpression>node.moduleSpecifier).text, parent);
|
||||
const moduleName = this._resolve((node.moduleSpecifier as ts.LiteralExpression).text, parent);
|
||||
|
||||
if ((<ts.NamespaceImport>node.importClause.namedBindings).name !== undefined) {
|
||||
if ((node.importClause.namedBindings as ts.NamespaceImport).name !== undefined) {
|
||||
// import * as foo from "baz";
|
||||
parent.members[(<ts.NamespaceImport>node.importClause.namedBindings).name.text] = new AST.Reference(moduleName, "*", true);
|
||||
parent.members[(node.importClause.namedBindings as ts.NamespaceImport).name.text] = new AST.Reference(moduleName, "*", true);
|
||||
}
|
||||
else if ((<ts.NamedImports>node.importClause.namedBindings).elements !== undefined) {
|
||||
else if ((node.importClause.namedBindings as ts.NamedImports).elements !== undefined) {
|
||||
// import { foo, bar } from "baz";
|
||||
for (const element of (<ts.NamedImports>node.importClause.namedBindings).elements) {
|
||||
var importedName = element.propertyName && element.propertyName.text || element.name.text;
|
||||
for (const element of (node.importClause.namedBindings as ts.NamedImports).elements) {
|
||||
const importedName = element.propertyName && element.propertyName.text || element.name.text;
|
||||
parent.members[element.name.text] = new AST.Reference(moduleName, importedName, true);
|
||||
}
|
||||
}
|
||||
@@ -473,22 +472,22 @@ class Walker {
|
||||
private _visitExportDeclaration(node: ts.ExportDeclaration, parent: AST.Module): void {
|
||||
if (node.moduleSpecifier !== undefined) {
|
||||
// export { foo } from "bar";
|
||||
var moduleName = this._resolve((<ts.LiteralExpression>node.moduleSpecifier).text, parent);
|
||||
const moduleName = this._resolve((node.moduleSpecifier as ts.LiteralExpression).text, parent);
|
||||
for (const element of node.exportClause.elements) {
|
||||
var importedName = element.propertyName && element.propertyName.text || element.name.text;
|
||||
const importedName = element.propertyName && element.propertyName.text || element.name.text;
|
||||
parent.members[element.name.text] = new AST.Reference(moduleName, importedName, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// export { foo };
|
||||
for (const element of node.exportClause.elements) {
|
||||
(<AST.CanBePrivate><any>parent.members[element.name.text]).isPrivate = false;
|
||||
(parent.members[element.name.text] as AST.CanBePrivate).isPrivate = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _resolve(relativeModuleName: string, currentModule: AST.Module): string {
|
||||
var result = ts.normalizeSlashes(path.join(currentModule.name, `../${ relativeModuleName }`));
|
||||
let result = ts.normalizeSlashes(path.join(currentModule.name, `../${ relativeModuleName }`));
|
||||
|
||||
if (result[0] !== ".") {
|
||||
result = `./${ result }`;
|
||||
@@ -498,7 +497,7 @@ class Walker {
|
||||
}
|
||||
|
||||
private _parseJSDoc(node: ts.Node): JSDoc {
|
||||
var comments = oldGetLeadingCommentRangesOfNode(node, this._currentSourceFile);
|
||||
let comments = oldGetLeadingCommentRangesOfNodeFromText(node, this._currentSourceFile.text);
|
||||
|
||||
if (comments === undefined) {
|
||||
comments = [];
|
||||
@@ -508,41 +507,41 @@ class Walker {
|
||||
comments = [comments[comments.length - 1]];
|
||||
}
|
||||
|
||||
var comment =
|
||||
const comment =
|
||||
(comments.length === 0) ?
|
||||
"" :
|
||||
this._currentSourceFile.text.substring(comments[0].pos, comments[0].end);
|
||||
|
||||
var commentStartIndex = comment.indexOf("/**");
|
||||
var commentEndIndex = comment.lastIndexOf("*/");
|
||||
const commentStartIndex = comment.indexOf("/**");
|
||||
const commentEndIndex = comment.lastIndexOf("*/");
|
||||
|
||||
var lines =
|
||||
const lines =
|
||||
(commentStartIndex === -1 || commentEndIndex === -1) ?
|
||||
[] :
|
||||
comment.substring(commentStartIndex + 2, commentEndIndex).split("\n").map(line => {
|
||||
var match = line.match(/^[ \t]*\* (.*)/);
|
||||
const match = line.match(/^[ \t]*\* (.*)/);
|
||||
if (match === null) {
|
||||
return "";
|
||||
}
|
||||
return match[1];
|
||||
});
|
||||
|
||||
var rootDescription = "";
|
||||
let rootDescription = "";
|
||||
|
||||
var parameters: { [name: string]: AST.Parameter } = Object.create(null);
|
||||
const parameters: { [name: string]: AST.Parameter } = Object.create(null);
|
||||
|
||||
var typeAnnotation: string = null;
|
||||
let typeAnnotation: string = null;
|
||||
|
||||
var returnType: AST.ReturnType = null;
|
||||
let returnType: AST.ReturnType = null;
|
||||
|
||||
var isAbstract = false;
|
||||
let isAbstract = false;
|
||||
|
||||
var lastRead: { description: string } = null;
|
||||
let lastRead: { description: string } = null;
|
||||
|
||||
for (const line of lines) {
|
||||
var firstWordMatch = line.match(/^\s*(\S+)(\s*)/);
|
||||
var firstWord = (firstWordMatch !== null) ? firstWordMatch[1] : "";
|
||||
var remainingLine = (firstWordMatch !== null) ? line.substring(firstWordMatch[0].length) : "";
|
||||
const firstWordMatch = line.match(/^\s*(\S+)(\s*)/);
|
||||
const firstWord = (firstWordMatch !== null) ? firstWordMatch[1] : "";
|
||||
let remainingLine = (firstWordMatch !== null) ? line.substring(firstWordMatch[0].length) : "";
|
||||
|
||||
if (firstWord[0] === "@") {
|
||||
lastRead = null;
|
||||
@@ -553,30 +552,33 @@ class Walker {
|
||||
isAbstract = true;
|
||||
break;
|
||||
|
||||
case "@param":
|
||||
var type: string;
|
||||
case "@param": {
|
||||
let type: string;
|
||||
[type, remainingLine] = this._readType(remainingLine);
|
||||
|
||||
var [, name, description] = remainingLine.match(/(\S+)\s*(.*)/);
|
||||
const [, name, description] = remainingLine.match(/(\S+)\s*(.*)/);
|
||||
|
||||
var subParameterMatch = name.match(/^(?:(.+)\.([^\.]+))|(?:(.+)\[("[^\[\]"]+")\])$/);
|
||||
const subParameterMatch = name.match(/^(?:(.+)\.([^\.]+))|(?:(.+)\[("[^\[\]"]+")\])$/);
|
||||
if (subParameterMatch === null) {
|
||||
parameters[name] = lastRead = new AST.Parameter(name, description, type);
|
||||
}
|
||||
else {
|
||||
var parentName = subParameterMatch[1] || subParameterMatch[3];
|
||||
var childName = subParameterMatch[2] || subParameterMatch[4];
|
||||
var parentParameter = parameters[parentName];
|
||||
const parentName = subParameterMatch[1] || subParameterMatch[3];
|
||||
const childName = subParameterMatch[2] || subParameterMatch[4];
|
||||
const parentParameter = parameters[parentName];
|
||||
parentParameter.subParameters.push(lastRead = new AST.Parameter(childName, description, type));
|
||||
}
|
||||
break;
|
||||
|
||||
case "@return":
|
||||
var [type, description] = this._readType(remainingLine);
|
||||
break;
|
||||
}
|
||||
|
||||
case "@return": {
|
||||
const [type, description] = this._readType(remainingLine);
|
||||
|
||||
returnType = lastRead = new AST.ReturnType(description, type);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "@type":
|
||||
[typeAnnotation] = this._readType(remainingLine);
|
||||
@@ -607,9 +609,9 @@ class Walker {
|
||||
return ["*", remainingLine];
|
||||
}
|
||||
|
||||
var index = -1;
|
||||
var numberOfUnterminatedBraces = 0;
|
||||
for (var i = 0; i < remainingLine.length; i++) {
|
||||
let index = -1;
|
||||
let numberOfUnterminatedBraces = 0;
|
||||
for (let i = 0; i < remainingLine.length; i++) {
|
||||
if (remainingLine[i] === "{") {
|
||||
numberOfUnterminatedBraces++;
|
||||
}
|
||||
@@ -627,7 +629,7 @@ class Walker {
|
||||
throw new Error("Unterminated type specifier.");
|
||||
}
|
||||
|
||||
var type = remainingLine.substr(1, index - 1);
|
||||
const type = remainingLine.substr(1, index - 1);
|
||||
remainingLine = remainingLine.substr(index + 1).replace(/^\s+/, "");
|
||||
|
||||
return [type, remainingLine];
|
||||
@@ -641,16 +643,24 @@ class Walker {
|
||||
return signatureDeclaration.typeParameters.map(typeParameter => typeParameter.name.text);
|
||||
}
|
||||
|
||||
private _getGenericsOfTypeReferenceNode(typeReferenceNode: ts.ExpressionWithTypeArguments): (AST.UnresolvedType | AST.IntrinsicTypeReference)[] {
|
||||
private _getGenericsOfTypeReferenceNode(typeReferenceNode: ts.ExpressionWithTypeArguments, intrinsicGenerics: string[]): (AST.UnresolvedType | AST.IntrinsicTypeReference)[] {
|
||||
if (typeReferenceNode.typeArguments === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var typeReference = <ts.TypeReference>this._typeChecker.getTypeAtLocation(typeReferenceNode);
|
||||
const typeReference = this._typeChecker.getTypeAtLocation(typeReferenceNode) as ts.TypeReference;
|
||||
|
||||
return typeReference.typeArguments.map(typeArgument => {
|
||||
if ((<ts.IntrinsicType>typeArgument).intrinsicName !== undefined) {
|
||||
return new AST.IntrinsicTypeReference((<ts.IntrinsicType>typeArgument).intrinsicName);
|
||||
if ((typeArgument as ts.IntrinsicType).intrinsicName !== undefined) {
|
||||
return new AST.IntrinsicTypeReference((typeArgument as ts.IntrinsicType).intrinsicName);
|
||||
}
|
||||
|
||||
if (typeArgument.flags & ts.TypeFlags.TypeParameter) {
|
||||
if (intrinsicGenerics.indexOf(typeArgument.symbol.name) !== -1) {
|
||||
return new AST.IntrinsicTypeReference(typeArgument.symbol.name);
|
||||
}
|
||||
|
||||
throw new Error(`Unbound type parameter ${ typeArgument.symbol.name }`);
|
||||
}
|
||||
|
||||
return new AST.UnresolvedType(typeArgument.symbol, []);
|
||||
@@ -669,12 +679,12 @@ class Walker {
|
||||
|
||||
private _connectParameters(astParameters: ts.ParameterDeclaration[], jsDocParameters: { [name: string]: AST.Parameter }, onMissingMessageCallback: (parameterName: string) => string) {
|
||||
return astParameters.map(parameter => {
|
||||
var parameterName = (<ts.Identifier>parameter.name).text;
|
||||
let parameterName = (parameter.name as ts.Identifier).text;
|
||||
if (parameterName[0] === "_") {
|
||||
parameterName = parameterName.substr(1);
|
||||
}
|
||||
|
||||
var jsDocParameter = jsDocParameters[parameterName];
|
||||
let jsDocParameter = jsDocParameters[parameterName];
|
||||
|
||||
if (jsDocParameter === undefined) {
|
||||
this._notifyIncorrectJsDoc(onMissingMessageCallback.call(this, parameterName));
|
||||
@@ -686,8 +696,8 @@ class Walker {
|
||||
}
|
||||
|
||||
private _notifyIncorrectJsDoc(message: string): void {
|
||||
var fileName = path.basename(this._currentSourceFile.fileName);
|
||||
if (fileName === "lib.core.d.ts" || fileName === "lib.dom.d.ts") {
|
||||
const fileName = path.basename(this._currentSourceFile.fileName);
|
||||
if (fileName === "lib.es5.d.ts" || fileName === "lib.dom.d.ts") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -696,17 +706,17 @@ class Walker {
|
||||
|
||||
link(rootNamespaceName: string): void {
|
||||
for (const moduleName of Object.keys(this.modules)) {
|
||||
var module = this.modules[moduleName];
|
||||
const module = this.modules[moduleName];
|
||||
|
||||
for (const memberName of Object.keys(module.members)) {
|
||||
var member = module.members[memberName];
|
||||
const member = module.members[memberName];
|
||||
|
||||
if (member instanceof AST.Class) {
|
||||
if (member.unresolvedBaseType instanceof AST.UnresolvedType) {
|
||||
member.baseType = this._resolveTypeReference(<AST.UnresolvedType>member.unresolvedBaseType);
|
||||
member.baseType = this._resolveTypeReference(member.unresolvedBaseType);
|
||||
}
|
||||
else {
|
||||
member.baseType = <AST.TypeReference | AST.IntrinsicTypeReference>member.unresolvedBaseType;
|
||||
member.baseType = member.unresolvedBaseType;
|
||||
}
|
||||
|
||||
member.interfaces = member.unresolvedInterfaces.map(interfase => {
|
||||
@@ -714,7 +724,7 @@ class Walker {
|
||||
return this._resolveTypeReference(interfase);
|
||||
}
|
||||
|
||||
return <AST.TypeReference | AST.IntrinsicTypeReference>interfase;
|
||||
return interfase;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -724,12 +734,12 @@ class Walker {
|
||||
return this._resolveTypeReference(baseType);
|
||||
}
|
||||
|
||||
return <AST.TypeReference | AST.IntrinsicTypeReference>baseType;
|
||||
return baseType;
|
||||
});
|
||||
}
|
||||
|
||||
else if (member instanceof AST.Enum) {
|
||||
var value = 0;
|
||||
let value = 0;
|
||||
for (const enumMember of member.members) {
|
||||
if (enumMember.value === null) {
|
||||
enumMember.value = value;
|
||||
@@ -751,17 +761,17 @@ class Walker {
|
||||
|
||||
private _moduleToNamespace(module: AST.Module): void {
|
||||
for (const memberName of Object.keys(module.members)) {
|
||||
var member = module.members[memberName];
|
||||
let member = module.members[memberName];
|
||||
|
||||
if (member instanceof AST.Reference) {
|
||||
if ((<AST.Reference>member).isPrivate) {
|
||||
if (member.isPrivate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (member.name === "*") {
|
||||
var newNamespace = this._scope.enter(new AST.Namespace(memberName));
|
||||
const newNamespace = this._scope.enter(new AST.Namespace(memberName));
|
||||
|
||||
var existingNamespace = this.namespaces[newNamespace.fullName];
|
||||
const existingNamespace = this.namespaces[newNamespace.fullName];
|
||||
if (existingNamespace !== undefined) {
|
||||
this._scope.leave();
|
||||
this._scope.enter(existingNamespace);
|
||||
@@ -770,10 +780,10 @@ class Walker {
|
||||
this.namespaces[newNamespace.fullName] = newNamespace;
|
||||
}
|
||||
|
||||
var referencedModuleName = (<AST.Reference>member).moduleName;
|
||||
var referencedModule = this.modules[referencedModuleName];
|
||||
let referencedModuleName = member.moduleName;
|
||||
let referencedModule = this.modules[referencedModuleName];
|
||||
if (referencedModule === undefined && ((referencedModuleName + "/index") in this.modules)) {
|
||||
(<AST.Reference>member).moduleName = referencedModuleName = referencedModuleName + "/index";
|
||||
member.moduleName = referencedModuleName = referencedModuleName + "/index";
|
||||
referencedModule = this.modules[referencedModuleName];
|
||||
}
|
||||
this._moduleToNamespace(referencedModule);
|
||||
@@ -782,56 +792,56 @@ class Walker {
|
||||
}
|
||||
else {
|
||||
while (member instanceof AST.Reference) {
|
||||
member = this.modules[(<AST.Reference>member).moduleName].members[member.name];
|
||||
member = this.modules[member.moduleName].members[member.name];
|
||||
}
|
||||
|
||||
this._scope.enter(<AST.NamespaceMember><any>member);
|
||||
this._scope.enter(member);
|
||||
this._scope.leave();
|
||||
(<AST.Namespace>this._scope.current).members[member.name] = <AST.NamespaceMember>member;
|
||||
(this._scope.current as AST.Namespace).members[member.name] = member;
|
||||
}
|
||||
}
|
||||
else if (!(<AST.CanBePrivate><any>member).isPrivate) {
|
||||
this._scope.enter(<AST.NamespaceMember>member);
|
||||
else if (!(member as AST.CanBePrivate).isPrivate) {
|
||||
this._scope.enter(member);
|
||||
this._scope.leave();
|
||||
(<AST.Namespace>this._scope.current).members[member.name] = <AST.NamespaceMember>member;
|
||||
(this._scope.current as AST.Namespace).members[member.name] = member;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _resolveTypeReference(unresolvedType: AST.UnresolvedType): AST.TypeReference {
|
||||
var node: ts.Node = unresolvedType.symbol.declarations[0];
|
||||
let node: ts.Node = unresolvedType.symbol.declarations[0];
|
||||
while (node.kind !== ts.SyntaxKind.SourceFile) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
var sourceFile = <ts.SourceFile>node;
|
||||
const sourceFile = node as ts.SourceFile;
|
||||
|
||||
var moduleName = this._moduleNameFromFileName(sourceFile.fileName);
|
||||
var module = this.modules[moduleName];
|
||||
const moduleName = this._moduleNameFromFileName(sourceFile.fileName);
|
||||
const module = this.modules[moduleName];
|
||||
|
||||
var result = module.members[unresolvedType.symbol.name];
|
||||
let result = module.members[unresolvedType.symbol.name];
|
||||
|
||||
if (result === undefined) {
|
||||
throw new Error(`Type ${ unresolvedType.symbol.name } could not be resolved.`);
|
||||
}
|
||||
|
||||
while (result instanceof AST.Reference) {
|
||||
result = this.modules[(<AST.Reference>result).moduleName].members[result.name];
|
||||
result = this.modules[result.moduleName].members[result.name];
|
||||
}
|
||||
|
||||
var resultGenerics = unresolvedType.generics.map(generic => {
|
||||
const resultGenerics = unresolvedType.generics.map(generic => {
|
||||
if (generic instanceof AST.UnresolvedType) {
|
||||
return this._resolveTypeReference(generic);
|
||||
}
|
||||
|
||||
return <AST.IntrinsicTypeReference>generic;
|
||||
return generic;
|
||||
});
|
||||
|
||||
return new AST.TypeReference(<AST.NamespaceMember><any>result, resultGenerics);
|
||||
return new AST.TypeReference(result, resultGenerics);
|
||||
}
|
||||
|
||||
private _moduleNameFromFileName(fileName: string): string {
|
||||
var result = ts.normalizeSlashes(path.relative(this._compiler.projectRoot, fileName));
|
||||
let result = ts.normalizeSlashes(path.relative(this._compiler.projectRoot, fileName));
|
||||
|
||||
result = result.substr(0, result.length - ".ts".length);
|
||||
|
||||
@@ -844,16 +854,14 @@ class Walker {
|
||||
}
|
||||
|
||||
export function walk(compiler: Compiler, root: string, rootNamespaceName: string) {
|
||||
var sourceFiles = compiler.sourceFiles;
|
||||
var rootFileName = ts.normalizeSlashes(path.resolve(root));
|
||||
var rootSourceFile = sourceFiles.filter(sourceFile => sourceFile.fileName === rootFileName)[0];
|
||||
const sourceFiles = compiler.sourceFiles;
|
||||
|
||||
var walker = new Walker(compiler);
|
||||
const walker = new Walker(compiler);
|
||||
|
||||
// Walk
|
||||
for (const sourceFile of sourceFiles) {
|
||||
if (
|
||||
path.basename(sourceFile.fileName) === "lib.core.d.ts" ||
|
||||
path.basename(sourceFile.fileName) === "lib.es5.d.ts" ||
|
||||
path.basename(sourceFile.fileName) === "lib.dom.d.ts" ||
|
||||
sourceFile.fileName.substr(-"references.d.ts".length) === "references.d.ts"
|
||||
) {
|
||||
|
||||
@@ -26,108 +26,52 @@ var UglifyJS = require("uglify-js");
|
||||
var FileTransform = require("async-build").FileTransform;
|
||||
|
||||
var Run = (function () {
|
||||
function Run(entry, outputLibraryName, unusedVarsToIgnore) {
|
||||
this._entry = path.resolve(entry).replace(/\\/g, "/");
|
||||
function Run(outputLibraryName, unusedVarsToIgnore) {
|
||||
this._outputLibraryName = outputLibraryName;
|
||||
this._unusedVarsToIgnore = unusedVarsToIgnore;
|
||||
|
||||
this._root = UglifyJS.parse(
|
||||
'(function (root, factory) {\n' +
|
||||
' var global = this;\n' +
|
||||
'\n' +
|
||||
' if (typeof define === "function" && define.amd) {\n' +
|
||||
' define([], function() {\n' +
|
||||
' return factory(global);\n' +
|
||||
' });\n' +
|
||||
' }\n' +
|
||||
' else if (typeof exports === "object" && typeof module === "object") {\n' +
|
||||
' module.exports = factory(global);\n' +
|
||||
' }\n' +
|
||||
' else if (typeof exports === "object") {\n' +
|
||||
' exports.libjass = factory(global);\n' +
|
||||
' }\n' +
|
||||
' else {\n' +
|
||||
' root.libjass = factory(global);\n' +
|
||||
' }\n' +
|
||||
'})(this, function (global) {\n' +
|
||||
' "use strict";\n' +
|
||||
' return (function (modules) {\n' +
|
||||
' var installedModules = Object.create(null);\n' +
|
||||
' function require(moduleId) {\n' +
|
||||
' if (installedModules[moduleId]) {\n' +
|
||||
' return installedModules[moduleId];\n' +
|
||||
' }\n' +
|
||||
'\n' +
|
||||
' var exports = installedModules[moduleId] = Object.create(null);\n' +
|
||||
' modules[moduleId](exports, require);\n' +
|
||||
' return exports;\n' +
|
||||
' }\n' +
|
||||
'\n' +
|
||||
' return require(0);\n' +
|
||||
' })([\n' +
|
||||
' ]);\n' +
|
||||
'});');
|
||||
this._root = UglifyJS.parse(fs.readFileSync(path.resolve(__filename, "..", "umd-wrapper.js"), "utf8"));
|
||||
|
||||
this._root.figure_out_scope({ screw_ie8: true });
|
||||
|
||||
this._licenseHeader = null;
|
||||
this._toInsert = null;
|
||||
|
||||
this._outputModulesArray = this._root.body[0].body.args[1].body[1].value.args[0].elements;
|
||||
|
||||
this._modules = Object.create(null);
|
||||
|
||||
this._rootSourceMap = UjsSourceMap({ file: this._outputLibraryName + ".js", root: "" });
|
||||
this._rootSourceMap = null;
|
||||
}
|
||||
|
||||
Run.prototype.addFile = function (file) {
|
||||
var _this = this;
|
||||
|
||||
var moduleName = (path.extname(file.path) === ".map") ? file.path.substr(0, file.path.length - ".js.map".length) : file.path.substr(0, file.path.length - ".js".length);
|
||||
if (!(moduleName in this._modules)) {
|
||||
this._modules[moduleName] = { id: null, root: null };
|
||||
}
|
||||
|
||||
var module = this._modules[moduleName];
|
||||
|
||||
var filenameWithoutExtension = path.relative(path.join(this._entry, ".."), moduleName).replace(/\\/g, "/");
|
||||
var tsFilename = filenameWithoutExtension + ".ts";
|
||||
var jsFilename = filenameWithoutExtension + ".js";
|
||||
|
||||
switch (path.extname(file.path)) {
|
||||
case ".js":
|
||||
module.root = UglifyJS.parse(file.contents.toString(), {
|
||||
filename: jsFilename,
|
||||
toplevel: null,
|
||||
});
|
||||
try {
|
||||
this._toInsert = UglifyJS.parse(file.contents.toString(), {
|
||||
filename: path.basename(file.path),
|
||||
toplevel: null,
|
||||
}).body;
|
||||
}
|
||||
catch (ex) {
|
||||
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
||||
throw new Error("UglifyJS parse error: " + ex.toString() + "\n");
|
||||
}
|
||||
|
||||
if (this._licenseHeader === null) {
|
||||
module.root.walk(new UglifyJS.TreeWalker(function (node, descend) {
|
||||
if (_this._licenseHeader !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.start) {
|
||||
(node.start.comments_before || []).some(function (comment, i) {
|
||||
if (comment.value.indexOf("Copyright") !== -1) {
|
||||
_this._licenseHeader = comment;
|
||||
_this._licenseHeader.value = _this._licenseHeader.value.split("\n").map(function (line) { return line.replace(/^\t/, ""); }).join("\n");
|
||||
node.start.comments_before.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}));
|
||||
this._root.start.comments_before = [this._licenseHeader];
|
||||
this._root.start.nlb = true;
|
||||
throw ex;
|
||||
}
|
||||
break;
|
||||
|
||||
case ".map":
|
||||
var sourceMapContents = JSON.parse(file.contents.toString());
|
||||
sourceMapContents.sources[0] = tsFilename;
|
||||
sourceMapContents.file = jsFilename;
|
||||
this._rootSourceMap.addInput(sourceMapContents);
|
||||
this._rootSourceMap.get().setSourceContent(tsFilename, fs.readFileSync(moduleName + ".ts", { encoding: "utf8" }));
|
||||
var rawSourceMap = JSON.parse(file.contents.toString());
|
||||
|
||||
this._rootSourceMap = UglifyJS.SourceMap({
|
||||
file: this._outputLibraryName + ".js",
|
||||
root: "",
|
||||
orig: rawSourceMap,
|
||||
});
|
||||
|
||||
var generator = this._rootSourceMap.get();
|
||||
|
||||
rawSourceMap.sources.forEach(function (sourceRelativePath, index) {
|
||||
generator.setSourceContent(sourceRelativePath, rawSourceMap.sourcesContent[index]);
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
@@ -135,63 +79,72 @@ var Run = (function () {
|
||||
Run.prototype.build = function (outputStream) {
|
||||
var _this = this;
|
||||
|
||||
// Assign IDs to all modules
|
||||
var moduleNames = Object.keys(this._modules);
|
||||
moduleNames.sort(function (name1, name2) { return (name1 === name2) ? 0 : (name1 < name2 ? -1 : 1); });
|
||||
moduleNames.unshift.apply(moduleNames, moduleNames.splice(moduleNames.indexOf(this._entry), 1));
|
||||
|
||||
moduleNames.forEach(function (moduleName, index) {
|
||||
_this._modules[moduleName].id = index;
|
||||
});
|
||||
// Splice in the TS output into the UMD wrapper.
|
||||
var insertionParent = this._root.body[0].body.args[1].body;
|
||||
|
||||
|
||||
// Merge modules
|
||||
moduleNames.forEach(function (moduleName) {
|
||||
var module = _this._modules[moduleName];
|
||||
|
||||
module.root.body.forEach(function (statement) {
|
||||
if (statement instanceof UglifyJS.AST_Var && statement.definitions.length === 1) {
|
||||
if (
|
||||
statement.definitions[0].value instanceof UglifyJS.AST_Call &&
|
||||
statement.definitions[0].value.expression.name === "require"
|
||||
) {
|
||||
var importRelativePath = statement.definitions[0].value.args[0].value;
|
||||
var importAbsolutePath = path.join(moduleName, "..", importRelativePath).replace(/\\/g, "/");
|
||||
var stringArg = statement.definitions[0].value.args[0];
|
||||
var importedModule = _this._modules[importAbsolutePath];
|
||||
if (importedModule === undefined) {
|
||||
importedModule = _this._modules[importAbsolutePath + "/index"];
|
||||
}
|
||||
statement.definitions[0].value.args[0] = new UglifyJS.AST_Number({ start: stringArg.start, end: stringArg.end, value: importedModule.id });
|
||||
}
|
||||
else if (statement.definitions[0].name.name === "__extends") {
|
||||
var importAbsolutePath = path.join(_this._entry, "..", "utility", "ts-helpers").replace(/\\/g, "/");
|
||||
statement.definitions[0].value = UglifyJS.parse('require(' + _this._modules[importAbsolutePath].id + ').__extends').body[0].body;
|
||||
}
|
||||
else if (statement.definitions[0].name.name === "__decorate") {
|
||||
var importAbsolutePath = path.join(_this._entry, "..", "utility", "ts-helpers").replace(/\\/g, "/");
|
||||
statement.definitions[0].value = UglifyJS.parse('require(' + _this._modules[importAbsolutePath].id + ').__decorate').body[0].body;
|
||||
this._toInsert.reverse();
|
||||
for (var i = this._toInsert.length - 1; i >= 0; i--) {
|
||||
var node = this._toInsert[i];
|
||||
if (node instanceof UglifyJS.AST_Var) {
|
||||
for (var j = 0; j < node.definitions.length; j++) {
|
||||
var definition = node.definitions[j];
|
||||
if (definition.name.name === "__extends" || definition.name.name === "__decorate") {
|
||||
definition.value = definition.value.right;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var wrapper = UglifyJS.parse('/* ' + module.id + ' ./' + path.relative(path.join(_this._entry, ".."), moduleName).replace(/\\/g, "/") + ' */ function x(exports, require) { }');
|
||||
var func = wrapper.body[0];
|
||||
func.body = module.root.body;
|
||||
func.name = null;
|
||||
_this._outputModulesArray.push(func);
|
||||
});
|
||||
insertionParent.splice(-1, 0, node);
|
||||
this._toInsert.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
this._root.figure_out_scope({ screw_ie8: true });
|
||||
insertionParent.splice.apply(insertionParent, [-1, 0].concat(this._toInsert));
|
||||
|
||||
|
||||
// Set if there are any unused variables apart from the ones in unusedVarsToIgnore
|
||||
var haveUnusedVars = false;
|
||||
// Fixups
|
||||
for (var i = 0; i < this._toInsert.length; i++) {
|
||||
var node = this._toInsert[i];
|
||||
if (node instanceof UglifyJS.AST_Statement && node.body instanceof UglifyJS.AST_Call && node.body.expression.name === "define") {
|
||||
var defineCall = node.body;
|
||||
|
||||
// Remove some things from the AST
|
||||
var nodesToRemove;
|
||||
defineCall.expression.name = "def";
|
||||
|
||||
nodesToRemove = [];
|
||||
if (defineCall.args[1].elements[0].value !== "require") {
|
||||
throw new Error("Expected first dep to be require");
|
||||
}
|
||||
defineCall.args[1].elements.shift();
|
||||
defineCall.args[2].argnames.shift();
|
||||
|
||||
if (defineCall.args[1].elements[0].value !== "exports") {
|
||||
throw new Error("Expected second dep to be exports");
|
||||
}
|
||||
defineCall.args[1].elements.shift();
|
||||
defineCall.args[2].argnames.push(defineCall.args[2].argnames.shift());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Remove all license headers except the one from the UMD wrapper
|
||||
var firstLicenseHeader = null;
|
||||
this._root.walk(new UglifyJS.TreeWalker(function (node, descend) {
|
||||
if (node.start) {
|
||||
(node.start.comments_before || []).some(function (comment, i) {
|
||||
if (comment.value.indexOf("Copyright") !== -1) {
|
||||
if (firstLicenseHeader === null) {
|
||||
firstLicenseHeader = comment;
|
||||
}
|
||||
else if (comment !== firstLicenseHeader) {
|
||||
node.start.comments_before.splice(i, 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
// Fixup anonymous functions to print a space after "function"
|
||||
@@ -207,7 +160,7 @@ var Run = (function () {
|
||||
this._root.walk(new UglifyJS.TreeWalker(function (node, descend) {
|
||||
if (node.start && node.start.comments_before) {
|
||||
node.start.comments_before.forEach(function (comment) {
|
||||
if (comment === _this._licenseHeader) {
|
||||
if (comment.value.indexOf("Copyright") !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -216,7 +169,7 @@ var Run = (function () {
|
||||
return;
|
||||
}
|
||||
|
||||
var indent = " "; // 9 spaces
|
||||
var indent = " "; // 5 spaces
|
||||
for (var i = 0; i < comment.col; i++) {
|
||||
indent += " ";
|
||||
}
|
||||
@@ -230,8 +183,16 @@ var Run = (function () {
|
||||
this._root.figure_out_scope({ screw_ie8: true });
|
||||
|
||||
|
||||
// Remove some things from the AST
|
||||
var nodesToRemove = [];
|
||||
|
||||
// Set if there are any unused variables apart from the ones in unusedVarsToIgnore
|
||||
var haveUnusedVars = false;
|
||||
|
||||
// Repeat because removing some declarations may make others unreferenced
|
||||
for (;;) {
|
||||
this._root.figure_out_scope({ screw_ie8: true });
|
||||
|
||||
// Unreferenced variable and function declarations, and unreferenced terminal function arguments
|
||||
this._root.walk(new UglifyJS.TreeWalker(function (node, descend) {
|
||||
if (node instanceof UglifyJS.AST_SymbolDeclaration && node.unreferenced()) {
|
||||
@@ -266,8 +227,6 @@ var Run = (function () {
|
||||
});
|
||||
|
||||
nodesToRemove = [];
|
||||
|
||||
this._root.figure_out_scope({ screw_ie8: true });
|
||||
}
|
||||
|
||||
|
||||
@@ -315,39 +274,23 @@ var Run = (function () {
|
||||
|
||||
// Output
|
||||
var output = {
|
||||
source_map: _this._rootSourceMap,
|
||||
source_map: this._rootSourceMap,
|
||||
ascii_only: true,
|
||||
beautify: true,
|
||||
comments: function (node, comment) {
|
||||
// If this is the first license header, keep it.
|
||||
if (comment === _this._licenseHeader) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If this is a license header, remove it.
|
||||
if (comment.value.indexOf("Copyright") !== -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this is a TypeScript reference comment, strip it.
|
||||
if (comment.value.substr(0, "/<reference path=".length) === "/<reference path=") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
comments: true,
|
||||
};
|
||||
|
||||
var stream = UglifyJS.OutputStream(output);
|
||||
this._root.print(stream);
|
||||
|
||||
outputStream.push({
|
||||
path: _this._outputLibraryName + ".js",
|
||||
contents: Buffer.concat([new Buffer(stream.toString()), new Buffer("\n//# sourceMappingURL=" + _this._outputLibraryName + ".js.map")])
|
||||
path: this._outputLibraryName + ".js",
|
||||
contents: Buffer.concat([new Buffer(stream.toString()), new Buffer("\n//# sourceMappingURL=" + this._outputLibraryName + ".js.map")])
|
||||
});
|
||||
|
||||
outputStream.push({
|
||||
path: _this._outputLibraryName + ".js.map",
|
||||
contents: new Buffer(_this._rootSourceMap.get().toString())
|
||||
path: this._outputLibraryName + ".js.map",
|
||||
contents: new Buffer(this._rootSourceMap.get().toString())
|
||||
});
|
||||
|
||||
// Print unused variables
|
||||
@@ -366,8 +309,8 @@ var Run = (function () {
|
||||
})();
|
||||
|
||||
module.exports = {
|
||||
build: function (entry, outputLibraryName, unusedVarsToIgnore) {
|
||||
var run = new Run(entry, outputLibraryName, unusedVarsToIgnore);
|
||||
build: function (outputLibraryName, unusedVarsToIgnore) {
|
||||
var run = new Run(outputLibraryName, unusedVarsToIgnore);
|
||||
|
||||
return new FileTransform(function (file) {
|
||||
run.addFile(file);
|
||||
@@ -376,7 +319,7 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
watch: function (entry, outputLibraryName, unusedVarsToIgnore) {
|
||||
watch: function (outputLibraryName, unusedVarsToIgnore) {
|
||||
var files = Object.create(null);
|
||||
|
||||
return new FileTransform(function (file) {
|
||||
@@ -384,7 +327,7 @@ module.exports = {
|
||||
files[file.path] = file;
|
||||
}
|
||||
else {
|
||||
var run = new Run(entry, outputLibraryName, unusedVarsToIgnore);
|
||||
var run = new Run(outputLibraryName, unusedVarsToIgnore);
|
||||
Object.keys(files).forEach(function (filename) {
|
||||
run.addFile(files[filename]);
|
||||
});
|
||||
@@ -452,6 +395,7 @@ module.exports = {
|
||||
file: path.basename(sourceMapFile.path),
|
||||
orig: sourceMapFile.contents.toString()
|
||||
}),
|
||||
ascii_only: true,
|
||||
comments: function (node, comment) {
|
||||
if (!firstLicenseHeaderFound && comment.value.indexOf("Copyright") !== -1) {
|
||||
firstLicenseHeaderFound = true;
|
||||
@@ -489,53 +433,6 @@ module.exports = {
|
||||
}
|
||||
};
|
||||
|
||||
var SourceMap = require.cache[require.resolve("uglify-js")].require("source-map");
|
||||
|
||||
function UjsSourceMap(options) {
|
||||
var orig_maps = Object.create(null);
|
||||
|
||||
var generator = new SourceMap.SourceMapGenerator({
|
||||
file: options.file,
|
||||
sourceRoot: options.root,
|
||||
});
|
||||
|
||||
return {
|
||||
addInput: function (rawSourceMap) {
|
||||
var consumer = new SourceMap.SourceMapConsumer(rawSourceMap);
|
||||
orig_maps[consumer.file] = consumer;
|
||||
},
|
||||
add: function (source, gen_line, gen_col, orig_line, orig_col, name) {
|
||||
var originalMap = orig_maps[source];
|
||||
if (originalMap === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var info = originalMap.originalPositionFor({
|
||||
line: orig_line,
|
||||
column: orig_col
|
||||
});
|
||||
|
||||
if (info.source === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
source = info.source;
|
||||
orig_line = info.line;
|
||||
orig_col = info.column;
|
||||
name = info.name || name;
|
||||
|
||||
generator.addMapping({
|
||||
generated : { line: gen_line, column: gen_col },
|
||||
original : { line: orig_line, column: orig_col },
|
||||
source : source,
|
||||
name : name
|
||||
});
|
||||
},
|
||||
get: function () { return generator; },
|
||||
toString: function () { return generator.toString(); },
|
||||
};
|
||||
}
|
||||
|
||||
var originalSymbolUnreferenced = UglifyJS.AST_Symbol.prototype.unreferenced;
|
||||
|
||||
// Workaround for https://github.com/mishoo/UglifyJS2/issues/789 - Nodes explicitly marked with ujs:unreferenced will not be warned for.
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
return req("index");
|
||||
});
|
||||
@@ -98,3 +98,7 @@
|
||||
.libjass-filters {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.libjass-filters * {
|
||||
color-interpolation-filters: sRGB;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"main": "lib/libjass.js",
|
||||
"scripts": {
|
||||
"prepublish": "node ./build.js clean default",
|
||||
"build": "tsc ./build/typescript/index.ts ./build/doc.ts ./build/node.d.ts ./build/typescript/typescript.d.ts ./node_modules/async-build/typings.d.ts -m commonjs -t es5 -noImplicitAny --moduleResolution classic",
|
||||
"build": "tsc -p ./build/tsconfig.json",
|
||||
"test": "node ./build.js test",
|
||||
"test-lib": "intern-client config=tests/intern reporters=Pretty",
|
||||
"test-minified": "intern-client config=tests/intern reporters=Pretty minified=true",
|
||||
@@ -26,13 +26,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"async": "1.x >=1.4",
|
||||
"async-build": "0.2.0",
|
||||
"firefox-profile": "0.3.x",
|
||||
"intern": "3.x >=3.0.1",
|
||||
"async-build": "0.3.1",
|
||||
"intern": "3.x >=3.2.0",
|
||||
"npm": "3.x",
|
||||
"pngjs": "2.2.0",
|
||||
"pngjs": "3.x",
|
||||
"sax": "1.x",
|
||||
"typescript": "1.7.5",
|
||||
"typescript": "next",
|
||||
"uglify-js": "2.x >=2.4.24"
|
||||
},
|
||||
"private": true
|
||||
|
||||
@@ -42,6 +42,8 @@ export { parser };
|
||||
import * as renderers from "./renderers";
|
||||
export { renderers };
|
||||
|
||||
export { serialize, deserialize } from "./serialization";
|
||||
|
||||
export { ASS } from "./types/ass";
|
||||
export { Attachment, AttachmentType } from "./types/attachment";
|
||||
export { Dialogue } from "./types/dialogue";
|
||||
@@ -50,28 +52,88 @@ export { Style } from "./types/style";
|
||||
|
||||
export { BorderStyle, Format, WrappingStyle } from "./types/misc";
|
||||
|
||||
export { version } from "./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.<T>=)} 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.<!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.
|
||||
*/
|
||||
export function configure(newConfig: {
|
||||
debugMode?: boolean,
|
||||
verboseMode?: boolean,
|
||||
Set?: typeof set.Set | null,
|
||||
Map?: typeof map.Map | null,
|
||||
Promise?: typeof promise.Promise | null,
|
||||
}): void {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
declare const exports: any;
|
||||
|
||||
// Getters below are to work around https://github.com/Microsoft/TypeScript/issues/6366
|
||||
|
||||
Object.defineProperties(exports, {
|
||||
debugMode: {
|
||||
get: () => settings.debugMode,
|
||||
set: settings.setDebugMode,
|
||||
set: value => {
|
||||
console.warn("Setter `libjass.debugMode = value` has been deprecated. Use `libjass.configure({ debugMode: value })` instead.");
|
||||
settings.setDebugMode(value);
|
||||
},
|
||||
},
|
||||
|
||||
verboseMode: {
|
||||
get: () => settings.verboseMode,
|
||||
set: settings.setVerboseMode,
|
||||
set: value => {
|
||||
console.warn("Setter `libjass.verboseMode = value` has been deprecated. Use `libjass.configure({ verboseMode: value })` instead.");
|
||||
settings.setVerboseMode(value);
|
||||
},
|
||||
},
|
||||
|
||||
Set: {
|
||||
get: () => set.Set,
|
||||
set: set.setImplementation,
|
||||
set: value => {
|
||||
console.warn("Setter `libjass.Set = value` has been deprecated. Use `libjass.configure({ Set: value })` instead.");
|
||||
set.setImplementation(value);
|
||||
},
|
||||
},
|
||||
|
||||
Map: {
|
||||
get: () => map.Map,
|
||||
set: map.setImplementation,
|
||||
set: value => {
|
||||
console.warn("Setter `libjass.Map = value` has been deprecated. Use `libjass.configure({ Map: value })` instead.");
|
||||
map.setImplementation(value);
|
||||
},
|
||||
},
|
||||
|
||||
Promise: {
|
||||
get: () => promise.Promise,
|
||||
set: promise.setImplementation,
|
||||
set: value => {
|
||||
console.warn("Setter `libjass.Promise = value` has been deprecated. Use `libjass.configure({ Promise: value })` instead.");
|
||||
promise.setImplementation(value);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -26,9 +26,9 @@ import { Map } from "../utility/map";
|
||||
* Parses a line into a {@link ./types/misc.Property}.
|
||||
*
|
||||
* @param {string} line
|
||||
* @return {!Property}
|
||||
* @return {Property}
|
||||
*/
|
||||
export function parseLineIntoProperty(line: string): Property {
|
||||
export function parseLineIntoProperty(line: string): Property | null {
|
||||
const colonPos = line.indexOf(":");
|
||||
if (colonPos === -1) {
|
||||
return null;
|
||||
@@ -45,9 +45,9 @@ export function parseLineIntoProperty(line: string): Property {
|
||||
*
|
||||
* @param {string} line
|
||||
* @param {!Array.<string>} formatSpecifier
|
||||
* @return {!TypedTemplate}
|
||||
* @return {TypedTemplate}
|
||||
*/
|
||||
export function parseLineIntoTypedTemplate(line: string, formatSpecifier: string[]): TypedTemplate {
|
||||
export function parseLineIntoTypedTemplate(line: string, formatSpecifier: string[]): TypedTemplate | null {
|
||||
const property = parseLineIntoProperty(line);
|
||||
if (property === null) {
|
||||
return null;
|
||||
|
||||
@@ -34,17 +34,17 @@ const rules = new Map<string, (parent: ParseNode) => ParseNode>();
|
||||
* @return {*} The value returned depends on the rule used.
|
||||
*/
|
||||
export function parse(input: string, rule: string): any {
|
||||
const run = new ParserRun(input, rule);
|
||||
const { result } = new ParserRun(input, rule);
|
||||
|
||||
if (run.result === null || run.result.end !== input.length) {
|
||||
if (result === null || result.end !== input.length) {
|
||||
if (debugMode) {
|
||||
console.error("Parse failed. %s %s %o", rule, input, run.result);
|
||||
console.error("Parse failed. %s %s %o", rule, input, result);
|
||||
}
|
||||
|
||||
throw new Error("Parse failed.");
|
||||
}
|
||||
|
||||
return run.result.value;
|
||||
return result.value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,15 +58,20 @@ class ParserRun {
|
||||
private _result: ParseNode;
|
||||
|
||||
constructor(private _input: string, rule: string) {
|
||||
const ruleFunction = rules.get(rule);
|
||||
if (ruleFunction === undefined) {
|
||||
throw new Error(`Could not find parser rule named ${ rule }`);
|
||||
}
|
||||
|
||||
this._parseTree = new ParseNode(null);
|
||||
|
||||
this._result = rules.get(rule).call(this, this._parseTree);
|
||||
this._result = ruleFunction.call(this, this._parseTree);
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {ParseNode}
|
||||
*/
|
||||
get result(): ParseNode {
|
||||
get result(): ParseNode | null {
|
||||
return this._result;
|
||||
}
|
||||
|
||||
@@ -89,15 +94,10 @@ class ParserRun {
|
||||
else {
|
||||
const whiteSpaceOrTextNode = this.parse_newline(current) || this.parse_hardspace(current) || this.parse_text(current);
|
||||
|
||||
if (whiteSpaceOrTextNode === null) {
|
||||
parent.pop();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (whiteSpaceOrTextNode.value instanceof parts.Text && current.value[current.value.length - 1] instanceof parts.Text) {
|
||||
// Merge consecutive text parts into one part
|
||||
const previousTextPart = <parts.Text>current.value[current.value.length - 1];
|
||||
current.value[current.value.length - 1] = new parts.Text(previousTextPart.value + (<parts.Text>whiteSpaceOrTextNode.value).value);
|
||||
const previousTextPart = current.value[current.value.length - 1] as parts.Text;
|
||||
current.value[current.value.length - 1] = new parts.Text(previousTextPart.value + (whiteSpaceOrTextNode.value as parts.Text).value);
|
||||
}
|
||||
else {
|
||||
current.value.push(whiteSpaceOrTextNode.value);
|
||||
@@ -113,7 +113,7 @@ class ParserRun {
|
||||
}
|
||||
|
||||
else if (part instanceof parts.Text && inDrawingMode) {
|
||||
current.value[i] = new parts.DrawingInstructions(<parts.drawing.Instruction[]>parse(part.value, "drawingInstructions"));
|
||||
current.value[i] = new parts.DrawingInstructions(parse(part.value, "drawingInstructions") as parts.drawing.Instruction[]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -124,7 +124,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_enclosedTags(parent: ParseNode): ParseNode {
|
||||
parse_enclosedTags(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
current.value = [];
|
||||
@@ -135,7 +135,7 @@ class ParserRun {
|
||||
}
|
||||
|
||||
for (let next = this._peek(); this._haveMore() && next !== "}"; next = this._peek()) {
|
||||
let childNode: ParseNode = null;
|
||||
let childNode: ParseNode | null = null;
|
||||
|
||||
if (this.read(current, "\\") !== null) {
|
||||
childNode =
|
||||
@@ -211,8 +211,8 @@ class ParserRun {
|
||||
// Merge consecutive comment parts into one part
|
||||
current.value[current.value.length - 1] =
|
||||
new parts.Comment(
|
||||
(<parts.Comment>current.value[current.value.length - 1]).value +
|
||||
(<parts.Comment>childNode.value).value
|
||||
(current.value[current.value.length - 1] as parts.Comment).value +
|
||||
(childNode.value as parts.Comment).value
|
||||
);
|
||||
}
|
||||
else {
|
||||
@@ -237,7 +237,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_newline(parent: ParseNode): ParseNode {
|
||||
parse_newline(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "\\N") === null) {
|
||||
@@ -254,7 +254,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_hardspace(parent: ParseNode): ParseNode {
|
||||
parse_hardspace(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "\\h") === null) {
|
||||
@@ -301,7 +301,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_a(parent: ParseNode): ParseNode {
|
||||
parse_tag_a(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "a") === null) {
|
||||
@@ -339,8 +339,8 @@ class ParserRun {
|
||||
|
||||
const valueNode = new ParseNode(current, next);
|
||||
|
||||
let value: number = null;
|
||||
switch (valueNode.value) {
|
||||
let value: number = -1;
|
||||
switch ((valueNode.value)) {
|
||||
case "1":
|
||||
value = 1;
|
||||
break;
|
||||
@@ -387,7 +387,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_alpha(parent: ParseNode): ParseNode {
|
||||
parse_tag_alpha(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -395,7 +395,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_an(parent: ParseNode): ParseNode {
|
||||
parse_tag_an(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "an") === null) {
|
||||
@@ -421,7 +421,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_b(parent: ParseNode): ParseNode {
|
||||
parse_tag_b(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "b") === null) {
|
||||
@@ -429,7 +429,7 @@ class ParserRun {
|
||||
return null;
|
||||
}
|
||||
|
||||
let valueNode: ParseNode = null;
|
||||
let valueNode: ParseNode | null = null;
|
||||
|
||||
let next = this._peek();
|
||||
|
||||
@@ -459,7 +459,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_be(parent: ParseNode): ParseNode {
|
||||
parse_tag_be(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -467,7 +467,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_blur(parent: ParseNode): ParseNode {
|
||||
parse_tag_blur(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -475,7 +475,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_bord(parent: ParseNode): ParseNode {
|
||||
parse_tag_bord(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_c(parent: ParseNode): ParseNode {
|
||||
parse_tag_c(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -491,7 +491,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_clip(parent: ParseNode): ParseNode {
|
||||
parse_tag_clip(parent: ParseNode): ParseNode | null {
|
||||
return this._parse_tag_clip_or_iclip("clip", parent);
|
||||
}
|
||||
|
||||
@@ -499,7 +499,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fad(parent: ParseNode): ParseNode {
|
||||
parse_tag_fad(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "fad") === null) {
|
||||
@@ -543,7 +543,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fade(parent: ParseNode): ParseNode {
|
||||
parse_tag_fade(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "fade") === null) {
|
||||
@@ -646,7 +646,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fax(parent: ParseNode): ParseNode {
|
||||
parse_tag_fax(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -654,7 +654,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fay(parent: ParseNode): ParseNode {
|
||||
parse_tag_fay(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -662,7 +662,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fn(parent: ParseNode): ParseNode {
|
||||
parse_tag_fn(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "fn") === null) {
|
||||
@@ -690,7 +690,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fr(parent: ParseNode): ParseNode {
|
||||
parse_tag_fr(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -698,7 +698,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_frx(parent: ParseNode): ParseNode {
|
||||
parse_tag_frx(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -706,7 +706,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fry(parent: ParseNode): ParseNode {
|
||||
parse_tag_fry(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -714,7 +714,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_frz(parent: ParseNode): ParseNode {
|
||||
parse_tag_frz(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -722,7 +722,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fs(parent: ParseNode): ParseNode {
|
||||
parse_tag_fs(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -730,7 +730,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fsplus(parent: ParseNode): ParseNode {
|
||||
parse_tag_fsplus(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "fs+") === null) {
|
||||
@@ -745,7 +745,7 @@ class ParserRun {
|
||||
return null;
|
||||
}
|
||||
|
||||
current.value = new parts.FontSizePlus(valueNode.value);
|
||||
current.value = new parts.FontSizePlus(valueNode.value / 10);
|
||||
|
||||
return current;
|
||||
}
|
||||
@@ -754,7 +754,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fsminus(parent: ParseNode): ParseNode {
|
||||
parse_tag_fsminus(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "fs-") === null) {
|
||||
@@ -769,7 +769,7 @@ class ParserRun {
|
||||
return null;
|
||||
}
|
||||
|
||||
current.value = new parts.FontSizeMinus(valueNode.value);
|
||||
current.value = new parts.FontSizeMinus(valueNode.value / 10);
|
||||
|
||||
return current;
|
||||
}
|
||||
@@ -778,7 +778,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fscx(parent: ParseNode): ParseNode {
|
||||
parse_tag_fscx(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "fscx") === null) {
|
||||
@@ -802,7 +802,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fscy(parent: ParseNode): ParseNode {
|
||||
parse_tag_fscy(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "fscy") === null) {
|
||||
@@ -826,7 +826,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_fsp(parent: ParseNode): ParseNode {
|
||||
parse_tag_fsp(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -834,7 +834,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_i(parent: ParseNode): ParseNode {
|
||||
parse_tag_i(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -842,7 +842,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_iclip(parent: ParseNode): ParseNode {
|
||||
parse_tag_iclip(parent: ParseNode): ParseNode | null {
|
||||
return this._parse_tag_clip_or_iclip("iclip", parent);
|
||||
}
|
||||
|
||||
@@ -850,7 +850,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_k(parent: ParseNode): ParseNode {
|
||||
parse_tag_k(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "k") === null) {
|
||||
@@ -874,7 +874,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_K(parent: ParseNode): ParseNode {
|
||||
parse_tag_K(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "K") === null) {
|
||||
@@ -898,7 +898,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_kf(parent: ParseNode): ParseNode {
|
||||
parse_tag_kf(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "kf") === null) {
|
||||
@@ -922,7 +922,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_ko(parent: ParseNode): ParseNode {
|
||||
parse_tag_ko(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "ko") === null) {
|
||||
@@ -946,7 +946,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_move(parent: ParseNode): ParseNode {
|
||||
parse_tag_move(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "move") === null) {
|
||||
@@ -998,8 +998,8 @@ class ParserRun {
|
||||
return null;
|
||||
}
|
||||
|
||||
let t1Node: ParseNode = null;
|
||||
let t2Node: ParseNode = null;
|
||||
let t1Node: ParseNode | null = null;
|
||||
let t2Node: ParseNode | null = null;
|
||||
|
||||
if (this.read(current, ",") !== null) {
|
||||
t1Node = this.parse_decimal(current);
|
||||
@@ -1037,7 +1037,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_org(parent: ParseNode): ParseNode {
|
||||
parse_tag_org(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "org") === null) {
|
||||
@@ -1081,7 +1081,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_p(parent: ParseNode): ParseNode {
|
||||
parse_tag_p(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1089,7 +1089,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_pbo(parent: ParseNode): ParseNode {
|
||||
parse_tag_pbo(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1097,7 +1097,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_pos(parent: ParseNode): ParseNode {
|
||||
parse_tag_pos(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "pos") === null) {
|
||||
@@ -1141,7 +1141,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_q(parent: ParseNode): ParseNode {
|
||||
parse_tag_q(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "q") === null) {
|
||||
@@ -1167,7 +1167,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_r(parent: ParseNode): ParseNode {
|
||||
parse_tag_r(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "r") === null) {
|
||||
@@ -1195,7 +1195,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_s(parent: ParseNode): ParseNode {
|
||||
parse_tag_s(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1203,7 +1203,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_shad(parent: ParseNode): ParseNode {
|
||||
parse_tag_shad(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1211,7 +1211,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_t(parent: ParseNode): ParseNode {
|
||||
parse_tag_t(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, "t") === null) {
|
||||
@@ -1224,9 +1224,9 @@ class ParserRun {
|
||||
return null;
|
||||
}
|
||||
|
||||
let startNode: ParseNode = null;
|
||||
let endNode: ParseNode = null;
|
||||
let accelNode: ParseNode = null;
|
||||
let startNode: ParseNode | null = null;
|
||||
let endNode: ParseNode | null = null;
|
||||
let accelNode: ParseNode | null = null;
|
||||
|
||||
const firstNode = this.parse_decimal(current);
|
||||
if (firstNode !== null) {
|
||||
@@ -1268,7 +1268,7 @@ class ParserRun {
|
||||
const transformTags: parts.Part[] = [];
|
||||
|
||||
for (let next = this._peek(); this._haveMore() && next !== ")" && next !== "}"; next = this._peek()) {
|
||||
let childNode: ParseNode = null;
|
||||
let childNode: ParseNode | null = null;
|
||||
|
||||
if (this.read(current, "\\") !== null) {
|
||||
childNode =
|
||||
@@ -1323,8 +1323,8 @@ class ParserRun {
|
||||
// Merge consecutive comment parts into one part
|
||||
transformTags[transformTags.length - 1] =
|
||||
new parts.Comment(
|
||||
(<parts.Comment>transformTags[transformTags.length - 1]).value +
|
||||
(<parts.Comment>childNode.value).value
|
||||
(transformTags[transformTags.length - 1] as parts.Comment).value +
|
||||
(childNode.value as parts.Comment).value
|
||||
);
|
||||
}
|
||||
else {
|
||||
@@ -1354,7 +1354,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_u(parent: ParseNode): ParseNode {
|
||||
parse_tag_u(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1362,7 +1362,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_xbord(parent: ParseNode): ParseNode {
|
||||
parse_tag_xbord(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1370,7 +1370,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_xshad(parent: ParseNode): ParseNode {
|
||||
parse_tag_xshad(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1378,7 +1378,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_ybord(parent: ParseNode): ParseNode {
|
||||
parse_tag_ybord(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1386,7 +1386,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_yshad(parent: ParseNode): ParseNode {
|
||||
parse_tag_yshad(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1394,7 +1394,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_1a(parent: ParseNode): ParseNode {
|
||||
parse_tag_1a(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1402,7 +1402,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_1c(parent: ParseNode): ParseNode {
|
||||
parse_tag_1c(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1410,7 +1410,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_2a(parent: ParseNode): ParseNode {
|
||||
parse_tag_2a(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1418,7 +1418,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_2c(parent: ParseNode): ParseNode {
|
||||
parse_tag_2c(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1426,7 +1426,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_3a(parent: ParseNode): ParseNode {
|
||||
parse_tag_3a(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1434,7 +1434,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_3c(parent: ParseNode): ParseNode {
|
||||
parse_tag_3c(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1442,7 +1442,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_4a(parent: ParseNode): ParseNode {
|
||||
parse_tag_4a(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1450,7 +1450,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_tag_4c(parent: ParseNode): ParseNode {
|
||||
parse_tag_4c(parent: ParseNode): ParseNode | null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
@@ -1461,7 +1461,7 @@ class ParserRun {
|
||||
parse_drawingInstructions(parent: ParseNode): ParseNode {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
let currentType: string = null;
|
||||
let currentType: string | null = null;
|
||||
const numberParts: ParseNode[] = [];
|
||||
|
||||
current.value = [];
|
||||
@@ -1498,11 +1498,7 @@ class ParserRun {
|
||||
}
|
||||
|
||||
const typePart = this.parse_text(current);
|
||||
if (typePart === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
const newType = (<parts.Text>typePart.value).value;
|
||||
const newType = (typePart.value as parts.Text).value;
|
||||
switch (newType) {
|
||||
case "m":
|
||||
case "l":
|
||||
@@ -1522,7 +1518,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_decimalInt32(parent: ParseNode): ParseNode {
|
||||
parse_decimalInt32(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
const isNegative = this.read(current, "-") !== null;
|
||||
@@ -1554,7 +1550,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_hexInt32(parent: ParseNode): ParseNode {
|
||||
parse_hexInt32(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
const isNegative = this.read(current, "-") !== null;
|
||||
@@ -1594,13 +1590,13 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_decimalOrHexInt32(parent: ParseNode): ParseNode {
|
||||
parse_decimalOrHexInt32(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
const valueNode =
|
||||
(this.read(current, "&H") !== null || this.read(current, "&h") !== null) ?
|
||||
this.parse_hexInt32(current) :
|
||||
this.parse_decimalInt32(current);
|
||||
this.parse_hexInt32(current) :
|
||||
this.parse_decimalInt32(current);
|
||||
|
||||
if (valueNode === null) {
|
||||
parent.pop();
|
||||
@@ -1616,7 +1612,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_decimal(parent: ParseNode): ParseNode {
|
||||
parse_decimal(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
const negative = (this.read(current, "-") !== null);
|
||||
@@ -1641,12 +1637,12 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_unsignedDecimal(parent: ParseNode): ParseNode {
|
||||
parse_unsignedDecimal(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
const characteristicNode = new ParseNode(current, "");
|
||||
|
||||
let mantissaNode: ParseNode = null;
|
||||
let mantissaNode: ParseNode | null = null;
|
||||
|
||||
for (let next = this._peek(); this._haveMore() && next >= "0" && next <= "9"; next = this._peek()) {
|
||||
characteristicNode.value += next;
|
||||
@@ -1679,7 +1675,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_enableDisable(parent: ParseNode): ParseNode {
|
||||
parse_enableDisable(parent: ParseNode): ParseNode | null {
|
||||
const next = this._peek();
|
||||
|
||||
if (next === "0" || next === "1") {
|
||||
@@ -1696,7 +1692,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_color(parent: ParseNode): ParseNode {
|
||||
parse_color(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
while (this.read(current, "&") !== null || this.read(current, "H") !== null) { }
|
||||
@@ -1724,7 +1720,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_alpha(parent: ParseNode): ParseNode {
|
||||
parse_alpha(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
while (this.read(current, "&") !== null || this.read(current, "H") !== null) { }
|
||||
@@ -1748,7 +1744,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parse_colorWithAlpha(parent: ParseNode): ParseNode {
|
||||
parse_colorWithAlpha(parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
const valueNode = this.parse_decimalOrHexInt32(current);
|
||||
@@ -1774,7 +1770,7 @@ class ParserRun {
|
||||
* @param {string} next
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
read(parent: ParseNode, next: string): ParseNode {
|
||||
read(parent: ParseNode, next: string): ParseNode | null {
|
||||
if (this._peek(next.length) !== next) {
|
||||
return null;
|
||||
}
|
||||
@@ -1805,7 +1801,7 @@ class ParserRun {
|
||||
* @param {!ParseNode} parent
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
private _parse_tag_clip_or_iclip(tagName: string, parent: ParseNode): ParseNode {
|
||||
private _parse_tag_clip_or_iclip(tagName: "clip" | "iclip", parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (this.read(current, tagName) === null) {
|
||||
@@ -1818,12 +1814,12 @@ class ParserRun {
|
||||
return null;
|
||||
}
|
||||
|
||||
let x1Node: ParseNode = null;
|
||||
let x2Node: ParseNode = null;
|
||||
let y1Node: ParseNode = null;
|
||||
let y2Node: ParseNode = null;
|
||||
let scaleNode: ParseNode = null;
|
||||
let commandsNode: ParseNode = null;
|
||||
let x1Node: ParseNode | null = null;
|
||||
let x2Node: ParseNode | null = null;
|
||||
let y1Node: ParseNode | null = null;
|
||||
let y2Node: ParseNode | null = null;
|
||||
let scaleNode: ParseNode | null = null;
|
||||
let commandsNode: ParseNode | null = null;
|
||||
|
||||
const firstNode = this.parse_decimal(current);
|
||||
|
||||
@@ -1851,6 +1847,10 @@ class ParserRun {
|
||||
}
|
||||
|
||||
x2Node = this.parse_decimal(current);
|
||||
if (x2Node === null) {
|
||||
parent.pop();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.read(current, ",") === null) {
|
||||
parent.pop();
|
||||
@@ -1858,6 +1858,10 @@ class ParserRun {
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
@@ -1868,7 +1872,7 @@ class ParserRun {
|
||||
commandsNode.value += next;
|
||||
}
|
||||
|
||||
current.value = new parts.VectorClip((scaleNode !== null) ? scaleNode.value : 1, <parts.drawing.Instruction[]>parse(commandsNode.value, "drawingInstructions"), tagName === "clip");
|
||||
current.value = new parts.VectorClip((scaleNode !== null) ? scaleNode.value : 1, parse(commandsNode.value, "drawingInstructions") as parts.drawing.Instruction[], tagName === "clip");
|
||||
}
|
||||
|
||||
if (this.read(current, ")") === null) {
|
||||
@@ -1891,19 +1895,18 @@ class ParserRun {
|
||||
function makeTagParserFunction(
|
||||
tagName: string,
|
||||
tagConstructor: { new (value: any): parts.Part },
|
||||
valueParser: (current: ParseNode) => ParseNode,
|
||||
valueParser: (current: ParseNode) => ParseNode | null,
|
||||
required: boolean
|
||||
): void {
|
||||
(<any>ParserRun.prototype)[`parse_tag_${ tagName }`] = function (parent: ParseNode): ParseNode {
|
||||
const self = <ParserRun>this;
|
||||
(ParserRun.prototype as any)[`parse_tag_${ tagName }`] = function (this: ParserRun, parent: ParseNode): ParseNode | null {
|
||||
const current = new ParseNode(parent);
|
||||
|
||||
if (self.read(current, tagName) === null) {
|
||||
if (this.read(current, tagName) === null) {
|
||||
parent.pop();
|
||||
return null;
|
||||
}
|
||||
|
||||
const valueNode = valueParser.call(self, current);
|
||||
const valueNode: ParseNode | null = valueParser.call(this, current);
|
||||
|
||||
if (valueNode !== null) {
|
||||
current.value = new tagConstructor(valueNode.value);
|
||||
@@ -1953,8 +1956,8 @@ makeTagParserFunction("4a", parts.ShadowAlpha, ParserRun.prototype.parse_alpha,
|
||||
makeTagParserFunction("4c", parts.ShadowColor, ParserRun.prototype.parse_color, false);
|
||||
|
||||
for (const key of Object.keys(ParserRun.prototype)) {
|
||||
if (key.indexOf("parse_") === 0 && typeof (<any>ParserRun.prototype)[key] === "function") {
|
||||
rules.set(key.substr("parse_".length), (<any>ParserRun.prototype)[key]);
|
||||
if (key.indexOf("parse_") === 0 && typeof (ParserRun.prototype as any)[key] === "function") {
|
||||
rules.set(key.substr("parse_".length), (ParserRun.prototype as any)[key]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1971,7 +1974,7 @@ class ParseNode {
|
||||
private _end: number;
|
||||
private _value: any;
|
||||
|
||||
constructor(private _parent: ParseNode, value: any = null) {
|
||||
constructor(private _parent: ParseNode | null, value: any = null) {
|
||||
if (_parent !== null) {
|
||||
_parent.children.push(this);
|
||||
}
|
||||
@@ -2003,7 +2006,7 @@ class ParseNode {
|
||||
/**
|
||||
* @type {ParseNode}
|
||||
*/
|
||||
get parent(): ParseNode {
|
||||
get parent(): ParseNode | null {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import { ASS } from "../types/ass";
|
||||
import { Style } from "../types/style";
|
||||
import { Dialogue } from "../types/dialogue";
|
||||
import { Attachment, AttachmentType } from "../types/attachment";
|
||||
import { Property, TypedTemplate } from "../types/misc";
|
||||
|
||||
import { Map } from "../utility/map";
|
||||
|
||||
@@ -55,7 +54,7 @@ export class StreamParser {
|
||||
|
||||
private _shouldSwallowBom: boolean = true;
|
||||
private _currentSection: Section = Section.ScriptInfo;
|
||||
private _currentAttachment: Attachment = null;
|
||||
private _currentAttachment: Attachment | null = null;
|
||||
|
||||
constructor(private _stream: Stream) {
|
||||
this._stream.nextLine().then(line => this._onNextLine(line), reason => {
|
||||
@@ -119,7 +118,7 @@ export class StreamParser {
|
||||
/**
|
||||
* @param {string} line
|
||||
*/
|
||||
private _onNextLine(line: string): void {
|
||||
private _onNextLine(line: string | null): void {
|
||||
if (line === null) {
|
||||
this.currentSection = Section.EOF;
|
||||
return;
|
||||
@@ -291,10 +290,10 @@ export class SrtStreamParser {
|
||||
|
||||
private _shouldSwallowBom: boolean = true;
|
||||
|
||||
private _currentDialogueNumber: string = null;
|
||||
private _currentDialogueStart: string = null;
|
||||
private _currentDialogueEnd: string = null;
|
||||
private _currentDialogueText: string = null;
|
||||
private _currentDialogueNumber: string | null = null;
|
||||
private _currentDialogueStart: string | null = null;
|
||||
private _currentDialogueEnd: string | null = null;
|
||||
private _currentDialogueText: string | null = null;
|
||||
|
||||
constructor(private _stream: Stream) {
|
||||
this._stream.nextLine().then(line => this._onNextLine(line), reason => {
|
||||
@@ -320,7 +319,7 @@ export class SrtStreamParser {
|
||||
/**
|
||||
* @param {string} line
|
||||
*/
|
||||
private _onNextLine(line: string): void {
|
||||
private _onNextLine(line: string | null): void {
|
||||
if (line === null) {
|
||||
if (this._currentDialogueNumber !== null && this._currentDialogueStart !== null && this._currentDialogueEnd !== null && this._currentDialogueText !== null) {
|
||||
this._ass.dialogues.push(new Dialogue(new Map([
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface ReadableStream {
|
||||
getReader(): ReadableStreamReader;
|
||||
}
|
||||
|
||||
interface ReadableStreamReader {
|
||||
export interface ReadableStreamReader {
|
||||
/**
|
||||
* @return {!Promise.<{ value?: Uint8Array, done: boolean }>}
|
||||
*/
|
||||
@@ -65,7 +65,7 @@ export interface Stream {
|
||||
/**
|
||||
* @return {!Promise.<?string>} A promise that will be resolved with the next line, or null if the stream is exhausted.
|
||||
*/
|
||||
nextLine(): Promise<string>;
|
||||
nextLine(): Promise<string | null>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,8 +81,8 @@ export class StringStream implements Stream {
|
||||
/**
|
||||
* @return {!Promise.<?string>} A promise that will be resolved with the next line, or null if the string has been completely read.
|
||||
*/
|
||||
nextLine(): Promise<string> {
|
||||
let result: Promise<string> = null;
|
||||
nextLine(): Promise<string | null> {
|
||||
let result: Promise<string | null>;
|
||||
|
||||
if (this._readTill < this._str.length) {
|
||||
const nextNewLinePos = this._str.indexOf("\n", this._readTill);
|
||||
@@ -96,7 +96,7 @@ export class StringStream implements Stream {
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = Promise.resolve<string>(null);
|
||||
result = Promise.resolve<string | null>(null);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -111,8 +111,8 @@ export class StringStream implements Stream {
|
||||
*/
|
||||
export class XhrStream implements Stream {
|
||||
private _readTill: number = 0;
|
||||
private _pendingDeferred: DeferredPromise<string> = null;
|
||||
private _failedError: ErrorEvent = null;
|
||||
private _pendingDeferred: DeferredPromise<string | null> | null = null;
|
||||
private _failedError: ErrorEvent | null = null;
|
||||
|
||||
constructor(private _xhr: XMLHttpRequest) {
|
||||
_xhr.addEventListener("progress", () => this._onXhrProgress(), false);
|
||||
@@ -181,7 +181,7 @@ export class XhrStream implements Stream {
|
||||
*/
|
||||
private _tryResolveNextLine(): void {
|
||||
if (this._failedError !== null) {
|
||||
this._pendingDeferred.reject(this._failedError);
|
||||
this._pendingDeferred!.reject(this._failedError);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -189,23 +189,23 @@ export class XhrStream implements Stream {
|
||||
|
||||
const nextNewLinePos = response.indexOf("\n", this._readTill);
|
||||
if (nextNewLinePos !== -1) {
|
||||
this._pendingDeferred.resolve(response.substring(this._readTill, nextNewLinePos));
|
||||
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);
|
||||
this._pendingDeferred!.reject(this._failedError);
|
||||
}
|
||||
|
||||
// No more data. This is the last line.
|
||||
else if (this._readTill < response.length) {
|
||||
this._pendingDeferred.resolve(response.substr(this._readTill));
|
||||
this._pendingDeferred!.resolve(response.substr(this._readTill));
|
||||
this._readTill = response.length;
|
||||
}
|
||||
else {
|
||||
this._pendingDeferred.resolve(null);
|
||||
this._pendingDeferred!.resolve(null);
|
||||
}
|
||||
|
||||
this._pendingDeferred = null;
|
||||
@@ -223,11 +223,11 @@ export class BrowserReadableStream implements Stream {
|
||||
private _reader: ReadableStreamReader;
|
||||
private _decoder: TextDecoder;
|
||||
private _buffer: string = "";
|
||||
private _pendingDeferred: DeferredPromise<string> = null;
|
||||
private _pendingDeferred: DeferredPromise<string | null> | null = null;
|
||||
|
||||
constructor(stream: ReadableStream, encoding: string) {
|
||||
this._reader = stream.getReader();
|
||||
this._decoder = new global.TextDecoder(encoding, { ignoreBOM: true });
|
||||
this._decoder = new global.TextDecoder!(encoding, { ignoreBOM: true });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,7 +250,7 @@ export class BrowserReadableStream implements Stream {
|
||||
private _tryResolveNextLine(): void {
|
||||
const nextNewLinePos = this._buffer.indexOf("\n");
|
||||
if (nextNewLinePos !== -1) {
|
||||
this._pendingDeferred.resolve(this._buffer.substr(0, nextNewLinePos));
|
||||
this._pendingDeferred!.resolve(this._buffer.substr(0, nextNewLinePos));
|
||||
this._buffer = this._buffer.substr(nextNewLinePos + 1);
|
||||
this._pendingDeferred = null;
|
||||
}
|
||||
@@ -266,10 +266,10 @@ export class BrowserReadableStream implements Stream {
|
||||
else {
|
||||
// No more data.
|
||||
if (this._buffer.length === 0) {
|
||||
this._pendingDeferred.resolve(null);
|
||||
this._pendingDeferred!.resolve(null);
|
||||
}
|
||||
else {
|
||||
this._pendingDeferred.resolve(this._buffer);
|
||||
this._pendingDeferred!.resolve(this._buffer);
|
||||
this._buffer = "";
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ enum DataType {
|
||||
Uint32,
|
||||
}
|
||||
|
||||
type StructMemberDefinition = { type: DataType; field?: string; };
|
||||
type StructMemberDefinition = { type: DataType; field: string; };
|
||||
|
||||
const fieldDecorators = new Map<DataType, (proto: any, field: string) => void>();
|
||||
|
||||
@@ -103,7 +103,7 @@ export function getTtfNames(attachment: Attachment): Set<string> {
|
||||
const reader = { dataView: new DataView(bytes.buffer), position: 0 };
|
||||
|
||||
const offsetTable = OffsetTable.read(reader);
|
||||
let nameTableRecord: TableRecord = null;
|
||||
let nameTableRecord: TableRecord | null = null;
|
||||
for (let i = 0; i < offsetTable.numTables; i++) {
|
||||
const tableRecord = TableRecord.read(reader);
|
||||
if (tableRecord.c1 + tableRecord.c2 + tableRecord.c3 + tableRecord.c4 === "name") {
|
||||
@@ -111,6 +111,9 @@ export function getTtfNames(attachment: Attachment): Set<string> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nameTableRecord === null) {
|
||||
throw new Error('Could not find "name" table record.');
|
||||
}
|
||||
|
||||
reader.position = nameTableRecord.offset;
|
||||
const nameTableHeader = NameTableHeader.read(reader);
|
||||
@@ -165,7 +168,7 @@ export function getTtfNames(attachment: Attachment): Set<string> {
|
||||
* @return {!function(new(): T)}
|
||||
*/
|
||||
function struct<T>(clazz: { new (): T; read(reader: DataReader): T; }): { new (): T; read(reader: DataReader): T; } {
|
||||
const fields: StructMemberDefinition[] = (<any>clazz).__fields;
|
||||
const fields: StructMemberDefinition[] = (clazz as any).__fields;
|
||||
|
||||
clazz.read = (reader: DataReader) => {
|
||||
const result: any = new clazz();
|
||||
@@ -205,7 +208,7 @@ function struct<T>(clazz: { new (): T; read(reader: DataReader): T; }): { new ()
|
||||
function field<T>(type: DataType): (proto: T, field: string) => void {
|
||||
let existingDecorator = fieldDecorators.get(type);
|
||||
if (existingDecorator === undefined) {
|
||||
existingDecorator =(proto: T, field: string) => {
|
||||
existingDecorator = (proto: T, field: string) => {
|
||||
const ctor: { __fields?: StructMemberDefinition[] } = proto.constructor;
|
||||
if (ctor.__fields === undefined) {
|
||||
ctor.__fields = [];
|
||||
|
||||
@@ -183,17 +183,17 @@ export class Italic {
|
||||
/**
|
||||
* A bold tag {\b}
|
||||
*
|
||||
* @param {*} value {\b1} -> true, {\b0} -> false, {\b###} -> weight of the bold (number), {\b} -> null
|
||||
* @param {?boolean|?number} value {\b1} -> true, {\b0} -> false, {\b###} -> weight of the bold (number), {\b} -> null
|
||||
*/
|
||||
export class Bold {
|
||||
constructor(private _value: Object) { }
|
||||
constructor(private _value: boolean | number | null) { }
|
||||
|
||||
/**
|
||||
* The value of this bold tag.
|
||||
*
|
||||
* @type {?boolean|?number}
|
||||
*/
|
||||
get value(): Object {
|
||||
get value(): boolean | number | null {
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
@@ -384,14 +384,14 @@ export class GaussianBlur {
|
||||
* @param {?string} value {\fn###} -> name (string), {\fn} -> null
|
||||
*/
|
||||
export class FontName {
|
||||
constructor(private _value: string) { }
|
||||
constructor(private _value: string | null) { }
|
||||
|
||||
/**
|
||||
* The value of this font name tag.
|
||||
*
|
||||
* @type {?string}
|
||||
*/
|
||||
get value(): string {
|
||||
get value(): string | null {
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
@@ -417,7 +417,7 @@ export class FontSize {
|
||||
/**
|
||||
* A font size increase tag {\fs+}
|
||||
*
|
||||
* @param {number} value {\fs+###} -> difference (number)
|
||||
* @param {number} value {\fs+###} -> relative difference (number, percentage)
|
||||
*/
|
||||
export class FontSizePlus {
|
||||
constructor(private _value: number) { }
|
||||
@@ -435,7 +435,7 @@ export class FontSizePlus {
|
||||
/**
|
||||
* A font size decrease tag {\fs-}
|
||||
*
|
||||
* @param {number} value {\fs-###} -> difference (number)
|
||||
* @param {number} value {\fs-###} -> relative difference (number, percentage)
|
||||
*/
|
||||
export class FontSizeMinus {
|
||||
constructor(private _value: number) { }
|
||||
@@ -456,14 +456,14 @@ export class FontSizeMinus {
|
||||
* @param {?number} value {\fscx###} -> scale (number), {\fscx} -> null
|
||||
*/
|
||||
export class FontScaleX {
|
||||
constructor(private _value: number) { }
|
||||
constructor(private _value: number | null) { }
|
||||
|
||||
/**
|
||||
* The value of this horizontal font scaling tag.
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
get value(): number {
|
||||
get value(): number | null {
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
@@ -474,14 +474,14 @@ export class FontScaleX {
|
||||
* @param {?number} value {\fscy###} -> scale (number), {\fscy} -> null
|
||||
*/
|
||||
export class FontScaleY {
|
||||
constructor(private _value: number) { }
|
||||
constructor(private _value: number | null) { }
|
||||
|
||||
/**
|
||||
* The value of this vertical font scaling tag.
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
get value(): number {
|
||||
get value(): number | null {
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
@@ -571,7 +571,7 @@ export class SkewX {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
get value() {
|
||||
get value(): number {
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
@@ -767,7 +767,7 @@ export class Alignment {
|
||||
/**
|
||||
* The value of this alignment tag.
|
||||
*
|
||||
* @type {?number}
|
||||
* @type {number}
|
||||
*/
|
||||
get value(): number {
|
||||
return this._value;
|
||||
@@ -852,14 +852,14 @@ export class WrappingStyle {
|
||||
* @param {?string} value {\r###} -> style name (string), {\r} -> null
|
||||
*/
|
||||
export class Reset {
|
||||
constructor(private _value: string) { }
|
||||
constructor(private _value: string | null) { }
|
||||
|
||||
/**
|
||||
* The value of this style reset tag.
|
||||
*
|
||||
* @type {?string}
|
||||
*/
|
||||
get value(): string {
|
||||
get value(): string | null {
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
@@ -899,11 +899,11 @@ export class Position {
|
||||
* @param {number} y1
|
||||
* @param {number} x2
|
||||
* @param {number} y2
|
||||
* @param {number} t1
|
||||
* @param {number} t2
|
||||
* @param {?number} t1
|
||||
* @param {?number} t2
|
||||
*/
|
||||
export class Move {
|
||||
constructor(private _x1: number, private _y1: number, private _x2: number, private _y2: number, private _t1: number, private _t2: number) { }
|
||||
constructor(private _x1: number, private _y1: number, private _x2: number, private _y2: number, private _t1: number | null, private _t2: number | null) { }
|
||||
|
||||
/**
|
||||
* The starting x value of this move tag.
|
||||
@@ -944,18 +944,18 @@ export class Move {
|
||||
/**
|
||||
* The start time of this move tag.
|
||||
*
|
||||
* @type {number}
|
||||
* @type {?number}
|
||||
*/
|
||||
get t1(): number {
|
||||
get t1(): number | null {
|
||||
return this._t1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The end time value of this move tag.
|
||||
*
|
||||
* @type {number}
|
||||
* @type {?number}
|
||||
*/
|
||||
get t2(): number {
|
||||
get t2(): number | null {
|
||||
return this._t2;
|
||||
}
|
||||
}
|
||||
@@ -1100,20 +1100,20 @@ export class ComplexFade {
|
||||
/**
|
||||
* A transform tag {\t}
|
||||
*
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
* @param {number} accel
|
||||
* @param {?number} start
|
||||
* @param {?number} end
|
||||
* @param {?number} accel
|
||||
* @param {!Array.<!libjass.parts.Tag>} tags
|
||||
*/
|
||||
export class Transform {
|
||||
constructor(private _start: number, private _end: number, private _accel: number, private _tags: Part[]) { }
|
||||
constructor(private _start: number | null, private _end: number | null, private _accel: number | null, private _tags: Part[]) { }
|
||||
|
||||
/**
|
||||
* The starting time of this transform tag.
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
get start(): number {
|
||||
get start(): number | null {
|
||||
return this._start;
|
||||
}
|
||||
|
||||
@@ -1122,7 +1122,7 @@ export class Transform {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
get end(): number {
|
||||
get end(): number | null {
|
||||
return this._end;
|
||||
}
|
||||
|
||||
@@ -1131,7 +1131,7 @@ export class Transform {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
get accel(): number {
|
||||
get accel(): number | null {
|
||||
return this._accel;
|
||||
}
|
||||
|
||||
@@ -1299,18 +1299,18 @@ const addToString = function (ctor: Function, ctorName: string) {
|
||||
if (!ctor.prototype.hasOwnProperty("toString")) {
|
||||
const propertyNames = Object.getOwnPropertyNames(ctor.prototype).filter(property => property !== "constructor");
|
||||
|
||||
ctor.prototype.toString = function () {
|
||||
ctor.prototype.toString = function (this: any) {
|
||||
return (
|
||||
ctorName + " { " +
|
||||
propertyNames.map(name => `${ name }: ${ (<any>this)[name] }`).join(", ") +
|
||||
propertyNames.map(name => `${ name }: ${ this[name] }`).join(", ") +
|
||||
((propertyNames.length > 0) ? " " : "") +
|
||||
"}"
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
import { registerClassPrototype } from "../webworker/misc";
|
||||
import { registerClass } from "../serialization";
|
||||
|
||||
declare const exports: any;
|
||||
|
||||
@@ -1318,14 +1318,14 @@ for (const key of Object.keys(exports)) {
|
||||
const value: any = exports[key];
|
||||
if (value instanceof Function) {
|
||||
addToString(value, key);
|
||||
registerClassPrototype(value.prototype);
|
||||
registerClass(value);
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of Object.keys(drawing)) {
|
||||
const value: any = (<any>drawing)[key];
|
||||
const value: any = (drawing as any)[key];
|
||||
if (value instanceof Function) {
|
||||
addToString(value, `Drawing${ key }`);
|
||||
registerClassPrototype(value.prototype);
|
||||
registerClass(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,10 +41,10 @@ import { ManualClock } from "./manual";
|
||||
export class AutoClock implements Clock {
|
||||
private _manualClock: ManualClock = new ManualClock();
|
||||
|
||||
private _nextAnimationFrameRequestId: number = null;
|
||||
private _nextAnimationFrameRequestId: number | null = null;
|
||||
|
||||
private _lastKnownExternalTime: number = null;
|
||||
private _lastKnownExternalTimeObtainedAt: number = null;
|
||||
private _lastKnownExternalTime: number | null = null;
|
||||
private _lastKnownExternalTimeObtainedAt: number | null = null;
|
||||
|
||||
constructor(private _getCurrentTime: () => number, private _autoPauseAfter: number) { }
|
||||
|
||||
@@ -214,7 +214,7 @@ export class AutoClock implements Clock {
|
||||
if (this._lastKnownExternalTime !== null && currentExternalTime === this._lastKnownExternalTime) {
|
||||
if (timeStamp - this._lastKnownExternalTimeObtainedAt > this._autoPauseAfter) {
|
||||
this._lastKnownExternalTimeObtainedAt = null;
|
||||
this._manualClock.pause();
|
||||
this._manualClock.seek(currentExternalTime);
|
||||
}
|
||||
else {
|
||||
this._manualClock.tick((timeStamp - this._lastKnownExternalTimeObtainedAt) / 1000 * this._manualClock.rate + this._lastKnownExternalTime);
|
||||
|
||||
@@ -26,5 +26,4 @@ export { VideoClock } from "./clocks/video";
|
||||
export { DefaultRenderer } from "./default";
|
||||
export { NullRenderer } from "./null";
|
||||
export { WebRenderer } from "./web/renderer";
|
||||
|
||||
export { RendererSettings, makeFontMapFromStyleElement } from "./settings";
|
||||
export { RendererSettings } from "./settings";
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
|
||||
import { Clock, ClockEvent } from "./clocks/base";
|
||||
|
||||
import { RendererSettings, toRendererSettings } from "./settings";
|
||||
import { RendererSettings } from "./settings";
|
||||
|
||||
import { verboseMode } from "../settings";
|
||||
import { debugMode, verboseMode } from "../settings";
|
||||
|
||||
import { ASS } from "../types/ass";
|
||||
import { Dialogue } from "../types/dialogue";
|
||||
@@ -44,7 +44,7 @@ export class NullRenderer {
|
||||
constructor(private _ass: ASS, private _clock: Clock, settings?: RendererSettings) {
|
||||
this._id = ++NullRenderer._lastRendererId;
|
||||
|
||||
this._settings = toRendererSettings(settings);
|
||||
this._settings = RendererSettings.from(settings);
|
||||
|
||||
this._clock.addEventListener(ClockEvent.Play, () => this._onClockPlay());
|
||||
this._clock.addEventListener(ClockEvent.Tick, () => this._onClockTick());
|
||||
@@ -159,14 +159,21 @@ export class NullRenderer {
|
||||
}
|
||||
|
||||
for (const dialogue of this._ass.dialogues) {
|
||||
if (dialogue.end > currentTime) {
|
||||
if (dialogue.start <= currentTime) {
|
||||
// This dialogue is visible right now. Draw it.
|
||||
this.draw(dialogue);
|
||||
try {
|
||||
if (dialogue.end > currentTime) {
|
||||
if (dialogue.start <= currentTime) {
|
||||
// This dialogue is visible right now. Draw it.
|
||||
this.draw(dialogue);
|
||||
}
|
||||
else if (dialogue.start <= (currentTime + this._settings.preRenderTime)) {
|
||||
// This dialogue will be visible soon. Pre-render it.
|
||||
this.preRender(dialogue);
|
||||
}
|
||||
}
|
||||
else if (dialogue.start <= (currentTime + this._settings.preRenderTime)) {
|
||||
// This dialogue will be visible soon. Pre-render it.
|
||||
this.preRender(dialogue);
|
||||
}
|
||||
catch (ex) {
|
||||
if (debugMode) {
|
||||
console.error(`Rendering dialogue ${ dialogue.id } failed.`, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,15 +18,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
define(["intern/dojo/node!firefox-profile"], function (FirefoxProfile) {
|
||||
return {
|
||||
load: function (id, require, callback) {
|
||||
var firefoxProfile = new FirefoxProfile();
|
||||
|
||||
firefoxProfile.setPreference("browser.displayedE10SPrompt.1", 5);
|
||||
firefoxProfile.setPreference("browser.tabs.remote.autostart.2", false);
|
||||
|
||||
firefoxProfile.encoded(callback);
|
||||
}
|
||||
};
|
||||
});
|
||||
interface Array<T> {
|
||||
/**
|
||||
* Returns the elements of an array that meet the condition specified in a callback function.
|
||||
* @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array.
|
||||
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
|
||||
*/
|
||||
filter<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];
|
||||
}
|
||||
@@ -18,12 +18,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { debugMode } from "../settings";
|
||||
import { Map } from "../utility/map";
|
||||
|
||||
/**
|
||||
* Settings for the renderer.
|
||||
*/
|
||||
export interface RendererSettings {
|
||||
export class RendererSettings {
|
||||
/**
|
||||
* A map of font name to one or more URLs of that font. If provided, the fonts in this map are pre-loaded by the WebRenderer when it's created.
|
||||
*
|
||||
@@ -37,14 +38,14 @@ export interface RendererSettings {
|
||||
*
|
||||
* Only the first and second forms allow you to use local fonts. The third form only allows you to use remote fonts.
|
||||
*
|
||||
* If you have a <style> or <link> element on the page containing @font-face rules, you can use the {@link libjass.renderers.makeFontMapFromStyleElement}
|
||||
* If you have a <style> or <link> element on the page containing @font-face rules, you can use the {@link libjass.renderers.RendererSettings.makeFontMapFromStyleElement}
|
||||
* convenience method to create a font map.
|
||||
*
|
||||
* Defaults to null.
|
||||
*
|
||||
* @type {!Map.<string, (string|!Array.<string>)>}
|
||||
* @type {Map.<string, (string|!Array.<string>)>}
|
||||
*/
|
||||
fontMap: Map<string, string | string[]>;
|
||||
fontMap: Map<string, string | string[]> | null;
|
||||
|
||||
/**
|
||||
* Subtitles will be pre-rendered for this amount of time (seconds).
|
||||
@@ -96,74 +97,77 @@ export interface RendererSettings {
|
||||
* @type {boolean}
|
||||
*/
|
||||
useAttachedFonts: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an arbitrary object into a {@link libjass.renderers.RendererSettings} object.
|
||||
*
|
||||
* @param {*} object
|
||||
* @return {!libjass.renderers.RendererSettings}
|
||||
*/
|
||||
export function toRendererSettings(object?: any): RendererSettings {
|
||||
if (object === undefined || object === null) {
|
||||
object = {};
|
||||
}
|
||||
/**
|
||||
* A convenience method to create a font map from a <style> or <link> element that contains @font-face rules. There should be one @font-face rule for each font name, mapping to a font file URL.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* @font-face {
|
||||
* font-family: "Helvetica";
|
||||
* src: url("/fonts/helvetica.ttf"), local("Arial");
|
||||
* }
|
||||
*
|
||||
* More complicated @font-face syntax like format() or multi-line src are not supported.
|
||||
*
|
||||
* @param {!LinkStyle} linkStyle
|
||||
* @return {!Map.<string, string>}
|
||||
*/
|
||||
static makeFontMapFromStyleElement(linkStyle: LinkStyle): Map<string, string> {
|
||||
const fontMap = new Map<string, string>();
|
||||
|
||||
const {
|
||||
fontMap = null,
|
||||
preRenderTime = 5,
|
||||
preciseOutlines = false,
|
||||
enableSvg = true,
|
||||
fallbackFonts = 'Arial, Helvetica, sans-serif, "Segoe UI Symbol"',
|
||||
useAttachedFonts = false
|
||||
} = <RendererSettings>object;
|
||||
const styleSheet = linkStyle.sheet as CSSStyleSheet;
|
||||
for (let i = 0; i < styleSheet.cssRules.length; i++) {
|
||||
const rule = styleSheet.cssRules[i];
|
||||
|
||||
return {
|
||||
fontMap,
|
||||
preRenderTime,
|
||||
preciseOutlines,
|
||||
enableSvg,
|
||||
fallbackFonts,
|
||||
useAttachedFonts,
|
||||
};
|
||||
}
|
||||
if (isFontFaceRule(rule)) {
|
||||
const name = rule.style.getPropertyValue("font-family").match(/^["']?(.*?)["']?$/)![1];
|
||||
|
||||
/**
|
||||
* A convenience method to create a font map from a <style> or <link> element that contains @font-face rules. There should be one @font-face rule for each font name, mapping to a font file URL.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* @font-face {
|
||||
* font-family: "Helvetica";
|
||||
* src: url("/fonts/helvetica.ttf"), local("Arial");
|
||||
* }
|
||||
*
|
||||
* @param {!LinkStyle} linkStyle
|
||||
* @return {!Map.<string, string>}
|
||||
*/
|
||||
export function makeFontMapFromStyleElement(linkStyle: LinkStyle): Map<string, string> {
|
||||
const fontMap = new Map<string, string>();
|
||||
let src = rule.style.getPropertyValue("src");
|
||||
if (!src) {
|
||||
src = rule.cssText.split("\n")
|
||||
.map(line => line.match(/src:\s*([^;]+?)\s*;/))
|
||||
.filter((matches): matches is RegExpMatchArray => matches !== null)
|
||||
.map(matches => matches[1])[0];
|
||||
}
|
||||
|
||||
const styleSheet = <CSSStyleSheet>linkStyle.sheet;
|
||||
for (let i = 0; i < styleSheet.cssRules.length; i++) {
|
||||
const rule = styleSheet.cssRules[i];
|
||||
|
||||
if (isFontFaceRule(rule)) {
|
||||
const name = rule.style.getPropertyValue("font-family").match(/^["']?(.*?)["']?$/)[1];
|
||||
|
||||
let src = rule.style.getPropertyValue("src");
|
||||
if (!src) {
|
||||
src = rule.cssText.split("\n")
|
||||
.map(line => line.match(/src:\s*([^;]+?)\s*;/))
|
||||
.filter(matches => matches !== null)
|
||||
.map(matches => matches[1])[0];
|
||||
fontMap.set(name, src);
|
||||
}
|
||||
|
||||
fontMap.set(name, src);
|
||||
}
|
||||
|
||||
return fontMap;
|
||||
}
|
||||
|
||||
return fontMap;
|
||||
/**
|
||||
* Converts an arbitrary object into a {@link libjass.renderers.RendererSettings} object.
|
||||
*
|
||||
* @param {*} object
|
||||
* @return {!libjass.renderers.RendererSettings}
|
||||
*/
|
||||
static from(object?: any): RendererSettings {
|
||||
if (object === undefined || object === null) {
|
||||
object = {};
|
||||
}
|
||||
|
||||
const {
|
||||
fontMap = null,
|
||||
preRenderTime = 5,
|
||||
preciseOutlines = false,
|
||||
enableSvg = testSupportsSvg(),
|
||||
fallbackFonts = 'Arial, Helvetica, sans-serif, "Segoe UI Symbol"',
|
||||
useAttachedFonts = false,
|
||||
} = object as RendererSettings;
|
||||
|
||||
const result = new RendererSettings();
|
||||
result.fontMap = fontMap;
|
||||
result.preRenderTime = preRenderTime;
|
||||
result.preciseOutlines = preciseOutlines;
|
||||
result.enableSvg = enableSvg;
|
||||
result.fallbackFonts = fallbackFonts;
|
||||
result.useAttachedFonts = useAttachedFonts;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,3 +177,63 @@ export function makeFontMapFromStyleElement(linkStyle: LinkStyle): Map<string, s
|
||||
function isFontFaceRule(rule: CSSRule): rule is CSSFontFaceRule {
|
||||
return rule.type === CSSRule.FONT_FACE_RULE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this environment may support SVG filter effects. May return false positives.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
function testSupportsSvg(): boolean {
|
||||
if (debugMode) {
|
||||
console.log("Testing whether SVG filter effects are supported.");
|
||||
}
|
||||
|
||||
if (typeof document === "undefined") {
|
||||
if (debugMode) {
|
||||
console.log("This doesn't look like a browser. Assuming it doesn't support SVG filter effects.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const morphologyFilter = document.createElementNS("http://www.w3.org/2000/svg", "feMorphology");
|
||||
|
||||
// https://connect.microsoft.com/IE/feedback/details/2375800
|
||||
try {
|
||||
morphologyFilter.radiusX.baseVal = 1;
|
||||
}
|
||||
catch (ex) {
|
||||
if (debugMode) {
|
||||
if (ex instanceof DOMException) {
|
||||
const domException = ex as DOMException;
|
||||
if (domException.code === DOMException.NO_MODIFICATION_ALLOWED_ERR) {
|
||||
console.log("Setting SVGFEMorphologyElement.radiusX.baseVal threw NoModificationAllowedError. This browser doesn't support SVG DOM correctly.");
|
||||
}
|
||||
else {
|
||||
console.log(`Setting SVGFEMorphologyElement.radiusX.baseVal threw unexpected DOMException code ${ domException.code }. This browser doesn't support SVG DOM correctly.`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log(`Setting SVGFEMorphologyElement.radiusX.baseVal threw unexpected exception ${ ex }. This browser doesn't support SVG SVG DOM correctly.`);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://connect.microsoft.com/IE/feedback/details/2375757
|
||||
morphologyFilter.setAttribute("radius", "1");
|
||||
if (morphologyFilter.cloneNode().getAttribute("radius") !== "1") {
|
||||
if (debugMode) {
|
||||
console.log("SVGFEMorphologyElement's radius attribute was corrupted when cloned. This browser doesn't support SVG DOM correctly.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (debugMode) {
|
||||
console.log("This browser may support SVG filter effects.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ export class AnimationCollection {
|
||||
* @param {!Array.<!libjass.renderers.Keyframe>} keyframes
|
||||
*/
|
||||
add(timingFunction: string, keyframes: Keyframe[]): void {
|
||||
let start: number = null;
|
||||
let end: number = null;
|
||||
let start: number | null = null;
|
||||
let end: number | null = null;
|
||||
|
||||
for (const keyframe of keyframes) {
|
||||
if (start === null) {
|
||||
@@ -82,6 +82,10 @@ export class AnimationCollection {
|
||||
end = keyframe.time;
|
||||
}
|
||||
|
||||
if (start === null || end === null) {
|
||||
throw new Error("Atleast one keyframe must be provided.");
|
||||
}
|
||||
|
||||
let ruleCssText = "";
|
||||
|
||||
for (const keyframe of keyframes) {
|
||||
|
||||
@@ -54,43 +54,76 @@ export class DrawingStyles {
|
||||
* @return {!SVGSVGElement}
|
||||
*/
|
||||
toSVG(drawingInstructions: parts.DrawingInstructions, fillColor: parts.Color): SVGSVGElement {
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svg.setAttribute("version", "1.1");
|
||||
|
||||
if (drawingInstructions.instructions.length === 0) {
|
||||
return svg;
|
||||
}
|
||||
|
||||
const scaleFactor = Math.pow(2, this._scale - 1);
|
||||
const scaleX = this._outputScaleX / scaleFactor;
|
||||
const scaleY = this._outputScaleY / scaleFactor;
|
||||
|
||||
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
let pathString = "";
|
||||
|
||||
let bboxWidth = 0;
|
||||
let bboxHeight = 0;
|
||||
let bboxMinX = Infinity;
|
||||
let bboxMaxX = -Infinity;
|
||||
let bboxMinY = Infinity;
|
||||
let bboxMaxY = -Infinity;
|
||||
|
||||
for (const instruction of drawingInstructions.instructions) {
|
||||
if (instruction instanceof parts.drawing.MoveInstruction) {
|
||||
path.pathSegList.appendItem(path.createSVGPathSegMovetoAbs(instruction.x, instruction.y + this._baselineOffset));
|
||||
bboxWidth = Math.max(bboxWidth, instruction.x);
|
||||
bboxHeight = Math.max(bboxHeight, instruction.y + this._baselineOffset);
|
||||
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) {
|
||||
path.pathSegList.appendItem(path.createSVGPathSegLinetoAbs(instruction.x, instruction.y + this._baselineOffset));
|
||||
bboxWidth = Math.max(bboxWidth, instruction.x);
|
||||
bboxHeight = Math.max(bboxHeight, instruction.y + this._baselineOffset);
|
||||
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) {
|
||||
path.pathSegList.appendItem(path.createSVGPathSegCurvetoCubicAbs(instruction.x3, instruction.y3 + this._baselineOffset, instruction.x1, instruction.y1 + this._baselineOffset, instruction.x2, instruction.y2 + this._baselineOffset));
|
||||
bboxWidth = Math.max(bboxWidth, instruction.x1, instruction.x2, instruction.x3);
|
||||
bboxHeight = Math.max(bboxHeight, instruction.y1 + this._baselineOffset, instruction.y2 + this._baselineOffset, instruction.y3 + this._baselineOffset);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svg.setAttribute("version", "1.1");
|
||||
svg.width.baseVal.valueAsString = `${ (bboxWidth * scaleX).toFixed(3) }px`;
|
||||
svg.height.baseVal.valueAsString = `${ (bboxHeight * scaleY).toFixed(3) }px`;
|
||||
bboxMinX *= scaleX;
|
||||
bboxMaxX *= scaleX;
|
||||
bboxMinY *= scaleY;
|
||||
bboxMaxY *= scaleY;
|
||||
|
||||
const bboxWidth = bboxMaxX - bboxMinX;
|
||||
const 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`;
|
||||
|
||||
const 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;
|
||||
|
||||
@@ -111,8 +111,8 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
super(ass, clock, (() => {
|
||||
if (!(_libjassSubsWrapper instanceof HTMLDivElement)) {
|
||||
const temp = settings;
|
||||
settings = <any>_libjassSubsWrapper;
|
||||
_libjassSubsWrapper = <any>temp;
|
||||
settings = _libjassSubsWrapper as any;
|
||||
_libjassSubsWrapper = temp as any;
|
||||
console.warn("WebRenderer's constructor now takes libjassSubsWrapper as the third parameter and settings as the fourth parameter. Please update the caller.");
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
return;
|
||||
}
|
||||
|
||||
let ttfNames: Set<string> = null;
|
||||
let ttfNames: Set<string>;
|
||||
try {
|
||||
ttfNames = getTtfNames(attachment);
|
||||
}
|
||||
@@ -215,7 +215,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
}
|
||||
});
|
||||
|
||||
let fontFetchPromise: Promise<FontFace>;
|
||||
let fontFetchPromise: Promise<FontFace | null>;
|
||||
if (existingFontFaces.length === 0) {
|
||||
const fontFace = new FontFace(fontFamily, source);
|
||||
const quotedFontFace = new FontFace(`"${ fontFamily }"`, source);
|
||||
@@ -223,7 +223,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
global.document.fonts.add(fontFace);
|
||||
global.document.fonts.add(quotedFontFace);
|
||||
|
||||
fontFetchPromise = Promise_any([fontFace.load(), quotedFontFace.load()])
|
||||
fontFetchPromise = Promise_any([fontFace.load(), quotedFontFace.load()]);
|
||||
}
|
||||
else {
|
||||
fontFetchPromise = Promise_any(existingFontFaces.map(fontFace => fontFace.load()));
|
||||
@@ -254,7 +254,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
|
||||
// A url() URL. Extract the raw URL.
|
||||
return match[2];
|
||||
}).filter(url => url !== null);
|
||||
}).filter((url): url is string => url !== null);
|
||||
|
||||
const attachedFontUrls = attachedFontsMap.get(fontFamily);
|
||||
if (attachedFontUrls !== undefined) {
|
||||
@@ -266,7 +266,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
let fontFetchPromise = fontFetchPromisesCache.get(url);
|
||||
if (fontFetchPromise === undefined) {
|
||||
fontFetchPromise =
|
||||
new Promise<void>((resolve, reject) => {
|
||||
new Promise<null>((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.addEventListener("load", () => {
|
||||
if (debugMode) {
|
||||
@@ -290,7 +290,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
|
||||
const allFontsFetchedPromise =
|
||||
(thisFontFamilysFetchPromises.length === 0) ?
|
||||
Promise.resolve<void>(null) :
|
||||
Promise.resolve(null) :
|
||||
Promise_first(thisFontFamilysFetchPromises).catch(reason => {
|
||||
console.warn(`Fetching fonts for ${ fontFamily } at ${ urls.join(", ") } failed: %o`, reason);
|
||||
return null;
|
||||
@@ -354,12 +354,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
* @param {!libjass.Dialogue} dialogue
|
||||
* @return {PreRenderedSub}
|
||||
*/
|
||||
preRender(dialogue: Dialogue): PreRenderedSub {
|
||||
const alreadyPreRenderedSub = this._preRenderedSubs.get(dialogue.id);
|
||||
if (alreadyPreRenderedSub) {
|
||||
return alreadyPreRenderedSub;
|
||||
}
|
||||
|
||||
preRender(dialogue: Dialogue): PreRenderedSub | null {
|
||||
const currentTimeRelativeToDialogueStart = this.clock.currentTime - dialogue.start;
|
||||
|
||||
if (dialogue.containsTransformTag && currentTimeRelativeToDialogueStart < 0) {
|
||||
@@ -367,6 +362,11 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const alreadyPreRenderedSub = this._preRenderedSubs.get(dialogue.id);
|
||||
if (alreadyPreRenderedSub) {
|
||||
return alreadyPreRenderedSub;
|
||||
}
|
||||
|
||||
const sub = document.createElement("div");
|
||||
|
||||
sub.style.marginLeft = `${ (this._scaleX * dialogue.style.marginLeft).toFixed(3) }px`;
|
||||
@@ -389,15 +389,15 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
const svgDefsElement = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
||||
svgElement.appendChild(svgDefsElement);
|
||||
|
||||
let currentSpan: HTMLSpanElement = null;
|
||||
let currentSpan: HTMLSpanElement | null = null;
|
||||
const currentSpanStyles = new SpanStyles(this, dialogue, this._scaleX, this._scaleY, this.settings, this._fontSizeElement, svgDefsElement, this._fontMetricsCache);
|
||||
|
||||
let currentAnimationCollection: AnimationCollection = null;
|
||||
let currentAnimationCollection: AnimationCollection | null = null;
|
||||
|
||||
let previousAddNewLine = false; // If two or more \N's are encountered in sequence, then all but the first will be created using currentSpanStyles.makeNewLine() instead
|
||||
const startNewSpan = (addNewLine: boolean): void => {
|
||||
if (currentSpan !== null && currentSpan.hasChildNodes()) {
|
||||
sub.appendChild(currentSpanStyles.setStylesOnSpan(currentSpan, currentAnimationCollection));
|
||||
sub.appendChild(currentSpanStyles.setStylesOnSpan(currentSpan, currentAnimationCollection!));
|
||||
}
|
||||
|
||||
if (currentAnimationCollection !== null) {
|
||||
@@ -486,11 +486,11 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
}
|
||||
|
||||
else if (part instanceof parts.FontSizePlus) {
|
||||
currentSpanStyles.fontSize += part.value;
|
||||
currentSpanStyles.fontSize *= (1 + part.value);
|
||||
}
|
||||
|
||||
else if (part instanceof parts.FontSizeMinus) {
|
||||
currentSpanStyles.fontSize -= part.value;
|
||||
currentSpanStyles.fontSize *= (1 - part.value);
|
||||
}
|
||||
|
||||
else if (part instanceof parts.FontScaleX) {
|
||||
@@ -571,11 +571,11 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
else if (part instanceof parts.ColorKaraoke) {
|
||||
startNewSpan(false);
|
||||
|
||||
currentAnimationCollection.add("step-end", [
|
||||
currentAnimationCollection!.add("step-end", [
|
||||
new Keyframe(0, new Map([
|
||||
["color", currentSpanStyles.secondaryColor.withAlpha(currentSpanStyles.secondaryAlpha).toString()],
|
||||
["color", currentSpanStyles.secondaryColor!.withAlpha(currentSpanStyles.secondaryAlpha!).toString()],
|
||||
])), new Keyframe(karaokeTimesAccumulator, new Map([
|
||||
["color", currentSpanStyles.primaryColor.withAlpha(currentSpanStyles.primaryAlpha).toString()],
|
||||
["color", currentSpanStyles.primaryColor!.withAlpha(currentSpanStyles.primaryAlpha!).toString()],
|
||||
]))
|
||||
]);
|
||||
|
||||
@@ -587,7 +587,13 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
}
|
||||
|
||||
else if (part instanceof parts.Reset) {
|
||||
currentSpanStyles.reset(this.ass.styles.get(part.value));
|
||||
const newStyleName = part.value;
|
||||
if (newStyleName === null) {
|
||||
currentSpanStyles.reset(null);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.reset(this.ass.styles.get(newStyleName));
|
||||
}
|
||||
}
|
||||
|
||||
else if (part instanceof parts.Position) {
|
||||
@@ -601,10 +607,10 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
dialogueAnimationCollection.add("linear", [new Keyframe(0, new Map([
|
||||
["left", `${ (this._scaleX * part.x1).toFixed(3) }px`],
|
||||
["top", `${ (this._scaleY * part.y1).toFixed(3) }px`],
|
||||
])), new Keyframe(part.t1, new Map([
|
||||
])), new Keyframe(part.t1!, new Map([
|
||||
["left", `${ (this._scaleX * part.x1).toFixed(3) }px`],
|
||||
["top", `${ (this._scaleY * part.y1).toFixed(3) }px`],
|
||||
])), new Keyframe(part.t2, new Map([
|
||||
])), new Keyframe(part.t2!, new Map([
|
||||
["left", `${ (this._scaleX * part.x2).toFixed(3) }px`],
|
||||
["top", `${ (this._scaleY * part.y2).toFixed(3) }px`],
|
||||
])), new Keyframe(dialogue.end - dialogue.start, new Map([
|
||||
@@ -645,124 +651,259 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
const progression =
|
||||
(currentTimeRelativeToDialogueStart <= part.start) ? 0 :
|
||||
(currentTimeRelativeToDialogueStart >= part.end) ? 1 :
|
||||
Math.pow((currentTimeRelativeToDialogueStart - part.start) / (part.end - part.start), part.accel);
|
||||
Math.pow((currentTimeRelativeToDialogueStart - part.start) / (part.end - part.start), part.accel!);
|
||||
|
||||
for (const tag of part.tags) {
|
||||
if (tag instanceof parts.Border) {
|
||||
currentSpanStyles.outlineWidth += progression * (tag.value - currentSpanStyles.outlineWidth);
|
||||
currentSpanStyles.outlineHeight += progression * (tag.value - currentSpanStyles.outlineHeight);
|
||||
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) {
|
||||
currentSpanStyles.outlineWidth += progression * (tag.value - currentSpanStyles.outlineWidth);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.outlineWidth += progression * (tag.value - currentSpanStyles.outlineWidth);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.outlineWidth = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.BorderY) {
|
||||
currentSpanStyles.outlineHeight += progression * (tag.value - currentSpanStyles.outlineHeight);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.outlineHeight += progression * (tag.value - currentSpanStyles.outlineHeight);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.outlineHeight = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.Shadow) {
|
||||
currentSpanStyles.shadowDepthX += progression * (tag.value - currentSpanStyles.shadowDepthX);
|
||||
currentSpanStyles.shadowDepthY += progression * (tag.value - currentSpanStyles.shadowDepthY);
|
||||
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) {
|
||||
currentSpanStyles.shadowDepthX += progression * (tag.value - currentSpanStyles.shadowDepthX);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.shadowDepthX += progression * (tag.value - currentSpanStyles.shadowDepthX);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.shadowDepthX = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.ShadowY) {
|
||||
currentSpanStyles.shadowDepthY += progression * (tag.value - currentSpanStyles.shadowDepthY);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.shadowDepthY += progression * (tag.value - currentSpanStyles.shadowDepthY);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.shadowDepthY = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.Blur) {
|
||||
currentSpanStyles.blur += progression * (tag.value - currentSpanStyles.blur);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.blur += progression * (tag.value - currentSpanStyles.blur);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.blur = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.GaussianBlur) {
|
||||
currentSpanStyles.gaussianBlur += progression * (tag.value - currentSpanStyles.gaussianBlur);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.gaussianBlur += progression * (tag.value - currentSpanStyles.gaussianBlur);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.gaussianBlur = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.FontSize) {
|
||||
currentSpanStyles.fontSize += progression * (tag.value - currentSpanStyles.fontSize);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.fontSize += progression * (tag.value - currentSpanStyles.fontSize);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.fontSize = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.FontSizePlus) {
|
||||
currentSpanStyles.fontSize += progression * tag.value;
|
||||
currentSpanStyles.fontSize *= (1 + progression * tag.value);
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.FontSizeMinus) {
|
||||
currentSpanStyles.fontSize -= progression * tag.value;
|
||||
currentSpanStyles.fontSize *= (1 - progression * tag.value);
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.FontScaleX) {
|
||||
currentSpanStyles.fontScaleX += progression * (tag.value - currentSpanStyles.fontScaleX);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.fontScaleX += progression * (tag.value - currentSpanStyles.fontScaleX);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.fontScaleX = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.FontScaleY) {
|
||||
currentSpanStyles.fontScaleY += progression * (tag.value - currentSpanStyles.fontScaleY);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.fontScaleY += progression * (tag.value - currentSpanStyles.fontScaleY);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.fontScaleY = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.LetterSpacing) {
|
||||
currentSpanStyles.letterSpacing += progression * (tag.value - currentSpanStyles.letterSpacing);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.letterSpacing += progression * (tag.value - currentSpanStyles.letterSpacing);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.letterSpacing = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.RotateX) {
|
||||
currentSpanStyles.rotationX += progression * (tag.value - currentSpanStyles.rotationX);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.rotationX += progression * (tag.value - currentSpanStyles.rotationX);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.rotationX = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.RotateY) {
|
||||
currentSpanStyles.rotationY += progression * (tag.value - currentSpanStyles.rotationY);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.rotationY += progression * (tag.value - currentSpanStyles.rotationY);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.rotationY = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.RotateZ) {
|
||||
currentSpanStyles.rotationZ += progression * (tag.value - currentSpanStyles.rotationZ);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.rotationZ += progression * (tag.value - currentSpanStyles.rotationZ);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.rotationZ = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.SkewX) {
|
||||
currentSpanStyles.skewX += progression * (tag.value - currentSpanStyles.skewX);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.skewX += progression * (tag.value - currentSpanStyles.skewX);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.skewX = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.SkewY) {
|
||||
currentSpanStyles.skewY += progression * (tag.value - currentSpanStyles.skewY);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.skewY += progression * (tag.value - currentSpanStyles.skewY);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.skewY = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.PrimaryColor) {
|
||||
currentSpanStyles.primaryColor = currentSpanStyles.primaryColor.interpolate(tag.value, progression);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.primaryColor = currentSpanStyles.primaryColor!.interpolate(tag.value, progression);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.primaryColor = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.SecondaryColor) {
|
||||
currentSpanStyles.secondaryColor = currentSpanStyles.secondaryColor.interpolate(tag.value, progression);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.secondaryColor = currentSpanStyles.secondaryColor!.interpolate(tag.value, progression);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.secondaryColor = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.OutlineColor) {
|
||||
currentSpanStyles.outlineColor = currentSpanStyles.outlineColor.interpolate(tag.value, progression);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.outlineColor = currentSpanStyles.outlineColor!.interpolate(tag.value, progression);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.outlineColor = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.ShadowColor) {
|
||||
currentSpanStyles.shadowColor = currentSpanStyles.shadowColor.interpolate(tag.value, progression);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.shadowColor = currentSpanStyles.shadowColor!.interpolate(tag.value, progression);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.shadowColor = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.Alpha) {
|
||||
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);
|
||||
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) {
|
||||
currentSpanStyles.primaryAlpha += progression * (tag.value - currentSpanStyles.primaryAlpha);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.primaryAlpha += progression * (tag.value - currentSpanStyles.primaryAlpha);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.primaryAlpha = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.SecondaryAlpha) {
|
||||
currentSpanStyles.secondaryAlpha += progression * (tag.value - currentSpanStyles.secondaryAlpha);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.secondaryAlpha += progression * (tag.value - currentSpanStyles.secondaryAlpha);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.secondaryAlpha = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.OutlineAlpha) {
|
||||
currentSpanStyles.outlineAlpha += progression * (tag.value - currentSpanStyles.outlineAlpha);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.outlineAlpha += progression * (tag.value - currentSpanStyles.outlineAlpha);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.outlineAlpha = null;
|
||||
}
|
||||
}
|
||||
|
||||
else if (tag instanceof parts.ShadowAlpha) {
|
||||
currentSpanStyles.shadowAlpha += progression * (tag.value - currentSpanStyles.shadowAlpha);
|
||||
if (tag.value !== null) {
|
||||
currentSpanStyles.shadowAlpha += progression * (tag.value - currentSpanStyles.shadowAlpha);
|
||||
}
|
||||
else {
|
||||
currentSpanStyles.shadowAlpha = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -778,17 +919,17 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
}
|
||||
|
||||
else if (part instanceof parts.DrawingInstructions) {
|
||||
currentSpan.appendChild(currentDrawingStyles.toSVG(part, currentSpanStyles.primaryColor.withAlpha(currentSpanStyles.primaryAlpha)));
|
||||
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"));
|
||||
currentSpan!.appendChild(document.createTextNode(part.value + "\u200C"));
|
||||
startNewSpan(false);
|
||||
}
|
||||
|
||||
else if (debugMode && part instanceof parts.Comment) {
|
||||
currentSpan.appendChild(document.createTextNode(part.value));
|
||||
currentSpan!.appendChild(document.createTextNode(part.value));
|
||||
startNewSpan(false);
|
||||
}
|
||||
|
||||
@@ -797,23 +938,35 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
}
|
||||
}
|
||||
|
||||
let divTransformStyle = "";
|
||||
const transformOrigin = WebRenderer._transformOrigins[dialogue.alignment];
|
||||
const transformOriginString = `${ transformOrigin[0] }% ${ transformOrigin[1] }%`;
|
||||
|
||||
for (const part of dialogue.parts) {
|
||||
if (part instanceof parts.Position || part instanceof parts.Move) {
|
||||
const transformOrigin = WebRenderer._transformOrigins[dialogue.alignment];
|
||||
|
||||
const divTransformStyle = `translate(${ -transformOrigin[0] }%, ${ -transformOrigin[1] }%) translate(-${ sub.style.marginLeft }, -${ sub.style.marginTop })`;
|
||||
const transformOriginString = `${ transformOrigin[0] }% ${ transformOrigin[1] }%`;
|
||||
|
||||
sub.style.webkitTransform = divTransformStyle;
|
||||
sub.style.webkitTransformOrigin = transformOriginString;
|
||||
|
||||
sub.style.transform = divTransformStyle;
|
||||
sub.style.transformOrigin = transformOriginString;
|
||||
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 WrappingStyle.EndOfLineWrapping:
|
||||
sub.style.whiteSpace = "pre-wrap";
|
||||
@@ -876,21 +1029,23 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
console.log(dialogue.toString());
|
||||
}
|
||||
|
||||
let preRenderedSub = this._preRenderedSubs.get(dialogue.id);
|
||||
let thePreRenderedSub = this._preRenderedSubs.get(dialogue.id);
|
||||
|
||||
if (preRenderedSub === undefined) {
|
||||
preRenderedSub = this.preRender(dialogue);
|
||||
if (thePreRenderedSub === undefined) {
|
||||
thePreRenderedSub = this.preRender(dialogue)!;
|
||||
|
||||
if (debugMode) {
|
||||
console.log(dialogue.toString());
|
||||
}
|
||||
}
|
||||
|
||||
const result = <HTMLDivElement>preRenderedSub.sub.cloneNode(true);
|
||||
const preRenderedSub = thePreRenderedSub;
|
||||
|
||||
const result = preRenderedSub.sub.cloneNode(true);
|
||||
|
||||
const applyAnimationDelays = (node: HTMLElement) => {
|
||||
const animationNames = node.style.animationName || node.style.webkitAnimationName;
|
||||
if (animationNames !== undefined && animationNames !== "") {
|
||||
const animationNames = node.style.animationName || node.style.webkitAnimationName || "";
|
||||
if (animationNames !== "") {
|
||||
const animationDelays = animationNames.split(",").map(name => {
|
||||
name = name.trim();
|
||||
const delay = preRenderedSub.animationDelays.get(name);
|
||||
@@ -900,11 +1055,11 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
node.style.webkitAnimationDelay = animationDelays;
|
||||
node.style.animationDelay = animationDelays;
|
||||
}
|
||||
}
|
||||
};
|
||||
applyAnimationDelays(result);
|
||||
const animatedDescendants = result.querySelectorAll('[style*="animation:"]');
|
||||
for (let i = 0; i < animatedDescendants.length; i++) {
|
||||
applyAnimationDelays(<HTMLElement>animatedDescendants[i]);
|
||||
applyAnimationDelays(animatedDescendants[i] as HTMLElement);
|
||||
}
|
||||
|
||||
const layer = dialogue.layer;
|
||||
@@ -916,7 +1071,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
layerWrapper.className = `layer layer${ layer }`;
|
||||
|
||||
// Find the next greater layer div and insert this div before that one
|
||||
let insertBeforeElement: HTMLDivElement = null;
|
||||
let insertBeforeElement: HTMLDivElement | null = null;
|
||||
for (let insertBeforeLayer = layer + 1; insertBeforeLayer < this._layerWrappers.length && insertBeforeElement === null; insertBeforeLayer++) {
|
||||
if (this._layerWrappers[insertBeforeLayer] !== undefined) {
|
||||
insertBeforeElement = this._layerWrappers[insertBeforeLayer];
|
||||
@@ -929,13 +1084,14 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
this._layerAlignmentWrappers[layer] = [];
|
||||
}
|
||||
|
||||
if (this._layerAlignmentWrappers[layer][alignment] === undefined) {
|
||||
const layerAlignmentWrapper = document.createElement("div");
|
||||
let 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
|
||||
const layerWrapper = this._layerWrappers[layer];
|
||||
let insertBeforeElement: HTMLDivElement = null;
|
||||
let insertBeforeElement: HTMLDivElement | null = null;
|
||||
for (let insertBeforeAlignment = alignment + 1; insertBeforeAlignment < this._layerAlignmentWrappers[layer].length && insertBeforeElement === null; insertBeforeAlignment++) {
|
||||
if (this._layerAlignmentWrappers[layer][insertBeforeAlignment] !== undefined) {
|
||||
insertBeforeElement = this._layerAlignmentWrappers[layer][insertBeforeAlignment];
|
||||
@@ -943,18 +1099,23 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
}
|
||||
|
||||
layerWrapper.insertBefore(layerAlignmentWrapper, insertBeforeElement);
|
||||
|
||||
this._layerAlignmentWrappers[layer][alignment] = layerAlignmentWrapper;
|
||||
}
|
||||
|
||||
this._layerAlignmentWrappers[layer][alignment].appendChild(result);
|
||||
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
|
||||
const dialogueAnimationStylesElement = result.getElementsByTagName("style")[0];
|
||||
if (dialogueAnimationStylesElement !== undefined) {
|
||||
const sheet = <CSSStyleSheet>dialogueAnimationStylesElement.sheet;
|
||||
const sheet = dialogueAnimationStylesElement.sheet as CSSStyleSheet;
|
||||
if (sheet.cssRules.length === 0) {
|
||||
sheet.cssText = dialogueAnimationStylesElement.textContent;
|
||||
sheet.cssText = dialogueAnimationStylesElement.textContent!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1016,7 +1177,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
*/
|
||||
private _calculateFontMetricsAfterFetch(fontFamily: string, fontFetchPromise: Promise<any>): Promise<[number, number]> {
|
||||
return fontFetchPromise.then(() => {
|
||||
const fontSizeElement = <HTMLDivElement>this._fontSizeElement.cloneNode(true);
|
||||
const fontSizeElement = this._fontSizeElement.cloneNode(true);
|
||||
this._libjassSubsWrapper.appendChild(fontSizeElement);
|
||||
return Promise_finally(calculateFontMetrics(fontFamily, this.settings.fallbackFonts, fontSizeElement), () => {
|
||||
this._libjassSubsWrapper.removeChild(fontSizeElement);
|
||||
@@ -1037,7 +1198,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
}
|
||||
|
||||
private static _transformOrigins: number[][] = [
|
||||
null,
|
||||
[],
|
||||
[0, 100], [50, 100], [100, 100],
|
||||
[0, 50], [50, 50], [100, 50],
|
||||
[0, 0], [50, 0], [100, 0]
|
||||
@@ -1062,7 +1223,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
|
||||
}
|
||||
mixin(WebRenderer, [EventSource]);
|
||||
|
||||
interface PreRenderedSub {
|
||||
export interface PreRenderedSub {
|
||||
/** @type {!HTMLDivElement} */
|
||||
sub: HTMLDivElement;
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ export class SpanStyles {
|
||||
private _defaultStyle: Style;
|
||||
|
||||
private _italic: boolean;
|
||||
private _bold: Object;
|
||||
private _bold: boolean | number;
|
||||
private _underline: boolean;
|
||||
private _strikeThrough: boolean;
|
||||
|
||||
@@ -103,7 +103,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
reset(newStyle: Style): void {
|
||||
reset(newStyle: Style | undefined | null): void {
|
||||
if (newStyle === undefined || newStyle === null) {
|
||||
newStyle = this._defaultStyle;
|
||||
}
|
||||
@@ -127,12 +127,12 @@ export class SpanStyles {
|
||||
|
||||
this.letterSpacing = newStyle.letterSpacing;
|
||||
|
||||
this._rotationX = null;
|
||||
this._rotationY = null;
|
||||
this._rotationZ = newStyle.rotationZ;
|
||||
this.rotationX = 0;
|
||||
this.rotationY = 0;
|
||||
this.rotationZ = newStyle.rotationZ;
|
||||
|
||||
this._skewX = null;
|
||||
this._skewY = null;
|
||||
this.skewX = 0;
|
||||
this.skewY = 0;
|
||||
|
||||
this.primaryColor = newStyle.primaryColor;
|
||||
this.secondaryColor = newStyle.secondaryColor;
|
||||
@@ -168,11 +168,9 @@ export class SpanStyles {
|
||||
else if (this._bold !== false) {
|
||||
fontStyleOrWeight += this._bold + " ";
|
||||
}
|
||||
const fontSize = (
|
||||
this._scaleY *
|
||||
fontSizeForLineHeight(this._fontName, this._fontSize * (isTextOnlySpan ? this._fontScaleX : 1), this._settings.fallbackFonts, this._fontSizeElement, this._fontMetricsCache)
|
||||
).toFixed(3);
|
||||
const lineHeight = (this._scaleY * this._fontSize).toFixed(3);
|
||||
|
||||
const lineHeight = this._scaleY * (isTextOnlySpan ? this._fontScaleX : 1) * this._fontSize;
|
||||
const fontSize = fontSizeForLineHeight(this._fontName, lineHeight, this._settings.fallbackFonts, this._fontSizeElement, this._fontMetricsCache);
|
||||
|
||||
let fonts = this._fontName;
|
||||
|
||||
@@ -193,7 +191,7 @@ export class SpanStyles {
|
||||
fonts += `, ${ this._settings.fallbackFonts }`;
|
||||
}
|
||||
|
||||
span.style.font = `${ fontStyleOrWeight }${ fontSize }px/${ lineHeight }px ${ fonts }`;
|
||||
span.style.font = `${ fontStyleOrWeight }${ fontSize.toFixed(3) }px/${ lineHeight.toFixed(3) }px ${ fonts }`;
|
||||
|
||||
let textDecoration = "";
|
||||
if (this._underline) {
|
||||
@@ -218,19 +216,8 @@ export class SpanStyles {
|
||||
transform += `scaleY(${ this._fontScaleY }) `;
|
||||
}
|
||||
}
|
||||
if (this._rotationY !== null) {
|
||||
transform += `rotateY(${ this._rotationY }deg) `;
|
||||
}
|
||||
if (this._rotationX !== null) {
|
||||
transform += `rotateX(${ this._rotationX }deg) `;
|
||||
}
|
||||
if (this._rotationZ !== 0) {
|
||||
transform += `rotateZ(${ -1 * this._rotationZ }deg) `;
|
||||
}
|
||||
if (this._skewX !== null || this._skewY !== null) {
|
||||
const skewX = SpanStyles._valueOrDefault(this._skewX, 0);
|
||||
const skewY = SpanStyles._valueOrDefault(this._skewY, 0);
|
||||
transform += `matrix(1, ${ skewY }, ${ skewX }, 1, 0, 0) `;
|
||||
if (this._skewX !== 0 || this._skewY !== 0) {
|
||||
transform += `matrix(1, ${ this._skewY }, ${ this._skewX }, 1, 0, 0) `;
|
||||
}
|
||||
if (transform !== "") {
|
||||
span.style.webkitTransform = transform;
|
||||
@@ -244,9 +231,8 @@ export class SpanStyles {
|
||||
|
||||
const outlineWidth = this._scaleX * this._outlineWidth;
|
||||
const outlineHeight = this._scaleY * this._outlineHeight;
|
||||
|
||||
const filterWrapperSpan = document.createElement("span");
|
||||
filterWrapperSpan.appendChild(span);
|
||||
const shadowDepthX = this._scaleX * this._shadowDepthX;
|
||||
const shadowDepthY = this._scaleY * this._shadowDepthY;
|
||||
|
||||
let primaryColor = this._primaryColor.withAlpha(this._primaryAlpha);
|
||||
let outlineColor = this._outlineColor.withAlpha(this._outlineAlpha);
|
||||
@@ -268,55 +254,74 @@ export class SpanStyles {
|
||||
span.style.color = primaryColor.toString();
|
||||
|
||||
if (this._settings.enableSvg) {
|
||||
this._setSvgOutlineOnSpan(filterWrapperSpan, outlineWidth, outlineHeight, outlineColor, this._primaryAlpha);
|
||||
this._svg(
|
||||
span,
|
||||
outlineWidth, outlineHeight, outlineColor,
|
||||
shadowDepthX, shadowDepthY, shadowColor
|
||||
);
|
||||
}
|
||||
else {
|
||||
this._setTextShadowOutlineOnSpan(span, outlineWidth, outlineHeight, outlineColor);
|
||||
}
|
||||
|
||||
if (this._shadowDepthX !== 0 || this._shadowDepthY !== 0) {
|
||||
const shadowCssString = `${ shadowColor.toString() } ${ (this._shadowDepthX * this._scaleX).toFixed(3) }px ${ (this._shadowDepthY * this._scaleY).toFixed(3) }px 0px`;
|
||||
if (span.style.textShadow === "") {
|
||||
span.style.textShadow = shadowCssString;
|
||||
}
|
||||
else {
|
||||
span.style.textShadow += ", " + shadowCssString;
|
||||
}
|
||||
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"
|
||||
filterWrapperSpan.style.display = "inline-block";
|
||||
span.style.display = "inline-block";
|
||||
}
|
||||
|
||||
span.style.webkitAnimation = animationCollection.animationStyle;
|
||||
span.style.animation = animationCollection.animationStyle;
|
||||
|
||||
return filterWrapperSpan;
|
||||
return span;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!HTMLSpanElement} filterWrapperSpan
|
||||
* @param {!HTMLSpanElement} span
|
||||
* @param {number} outlineWidth
|
||||
* @param {number} outlineHeight
|
||||
* @param {!libjass.parts.Color} outlineColor
|
||||
* @param {number} primaryAlpha
|
||||
* @param {number} shadowDepthX
|
||||
* @param {number} shadowDepthY
|
||||
* @param {!libjass.parts.Color} shadowColor
|
||||
*/
|
||||
private _setSvgOutlineOnSpan(filterWrapperSpan: HTMLSpanElement, outlineWidth: number, outlineHeight: number, outlineColor: Color, primaryAlpha: number): void {
|
||||
const filterId = `svg-filter-${ this._id }-${ this._nextFilterId++ }`;
|
||||
|
||||
private _svg(
|
||||
span: HTMLSpanElement,
|
||||
outlineWidth: number, outlineHeight: number, outlineColor: Color,
|
||||
shadowDepthX: number, shadowDepthY: number, shadowColor: Color
|
||||
): void {
|
||||
const filterElement = document.createElementNS("http://www.w3.org/2000/svg", "filter");
|
||||
filterElement.id = filterId;
|
||||
filterElement.x.baseVal.valueAsString = "-50%";
|
||||
filterElement.width.baseVal.valueAsString = "200%";
|
||||
filterElement.y.baseVal.valueAsString = "-50%";
|
||||
filterElement.height.baseVal.valueAsString = "200%";
|
||||
|
||||
if (outlineWidth > 0 || outlineHeight > 0) {
|
||||
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
|
||||
const source = document.createElementNS("http://www.w3.org/2000/svg", "feComponentTransfer");
|
||||
filterElement.appendChild(source);
|
||||
source.in1.baseVal = "SourceAlpha";
|
||||
source.result.baseVal = "source";
|
||||
|
||||
const 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
|
||||
const mergedOutlines = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
|
||||
filterElement.appendChild(mergedOutlines);
|
||||
mergedOutlines.result.baseVal = "outline-alpha";
|
||||
|
||||
let outlineNumber = 0;
|
||||
|
||||
const increment = (!this._settings.preciseOutlines && this._gaussianBlur > 0) ? this._gaussianBlur : 1;
|
||||
@@ -351,78 +356,61 @@ export class SpanStyles {
|
||||
}
|
||||
}
|
||||
})((x: number, y: number): void => {
|
||||
const outlineId = `outline${ outlineNumber }`;
|
||||
|
||||
const outlineFilter = document.createElementNS("http://www.w3.org/2000/svg", "feMorphology");
|
||||
filterElement.appendChild(outlineFilter);
|
||||
filterElement.insertBefore(outlineFilter, mergedOutlines);
|
||||
outlineFilter.in1.baseVal = "source";
|
||||
outlineFilter.operator.baseVal = SVGFEMorphologyElement.SVG_MORPHOLOGY_OPERATOR_DILATE;
|
||||
outlineFilter.radiusX.baseVal = x;
|
||||
outlineFilter.radiusY.baseVal = y;
|
||||
outlineFilter.result.baseVal = `outline${ outlineNumber }`;
|
||||
outlineFilter.result.baseVal = outlineId;
|
||||
|
||||
const outlineReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
||||
mergedOutlines.appendChild(outlineReferenceNode);
|
||||
outlineReferenceNode.in1.baseVal = outlineId;
|
||||
|
||||
outlineNumber++;
|
||||
});
|
||||
|
||||
// Start with SourceAlpha. Leave the alpha as 0 if it's 0, and set it to 1 if it's greater than 0
|
||||
const source = document.createElementNS("http://www.w3.org/2000/svg", "feComponentTransfer");
|
||||
filterElement.insertBefore(source, filterElement.firstElementChild);
|
||||
source.in1.baseVal = "SourceAlpha";
|
||||
source.result.baseVal = "source";
|
||||
((addOutline: (x: number, y: number) => void) => {
|
||||
if ((outlineWidth % 1) > 0) {
|
||||
addOutline(outlineWidth, 0);
|
||||
addOutline(-outlineWidth, 0);
|
||||
}
|
||||
|
||||
const sourceAlphaTransferNode = document.createElementNS("http://www.w3.org/2000/svg", "feFuncA");
|
||||
source.appendChild(sourceAlphaTransferNode);
|
||||
sourceAlphaTransferNode.type.baseVal = SVGComponentTransferFunctionElement.SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
|
||||
if ((outlineHeight % 1) > 0) {
|
||||
addOutline(0, outlineHeight);
|
||||
addOutline(0, -outlineHeight);
|
||||
}
|
||||
})((x: number, y: number): void => {
|
||||
const outlineId = `outline${ outlineNumber }`;
|
||||
|
||||
/* 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 = (primaryAlpha === 0) ? 1 : (1 / primaryAlpha);
|
||||
const outlineFilter = document.createElementNS("http://www.w3.org/2000/svg", "feOffset");
|
||||
filterElement.insertBefore(outlineFilter, mergedOutlines);
|
||||
outlineFilter.in1.baseVal = "source";
|
||||
outlineFilter.dx.baseVal = x;
|
||||
outlineFilter.dy.baseVal = y;
|
||||
outlineFilter.result.baseVal = outlineId;
|
||||
|
||||
sourceAlphaTransferNode.intercept.baseVal = 0;
|
||||
|
||||
// Merge the individual outlines
|
||||
const mergedOutlines = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
|
||||
filterElement.appendChild(mergedOutlines);
|
||||
|
||||
for (let i = 0; i < outlineNumber; i++) {
|
||||
const outlineReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
||||
mergedOutlines.appendChild(outlineReferenceNode);
|
||||
outlineReferenceNode.in1.baseVal = `outline${ i }`;
|
||||
}
|
||||
outlineReferenceNode.in1.baseVal = outlineId;
|
||||
|
||||
outlineNumber++;
|
||||
});
|
||||
|
||||
// Color it with the outline color
|
||||
const coloredSource = document.createElementNS("http://www.w3.org/2000/svg", "feComponentTransfer");
|
||||
filterElement.appendChild(coloredSource);
|
||||
coloredSource.setAttribute("color-interpolation-filters", "sRGB");
|
||||
|
||||
const outlineRedTransferNode = document.createElementNS("http://www.w3.org/2000/svg", "feFuncR");
|
||||
coloredSource.appendChild(outlineRedTransferNode);
|
||||
outlineRedTransferNode.type.baseVal = SVGComponentTransferFunctionElement.SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
|
||||
outlineRedTransferNode.slope.baseVal = 0;
|
||||
outlineRedTransferNode.intercept.baseVal = outlineColor.red / 255 * outlineColor.alpha;
|
||||
|
||||
const outlineGreenTransferNode = document.createElementNS("http://www.w3.org/2000/svg", "feFuncG");
|
||||
coloredSource.appendChild(outlineGreenTransferNode);
|
||||
outlineGreenTransferNode.type.baseVal = SVGComponentTransferFunctionElement.SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
|
||||
outlineGreenTransferNode.slope.baseVal = 0;
|
||||
outlineGreenTransferNode.intercept.baseVal = outlineColor.green / 255 * outlineColor.alpha;
|
||||
|
||||
const outlineBlueTransferNode = document.createElementNS("http://www.w3.org/2000/svg", "feFuncB");
|
||||
coloredSource.appendChild(outlineBlueTransferNode);
|
||||
outlineBlueTransferNode.type.baseVal = SVGComponentTransferFunctionElement.SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
|
||||
outlineBlueTransferNode.slope.baseVal = 0;
|
||||
outlineBlueTransferNode.intercept.baseVal = outlineColor.blue / 255 * outlineColor.alpha;
|
||||
|
||||
const outlineAlphaTransferNode = document.createElementNS("http://www.w3.org/2000/svg", "feFuncA");
|
||||
coloredSource.appendChild(outlineAlphaTransferNode);
|
||||
outlineAlphaTransferNode.type.baseVal = SVGComponentTransferFunctionElement.SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
|
||||
outlineAlphaTransferNode.slope.baseVal = outlineColor.alpha;
|
||||
outlineAlphaTransferNode.intercept.baseVal = 0;
|
||||
const coloredOutline = createComponentTransferFilter(outlineColor);
|
||||
filterElement.appendChild(coloredOutline);
|
||||
coloredOutline.in1.baseVal = "outline-alpha";
|
||||
|
||||
// Blur the merged outline
|
||||
if (this._gaussianBlur > 0) {
|
||||
const 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;
|
||||
}
|
||||
@@ -433,21 +421,65 @@ export class SpanStyles {
|
||||
blurFilter.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
|
||||
}
|
||||
|
||||
// Cut out the source, so only the outline remains
|
||||
const outlineCutoutNode = document.createElementNS("http://www.w3.org/2000/svg", "feComposite");
|
||||
filterElement.appendChild(outlineCutoutNode);
|
||||
outlineCutoutNode.in2.baseVal = "source";
|
||||
outlineCutoutNode.operator.baseVal = SVGFECompositeElement.SVG_FECOMPOSITE_OPERATOR_OUT;
|
||||
// Cut out the source, so only the exterior remains
|
||||
const 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";
|
||||
|
||||
// Merge the outline with the SourceGraphic
|
||||
const mergedOutlineAndSourceGraphic = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
|
||||
filterElement.appendChild(mergedOutlineAndSourceGraphic);
|
||||
if (shadowDepthX > 0 || shadowDepthY > 0) {
|
||||
const 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
|
||||
const coloredShadow = createComponentTransferFilter(shadowColor);
|
||||
filterElement.appendChild(coloredShadow);
|
||||
|
||||
let lastFilter: SVGFEComponentTransferElement | SVGFEGaussianBlurElement | SVGFEConvolveMatrixElement = coloredShadow;
|
||||
|
||||
// Blur the shadow
|
||||
if (this._gaussianBlur > 0) {
|
||||
const 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 (let i = 0; i < this._blur; i++) {
|
||||
const 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
|
||||
const mergedResult = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
|
||||
filterElement.appendChild(mergedResult);
|
||||
|
||||
if (shadowDepthX > 0 || shadowDepthY > 0) {
|
||||
const shadowReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
||||
mergedResult.appendChild(shadowReferenceNode);
|
||||
shadowReferenceNode.in1.baseVal = "shadow";
|
||||
}
|
||||
|
||||
const outlineReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
||||
mergedOutlineAndSourceGraphic.appendChild(outlineReferenceNode);
|
||||
mergedResult.appendChild(outlineReferenceNode);
|
||||
outlineReferenceNode.in1.baseVal = "outline-colored";
|
||||
|
||||
const sourceGraphicReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
||||
mergedOutlineAndSourceGraphic.appendChild(sourceGraphicReferenceNode);
|
||||
mergedResult.appendChild(sourceGraphicReferenceNode);
|
||||
sourceGraphicReferenceNode.in1.baseVal = "SourceGraphic";
|
||||
}
|
||||
else {
|
||||
@@ -455,6 +487,8 @@ export class SpanStyles {
|
||||
if (this._gaussianBlur > 0) {
|
||||
const 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;
|
||||
}
|
||||
@@ -467,10 +501,18 @@ export class SpanStyles {
|
||||
}
|
||||
|
||||
if (filterElement.childElementCount > 0) {
|
||||
this._svgDefsElement.appendChild(filterElement);
|
||||
const filterId = `libjass-svg-filter-${ this._id }-${ this._nextFilterId++ }`;
|
||||
|
||||
filterWrapperSpan.style.webkitFilter = `url("#${ filterId }")`;
|
||||
filterWrapperSpan.style.filter = `url("#${ filterId }")`;
|
||||
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%";
|
||||
|
||||
const filterProperty = `url("#${ filterId }")`;
|
||||
span.style.webkitFilter = filterProperty;
|
||||
span.style.filter = filterProperty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,10 +521,18 @@ export class SpanStyles {
|
||||
* @param {number} outlineWidth
|
||||
* @param {number} outlineHeight
|
||||
* @param {!libjass.parts.Color} outlineColor
|
||||
* @param {number} shadowDepthX
|
||||
* @param {number} shadowDepthY
|
||||
* @param {!libjass.parts.Color} shadowColor
|
||||
*/
|
||||
private _setTextShadowOutlineOnSpan(span: HTMLSpanElement, outlineWidth: number, outlineHeight: number, outlineColor: Color): void {
|
||||
private _textShadow(
|
||||
span: HTMLSpanElement,
|
||||
outlineWidth: number, outlineHeight: number, outlineColor: Color,
|
||||
shadowDepthX: number, shadowDepthY: number, shadowColor: Color
|
||||
): void {
|
||||
if (outlineWidth > 0 || outlineHeight > 0) {
|
||||
let outlineCssString = "";
|
||||
let shadowCssString = "";
|
||||
|
||||
((addOutline: (x: number, y: number) => void) => {
|
||||
for (let x = 0; x <= outlineWidth; x++) {
|
||||
@@ -503,11 +553,34 @@ export class SpanStyles {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((outlineWidth % 1) > 0) {
|
||||
addOutline(outlineWidth, 0);
|
||||
addOutline(-outlineWidth, 0);
|
||||
}
|
||||
|
||||
if ((outlineHeight % 1) > 0) {
|
||||
addOutline(0, outlineHeight);
|
||||
addOutline(0, -outlineHeight);
|
||||
}
|
||||
})((x: number, y: number): void => {
|
||||
outlineCssString += `, ${ outlineColor.toString() } ${ x }px ${ y }px ${ this._gaussianBlur.toFixed(3) }px`;
|
||||
outlineCssString += `, ${ outlineColor.toString() } ${ x.toFixed(3) }px ${ y.toFixed(3) }px ${ this._gaussianBlur.toFixed(3) }px`;
|
||||
|
||||
if (this._shadowDepthX !== 0 || this._shadowDepthY !== 0) {
|
||||
shadowCssString += `, ${ shadowColor.toString() } ${ (x + shadowDepthX).toFixed(3) }px ${ (y + shadowDepthY).toFixed(3) }px ${ this._gaussianBlur.toFixed(3) }px`;
|
||||
}
|
||||
});
|
||||
|
||||
span.style.textShadow = outlineCssString.substr(", ".length);
|
||||
span.style.textShadow = (outlineCssString + shadowCssString).substr(", ".length);
|
||||
}
|
||||
else if (this._shadowDepthX !== 0 || this._shadowDepthY !== 0) {
|
||||
const 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,16 +599,16 @@ export class SpanStyles {
|
||||
* @type {?boolean}
|
||||
*/
|
||||
set italic(value: boolean) {
|
||||
this._italic = SpanStyles._valueOrDefault(value, this._defaultStyle.italic);
|
||||
this._italic = valueOrDefault(value, this._defaultStyle.italic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bold property. null defaults it to the default style's value.
|
||||
*
|
||||
* @type {(?number|?boolean)}
|
||||
* @type {(?boolean|?number)}
|
||||
*/
|
||||
set bold(value: Object) {
|
||||
this._bold = SpanStyles._valueOrDefault(value, this._defaultStyle.bold);
|
||||
set bold(value: boolean | number | null) {
|
||||
this._bold = valueOrDefault(value, this._defaultStyle.bold);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -544,7 +617,7 @@ export class SpanStyles {
|
||||
* @type {?boolean}
|
||||
*/
|
||||
set underline(value: boolean) {
|
||||
this._underline = SpanStyles._valueOrDefault(value, this._defaultStyle.underline);
|
||||
this._underline = valueOrDefault(value, this._defaultStyle.underline);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -553,7 +626,7 @@ export class SpanStyles {
|
||||
* @type {?boolean}
|
||||
*/
|
||||
set strikeThrough(value: boolean) {
|
||||
this._strikeThrough = SpanStyles._valueOrDefault(value, this._defaultStyle.strikeThrough);
|
||||
this._strikeThrough = valueOrDefault(value, this._defaultStyle.strikeThrough);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -561,7 +634,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get outlineWidth() {
|
||||
get outlineWidth(): number | null {
|
||||
return this._outlineWidth;
|
||||
}
|
||||
|
||||
@@ -570,17 +643,17 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set outlineWidth(value: number) {
|
||||
this._outlineWidth = SpanStyles._valueOrDefault(value, this._defaultStyle.outlineThickness);
|
||||
set outlineWidth(value: number | null) {
|
||||
this._outlineWidth = valueOrDefault(value, this._defaultStyle.outlineThickness);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the outline width property.
|
||||
* Gets the outline height property.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get outlineHeight() {
|
||||
return this._outlineWidth;
|
||||
get outlineHeight(): number | null {
|
||||
return this._outlineHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -588,8 +661,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set outlineHeight(value: number) {
|
||||
this._outlineHeight = SpanStyles._valueOrDefault(value, this._defaultStyle.outlineThickness);
|
||||
set outlineHeight(value: number | null) {
|
||||
this._outlineHeight = valueOrDefault(value, this._defaultStyle.outlineThickness);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -597,7 +670,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get shadowDepthX() {
|
||||
get shadowDepthX(): number | null {
|
||||
return this._shadowDepthX;
|
||||
}
|
||||
|
||||
@@ -606,8 +679,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set shadowDepthX(value: number) {
|
||||
this._shadowDepthX = SpanStyles._valueOrDefault(value, this._defaultStyle.shadowDepth);
|
||||
set shadowDepthX(value: number | null) {
|
||||
this._shadowDepthX = valueOrDefault(value, this._defaultStyle.shadowDepth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -615,7 +688,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get shadowDepthY() {
|
||||
get shadowDepthY(): number | null {
|
||||
return this._shadowDepthY;
|
||||
}
|
||||
|
||||
@@ -624,8 +697,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set shadowDepthY(value: number) {
|
||||
this._shadowDepthY = SpanStyles._valueOrDefault(value, this._defaultStyle.shadowDepth);
|
||||
set shadowDepthY(value: number | null) {
|
||||
this._shadowDepthY = valueOrDefault(value, this._defaultStyle.shadowDepth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -633,7 +706,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get blur() {
|
||||
get blur(): number | null {
|
||||
return this._blur;
|
||||
}
|
||||
|
||||
@@ -642,8 +715,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set blur(value: number) {
|
||||
this._blur = SpanStyles._valueOrDefault(value, 0);
|
||||
set blur(value: number | null) {
|
||||
this._blur = valueOrDefault(value, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -651,7 +724,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get gaussianBlur() {
|
||||
get gaussianBlur(): number | null {
|
||||
return this._gaussianBlur;
|
||||
}
|
||||
|
||||
@@ -660,8 +733,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set gaussianBlur(value: number) {
|
||||
this._gaussianBlur = SpanStyles._valueOrDefault(value, 0);
|
||||
set gaussianBlur(value: number | null) {
|
||||
this._gaussianBlur = valueOrDefault(value, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -669,8 +742,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?string}
|
||||
*/
|
||||
set fontName(value: string) {
|
||||
this._fontName = SpanStyles._valueOrDefault(value, this._defaultStyle.fontName);
|
||||
set fontName(value: string | null) {
|
||||
this._fontName = valueOrDefault(value, this._defaultStyle.fontName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -678,7 +751,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get fontSize() {
|
||||
get fontSize(): number | null {
|
||||
return this._fontSize;
|
||||
}
|
||||
|
||||
@@ -687,8 +760,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set fontSize(value: number) {
|
||||
this._fontSize = SpanStyles._valueOrDefault(value, this._defaultStyle.fontSize);
|
||||
set fontSize(value: number | null) {
|
||||
this._fontSize = valueOrDefault(value, this._defaultStyle.fontSize);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -696,7 +769,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get fontScaleX() {
|
||||
get fontScaleX(): number | null {
|
||||
return this._fontScaleX;
|
||||
}
|
||||
|
||||
@@ -705,8 +778,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set fontScaleX(value: number) {
|
||||
this._fontScaleX = SpanStyles._valueOrDefault(value, this._defaultStyle.fontScaleX);
|
||||
set fontScaleX(value: number | null) {
|
||||
this._fontScaleX = valueOrDefault(value, this._defaultStyle.fontScaleX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -714,7 +787,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get fontScaleY() {
|
||||
get fontScaleY(): number | null {
|
||||
return this._fontScaleY;
|
||||
}
|
||||
|
||||
@@ -723,8 +796,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set fontScaleY(value: number) {
|
||||
this._fontScaleY = SpanStyles._valueOrDefault(value, this._defaultStyle.fontScaleY);
|
||||
set fontScaleY(value: number | null) {
|
||||
this._fontScaleY = valueOrDefault(value, this._defaultStyle.fontScaleY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -732,7 +805,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get letterSpacing() {
|
||||
get letterSpacing(): number | null {
|
||||
return this._letterSpacing;
|
||||
}
|
||||
|
||||
@@ -741,16 +814,16 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set letterSpacing(value: number) {
|
||||
this._letterSpacing = SpanStyles._valueOrDefault(value, this._defaultStyle.letterSpacing);
|
||||
set letterSpacing(value: number | null) {
|
||||
this._letterSpacing = valueOrDefault(value, this._defaultStyle.letterSpacing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the X-axis rotation property.
|
||||
*
|
||||
* @type {?number}
|
||||
* @type {number}
|
||||
*/
|
||||
get rotationX() {
|
||||
get rotationX(): number | null {
|
||||
return this._rotationX;
|
||||
}
|
||||
|
||||
@@ -759,16 +832,16 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set rotationX(value: number) {
|
||||
this._rotationX = value;
|
||||
set rotationX(value: number | null) {
|
||||
this._rotationX = valueOrDefault(value, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Y-axis rotation property.
|
||||
*
|
||||
* @type {?number}
|
||||
* @type {number}
|
||||
*/
|
||||
get rotationY() {
|
||||
get rotationY(): number | null {
|
||||
return this._rotationY;
|
||||
}
|
||||
|
||||
@@ -777,16 +850,16 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set rotationY(value: number) {
|
||||
this._rotationY = value;
|
||||
set rotationY(value: number | null) {
|
||||
this._rotationY = valueOrDefault(value, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Z-axis rotation property.
|
||||
*
|
||||
* @type {?number}
|
||||
* @type {number}
|
||||
*/
|
||||
get rotationZ() {
|
||||
get rotationZ(): number | null {
|
||||
return this._rotationZ;
|
||||
}
|
||||
|
||||
@@ -795,16 +868,16 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set rotationZ(value: number) {
|
||||
this._rotationZ = SpanStyles._valueOrDefault(value, this._defaultStyle.rotationZ);
|
||||
set rotationZ(value: number | null) {
|
||||
this._rotationZ = valueOrDefault(value, this._defaultStyle.rotationZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the X-axis skew property.
|
||||
*
|
||||
* @type {?number}
|
||||
* @type {number}
|
||||
*/
|
||||
get skewX() {
|
||||
get skewX(): number | null {
|
||||
return this._skewX;
|
||||
}
|
||||
|
||||
@@ -813,16 +886,16 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set skewX(value: number) {
|
||||
this._skewX = value;
|
||||
set skewX(value: number | null) {
|
||||
this._skewX = valueOrDefault(value, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Y-axis skew property.
|
||||
*
|
||||
* @type {?number}
|
||||
* @type {number}
|
||||
*/
|
||||
get skewY() {
|
||||
get skewY(): number | null {
|
||||
return this._skewY;
|
||||
}
|
||||
|
||||
@@ -831,8 +904,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set skewY(value: number) {
|
||||
this._skewY = value;
|
||||
set skewY(value: number | null) {
|
||||
this._skewY = valueOrDefault(value, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -840,7 +913,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {!libjass.Color}
|
||||
*/
|
||||
get primaryColor(): Color {
|
||||
get primaryColor(): Color | null {
|
||||
return this._primaryColor;
|
||||
}
|
||||
|
||||
@@ -849,8 +922,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {libjass.Color}
|
||||
*/
|
||||
set primaryColor(value: Color) {
|
||||
this._primaryColor = SpanStyles._valueOrDefault(value, this._defaultStyle.primaryColor);
|
||||
set primaryColor(value: Color | null) {
|
||||
this._primaryColor = valueOrDefault(value, this._defaultStyle.primaryColor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -858,7 +931,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {!libjass.Color}
|
||||
*/
|
||||
get secondaryColor(): Color {
|
||||
get secondaryColor(): Color | null {
|
||||
return this._secondaryColor;
|
||||
}
|
||||
|
||||
@@ -867,8 +940,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {libjass.Color}
|
||||
*/
|
||||
set secondaryColor(value: Color) {
|
||||
this._secondaryColor = SpanStyles._valueOrDefault(value, this._defaultStyle.secondaryColor);
|
||||
set secondaryColor(value: Color | null) {
|
||||
this._secondaryColor = valueOrDefault(value, this._defaultStyle.secondaryColor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -876,7 +949,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {!libjass.Color}
|
||||
*/
|
||||
get outlineColor(): Color {
|
||||
get outlineColor(): Color | null {
|
||||
return this._outlineColor;
|
||||
}
|
||||
|
||||
@@ -885,8 +958,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {libjass.Color}
|
||||
*/
|
||||
set outlineColor(value: Color) {
|
||||
this._outlineColor = SpanStyles._valueOrDefault(value, this._defaultStyle.outlineColor);
|
||||
set outlineColor(value: Color | null) {
|
||||
this._outlineColor = valueOrDefault(value, this._defaultStyle.outlineColor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -894,7 +967,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {!libjass.Color}
|
||||
*/
|
||||
get shadowColor(): Color {
|
||||
get shadowColor(): Color | null {
|
||||
return this._shadowColor;
|
||||
}
|
||||
|
||||
@@ -903,8 +976,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {libjass.Color}
|
||||
*/
|
||||
set shadowColor(value: Color) {
|
||||
this._shadowColor = SpanStyles._valueOrDefault(value, this._defaultStyle.shadowColor);
|
||||
set shadowColor(value: Color | null) {
|
||||
this._shadowColor = valueOrDefault(value, this._defaultStyle.shadowColor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -912,7 +985,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get primaryAlpha(): number {
|
||||
get primaryAlpha(): number | null {
|
||||
return this._primaryAlpha;
|
||||
}
|
||||
|
||||
@@ -921,8 +994,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set primaryAlpha(value: number) {
|
||||
this._primaryAlpha = SpanStyles._valueOrDefault(value, this._defaultStyle.primaryColor.alpha);
|
||||
set primaryAlpha(value: number | null) {
|
||||
this._primaryAlpha = valueOrDefault(value, this._defaultStyle.primaryColor.alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -930,7 +1003,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get secondaryAlpha(): number {
|
||||
get secondaryAlpha(): number | null {
|
||||
return this._secondaryAlpha;
|
||||
}
|
||||
|
||||
@@ -939,8 +1012,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set secondaryAlpha(value: number) {
|
||||
this._secondaryAlpha = SpanStyles._valueOrDefault(value, this._defaultStyle.secondaryColor.alpha);
|
||||
set secondaryAlpha(value: number | null) {
|
||||
this._secondaryAlpha = valueOrDefault(value, this._defaultStyle.secondaryColor.alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -948,7 +1021,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get outlineAlpha(): number {
|
||||
get outlineAlpha(): number | null {
|
||||
return this._outlineAlpha;
|
||||
}
|
||||
|
||||
@@ -957,8 +1030,8 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set outlineAlpha(value: number) {
|
||||
this._outlineAlpha = SpanStyles._valueOrDefault(value, this._defaultStyle.outlineColor.alpha);
|
||||
set outlineAlpha(value: number | null) {
|
||||
this._outlineAlpha = valueOrDefault(value, this._defaultStyle.outlineColor.alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -966,7 +1039,7 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get shadowAlpha(): number {
|
||||
get shadowAlpha(): number | null {
|
||||
return this._shadowAlpha;
|
||||
}
|
||||
|
||||
@@ -975,9 +1048,50 @@ export class SpanStyles {
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set shadowAlpha(value: number) {
|
||||
this._shadowAlpha = SpanStyles._valueOrDefault(value, this._defaultStyle.shadowColor.alpha);
|
||||
set shadowAlpha(value: number | null) {
|
||||
this._shadowAlpha = valueOrDefault(value, this._defaultStyle.shadowColor.alpha);
|
||||
}
|
||||
|
||||
private static _valueOrDefault = <T>(newValue: T, defaultValue: T): T => ((newValue !== null) ? newValue : defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!libjass.parts.Color} color
|
||||
* @return {!SVGFEComponentTransferElement}
|
||||
*/
|
||||
function createComponentTransferFilter(color: Color): SVGFEComponentTransferElement {
|
||||
const result = document.createElementNS("http://www.w3.org/2000/svg", "feComponentTransfer");
|
||||
|
||||
const 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;
|
||||
|
||||
const 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;
|
||||
|
||||
const 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;
|
||||
|
||||
const 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}
|
||||
*/
|
||||
function valueOrDefault<T>(newValue: T | null, defaultValue: T): T {
|
||||
return ((newValue !== null) ? newValue : defaultValue);
|
||||
}
|
||||
|
||||
@@ -18,23 +18,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
interface Document {
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "defs"): SVGDefsElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "feComponentTransfer"): SVGFEComponentTransferElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "feComposite"): SVGFECompositeElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "feConvolveMatrix"): SVGFEConvolveMatrixElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "feFuncA"): SVGFEFuncAElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "feFuncB"): SVGFEFuncBElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "feFuncG"): SVGFEFuncGElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "feFuncR"): SVGFEFuncRElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "feGaussianBlur"): SVGFEGaussianBlurElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "feMerge"): SVGFEMergeElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "feMergeNode"): SVGFEMergeNodeElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "feMorphology"): SVGFEMorphologyElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "filter"): SVGFilterElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "g"): SVGGElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "path"): SVGPathElement;
|
||||
createElementNS(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: "svg"): SVGSVGElement;
|
||||
interface Node {
|
||||
cloneNode(deep?: boolean): this;
|
||||
}
|
||||
|
||||
interface SVGFEComponentTransferElement {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { Map } from "./utility/map";
|
||||
|
||||
const classes = new Map<number, Function & { fromJSON?: (obj: any) => any }>();
|
||||
|
||||
/**
|
||||
* Registers a class as a serializable type.
|
||||
*
|
||||
* @param {function(new:*)} clazz
|
||||
*/
|
||||
export function registerClass(clazz: Function & { fromJSON?: (obj: any) => any }): void {
|
||||
clazz.prototype._classTag = classes.size;
|
||||
classes.set(clazz.prototype._classTag, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the given object.
|
||||
*
|
||||
* @param {*} obj
|
||||
* @return {string}
|
||||
*/
|
||||
export function serialize(obj: any): string {
|
||||
return JSON.stringify(obj, (/* ujs:unreferenced */ key: string, value: any) => {
|
||||
if (value && (value._classTag !== undefined) && !Object.prototype.hasOwnProperty.call(value, "_classTag")) {
|
||||
// Copy the _classTag from this object's prototype to itself, so that it will be serialized.
|
||||
value._classTag = value._classTag;
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @return {*}
|
||||
*/
|
||||
export function deserialize(str: string): any {
|
||||
return JSON.parse(str, (/* ujs:unreferenced */ key: string, value: any) => {
|
||||
if (value && (value._classTag !== undefined)) {
|
||||
const clazz = classes.get(value._classTag);
|
||||
if (clazz === undefined) {
|
||||
throw new Error(`Unknown class of tag ${ value._classTag } cannot be deserialized.`);
|
||||
}
|
||||
|
||||
if (typeof clazz.fromJSON === "function") {
|
||||
value = clazz.fromJSON(value);
|
||||
}
|
||||
else {
|
||||
const hydratedValue = Object.create(clazz.prototype);
|
||||
for (const key of Object.keys(value)) {
|
||||
hydratedValue[key] = value[key];
|
||||
}
|
||||
value = hydratedValue;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}
|
||||
@@ -1,9 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
"lib": ["es5", "dom"],
|
||||
|
||||
"experimentalDecorators": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": false,
|
||||
"strictNullChecks": true,
|
||||
|
||||
"target": "es5",
|
||||
"module": "amd",
|
||||
"moduleResolution": "node",
|
||||
"outFile": "../lib/libjass.js",
|
||||
"noImplicitUseStrict": true,
|
||||
"sourceMap": true,
|
||||
"target": "ES5",
|
||||
"experimentalDecorators": true
|
||||
"inlineSources": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,32 +25,35 @@ import { ScriptProperties } from "./script-properties";
|
||||
|
||||
import { Format } from "./misc";
|
||||
|
||||
import { verboseMode } from "../settings";
|
||||
import { debugMode, verboseMode } from "../settings";
|
||||
|
||||
import * as parser from "../parser";
|
||||
import { parseLineIntoTypedTemplate } from "../parser/misc";
|
||||
import { ReadableStream, TextDecoder, TextDecoderConstructor } from "../parser/streams";
|
||||
import { ReadableStream, TextDecoderConstructor } from "../parser/streams";
|
||||
|
||||
import { Map } from "../utility/map";
|
||||
import { Promise } from "../utility/promise";
|
||||
|
||||
import { registerClass as serializable } from "../serialization";
|
||||
|
||||
declare const global: {
|
||||
fetch?(url: string): Promise<{ body: ReadableStream; ok?: boolean; status?: number; }>;
|
||||
ReadableStream?: { prototype: ReadableStream; };
|
||||
ReadableStream?: Function & { prototype: ReadableStream; };
|
||||
TextDecoder?: TextDecoderConstructor;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@serializable
|
||||
export class ASS {
|
||||
private _properties: ScriptProperties = new ScriptProperties();
|
||||
private _styles: Map<string, Style> = new Map<string, Style>();
|
||||
private _dialogues: Dialogue[] = [];
|
||||
private _attachments: Attachment[] = [];
|
||||
|
||||
private _stylesFormatSpecifier: string[] = null;
|
||||
private _dialoguesFormatSpecifier: string[] = null;
|
||||
private _stylesFormatSpecifier: string[] | null = null;
|
||||
private _dialoguesFormatSpecifier: string[] | null = null;
|
||||
|
||||
/**
|
||||
* The properties of this script.
|
||||
@@ -91,36 +94,36 @@ export class ASS {
|
||||
/**
|
||||
* The format specifier for the styles section.
|
||||
*
|
||||
* @type {!Array.<string>}
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
get stylesFormatSpecifier(): string[] {
|
||||
get stylesFormatSpecifier(): string[] | null {
|
||||
return this._stylesFormatSpecifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* The format specifier for the styles section.
|
||||
*
|
||||
* @type {!Array.<string>}
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
get dialoguesFormatSpecifier(): string[] {
|
||||
get dialoguesFormatSpecifier(): string[] | null {
|
||||
return this._dialoguesFormatSpecifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* The format specifier for the events section.
|
||||
*
|
||||
* @type {!Array.<string>}
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
set stylesFormatSpecifier(value: string[]) {
|
||||
set stylesFormatSpecifier(value: string[] | null) {
|
||||
this._stylesFormatSpecifier = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The format specifier for the events section.
|
||||
*
|
||||
* @type {!Array.<string>}
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
set dialoguesFormatSpecifier(value: string[]) {
|
||||
set dialoguesFormatSpecifier(value: string[] | null) {
|
||||
this._dialoguesFormatSpecifier = value;
|
||||
}
|
||||
|
||||
@@ -139,6 +142,10 @@ export class ASS {
|
||||
* @param {string} line The line from the script that contains the new style.
|
||||
*/
|
||||
addStyle(line: string): void {
|
||||
if (this._stylesFormatSpecifier === null) {
|
||||
throw new Error("stylesFormatSpecifier is not set.");
|
||||
}
|
||||
|
||||
const styleLine = parseLineIntoTypedTemplate(line, this._stylesFormatSpecifier);
|
||||
if (styleLine === null || styleLine.type !== "Style") {
|
||||
return;
|
||||
@@ -163,6 +170,10 @@ export class ASS {
|
||||
* @param {string} line The line from the script that contains the new event.
|
||||
*/
|
||||
addEvent(line: string): void {
|
||||
if (this._dialoguesFormatSpecifier === null) {
|
||||
throw new Error("dialoguesFormatSpecifier is not set.");
|
||||
}
|
||||
|
||||
const dialogueLine = parseLineIntoTypedTemplate(line, this._dialoguesFormatSpecifier);
|
||||
if (dialogueLine === null || dialogueLine.type !== "Dialogue") {
|
||||
return;
|
||||
@@ -189,14 +200,37 @@ export class ASS {
|
||||
this._attachments.push(attachment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom JSON serialization for ASS objects.
|
||||
*
|
||||
* @return {!*}
|
||||
*/
|
||||
toJSON(): any {
|
||||
const result = Object.create(null);
|
||||
|
||||
result._properties = this._properties;
|
||||
|
||||
result._styles = Object.create(null);
|
||||
this._styles.forEach((style, name) => result._styles[name] = style);
|
||||
|
||||
result._dialogues = this._dialogues;
|
||||
result._attachments = this._attachments;
|
||||
result._stylesFormatSpecifier = this._stylesFormatSpecifier;
|
||||
result._dialoguesFormatSpecifier = this._dialoguesFormatSpecifier;
|
||||
|
||||
result._classTag = (ASS.prototype as any)._classTag;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ASS object from the raw text of an ASS script.
|
||||
*
|
||||
* @param {string} raw The raw text of the script.
|
||||
* @param {number=0} type The type of the script. One of the {@link libjass.Format} constants.
|
||||
* @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.<!libjass.ASS>}
|
||||
*/
|
||||
static fromString(raw: string, type: Format = Format.ASS): Promise<ASS> {
|
||||
static fromString(raw: string, type: Format | "ass" | "srt" = Format.ASS): Promise<ASS> {
|
||||
return ASS.fromStream(new parser.StringStream(raw), type);
|
||||
}
|
||||
|
||||
@@ -204,17 +238,19 @@ export class ASS {
|
||||
* 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=0} type The type of the script. One of the {@link libjass.Format} constants.
|
||||
* @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.<!libjass.ASS>} A promise that will be resolved with the ASS object when it has been fully parsed
|
||||
*/
|
||||
static fromStream(stream: parser.Stream, type: Format = Format.ASS): Promise<ASS> {
|
||||
static fromStream(stream: parser.Stream, type: Format | "ass" | "srt" = Format.ASS): Promise<ASS> {
|
||||
switch (type) {
|
||||
case Format.ASS:
|
||||
case "ass":
|
||||
return new parser.StreamParser(stream).ass;
|
||||
case Format.SRT:
|
||||
case "srt":
|
||||
return new parser.SrtStreamParser(stream).ass;
|
||||
default:
|
||||
throw new Error(`Illegal value of type: ${ type }`);
|
||||
throw new Error(`Invalid value of type: ${ type }`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,10 +258,10 @@ export class ASS {
|
||||
* Creates an ASS object from the given URL.
|
||||
*
|
||||
* @param {string} url The URL of the script.
|
||||
* @param {number=0} type The type of the script. One of the {@link libjass.Format} constants.
|
||||
* @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.<!libjass.ASS>} A promise that will be resolved with the ASS object when it has been fully parsed
|
||||
*/
|
||||
static fromUrl(url: string, type: Format = Format.ASS): Promise<ASS> {
|
||||
static fromUrl(url: string, type: Format | "ass" | "srt" = Format.ASS): Promise<ASS> {
|
||||
let fetchPromise: Promise<ASS>;
|
||||
|
||||
if (
|
||||
@@ -246,7 +282,9 @@ export class ASS {
|
||||
}
|
||||
|
||||
return fetchPromise.catch(reason => {
|
||||
console.warn("fetch() failed, falling back to XHR: %o", reason);
|
||||
if (debugMode) {
|
||||
console.log("fetch() failed, falling back to XHR: %o", reason);
|
||||
}
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
const result = ASS.fromStream(new parser.XhrStream(xhr), type);
|
||||
@@ -261,10 +299,35 @@ export class ASS {
|
||||
*
|
||||
* @param {!ReadableStream} stream
|
||||
* @param {string="utf-8"} encoding
|
||||
* @param {number=0} type The type of the script. One of the {@link libjass.Format} constants.
|
||||
* @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.<!libjass.ASS>} A promise that will be resolved with the ASS object when it has been fully parsed
|
||||
*/
|
||||
static fromReadableStream(stream: ReadableStream, encoding: string = "utf-8", type: Format = Format.ASS): Promise<ASS> {
|
||||
static fromReadableStream(stream: ReadableStream, encoding: string = "utf-8", type: Format | "ass" | "srt" = Format.ASS): Promise<ASS> {
|
||||
return ASS.fromStream(new parser.BrowserReadableStream(stream, encoding), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom deserialization for ASS objects.
|
||||
*
|
||||
* @param {!*} obj
|
||||
* @return {!libjass.ASS}
|
||||
*/
|
||||
static fromJSON(obj: any): ASS {
|
||||
const result: ASS = Object.create(ASS.prototype);
|
||||
|
||||
result._properties = obj._properties;
|
||||
|
||||
result._styles = new Map<string, Style>();
|
||||
for (const name of Object.keys(obj._styles)) {
|
||||
const style = obj._styles[name];
|
||||
result._styles.set(name, style);
|
||||
}
|
||||
|
||||
result._dialogues = obj._dialogues;
|
||||
result._attachments = obj._attachments;
|
||||
result._stylesFormatSpecifier = obj._stylesFormatSpecifier;
|
||||
result._dialoguesFormatSpecifier = obj._dialoguesFormatSpecifier;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { registerClass as serializable } from "../serialization";
|
||||
|
||||
/**
|
||||
* The type of an attachment.
|
||||
*/
|
||||
@@ -32,6 +34,7 @@ export enum AttachmentType {
|
||||
* @param {string} filename The filename of this attachment.
|
||||
* @param {number} type The type of this attachment.
|
||||
*/
|
||||
@serializable
|
||||
export class Attachment {
|
||||
private _contents: string = "";
|
||||
|
||||
|
||||
@@ -23,12 +23,12 @@ import { Style } from "./style";
|
||||
|
||||
import { valueOrDefault } from "./misc";
|
||||
|
||||
import { parseLineIntoTypedTemplate } from "../parser/misc";
|
||||
|
||||
import { parse } from "../parser/parse";
|
||||
|
||||
import * as parts from "../parts";
|
||||
|
||||
import { registerClass as serializable } from "../serialization";
|
||||
|
||||
import { debugMode } from "../settings";
|
||||
|
||||
import { Map } from "../utility/map";
|
||||
@@ -44,6 +44,7 @@ import { Map } from "../utility/map";
|
||||
* @param {string} template["Text"] The text of this dialogue
|
||||
* @param {!libjass.ASS} ass The ASS object to which this dialogue belongs
|
||||
*/
|
||||
@serializable
|
||||
export class Dialogue {
|
||||
private static _lastDialogueId = -1;
|
||||
|
||||
@@ -58,7 +59,7 @@ export class Dialogue {
|
||||
private _alignment: number;
|
||||
|
||||
private _rawPartsString: string;
|
||||
private _parts: parts.Part[] = null;
|
||||
private _parts: parts.Part[] | null = null;
|
||||
|
||||
private _containsTransformTag: boolean = false;
|
||||
|
||||
@@ -74,34 +75,45 @@ export class Dialogue {
|
||||
this._id = ++Dialogue._lastDialogueId;
|
||||
|
||||
let styleName = template.get("style");
|
||||
if (styleName !== undefined && styleName !== null && styleName.constructor === String) {
|
||||
if (typeof styleName === "string") {
|
||||
styleName = styleName.replace(/^\*+/, "");
|
||||
if (styleName.match(/^Default$/i) !== null) {
|
||||
styleName = "Default";
|
||||
}
|
||||
}
|
||||
|
||||
this._style = ass.styles.get(styleName);
|
||||
if (this._style === undefined) {
|
||||
let style = (styleName !== undefined) ? ass.styles.get(styleName) : undefined;
|
||||
if (style === undefined) {
|
||||
if (debugMode) {
|
||||
console.warn(`Unrecognized style ${ styleName }. Falling back to "Default"`);
|
||||
}
|
||||
|
||||
this._style = ass.styles.get("Default");
|
||||
}
|
||||
if (this._style === undefined) {
|
||||
throw new Error(`Unrecognized style ${ styleName }`);
|
||||
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;
|
||||
|
||||
this._start = Dialogue._toTime(template.get("start"));
|
||||
this._end = Dialogue._toTime(template.get("end"));
|
||||
const start = template.get("start");
|
||||
if (typeof start !== "string") {
|
||||
throw new Error(`Dialogue start time ${ start } is not a string.`);
|
||||
}
|
||||
this._start = toTime(start);
|
||||
|
||||
const 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(valueOrDefault(template, "layer", parseInt, value => !isNaN(value), "0"), 0);
|
||||
|
||||
this._rawPartsString = template.get("text");
|
||||
if (this._rawPartsString === undefined || this._rawPartsString === null || this._rawPartsString.constructor !== String) {
|
||||
throw new Error("Dialogue doesn't have any text.");
|
||||
const text = template.get("text");
|
||||
if (typeof text !== "string") {
|
||||
throw new Error(`Dialogue text ${ text } is not a string.`);
|
||||
}
|
||||
this._rawPartsString = text;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,7 +184,7 @@ export class Dialogue {
|
||||
this._parsePartsString();
|
||||
}
|
||||
|
||||
return this._parts;
|
||||
return this._parts!;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,7 +211,7 @@ export class Dialogue {
|
||||
* Parses this dialogue's parts from the raw parts string.
|
||||
*/
|
||||
private _parsePartsString(): void {
|
||||
this._parts = <parts.Part[]>parse(this._rawPartsString, "dialogueParts");
|
||||
this._parts = parse(this._rawPartsString, "dialogueParts") as parts.Part[];
|
||||
|
||||
this._alignment = this._style.alignment;
|
||||
|
||||
@@ -209,7 +221,7 @@ export class Dialogue {
|
||||
}
|
||||
else if (part instanceof parts.Move) {
|
||||
if (part.t1 === null || part.t2 === null) {
|
||||
this._parts[index] =
|
||||
this._parts![index] =
|
||||
new parts.Move(
|
||||
part.x1, part.y1, part.x2, part.y2,
|
||||
0, this._end - this._start
|
||||
@@ -218,7 +230,7 @@ export class Dialogue {
|
||||
}
|
||||
else if (part instanceof parts.Transform) {
|
||||
if (part.start === null || part.end === null || part.accel === null) {
|
||||
this._parts[index] =
|
||||
this._parts![index] =
|
||||
new parts.Transform(
|
||||
(part.start === null) ? 0 : part.start,
|
||||
(part.end === null) ? (this._end - this._start) : part.end,
|
||||
@@ -245,14 +257,14 @@ ${ possiblyIncorrectParses.join("\n") }`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 static _toTime(str: string): number {
|
||||
return str.split(":").reduce<number>((previousValue, currentValue) => previousValue * 60 + parseFloat(currentValue), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
function toTime(str: string): number {
|
||||
return str.split(":").reduce<number>((previousValue, currentValue) => previousValue * 60 + parseFloat(currentValue), 0);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ export interface TypedTemplate {
|
||||
* @param {T} defaultValue
|
||||
* @return {T}
|
||||
*/
|
||||
export function valueOrDefault<T>(template: Map<string, string>, key: string, converter: (str: string) => T, validator: (value: T) => boolean, defaultValue: string): T {
|
||||
export function valueOrDefault<T>(template: Map<string, string>, key: string, converter: (str: string) => T, validator: ((value: T) => boolean) | null, defaultValue: string): T {
|
||||
const value = template.get(key);
|
||||
if (value === undefined) {
|
||||
return converter(defaultValue);
|
||||
|
||||
@@ -18,11 +18,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { registerClass as serializable } from "../serialization";
|
||||
|
||||
import { WrappingStyle } from "./misc";
|
||||
|
||||
/**
|
||||
* This class represents the properties of a {@link libjass.ASS} script.
|
||||
*/
|
||||
@serializable
|
||||
export class ScriptProperties {
|
||||
private _resolutionX: number;
|
||||
private _resolutionY: number;
|
||||
|
||||
@@ -24,6 +24,8 @@ import { parse } from "../parser/parse";
|
||||
|
||||
import { Color } from "../parts";
|
||||
|
||||
import { registerClass as serializable } from "../serialization";
|
||||
|
||||
import { Map } from "../utility/map";
|
||||
|
||||
/**
|
||||
@@ -50,6 +52,7 @@ import { Map } from "../utility/map";
|
||||
* @param {string} template["MarginR"] The right margin
|
||||
* @param {string} template["MarginV"] The vertical margin
|
||||
*/
|
||||
@serializable
|
||||
export class Style {
|
||||
private _name: string;
|
||||
|
||||
@@ -93,11 +96,11 @@ export class Style {
|
||||
template = normalizedTemplate;
|
||||
}
|
||||
|
||||
this._name = template.get("name");
|
||||
if (this._name === undefined || this._name === null || this._name.constructor !== String) {
|
||||
throw new Error("Style doesn't have a name.");
|
||||
const name = template.get("name");
|
||||
if (typeof name !== "string") {
|
||||
throw new Error(`Style name ${ name } is not a string.`);
|
||||
}
|
||||
this._name = this._name.replace(/^\*+/, "");
|
||||
this._name = name.replace(/^\*+/, "");
|
||||
|
||||
this._italic = !!valueOrDefault(template, "italic", parseFloat, value => !isNaN(value), "0");
|
||||
this._bold = !!valueOrDefault(template, "bold", parseFloat, value => !isNaN(value), "0");
|
||||
@@ -114,13 +117,13 @@ export class Style {
|
||||
|
||||
this._rotationZ = valueOrDefault(template, "angle", parseFloat, value => !isNaN(value), "0");
|
||||
|
||||
this._primaryColor = valueOrDefault(template, "primarycolour", str => <Color>parse(str, "colorWithAlpha"), null, "&H00FFFFFF");
|
||||
this._secondaryColor = valueOrDefault(template, "secondarycolour", str => <Color>parse(str, "colorWithAlpha"), null, "&H00FFFF00");
|
||||
this._outlineColor = valueOrDefault(template, "outlinecolour", str => <Color>parse(str, "colorWithAlpha"), null, "&H00000000");
|
||||
this._shadowColor = valueOrDefault(template, "backcolour", str => <Color>parse(str, "colorWithAlpha"), null, "&H80000000");
|
||||
this._primaryColor = valueOrDefault(template, "primarycolour", str => parse(str, "colorWithAlpha") as Color, null, "&H00FFFFFF");
|
||||
this._secondaryColor = valueOrDefault(template, "secondarycolour", str => parse(str, "colorWithAlpha") as Color, null, "&H00FFFF00");
|
||||
this._outlineColor = valueOrDefault(template, "outlinecolour", str => parse(str, "colorWithAlpha") as Color, null, "&H00000000");
|
||||
this._shadowColor = valueOrDefault(template, "backcolour", str => parse(str, "colorWithAlpha") as Color, null, "&H80000000");
|
||||
|
||||
this._outlineThickness = valueOrDefault(template, "outline", parseFloat, value => value >= 0, "2");
|
||||
this._borderStyle = valueOrDefault(template, "borderstyle", parseInt, value => (<any>BorderStyle)[(<any>BorderStyle)[value]] === value, "1");
|
||||
this._borderStyle = valueOrDefault(template, "borderstyle", parseInt, value => (BorderStyle as any)[(BorderStyle as any)[value]] === value, "1");
|
||||
|
||||
this._shadowDepth = valueOrDefault(template, "shadow", parseFloat, value => value >= 0, "3");
|
||||
|
||||
|
||||
@@ -18,16 +18,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare const global: {
|
||||
Map?: typeof Map;
|
||||
};
|
||||
|
||||
export interface Map<K, V> {
|
||||
/**
|
||||
* @param {K} key
|
||||
* @return {V}
|
||||
* @return {?V}
|
||||
*/
|
||||
get(key: K): V;
|
||||
get(key: K): V | undefined;
|
||||
|
||||
/**
|
||||
* @param {K} key
|
||||
@@ -40,7 +36,7 @@ export interface Map<K, V> {
|
||||
* @param {V} value
|
||||
* @return {libjass.Map.<K, V>} This map
|
||||
*/
|
||||
set(key: K, value?: V): Map<K, V>;
|
||||
set(key: K, value: V): this;
|
||||
|
||||
/**
|
||||
* @param {K} key
|
||||
@@ -56,7 +52,7 @@ export interface Map<K, V> {
|
||||
* @param {function(V, K, libjass.Map.<K, V>)} callbackfn A function that is called with each key and value in the map.
|
||||
* @param {*} thisArg
|
||||
*/
|
||||
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void;
|
||||
forEach(callbackfn: (value: V, index: K, map: this) => void, thisArg?: any): void;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
@@ -64,18 +60,6 @@ export interface Map<K, V> {
|
||||
size: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to the global implementation of Map if the environment has one, else set to {@link ./utility/map.SimpleMap}
|
||||
*
|
||||
* Set it to null to force {@link ./utility/map.SimpleMap} to be used even if a global Map is present.
|
||||
*
|
||||
* @type {function(new:Map, !Array.<!Array.<*>>=)}
|
||||
*/
|
||||
export var Map: {
|
||||
new <K, V>(iterable?: [K, V][]): Map<K, V>;
|
||||
prototype: Map<any, any>;
|
||||
} = global.Map;
|
||||
|
||||
/**
|
||||
* Map implementation for browsers that don't support it. Only supports keys which are of Number or String type, or which have a property called "id".
|
||||
*
|
||||
@@ -83,7 +67,7 @@ export var Map: {
|
||||
*
|
||||
* @param {!Array.<!Array.<*>>=} iterable Only an array of elements (where each element is a 2-tuple of key and value) is supported.
|
||||
*/
|
||||
class SimpleMap<K, V> {
|
||||
class SimpleMap<K, V> implements Map<K, V> {
|
||||
private _keys: { [key: string]: K };
|
||||
private _values: { [key: string]: V };
|
||||
private _size: number;
|
||||
@@ -106,9 +90,9 @@ class SimpleMap<K, V> {
|
||||
|
||||
/**
|
||||
* @param {K} key
|
||||
* @return {V}
|
||||
* @return {?V}
|
||||
*/
|
||||
get(key: K): V {
|
||||
get(key: K): V | undefined {
|
||||
const property = this._keyToProperty(key);
|
||||
|
||||
if (property === null) {
|
||||
@@ -137,7 +121,7 @@ class SimpleMap<K, V> {
|
||||
* @param {V} value
|
||||
* @return {libjass.Map.<K, V>} This map
|
||||
*/
|
||||
set(key: K, value: V): Map<K, V> {
|
||||
set(key: K, value: V): this {
|
||||
const property = this._keyToProperty(key);
|
||||
|
||||
if (property === null) {
|
||||
@@ -188,7 +172,7 @@ class SimpleMap<K, V> {
|
||||
* @param {function(V, K, libjass.Map.<K, V>)} callbackfn A function that is called with each key and value in the map.
|
||||
* @param {*} thisArg
|
||||
*/
|
||||
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void {
|
||||
forEach(callbackfn: (value: V, index: K, map: this) => void, thisArg?: any): void {
|
||||
for (const property of Object.keys(this._keys)) {
|
||||
callbackfn.call(thisArg, this._values[property], this._keys[property], this);
|
||||
}
|
||||
@@ -205,9 +189,9 @@ class SimpleMap<K, V> {
|
||||
* Converts the given key into a property name for the internal map.
|
||||
*
|
||||
* @param {K} key
|
||||
* @return {string}
|
||||
* @return {?string}
|
||||
*/
|
||||
private _keyToProperty(key: K): string {
|
||||
private _keyToProperty(key: K): string | null {
|
||||
if (typeof key === "number") {
|
||||
return `#${ key }`;
|
||||
}
|
||||
@@ -216,15 +200,29 @@ class SimpleMap<K, V> {
|
||||
return `'${ key }`;
|
||||
}
|
||||
|
||||
if ((<any>key).id !== undefined) {
|
||||
return `!${ (<any>key).id }`;
|
||||
if ((key as any).id !== undefined) {
|
||||
return `!${ (key as any).id }`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (Map === undefined || typeof Map.prototype.forEach !== "function" || (() => {
|
||||
/**
|
||||
* Set to the global implementation of Map if the environment has one, else set to {@link ./utility/map.SimpleMap}
|
||||
*
|
||||
* Can be set to a value using {@link libjass.configure}
|
||||
*
|
||||
* Set it to null to force {@link ./utility/map.SimpleMap} to be used even if a global Map is present.
|
||||
*
|
||||
* @type {function(new:Map, !Array.<!Array.<*>>=)}
|
||||
*/
|
||||
export var Map: {
|
||||
new <K, V>(iterable?: [K, V][]): Map<K, V>;
|
||||
prototype: Map<any, any>;
|
||||
} = global.Map || SimpleMap;
|
||||
|
||||
if (typeof Map.prototype.forEach !== "function" || (() => {
|
||||
try {
|
||||
return new Map([[1, "foo"], [2, "bar"]]).size !== 2;
|
||||
}
|
||||
@@ -235,12 +233,16 @@ if (Map === undefined || typeof Map.prototype.forEach !== "function" || (() => {
|
||||
Map = SimpleMap;
|
||||
}
|
||||
|
||||
declare var global: {
|
||||
Map?: typeof Map;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the Map implementation used by libjass to the provided one. If null, {@link ./utility/map.SimpleMap} is used.
|
||||
*
|
||||
* @param {?function(new:Map, !Array.<!Array.<*>>=)} value
|
||||
*/
|
||||
export function setImplementation(value: typeof Map): void {
|
||||
export function setImplementation(value: typeof Map | null): void {
|
||||
if (value !== null) {
|
||||
Map = value;
|
||||
}
|
||||
|
||||
@@ -18,15 +18,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare const global: {
|
||||
Promise?: typeof Promise;
|
||||
MutationObserver?: typeof MutationObserver;
|
||||
WebkitMutationObserver?: typeof MutationObserver;
|
||||
process?: {
|
||||
nextTick(callback: () => void): void;
|
||||
}
|
||||
};
|
||||
|
||||
export interface Thenable<T> {
|
||||
/** @type {function(this:!Thenable.<T>, function(T|!Thenable.<T>), function(*))} */
|
||||
then: ThenableThen<T>;
|
||||
@@ -34,53 +25,38 @@ export interface Thenable<T> {
|
||||
|
||||
export interface ThenableThen<T> {
|
||||
/** @type {function(this:!Thenable.<T>, function(T|!Thenable.<T>), function(*))} */
|
||||
(resolve: (resolution: T | Thenable<T>) => void, reject: (reason: any) => void): void;
|
||||
(this: Thenable<T>, resolve: ((resolution: T | Thenable<T>) => void) | undefined, reject: ((reason: any) => void) | undefined): void;
|
||||
}
|
||||
|
||||
export interface Promise<T> extends Thenable<T> {
|
||||
/**
|
||||
* @param {?function(T):!Thenable.<U>} onFulfilled
|
||||
* @param {function(T):!Thenable.<U>} onFulfilled
|
||||
* @param {?function(*):(U|!Thenable.<U>)} onRejected
|
||||
* @return {!Promise.<U>}
|
||||
*/
|
||||
then<U>(onFulfilled?: (value: T) => Thenable<U>, onRejected?: (reason: any) => U | Thenable<U>): Promise<U>;
|
||||
then<U>(onFulfilled: (value: T) => Thenable<U> | undefined, onRejected?: (reason: any) => U | Thenable<U>): Promise<U>;
|
||||
|
||||
/**
|
||||
* @param {?function(T):U} onFulfilled
|
||||
* @param {function(T):U} onFulfilled
|
||||
* @param {?function(*):(U|!Thenable.<U>)} onRejected
|
||||
* @return {!Promise.<U>}
|
||||
*/
|
||||
then<U>(onFulfilled?: (value: T) => U, onRejected?: (reason: any) => U | Thenable<U>): Promise<U>;
|
||||
then<U>(onFulfilled: (value: T) => U | undefined, onRejected?: (reason: any) => U | Thenable<U>): Promise<U>;
|
||||
|
||||
/**
|
||||
* @param {function(*):(T|!Thenable.<T>)} onRejected
|
||||
* @return {!Promise.<T>}
|
||||
*/
|
||||
catch(onRejected?: (reason: any) => T | Thenable<T>): Promise<T>
|
||||
catch(onRejected: (reason: any) => T | Thenable<T>): Promise<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to the global implementation of Promise if the environment has one, else set to {@link ./utility/promise.SimplePromise}
|
||||
*
|
||||
* Set it to null to force {@link ./utility/promise.SimplePromise} to be used even if a global Promise is present.
|
||||
*
|
||||
* @type {function(new:Promise)}
|
||||
*/
|
||||
export var Promise: {
|
||||
new <T>(init: (resolve: (value: T | Thenable<T>) => void, reject: (reason: any) => void) => void): Promise<T>;
|
||||
prototype: Promise<any>;
|
||||
resolve<T>(value: T | Thenable<T>): Promise<T>;
|
||||
reject<T>(reason: any): Promise<T>;
|
||||
all<T>(values: (T | Thenable<T>)[]): Promise<T[]>;
|
||||
race<T>(values: (T | Thenable<T>)[]): Promise<T>;
|
||||
} = global.Promise;
|
||||
|
||||
// Based on https://github.com/petkaantonov/bluebird/blob/1b1467b95442c12378d0ea280ede61d640ab5510/src/schedule.js
|
||||
const enqueueJob: (callback: () => void) => void = (function () {
|
||||
const MutationObserver = global.MutationObserver || global.WebkitMutationObserver;
|
||||
if (global.process !== undefined && typeof global.process.nextTick === "function") {
|
||||
const process = global.process;
|
||||
return (callback: () => void) => {
|
||||
global.process.nextTick(callback);
|
||||
process.nextTick(callback);
|
||||
};
|
||||
}
|
||||
else if (MutationObserver !== undefined) {
|
||||
@@ -131,7 +107,7 @@ class SimplePromise<T> {
|
||||
private _fulfillReactions: FulfilledPromiseReaction<T, any>[] = [];
|
||||
private _rejectReactions: RejectedPromiseReaction<any>[] = [];
|
||||
|
||||
private _fulfilledValue: T = null;
|
||||
private _fulfilledValue: T | null = null;
|
||||
private _rejectedReason: any = null;
|
||||
|
||||
constructor(executor: (resolve: (resolution: T | Thenable<T>) => void, reject: (reason: any) => void) => void) {
|
||||
@@ -153,11 +129,11 @@ class SimplePromise<T> {
|
||||
* @param {?function(*):(U|!Thenable.<U>)} onRejected
|
||||
* @return {!Promise.<U>}
|
||||
*/
|
||||
then<U>(onFulfilled: (value: T) => U | Thenable<U>, onRejected: (reason: any) => U | Thenable<U>): Promise<U> {
|
||||
then<U>(onFulfilled: ((value: T) => U | Thenable<U>) | undefined, onRejected?: (reason: any) => U | Thenable<U>): Promise<U> {
|
||||
const resultCapability = new DeferredPromise<U>();
|
||||
|
||||
if (typeof onFulfilled !== "function") {
|
||||
onFulfilled = (value: T) => <U><any>value;
|
||||
onFulfilled = (value: T) => value as any as U;
|
||||
}
|
||||
|
||||
if (typeof onRejected !== "function") {
|
||||
@@ -181,7 +157,7 @@ class SimplePromise<T> {
|
||||
break;
|
||||
|
||||
case SimplePromiseState.FULFILLED:
|
||||
this._enqueueFulfilledReactionJob(fulfillReaction, this._fulfilledValue);
|
||||
this._enqueueFulfilledReactionJob(fulfillReaction, this._fulfilledValue!);
|
||||
break;
|
||||
|
||||
case SimplePromiseState.REJECTED:
|
||||
@@ -196,8 +172,8 @@ class SimplePromise<T> {
|
||||
* @param {function(*):(T|!Thenable.<T>)} onRejected
|
||||
* @return {!Promise.<T>}
|
||||
*/
|
||||
catch(onRejected?: (reason: any) => T | Thenable<T>): Promise<T> {
|
||||
return this.then(null, onRejected);
|
||||
catch(onRejected: (reason: any) => T | Thenable<T>): Promise<T> {
|
||||
return this.then(undefined, onRejected);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,12 +252,12 @@ class SimplePromise<T> {
|
||||
}
|
||||
|
||||
if (resolution === null || (typeof resolution !== "object" && typeof resolution !== "function")) {
|
||||
this._fulfill(<T>resolution);
|
||||
this._fulfill(resolution as T);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var then = (<Thenable<T>>resolution).then;
|
||||
var then = (resolution as Thenable<T>).then;
|
||||
}
|
||||
catch (ex) {
|
||||
this._reject(ex);
|
||||
@@ -289,11 +265,11 @@ class SimplePromise<T> {
|
||||
}
|
||||
|
||||
if (typeof then !== "function") {
|
||||
this._fulfill(<T>resolution);
|
||||
this._fulfill(resolution as T);
|
||||
return;
|
||||
}
|
||||
|
||||
enqueueJob(() => this._resolveWithThenable(<Thenable<T>>resolution, then));
|
||||
enqueueJob(() => this._resolveWithThenable(resolution as Thenable<T>, then));
|
||||
};
|
||||
|
||||
const reject = (reason: any): void => {
|
||||
@@ -331,8 +307,8 @@ class SimplePromise<T> {
|
||||
const reactions = this._fulfillReactions;
|
||||
|
||||
this._fulfilledValue = value;
|
||||
this._fulfillReactions = undefined;
|
||||
this._rejectReactions = undefined;
|
||||
this._fulfillReactions = [];
|
||||
this._rejectReactions = [];
|
||||
this._state = SimplePromiseState.FULFILLED;
|
||||
|
||||
for (const reaction of reactions) {
|
||||
@@ -347,8 +323,8 @@ class SimplePromise<T> {
|
||||
const reactions = this._rejectReactions;
|
||||
|
||||
this._rejectedReason = reason;
|
||||
this._fulfillReactions = undefined;
|
||||
this._rejectReactions = undefined;
|
||||
this._fulfillReactions = [];
|
||||
this._rejectReactions = [];
|
||||
this._state = SimplePromiseState.REJECTED;
|
||||
|
||||
for (const reaction of reactions) {
|
||||
@@ -401,9 +377,32 @@ class SimplePromise<T> {
|
||||
}
|
||||
}
|
||||
|
||||
if (Promise === undefined) {
|
||||
Promise = SimplePromise;
|
||||
}
|
||||
/**
|
||||
* Set to the global implementation of Promise if the environment has one, else set to {@link ./utility/promise.SimplePromise}
|
||||
*
|
||||
* Can be set to a value using {@link libjass.configure}
|
||||
*
|
||||
* Set it to null to force {@link ./utility/promise.SimplePromise} to be used even if a global Promise is present.
|
||||
*
|
||||
* @type {function(new:Promise)}
|
||||
*/
|
||||
export var Promise: {
|
||||
new <T>(init: (resolve: (value: T | Thenable<T>) => void, reject: (reason: any) => void) => void): Promise<T>;
|
||||
prototype: Promise<any>;
|
||||
resolve<T>(value: T | Thenable<T>): Promise<T>;
|
||||
reject<T>(reason: any): Promise<T>;
|
||||
all<T>(values: (T | Thenable<T>)[]): Promise<T[]>;
|
||||
race<T>(values: (T | Thenable<T>)[]): Promise<T>;
|
||||
} = global.Promise || SimplePromise;
|
||||
|
||||
declare var global: {
|
||||
Promise?: typeof Promise;
|
||||
MutationObserver?: typeof MutationObserver;
|
||||
WebkitMutationObserver?: typeof MutationObserver;
|
||||
process?: {
|
||||
nextTick(callback: () => void): void;
|
||||
}
|
||||
};
|
||||
|
||||
interface FulfilledPromiseReaction<T, U> {
|
||||
/** @type {!libjass.DeferredPromise.<U>} */
|
||||
@@ -441,7 +440,7 @@ enum SimplePromiseState {
|
||||
*
|
||||
* @param {?function(new:Promise)} value
|
||||
*/
|
||||
export function setImplementation(value: typeof Promise): void {
|
||||
export function setImplementation(value: typeof Promise | null): void {
|
||||
if (value !== null) {
|
||||
Promise = value;
|
||||
}
|
||||
|
||||
@@ -18,16 +18,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare const global: {
|
||||
Set?: typeof Set;
|
||||
};
|
||||
|
||||
export interface Set<T> {
|
||||
/**
|
||||
* @param {T} value
|
||||
* @return {libjass.Set.<T>} This set
|
||||
*/
|
||||
add(value: T): Set<T>;
|
||||
add(value: T): this;
|
||||
|
||||
/**
|
||||
*/
|
||||
@@ -43,7 +39,7 @@ export interface Set<T> {
|
||||
* @param {function(T, T, libjass.Set.<T>)} callbackfn A function that is called with each value in the set.
|
||||
* @param {*} thisArg
|
||||
*/
|
||||
forEach(callbackfn: (value: T, index: T, set: Set<T>) => void, thisArg?: any): void;
|
||||
forEach(callbackfn: (value: T, index: T, set: this) => void, thisArg?: any): void;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
@@ -51,18 +47,6 @@ export interface Set<T> {
|
||||
size: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to the global implementation of Set if the environment has one, else set to {@link ./utility/set.SimpleSet}
|
||||
*
|
||||
* Set it to null to force {@link ./utility/set.SimpleSet} to be used even if a global Set is present.
|
||||
*
|
||||
* @type {function(new:Set, !Array.<T>=)}
|
||||
*/
|
||||
export var Set: {
|
||||
new <T>(iterable?: T[]): Set<T>;
|
||||
prototype: Set<any>;
|
||||
} = global.Set;
|
||||
|
||||
/**
|
||||
* Set implementation for browsers that don't support it. Only supports Number and String elements.
|
||||
*
|
||||
@@ -70,7 +54,7 @@ export var Set: {
|
||||
*
|
||||
* @param {!Array.<T>=} iterable Only an array of values is supported.
|
||||
*/
|
||||
class SimpleSet<T> {
|
||||
class SimpleSet<T> implements Set<T> {
|
||||
private _elements: { [key: string]: T };
|
||||
private _size: number;
|
||||
|
||||
@@ -94,7 +78,7 @@ class SimpleSet<T> {
|
||||
* @param {T} value
|
||||
* @return {libjass.Set.<T>} This set
|
||||
*/
|
||||
add(value: T): Set<T> {
|
||||
add(value: T): this {
|
||||
const property = this._toProperty(value);
|
||||
|
||||
if (property === null) {
|
||||
@@ -135,7 +119,7 @@ class SimpleSet<T> {
|
||||
* @param {function(T, T, libjass.Set.<T>)} callbackfn A function that is called with each value in the set.
|
||||
* @param {*} thisArg
|
||||
*/
|
||||
forEach(callbackfn: (value: T, index: T, set: Set<T>) => void, thisArg?: any): void {
|
||||
forEach(callbackfn: (value: T, index: T, set: this) => void, thisArg?: any): void {
|
||||
for (const property of Object.keys(this._elements)) {
|
||||
const element = this._elements[property];
|
||||
callbackfn.call(thisArg, element, element, this);
|
||||
@@ -153,9 +137,9 @@ class SimpleSet<T> {
|
||||
* Converts the given value into a property name for the internal map.
|
||||
*
|
||||
* @param {T} value
|
||||
* @return {string}
|
||||
* @return {?string}
|
||||
*/
|
||||
private _toProperty(value: T): string {
|
||||
private _toProperty(value: T): string | null {
|
||||
if (typeof value === "number") {
|
||||
return `#${ value }`;
|
||||
}
|
||||
@@ -168,7 +152,21 @@ class SimpleSet<T> {
|
||||
}
|
||||
}
|
||||
|
||||
if (Set === undefined || typeof Set.prototype.forEach !== "function" || (() => {
|
||||
/**
|
||||
* Set to the global implementation of Set if the environment has one, else set to {@link ./utility/set.SimpleSet}
|
||||
*
|
||||
* Can be set to a value using {@link libjass.configure}
|
||||
*
|
||||
* Set it to null to force {@link ./utility/set.SimpleSet} to be used even if a global Set is present.
|
||||
*
|
||||
* @type {function(new:Set, !Array.<T>=)}
|
||||
*/
|
||||
export var Set: {
|
||||
new <T>(iterable?: T[]): Set<T>;
|
||||
prototype: Set<any>;
|
||||
} = global.Set || SimpleSet;
|
||||
|
||||
if (typeof Set.prototype.forEach !== "function" || (() => {
|
||||
try {
|
||||
return new Set([1, 2]).size !== 2;
|
||||
}
|
||||
@@ -179,12 +177,16 @@ if (Set === undefined || typeof Set.prototype.forEach !== "function" || (() => {
|
||||
Set = SimpleSet;
|
||||
}
|
||||
|
||||
declare var global: {
|
||||
Set?: typeof Set;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the Set implementation used by libjass to the provided one. If null, {@link ./utility/set.SimpleSet} is used.
|
||||
*
|
||||
* @param {?function(new:Set, !Array.<T>=)} value
|
||||
*/
|
||||
export function setImplementation(value: typeof Set): void {
|
||||
export function setImplementation(value: typeof Set | null): void {
|
||||
if (value !== null) {
|
||||
Set = value;
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class inheritance shim.
|
||||
*
|
||||
* @param {!Function} derived
|
||||
* @param {!Function} base
|
||||
*/
|
||||
export function __extends(derived: any, base: any): void {
|
||||
for (const property in base) {
|
||||
if (base.hasOwnProperty(property)) {
|
||||
derived[property] = base[property];
|
||||
}
|
||||
}
|
||||
|
||||
function __() {
|
||||
this.constructor = derived;
|
||||
}
|
||||
|
||||
__.prototype = base.prototype;
|
||||
|
||||
derived.prototype = new (<any>__)();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorator shim.
|
||||
*
|
||||
* @param {!Array.<!Function>} decorators
|
||||
* @param {!*} target
|
||||
* @param {string=} key
|
||||
* @return {*}
|
||||
*/
|
||||
export function __decorate(decorators: Function[], target: any, key?: string): any {
|
||||
if (arguments.length < 3) {
|
||||
return decorateClass(<any>decorators.reverse(), target);
|
||||
}
|
||||
else {
|
||||
decorateField(<any>decorators.reverse(), target, key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class decorator shim.
|
||||
*
|
||||
* @param {!Array.<function(function(new(): T)): function(new(): T)>} decorators
|
||||
* @param {function(new(): T)} clazz
|
||||
* @return {function(new(): T)}
|
||||
*/
|
||||
function decorateClass<T>(decorators: ((clazz: { new (...args: any[]): T }) => { new (...args: any[]): T })[], clazz: { new (...args: any[]): T }): { new (...args: any[]): T } {
|
||||
for (const decorator of decorators) {
|
||||
clazz = decorator(clazz) || clazz;
|
||||
}
|
||||
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class member decorator shim.
|
||||
*
|
||||
* @param {!Array.<function(T, string)>} decorators
|
||||
* @param {!T} proto
|
||||
* @param {string} name
|
||||
*/
|
||||
function decorateField<T>(decorators: ((proto: T, name: string) => void)[], proto: T, name: string): void {
|
||||
for (const decorator of decorators) {
|
||||
decorator(proto, name);
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { serialize, deserialize } from "../serialization";
|
||||
|
||||
import { Map } from "../utility/map";
|
||||
|
||||
import { Promise, DeferredPromise } from "../utility/promise";
|
||||
|
||||
import { WorkerCommands } from "./commands";
|
||||
|
||||
import { getWorkerCommandHandler, serialize, deserialize, registerWorkerCommand } from "./misc";
|
||||
import { getWorkerCommandHandler, registerWorkerCommand } from "./misc";
|
||||
|
||||
/**
|
||||
* Represents a communication channel between the host and the web worker. An instance of this class is created by calling {@link libjass.webworker.createWorker}
|
||||
@@ -50,7 +51,7 @@ export interface WorkerCommandHandler {
|
||||
/**
|
||||
* The interface implemented by a communication channel to the other side.
|
||||
*/
|
||||
interface WorkerCommunication {
|
||||
export interface WorkerCommunication {
|
||||
/**
|
||||
* @param {"message"} type
|
||||
* @param {function(!MessageEvent): *} listener
|
||||
@@ -135,7 +136,7 @@ export class WorkerChannelImpl implements WorkerChannel {
|
||||
private _pendingRequests = new Map<number, DeferredPromise<any>>();
|
||||
|
||||
constructor(private _comm: WorkerCommunication) {
|
||||
this._comm.addEventListener("message", ev => this._onMessage(<string>ev.data), false);
|
||||
this._comm.addEventListener("message", ev => this._onMessage(ev.data as string), false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,10 +183,10 @@ export class WorkerChannelImpl implements WorkerChannel {
|
||||
* @param {string} rawMessage
|
||||
*/
|
||||
private _onMessage(rawMessage: string): void {
|
||||
const message = <{ command: WorkerCommands }>deserialize(rawMessage);
|
||||
const message = deserialize(rawMessage) as { command: WorkerCommands };
|
||||
|
||||
if (message.command === WorkerCommands.Response) {
|
||||
const responseMessage = <WorkerResponseMessage><any>message;
|
||||
const responseMessage = message as any as WorkerResponseMessage;
|
||||
|
||||
const deferred = this._pendingRequests.get(responseMessage.requestId);
|
||||
if (deferred !== undefined) {
|
||||
@@ -199,7 +200,7 @@ export class WorkerChannelImpl implements WorkerChannel {
|
||||
}
|
||||
}
|
||||
else {
|
||||
const requestMessage = <WorkerRequestMessage>message;
|
||||
const requestMessage = message as WorkerRequestMessage;
|
||||
const requestId = requestMessage.requestId;
|
||||
|
||||
const commandCallback = getWorkerCommandHandler(requestMessage.command);
|
||||
@@ -216,4 +217,4 @@ export class WorkerChannelImpl implements WorkerChannel {
|
||||
}
|
||||
}
|
||||
|
||||
registerWorkerCommand(WorkerCommands.Ping, parameters => new Promise<void>(resolve => resolve(null)));
|
||||
registerWorkerCommand(WorkerCommands.Ping, parameters => Promise.resolve(null));
|
||||
|
||||
@@ -18,17 +18,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Map } from "../utility/map";
|
||||
|
||||
import { Promise, DeferredPromise } from "../utility/promise";
|
||||
|
||||
import { WorkerChannel, WorkerChannelImpl } from "./channel";
|
||||
export { WorkerChannel } from "./channel";
|
||||
|
||||
export { WorkerCommands } from "./commands";
|
||||
|
||||
declare const exports: any;
|
||||
|
||||
/**
|
||||
* Indicates whether web workers are supposed in this environment or not.
|
||||
*
|
||||
@@ -36,7 +30,7 @@ declare const exports: any;
|
||||
*/
|
||||
export const supported = typeof Worker !== "undefined";
|
||||
|
||||
const _scriptNode = (typeof document !== "undefined" && document.currentScript !== undefined) ? document.currentScript : null;
|
||||
const _scriptNode = (typeof document !== "undefined" && document.currentScript !== undefined) ? (document.currentScript as HTMLScriptElement) : null;
|
||||
|
||||
/**
|
||||
* Create a new web worker and returns a {@link libjass.webworker.WorkerChannel} to it.
|
||||
@@ -45,7 +39,15 @@ const _scriptNode = (typeof document !== "undefined" && document.currentScript !
|
||||
* the path will be determined from the src attribute of the <script> element that contains the currently running copy of libjass.js
|
||||
* @return {!libjass.webworker.WorkerChannel} A communication channel to the new web worker.
|
||||
*/
|
||||
export function createWorker(scriptPath: string = _scriptNode.src): WorkerChannel {
|
||||
export function createWorker(scriptPath?: string): WorkerChannel {
|
||||
if (scriptPath === undefined) {
|
||||
if (_scriptNode === null) {
|
||||
throw new Error("Could not auto-detect path of libjass.js, and explicit path was not passed in.");
|
||||
}
|
||||
|
||||
scriptPath = _scriptNode.src;
|
||||
}
|
||||
|
||||
return new WorkerChannelImpl(new Worker(scriptPath));
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ import { WorkerCommandHandler } from "./channel";
|
||||
|
||||
const workerCommands = new Map<WorkerCommands, WorkerCommandHandler>();
|
||||
|
||||
const classPrototypes = new Map<number, any>();
|
||||
|
||||
/**
|
||||
* Registers a handler for the given worker command.
|
||||
*
|
||||
@@ -43,50 +41,6 @@ export function registerWorkerCommand(command: WorkerCommands, handler: WorkerCo
|
||||
* @param {number} command
|
||||
* @return {?function(*, function(*, *))}
|
||||
*/
|
||||
export function getWorkerCommandHandler(command: WorkerCommands): WorkerCommandHandler {
|
||||
export function getWorkerCommandHandler(command: WorkerCommands): WorkerCommandHandler | undefined {
|
||||
return workerCommands.get(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a prototype as a deserializable type.
|
||||
*
|
||||
* @param {!*} prototype
|
||||
*/
|
||||
export function registerClassPrototype(prototype: any): void {
|
||||
prototype._classTag = classPrototypes.size;
|
||||
classPrototypes.set(prototype._classTag, prototype);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*} obj
|
||||
* @return {string}
|
||||
*/
|
||||
export function serialize(obj: any): string {
|
||||
return JSON.stringify(obj, (/* ujs:unreferenced */ key: string, value: any) => {
|
||||
if (value && value._classTag !== undefined) {
|
||||
value._classTag = value._classTag;
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @return {*}
|
||||
*/
|
||||
export function deserialize(str: string): any {
|
||||
return JSON.parse(str, (/* ujs:unreferenced */ key: string, value: any) => {
|
||||
if (value && value._classTag !== undefined) {
|
||||
const hydratedValue = Object.create(classPrototypes.get(value._classTag));
|
||||
for (const key of Object.keys(value)) {
|
||||
if (key !== "_classTag") {
|
||||
hydratedValue[key] = value[key];
|
||||
}
|
||||
}
|
||||
value = hydratedValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,13 +18,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
interface Document {
|
||||
/**
|
||||
* @type {!HTMLScriptElement}
|
||||
*/
|
||||
currentScript: HTMLScriptElement;
|
||||
}
|
||||
|
||||
interface WorkerGlobalScope {
|
||||
/**
|
||||
* @param {*} message
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
Title:
|
||||
ScriptType: v4.00+
|
||||
WrapStyle: 0
|
||||
PlayResX: 256
|
||||
PlayResY: 144
|
||||
PlayResX: 1280
|
||||
PlayResY: 720
|
||||
Scroll Position: 0
|
||||
Active Line: 0
|
||||
Video Zoom Percent: 1
|
||||
@@ -11,7 +11,7 @@ ScaledBorderAndShadow: yes
|
||||
|
||||
[V4+ Styles]
|
||||
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
||||
Style: Default,Arial,100,&H7F0000FF,&H00FFFFFF,&H7F000000,&H7F000000,0,0,0,0,100,100,0,0,1,2,0,2,15,15,15,1
|
||||
Style: Default,Arial,500,&H7F0000FF,&H00FFFFFF,&H7F000000,&H7F000000,0,0,0,0,100,100,0,0,1,10,0,2,75,75,75,1
|
||||
|
||||
[Events]
|
||||
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
||||
|
||||
@@ -21,10 +21,11 @@
|
||||
define(["intern!tdd", "require", "tests/support/test-page"], function (tdd, require, TestPage) {
|
||||
tdd.suite("alpha", function () {
|
||||
tdd.test("Basic", function () {
|
||||
var testPage = new TestPage(this.remote, require.toUrl("tests/support/browser-test-page.html"), "/tests/functional/alpha/alpha.ass", 256, 144, "rgb(47, 163, 254)");
|
||||
var testPage = new TestPage(this.remote, require.toUrl("tests/support/browser-test-page.html"), "/tests/functional/alpha/alpha.ass", 1280, 720, "rgb(47, 163, 254)");
|
||||
return testPage
|
||||
.prepare()
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(1, require.toUrl("./alpha-1.png")); });
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(1, require.toUrl("./alpha-1.png")); })
|
||||
.then(function (testPage) { return testPage.done(); });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,32 @@
|
||||
[Script Info]
|
||||
Title:
|
||||
ScriptType: v4.00+
|
||||
WrapStyle: 0
|
||||
PlayResX: 1280
|
||||
PlayResY: 720
|
||||
Scroll Position: 0
|
||||
Active Line: 0
|
||||
Video Zoom Percent: 1
|
||||
ScaledBorderAndShadow: yes
|
||||
|
||||
[V4+ Styles]
|
||||
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
||||
Style: Default,Arial,90,&H00FFFFFF,&H00000000,&H00000000,&H96000000,0,0,0,0,100,100,0,0,1,0,0,2,75,75,75,1
|
||||
|
||||
[Events]
|
||||
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
||||
Dialogue: 1,0:00:00.00,0:00:01.00,Default,,0,0,0,,{\an1\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:00.00,0:00:01.00,Default,,0,0,0,,{\an2\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:00.00,0:00:01.00,Default,,0,0,0,,{\an3\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:00.00,0:00:01.00,Default,,0,0,0,,{\an7\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:00.00,0:00:01.00,Default,,0,0,0,,{\an8\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:00.00,0:00:01.00,Default,,0,0,0,,{\an9\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:01.00,0:00:02.00,Default,,0,0,0,,{\an1\pos(640,360)\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:02.00,0:00:03.00,Default,,0,0,0,,{\an2\pos(640,360)\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:03.00,0:00:04.00,Default,,0,0,0,,{\an3\pos(640,360)\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:04.00,0:00:05.00,Default,,0,0,0,,{\an4\pos(640,360)\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:05.00,0:00:06.00,Default,,0,0,0,,{\an5\pos(640,360)\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:06.00,0:00:07.00,Default,,0,0,0,,{\an6\pos(640,360)\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:07.00,0:00:08.00,Default,,0,0,0,,{\an7\pos(640,360)\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:08.00,0:00:09.00,Default,,0,0,0,,{\an8\pos(640,360)\frz45}MM\NMM
|
||||
Dialogue: 1,0:00:09.00,0:00:10.00,Default,,0,0,0,,{\an9\pos(640,360)\frz45}MM\NMM
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define(["intern!tdd", "require", "tests/support/test-page"], function (tdd, require, TestPage) {
|
||||
tdd.suite("fr", function () {
|
||||
tdd.test("frz", function () {
|
||||
var testPage = new TestPage(this.remote, require.toUrl("tests/support/browser-test-page.html"), "/tests/functional/fr/frz.ass", 1280, 720);
|
||||
return testPage
|
||||
.prepare()
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(0.5, require.toUrl("./frz-01.png")); })
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(1.5, require.toUrl("./frz-02.png")); })
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(2.5, require.toUrl("./frz-03.png")); })
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(3.5, require.toUrl("./frz-04.png")); })
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(4.5, require.toUrl("./frz-05.png")); })
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(5.5, require.toUrl("./frz-06.png")); })
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(6.5, require.toUrl("./frz-07.png")); })
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(7.5, require.toUrl("./frz-08.png")); })
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(8.5, require.toUrl("./frz-09.png")); })
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(9.5, require.toUrl("./frz-10.png")); })
|
||||
.then(function (testPage) { return testPage.done(); });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
@@ -2,8 +2,8 @@
|
||||
Title:
|
||||
ScriptType: v4.00+
|
||||
WrapStyle: 0
|
||||
PlayResX: 256
|
||||
PlayResY: 144
|
||||
PlayResX: 1280
|
||||
PlayResY: 720
|
||||
Scroll Position: 0
|
||||
Active Line: 0
|
||||
Video Zoom Percent: 1
|
||||
@@ -11,7 +11,7 @@ ScaledBorderAndShadow: yes
|
||||
|
||||
[V4+ Styles]
|
||||
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
||||
Style: Default,Arial,36,&H000000FF,&H00FFFFFF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,1.2,2,15,15,15,1
|
||||
Style: Default,Arial,180,&H000000FF,&H00FFFFFF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,10,6,2,75,75,75,1
|
||||
|
||||
[Events]
|
||||
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
||||
|
||||
@@ -21,10 +21,11 @@
|
||||
define(["intern!tdd", "require", "tests/support/test-page"], function (tdd, require, TestPage) {
|
||||
tdd.suite("fscx and fscy", function () {
|
||||
tdd.test("Basic", function () {
|
||||
var testPage = new TestPage(this.remote, require.toUrl("tests/support/browser-test-page.html"), "/tests/functional/fsc/fsc.ass", 256, 144);
|
||||
var testPage = new TestPage(this.remote, require.toUrl("tests/support/browser-test-page.html"), "/tests/functional/fsc/fsc.ass", 1280, 720);
|
||||
return testPage
|
||||
.prepare()
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(1, require.toUrl("./fsc-1.png")); });
|
||||
.then(function (testPage) { return testPage.seekAndCompareScreenshot(1, require.toUrl("./fsc-1.png")); })
|
||||
.then(function (testPage) { return testPage.done(); });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 15 KiB |
@@ -6,12 +6,12 @@ ScriptType: v4.00+
|
||||
WrapStyle: 0
|
||||
ScaledBorderAndShadow: yes
|
||||
YCbCr Matrix: None
|
||||
PlayResX: 256
|
||||
PlayResY: 144
|
||||
PlayResX: 1280
|
||||
PlayResY: 720
|
||||
|
||||
[V4+ Styles]
|
||||
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
||||
Style: Default,Arial,36,&H00FFFFFF,&H000000FF,&H00000000,&H96000000,0,0,0,0,100,100,0,0,1,2,1.2,2,15,15,15,1
|
||||
Style: Default,Arial,180,&H00FFFFFF,&H000000FF,&H00000000,&H96000000,0,0,0,0,100,100,0,0,1,10,6,2,75,75,75,1
|
||||
|
||||
[Events]
|
||||
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
||||
|
||||