1 Commits

Author SHA1 Message Date
Arnavion 3d3ef58d73 Removed RendererSettings class. 2016-01-25 13:22:26 -08:00
151 changed files with 1660 additions and 2255 deletions
+8 -10
View File
@@ -1,14 +1,12 @@
/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
/src/version.ts
npm-debug.log
-1
View File
@@ -4,7 +4,6 @@ node_js:
- "0.12"
- "4"
- "5"
- "6"
before_script:
- "node ./build.js doc"
sudo: false
+1 -2
View File
@@ -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 [RendererSettings.makeFontMapFromStyleElement()](http://arnavion.github.io/libjass/api.xhtml#libjass.renderers.RendererSettings.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 [libjass.renderers.makeFontMapFromStyleElement()](http://arnavion.github.io/libjass/api.xhtml#libjass.renderers.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,7 +137,6 @@ 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
+6 -42
View File
@@ -67,43 +67,7 @@ task("build-tools", function (callback) {
task("default", ["libjass.js", "libjass.min.js"]);
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) {
task("libjass.js", ["build-tools"], function (callback) {
fs.exists("./lib/libjass.js", function (exists) {
if (exists) {
callback();
@@ -112,7 +76,7 @@ task("libjass.js", ["build-tools", "version.ts"], function (callback) {
callback(null, task.src("./src/tsconfig.json")
.pipe(TypeScript.build("./src/index.ts", "libjass"))
.pipe(UglifyJS.build("libjass", ["AttachmentType", "BorderStyle", "ClockEvent", "Format", "WorkerCommands", "WrappingStyle"]))
.pipe(UglifyJS.build("./src/index", "libjass", ["AttachmentType", "BorderStyle", "ClockEvent", "Format", "WorkerCommands", "WrappingStyle"]))
.pipe(task.dest("./lib")));
});
});
@@ -130,9 +94,9 @@ task("libjass.min.js", ["libjass.js"], function (callback) {
});
});
task("clean", task.clean(["./src/version.ts", "./lib/libjass.js", "./lib/libjass.js.map", "./lib/libjass.min.js", "./lib/libjass.min.js.map"]));
task("clean", task.clean(["./lib/libjass.js", "./lib/libjass.js.map", "./lib/libjass.min.js", "./lib/libjass.min.js.map"]));
task("watch", ["build-tools", "version.ts"], function (callback) {
task("watch", ["build-tools"], function (callback) {
npm.load(function () {
var testRunning = false;
var rerunTest = false;
@@ -161,7 +125,7 @@ task("watch", ["build-tools", "version.ts"], function (callback) {
task.src("./src/tsconfig.json")
.pipe(TypeScript.watch("./src/index.ts", "libjass"))
.pipe(UglifyJS.watch("libjass", ["BorderStyle", "ClockEvent", "Format", "WorkerCommands", "WrappingStyle"]))
.pipe(UglifyJS.watch("./src/index", "libjass", ["BorderStyle", "ClockEvent", "Format", "WorkerCommands", "WrappingStyle"]))
.pipe(task.dest("./lib"))
.pipe(new task.FileTransform(function (file) {
if (file.path === "libjass.js") {
@@ -201,7 +165,7 @@ task("demo", ["libjass.js"], function () {
task("doc", ["make-doc", "test-doc"]);
task("make-doc", ["build-tools", "version.ts"], function () {
task("make-doc", ["build-tools"], function () {
var Doc = require("./build/doc.js");
return task.src("./src/tsconfig.json")
+69 -60
View File
@@ -18,14 +18,18 @@
* limitations under the License.
*/
import { FileTransform } from "async-build";
import * as fs from "fs";
import * as path from "path";
import { Transform } from "stream";
import { File, 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[] {
let result: T[] = [];
var result: T[] = [];
for (const a of arr) {
result = result.concat(a);
@@ -34,7 +38,7 @@ function flatten<T>(arr: T[][]): T[] {
return result;
}
const sorter = (() => {
var sorter = (() => {
function visibilitySorter(value1: { isPrivate?: boolean; isProtected?: boolean; }, value2: { isPrivate?: boolean; isProtected?: boolean; }) {
if (value1.isPrivate === value2.isPrivate && value1.isProtected === value2.isProtected) {
return 0;
@@ -59,10 +63,10 @@ const sorter = (() => {
return 0;
}
const types = [AST.Property, AST.Function, AST.Interface, AST.Class, AST.Enum];
var types = [AST.Property, AST.Function, AST.Interface, AST.Class, AST.Enum];
function typeSorter(value1: AST.ModuleMember | AST.NamespaceMember, value2: AST.ModuleMember | AST.NamespaceMember) {
let type1Index = -1;
let type2Index = -1;
var type1Index = -1;
var type2Index = -1;
types.every((type, index) => {
if (value1 instanceof type) {
@@ -81,11 +85,11 @@ const sorter = (() => {
return value1.name.localeCompare(value2.name);
}
const sorters: ((value1: AST.ModuleMember, value2: AST.ModuleMember) => number)[] = [visibilitySorter, typeSorter, nameSorter];
var sorters: ((value1: AST.ModuleMember, value2: AST.ModuleMember) => number)[] = [visibilitySorter, typeSorter, nameSorter];
return (value1: AST.ModuleMember, value2: AST.ModuleMember) => {
for (const sorter of sorters) {
const result = sorter(value1, value2);
for (var i = 0; i < sorters.length; i++) {
var result = sorters[i](value1, value2);
if (result !== 0) {
return result;
@@ -107,10 +111,10 @@ function sanitize(str: string) {
function toVariableName(item: { name: string }) {
// TODO: Handle non-letters (are both their toLowerCase() and toUpperCase())
const name = item.name;
let result = "";
var name = item.name;
var result = "";
for (let i = 0; i < name.length; i++) {
for (var i = 0; i < name.length; i++) {
if (name[i] === name[i].toLowerCase()) {
// This is lower case. Write it as lower case.
result += name[i];
@@ -165,15 +169,15 @@ function toUsageName(item: AST.Class | AST.Interface | AST.Function | AST.Proper
}
if (item.parent instanceof AST.Namespace) {
if ((item as AST.CanBePrivate).isPrivate) {
if ((<AST.Class | AST.Interface | AST.Function | AST.Enum>item).isPrivate) {
return item.name;
}
return item.fullName;
}
if ((item as AST.CanBeStatic).isStatic) {
return toUsageName(item.parent as AST.Class | AST.Interface) + '.' + item.name;
if ((<AST.Function>item).isStatic) {
return toUsageName(<AST.Class | AST.Interface>item.parent) + '.' + item.name;
}
return toVariableName(item.parent) + '.' + item.name;
@@ -184,12 +188,13 @@ function toId(item: { fullName?: string; name: string; }): string {
}
function toLink(item: AST.ModuleMember | AST.EnumMember | AST.TypeReference): string {
let result = `<a href="#${ toId(item) }">${ sanitize(item.name) }`;
var result = `<a href="#${ toId(item) }">${ sanitize(item.name) }`;
if (AST.hasGenerics(item) && item.generics.length > 0) {
const generics = item.generics as (string | AST.TypeReference | AST.IntrinsicTypeReference)[];
var itemWithGenerics = <AST.HasGenerics>item;
if (itemWithGenerics.generics !== undefined && itemWithGenerics.generics.length > 0) {
var generics = <(string | AST.TypeReference | AST.IntrinsicTypeReference)[]>itemWithGenerics.generics;
result += sanitize(`.<${ generics.map(generic =>
(generic instanceof AST.TypeReference || generic instanceof AST.IntrinsicTypeReference) ? generic.name : generic
(generic instanceof AST.TypeReference || generic instanceof AST.IntrinsicTypeReference) ? generic.name : <string>generic
).join(', ') }>`);
}
@@ -199,9 +204,9 @@ function toLink(item: AST.ModuleMember | AST.EnumMember | AST.TypeReference): st
}
function writeDescription(text: string): string {
let result = sanitize(text).replace(/\{@link ([^} ]+)\}/g, (substring, linkTarget) => `<a href="#${ linkTarget }">${ linkTarget }</a>`);
var result = sanitize(text).replace(/\{@link ([^} ]+)\}/g, (substring, linkTarget) => `<a href="#${ linkTarget }">${ linkTarget }</a>`);
let inCodeBlock = false;
var inCodeBlock = false;
result = result.split("\n").map(line => {
if (line.substr(0, " ".length) === " ") {
line = line.substr(" ".length);
@@ -284,7 +289,7 @@ function functionToHtml(func: AST.Function): string[] {
}
function interfaceToHtml(interfase: AST.Interface): string[] {
const members: AST.InterfaceMember[] = [];
var members: AST.InterfaceMember[] = [];
Object.keys(interfase.members).forEach(memberName => members.push(interfase.members[memberName]));
members.sort(sorter);
@@ -306,7 +311,7 @@ function interfaceToHtml(interfase: AST.Interface): string[] {
return functionToHtml(member).map(indenter(2));
}
else {
throw new Error(`Unrecognized member type: ${ (member as any).constructor.name }`);
throw new Error(`Unrecognized member type: ${ (<any>member.constructor).name }`);
}
}))).concat([
' </dd>',
@@ -316,7 +321,7 @@ function interfaceToHtml(interfase: AST.Interface): string[] {
}
function classToHtml(clazz: AST.Class): string[] {
const members: AST.InterfaceMember[] = [];
var members: AST.InterfaceMember[] = [];
Object.keys(clazz.members).forEach(memberName => members.push(clazz.members[memberName]));
members.sort(sorter);
@@ -326,7 +331,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(clazz.baseType) : clazz.baseType.name }` : '' }${
(clazz.baseType !== null) ? ` extends ${ (clazz.baseType instanceof AST.TypeReference) ? toLink(<AST.TypeReference>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) }`,
@@ -346,7 +351,7 @@ function classToHtml(clazz: AST.Class): string[] {
return functionToHtml(member).map(indenter(2));
}
else {
throw new Error(`Unrecognized member type: ${ (member as any).constructor.name }`);
throw new Error(`Unrecognized member type: ${ (<any>member.constructor).name }`);
}
}))).concat([
' </dd>',
@@ -402,26 +407,30 @@ function propertyToHtml(property: AST.Property): string[] {
}
export function build(outputFilePath: string, root: string, rootNamespaceName: string): FileTransform {
const compiler = new Compiler();
var compiler = new Compiler();
return new FileTransform(function (file: File): void {
var self: FileTransform = this;
return new FileTransform(function (file): void {
// Compile
compiler.compile(file);
// Walk
const walkResult = walk(compiler, root, rootNamespaceName);
const namespaces = walkResult.namespaces;
const modules = walkResult.modules;
var walkResult = walk(compiler, root, rootNamespaceName);
var namespaces = walkResult.namespaces;
var modules = walkResult.modules;
// Make HTML
const namespaceNames = Object.keys(namespaces)
var namespaceNames = Object.keys(namespaces)
.filter(namespaceName => namespaceName.substr(0, rootNamespaceName.length) === rootNamespaceName)
.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);
var moduleNames = Object.keys(modules).sort((ns1, ns2) => ns1.localeCompare(ns2));
this.push({
moduleNames = moduleNames.filter(moduleName => Object.keys(modules[moduleName].members).length > 0);
self.push({
path: outputFilePath,
contents: Buffer.concat([new Buffer(
`<?xml version="1.0" encoding="utf-8" ?>
@@ -581,9 +590,9 @@ export function build(outputFilePath: string, root: string, rootNamespaceName: s
<label><input type="checkbox" id="show-private" />Show private</label>
`
)].concat(namespaceNames.map(namespaceName => {
const namespace = namespaces[namespaceName];
var namespace = namespaces[namespaceName];
const namespaceMembers: AST.NamespaceMember[] = [];
var namespaceMembers: AST.NamespaceMember[] = [];
for (const memberName of Object.keys(namespace.members)) {
namespaceMembers.push(namespace.members[memberName]);
}
@@ -605,13 +614,13 @@ export function build(outputFilePath: string, root: string, rootNamespaceName: s
`
)]));
})).concat(moduleNames.map(moduleName => {
const module = modules[moduleName];
var module = modules[moduleName];
const moduleMembers: AST.ModuleMemberWithoutReference[] = [];
var moduleMembers: AST.ModuleMemberWithoutReference[] = [];
for (const memberName of Object.keys(module.members)) {
const member = module.members[memberName];
if ((member as AST.HasParent).parent === module) {
moduleMembers.push(member as AST.ModuleMemberWithoutReference);
var member = module.members[memberName];
if ((<AST.HasParent><any>member).parent === module) {
moduleMembers.push(<AST.ModuleMemberWithoutReference>member);
}
}
@@ -641,22 +650,22 @@ export function build(outputFilePath: string, root: string, rootNamespaceName: s
<div class="content">
`
)]).concat(flatten(namespaceNames.map(namespaceName => {
const namespace = namespaces[namespaceName];
var namespace = namespaces[namespaceName];
const namespaceMembers: AST.NamespaceMember[] = [];
var namespaceMembers: AST.NamespaceMember[] = [];
for (const memberName of Object.keys(namespace.members)) {
namespaceMembers.push(namespace.members[memberName]);
}
namespaceMembers.sort(sorter);
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 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 result = [new Buffer(
var result = [new Buffer(
` <section class="namespace">
<h1 id="${ sanitize(namespaceName) }">Namespace ${ sanitize(namespaceName) }</h1>
`
@@ -754,13 +763,13 @@ export function build(outputFilePath: string, root: string, rootNamespaceName: s
return result;
}))).concat(flatten(moduleNames.map(moduleName => {
const module = modules[moduleName];
var module = modules[moduleName];
const moduleMembers: AST.ModuleMember[] = [];
var moduleMembers: AST.ModuleMember[] = [];
for (const memberName of Object.keys(module.members)) {
const member = module.members[memberName];
if ((member as AST.HasParent).parent === module) {
moduleMembers.push(member);
var member = module.members[memberName];
if ((<AST.HasParent><any>member).parent === module) {
moduleMembers.push(<AST.ModuleMember>member);
}
}
@@ -770,13 +779,13 @@ export function build(outputFilePath: string, root: string, rootNamespaceName: s
moduleMembers.sort(sorter);
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 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 result = [new Buffer(
var result = [new Buffer(
` <section class="module">
<h1 id="${ sanitize(moduleName) }">Module ${ sanitize(moduleName) }</h1>
`
-6
View File
@@ -7,12 +7,6 @@ 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;
-25
View File
@@ -1,25 +0,0 @@
{
"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"
]
}
+2 -12
View File
@@ -18,7 +18,7 @@
* limitations under the License.
*/
import ts = require("typescript");
import * as ts from "typescript";
export class HasParent {
public parent: HasParent = null;
@@ -30,7 +30,7 @@ export class HasParent {
return this.name;
}
const parent = this.parent;
var parent = this.parent;
if (parent instanceof Namespace) {
return parent.getMemberFullName(this);
}
@@ -188,17 +188,7 @@ 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;
+186 -71
View File
@@ -18,70 +18,46 @@
* limitations under the License.
*/
import path = require("path");
import ts = require("typescript");
import * as fs from "fs";
import * as path from "path";
import { Transform } from "stream";
import * as ts from "typescript";
import { File, FileTransform } from "async-build";
import { File, FileTransform, FileWatcher } from "async-build";
import * as AST from "./ast";
import { walk } from "./walker";
export interface StreamingCompilerHost extends ts.CompilerHost {
export interface StreamingCompilerHost extends ts.CompilerHost, ts.ParseConfigHost {
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);
const projectConfig = ts.parseJsonConfigFileContent(JSON.parse(projectConfigFile.contents.toString()), ts.sys, this._projectRoot);
var projectConfig = ts.parseJsonConfigFileContent(JSON.parse(projectConfigFile.contents.toString()), this._host, this._projectRoot);
this._host = createCompilerHost(projectConfig.options);
this._program = ts.createProgram(projectConfig.fileNames, projectConfig.options, this._host);
const syntacticDiagnostics = this._program.getSyntacticDiagnostics();
var syntacticDiagnostics = this._program.getSyntacticDiagnostics();
if (syntacticDiagnostics.length > 0) {
this._reportDiagnostics(syntacticDiagnostics);
throw new Error("There were one or more syntactic diagnostics.");
}
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();
var globalDiagnostics = this._program.getGlobalDiagnostics();
if (globalDiagnostics.length > 0) {
this._reportDiagnostics(globalDiagnostics);
throw new Error("There were one or more global diagnostics.");
}
const semanticDiagnostics = this._program.getSemanticDiagnostics();
var semanticDiagnostics = this._program.getSemanticDiagnostics();
if (semanticDiagnostics.length > 0) {
this._reportDiagnostics(semanticDiagnostics);
throw new Error("There were one or more semantic diagnostics.");
@@ -91,7 +67,7 @@ export class Compiler {
writeFiles(outputStream: FileTransform) {
this._host.setOutputStream(outputStream);
const emitDiagnostics = this._program.emit().diagnostics;
var emitDiagnostics = this._program.emit().diagnostics;
if (emitDiagnostics.length > 0) {
this._reportDiagnostics(emitDiagnostics);
throw new Error("There were one or more emit diagnostics.");
@@ -112,10 +88,10 @@ export class Compiler {
private _reportDiagnostics(diagnostics: ts.Diagnostic[]) {
for (const diagnostic of diagnostics) {
let message = "";
var message = "";
if (diagnostic.file) {
const location = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
var location = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
message = `${ diagnostic.file.fileName }(${ location.line + 1 },${ location.character }): `;
}
@@ -129,23 +105,168 @@ export class Compiler {
};
}
export function build(root: string, rootNamespaceName: string): FileTransform {
const compiler = new 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;
return new FileTransform(function (projectConfigFile): void {
console.log("Compiling " + projectConfigFile.path + "...");
compiler.compile(projectConfigFile);
const walkResult = walk(compiler, root, rootNamespaceName);
var walkResult = walk(compiler, root, rootNamespaceName);
addJSDocComments(walkResult.modules);
compiler.writeFiles(this);
compiler.writeFiles(self);
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) {
@@ -156,18 +277,18 @@ function addJSDocComments(modules: { [name: string]: AST.Module }): void {
return;
}
const newComments: string[] = [];
var newComments: string[] = [];
if (current instanceof AST.Class) {
newComments.push("@constructor");
if (current.baseType !== null) {
const baseType = current.baseType;
var baseType = current.baseType;
newComments.push(
"@extends {" +
baseType.fullName + (
(baseType instanceof AST.TypeReference && baseType.generics.length) > 0 ?
(".<" + (baseType as AST.TypeReference).generics.map(generic => generic.fullName).join(", ") + ">") :
(".<" + (<AST.TypeReference>baseType).generics.map(generic => generic.fullName).join(", ") + ">") :
""
) +
"}"
@@ -197,37 +318,37 @@ function addJSDocComments(modules: { [name: string]: AST.Module }): void {
return;
}
if (current.parent instanceof AST.Namespace) {
newComments.push("@memberOf " + current.parent.fullName);
if ((<AST.HasParent><any>current).parent instanceof AST.Namespace) {
newComments.push("@memberOf " + (<AST.HasParent><any>current).parent.fullName);
}
if (AST.hasStringGenerics(current) && current.generics.length > 0) {
newComments.push("@template " + current.generics.join(", "));
if ((<AST.HasStringGenerics>current).generics !== undefined && (<AST.HasStringGenerics>current).generics.length > 0) {
newComments.push("@template " + (<AST.HasStringGenerics>current).generics.join(", "));
}
if ((current as AST.CanBePrivate).isPrivate) {
if ((<AST.CanBePrivate><any>current).isPrivate) {
newComments.push("@private");
}
if ((current as AST.CanBeProtected).isProtected) {
if ((<AST.CanBeProtected>current).isProtected) {
newComments.push("@protected");
}
if ((current as AST.CanBeStatic).isStatic) {
if ((<AST.CanBeStatic>current).isStatic) {
newComments.push("@static");
}
if (newComments.length > 0) {
if (current instanceof AST.Property) {
const nodes: ts.Node[] = [];
var 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) {
(node as any)["typescript-new-comment"] = newComments;
(<any>node)["typescript-new-comment"] = newComments;
}
}
else {
(current.astNode as any)["typescript-new-comment"] = newComments;
(<any>(<AST.Class | AST.Interface | AST.Function | AST.Enum>current).astNode)["typescript-new-comment"] = newComments;
}
}
}
@@ -237,7 +358,6 @@ function addJSDocComments(modules: { [name: string]: AST.Module }): void {
}
}
/*
class FakeSourceFile {
public text: string;
public lineMap: number[];
@@ -278,17 +398,15 @@ class FakeSourceFile {
}
var fakeSourceFiles: { [name: string]: FakeSourceFile } = Object.create(null);
*/
export const oldGetLeadingCommentRangesOfNodeFromText: typeof ts.getLeadingCommentRangesOfNodeFromText = ts.getLeadingCommentRangesOfNodeFromText.bind(ts);
export var oldGetLeadingCommentRangesOfNode: typeof ts.getLeadingCommentRangesOfNode = ts.getLeadingCommentRangesOfNode.bind(ts);
ts.getLeadingCommentRangesOfNode = (node: ts.Node, sourceFileOfNode: ts.SourceFile) => {
sourceFileOfNode = sourceFileOfNode || ts.getSourceFileOfNode(node);
/*
ts.getLeadingCommentRangesOfNodeFromText = (node: ts.Node, text: string) => {
const originalComments = oldGetLeadingCommentRangesOfNodeFromText(node, text);
var originalComments = oldGetLeadingCommentRangesOfNode(node, sourceFileOfNode);
if (originalComments !== undefined && (<any>node)["typescript-new-comment"] !== undefined) {
const sourceFileOfNode = ts.getSourceFileOfNode(node);
let fakeSourceFile = fakeSourceFiles[sourceFileOfNode.fileName];
var fakeSourceFile = fakeSourceFiles[sourceFileOfNode.fileName];
if (fakeSourceFile === undefined) {
fakeSourceFile = fakeSourceFiles[sourceFileOfNode.fileName] = new FakeSourceFile(sourceFileOfNode);
}
@@ -300,13 +418,10 @@ ts.getLeadingCommentRangesOfNodeFromText = (node: ts.Node, text: string) => {
};
var oldWriteCommentRange: typeof ts.writeCommentRange = ts.writeCommentRange.bind(ts);
ts.writeCommentRange = (text: string, lineMap: number[], writer: ts.EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) => {
ts.writeCommentRange = (currentSourceFile: ts.SourceFile, writer: ts.EmitTextWriter, comment: ts.CommentRange, newLine: string) => {
if ((<{ sourceFile: ts.SourceFile }><any>comment).sourceFile) {
const currentSourceFile = (<{ sourceFile: ts.SourceFile }><any>comment).sourceFile;
text = currentSourceFile.text;
lineMap = currentSourceFile.lineMap;
currentSourceFile = (<{ sourceFile: ts.SourceFile }><any>comment).sourceFile;
}
return oldWriteCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
return oldWriteCommentRange(currentSourceFile, writer, comment, newLine);
};
*/
+12 -17
View File
@@ -1,23 +1,18 @@
declare namespace ts {
interface EmitTextWriter { }
export interface EmitTextWriter { }
interface IntrinsicType extends Type {
export interface IntrinsicType extends Type {
intrinsicName: string;
}
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;
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;
}
+1 -1
View File
@@ -18,4 +18,4 @@
* limitations under the License.
*/
export { build } from "./compiler";
export { build, watch } from "./compiler";
+167 -175
View File
@@ -18,10 +18,10 @@
* limitations under the License.
*/
import path = require("path");
import ts = require("typescript");
import * as path from "path";
import * as ts from "typescript";
import { Compiler, oldGetLeadingCommentRangesOfNodeFromText } from "./compiler";
import { Compiler, oldGetLeadingCommentRangesOfNode } from "./compiler";
import * as AST from "./ast";
@@ -68,13 +68,13 @@ class Walker {
}
walk(sourceFile: ts.SourceFile): void {
const moduleName = this._moduleNameFromFileName(sourceFile.fileName);
var moduleName = this._moduleNameFromFileName(sourceFile.fileName);
if (!(moduleName in this.modules)) {
this.modules[moduleName] = new AST.Module(moduleName);
}
const module = this._scope.enter(this.modules[moduleName]);
var 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(node as ts.VariableStatement, parent);
this._visitVariableStatement(<ts.VariableStatement>node, parent);
break;
case ts.SyntaxKind.FunctionDeclaration:
this._visitFunctionDeclaration(node as ts.FunctionDeclaration, parent);
this._visitFunctionDeclaration(<ts.FunctionDeclaration>node, parent);
break;
case ts.SyntaxKind.ClassDeclaration:
this._visitClassDeclaration(node as ts.ClassDeclaration, parent);
this._visitClassDeclaration(<ts.ClassDeclaration>node, parent);
break;
case ts.SyntaxKind.InterfaceDeclaration:
this._visitInterfaceDeclaration(node as ts.InterfaceDeclaration, parent);
this._visitInterfaceDeclaration(<ts.InterfaceDeclaration>node, parent);
break;
case ts.SyntaxKind.EnumDeclaration:
this._visitEnumDeclaration(node as ts.EnumDeclaration, parent);
this._visitEnumDeclaration(<ts.EnumDeclaration>node, parent);
break;
case ts.SyntaxKind.ImportDeclaration:
this._visitImportDeclaration(node as ts.ImportDeclaration, parent);
this._visitImportDeclaration(<ts.ImportDeclaration>node, parent);
break;
case ts.SyntaxKind.ExportDeclaration:
this._visitExportDeclaration(node as ts.ExportDeclaration, parent);
this._visitExportDeclaration(<ts.ExportDeclaration>node, parent);
break;
case ts.SyntaxKind.ExpressionStatement:
@@ -121,7 +121,7 @@ class Walker {
break;
default:
console.error(node.kind, ts.SyntaxKind[node.kind], node);
console.error(node.kind, (<any>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(node as ts.PropertyDeclaration, clazz);
this._visitProperty(<ts.PropertyDeclaration>node, clazz);
break;
case ts.SyntaxKind.MethodSignature:
case ts.SyntaxKind.MethodDeclaration:
this._visitMethod(node as ts.MethodDeclaration, clazz);
this._visitMethod(<ts.MethodDeclaration>node, clazz);
break;
case ts.SyntaxKind.GetAccessor:
this._visitGetAccessor(node as ts.AccessorDeclaration, clazz);
this._visitGetAccessor(<ts.AccessorDeclaration>node, clazz);
break;
case ts.SyntaxKind.SetAccessor:
this._visitSetAccessor(node as ts.AccessorDeclaration, clazz);
this._visitSetAccessor(<ts.AccessorDeclaration>node, clazz);
break;
case ts.SyntaxKind.TypeParameter:
@@ -152,7 +152,7 @@ class Walker {
break;
default:
console.error(node.kind, ts.SyntaxKind[node.kind], node);
console.error(node.kind, (<any>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(node as ts.PropertyDeclaration, interfase);
this._visitProperty(<ts.PropertyDeclaration>node, interfase);
break;
case ts.SyntaxKind.MethodSignature:
case ts.SyntaxKind.MethodDeclaration:
this._visitMethod(node as ts.MethodDeclaration, interfase);
this._visitMethod(<ts.MethodDeclaration>node, interfase);
break;
case ts.SyntaxKind.TypeParameter:
@@ -176,24 +176,24 @@ class Walker {
break;
default:
console.error(node.kind, ts.SyntaxKind[node.kind], node);
console.error(node.kind, (<any>ts).SyntaxKind[node.kind], node);
throw new Error("Unrecognized node.");
}
}
private _visitProperty(node: ts.PropertyDeclaration, parent: AST.Class | AST.Interface) {
if (ts.hasModifier(node, ts.ModifierFlags.Private)) {
if ((node.flags & ts.NodeFlags.Private) === ts.NodeFlags.Private) {
return;
}
const jsDoc = this._parseJSDoc(node);
var jsDoc = this._parseJSDoc(node);
if (jsDoc.typeAnnotation === null) {
this._notifyIncorrectJsDoc(`Field ${ ts.getTextOfNode(node.name) } has no @type annotation.`);
jsDoc.typeAnnotation = "*";
}
const property = this._scope.enter(new AST.Property(ts.getTextOfNode(node.name)));
var 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) {
const jsDoc = this._parseJSDoc(node);
var jsDoc = this._parseJSDoc(node);
const parameters = this._connectParameters(node.parameters, jsDoc.parameters,
var 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("", "*");
}
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 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 generics = this._getGenericsOfSignatureDeclaration(node);
var generics = this._getGenericsOfSignatureDeclaration(node);
const method = this._scope.enter(new AST.Function(ts.getTextOfNode(node.name), node, jsDoc.description, generics, parameters, jsDoc.returnType, jsDoc.isAbstract, isPrivate, isProtected, isStatic));
var 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 {
const jsDoc = this._parseJSDoc(node);
var jsDoc = this._parseJSDoc(node);
const name = ts.getTextOfNode(node.name);
var name = ts.getTextOfNode(node.name);
const isPrivate = ts.hasModifier(node, ts.ModifierFlags.Private);
var isPrivate = (node.flags & ts.NodeFlags.Private) === ts.NodeFlags.Private;
let property = clazz.members[name] as AST.Property;
var property = <AST.Property>clazz.members[name];
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 {
const jsDoc = this._parseJSDoc(node);
var jsDoc = this._parseJSDoc(node);
const name = ts.getTextOfNode(node.name);
var name = ts.getTextOfNode(node.name);
const isPrivate = ts.hasModifier(node, ts.ModifierFlags.Private);
var isPrivate = (node.flags & ts.NodeFlags.Private) === ts.NodeFlags.Private;
let property = clazz.members[name] as AST.Property;
var property = <AST.Property>clazz.members[name];
if (property === undefined) {
this._scope.enter(property = new AST.Property(name));
@@ -274,18 +274,19 @@ class Walker {
return;
}
const declaration = node.declarationList.declarations[0];
if (ts.hasModifier(declaration, ts.ModifierFlags.Ambient)) {
var declaration = node.declarationList.declarations[0];
if ((declaration.flags & ts.NodeFlags.Ambient) === ts.NodeFlags.Ambient) {
return;
}
const jsDoc = this._parseJSDoc(node);
var jsDoc = this._parseJSDoc(node);
if (jsDoc.typeAnnotation === null) {
return;
}
const property = this._scope.enter(new AST.Property(ts.getTextOfNode(declaration.name)));
var 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;
@@ -293,13 +294,13 @@ class Walker {
}
private _visitFunctionDeclaration(node: ts.FunctionDeclaration, parent: AST.Module): void {
const jsDoc = this._parseJSDoc(node);
var jsDoc = this._parseJSDoc(node);
const isPrivate = !ts.hasModifier(node, ts.ModifierFlags.Export);
var isPrivate = (node.flags & ts.NodeFlags.Export) !== ts.NodeFlags.Export;
const generics = this._getGenericsOfSignatureDeclaration(node);
var generics = this._getGenericsOfSignatureDeclaration(node);
const parameters = this._connectParameters(node.parameters, jsDoc.parameters,
var parameters = this._connectParameters(node.parameters, jsDoc.parameters,
parameterName => `Could not find @param annotation for ${ parameterName } on function ${ node.name.text }`
);
@@ -312,7 +313,7 @@ class Walker {
jsDoc.returnType = new AST.ReturnType("", "*");
}
const freeFunction = this._scope.enter(new AST.Function(node.name.text, node, jsDoc.description, generics, parameters, jsDoc.returnType, jsDoc.isAbstract, isPrivate, false, false));
var 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;
@@ -320,32 +321,32 @@ class Walker {
}
private _visitClassDeclaration(node: ts.ClassDeclaration, parent: AST.Module): void {
const jsDoc = this._parseJSDoc(node);
var jsDoc = this._parseJSDoc(node);
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;
var baseTypeHeritageClauseElement = ts.getClassExtendsHeritageClauseElement(node) || null;
var baseType: AST.UnresolvedType = null;
if (baseTypeHeritageClauseElement !== null) {
baseType = new AST.UnresolvedType(
this._typeChecker.getTypeAtLocation(baseTypeHeritageClauseElement).symbol,
this._getGenericsOfTypeReferenceNode(baseTypeHeritageClauseElement, generics)
this._getGenericsOfTypeReferenceNode(baseTypeHeritageClauseElement)
);
}
const interfaces = (ts.getClassImplementsHeritageClauseElements(node) || []).map(type => new AST.UnresolvedType(
var interfaces = (ts.getClassImplementsHeritageClauseElements(node) || []).map(type => new AST.UnresolvedType(
this._typeChecker.getTypeAtLocation(type).symbol,
this._getGenericsOfTypeReferenceNode(type, generics)
this._getGenericsOfTypeReferenceNode(type)
));
const isPrivate = !ts.hasModifier(node, ts.ModifierFlags.Export);
var isPrivate = (node.flags & ts.NodeFlags.Export) !== ts.NodeFlags.Export;
let parameters: AST.Parameter[] = [];
var type = <ts.InterfaceType>this._typeChecker.getTypeAtLocation(node);
var generics = this._getGenericsOfInterfaceType(type);
var parameters: AST.Parameter[] = [];
if (type.symbol.members["__constructor"] !== undefined) {
parameters = this._connectParameters((type.symbol.members["__constructor"].declarations[0] as ts.ConstructorDeclaration).parameters, jsDoc.parameters,
parameters = this._connectParameters((<ts.ConstructorDeclaration>type.symbol.members["__constructor"].declarations[0]).parameters, jsDoc.parameters,
parameterName => `Could not find @param annotation for ${ parameterName } on constructor in class ${ node.name.text }`
);
}
@@ -353,11 +354,11 @@ class Walker {
this._notifyIncorrectJsDoc("There are @param annotations on this class but it has no constructors.");
}
const clazz = this._scope.enter(new AST.Class(node.name.text, node, jsDoc.description, generics, parameters, baseType, interfaces, jsDoc.isAbstract, isPrivate));
var 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.forEachProperty(type.symbol.exports, symbol => {
ts.forEachValue(type.symbol.exports, symbol => {
if (symbol.name === "prototype") {
return;
}
@@ -367,7 +368,7 @@ class Walker {
}
});
ts.forEachProperty(type.symbol.members, symbol => {
ts.forEachValue(type.symbol.members, symbol => {
for (const declaration of symbol.declarations) {
this._walkClassMember(declaration, clazz);
}
@@ -377,28 +378,28 @@ class Walker {
}
private _visitInterfaceDeclaration(node: ts.InterfaceDeclaration, parent: AST.Module): void {
const jsDoc = this._parseJSDoc(node);
var jsDoc = this._parseJSDoc(node);
const type = this._typeChecker.getTypeAtLocation(node) as ts.InterfaceType;
const generics = this._getGenericsOfInterfaceType(type);
const baseTypes = (ts.getInterfaceBaseTypeNodes(node) || []).map(type => new AST.UnresolvedType(
var baseTypes = (ts.getInterfaceBaseTypeNodes(node) || []).map(type => new AST.UnresolvedType(
this._typeChecker.getTypeAtLocation(type).symbol,
this._getGenericsOfTypeReferenceNode(type, generics)
this._getGenericsOfTypeReferenceNode(type)
));
const existingInterfaceType = parent.members[node.name.text];
var existingInterfaceType = parent.members[node.name.text];
if (existingInterfaceType !== undefined) {
return;
}
const isPrivate = !ts.hasModifier(node, ts.ModifierFlags.Export);
var isPrivate = (node.flags & ts.NodeFlags.Export) !== ts.NodeFlags.Export;
const interfase = this._scope.enter(new AST.Interface(node.name.text, node, jsDoc.description, generics, baseTypes, isPrivate));
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));
parent.members[interfase.name] = interfase;
ts.forEachProperty(type.symbol.members, symbol => {
ts.forEachValue(type.symbol.members, symbol => {
for (const declaration of symbol.declarations) {
this._walkInterfaceMember(declaration, interfase);
}
@@ -408,33 +409,33 @@ class Walker {
}
private _visitEnumDeclaration(node: ts.EnumDeclaration, parent: AST.Module): void {
const jsDoc = this._parseJSDoc(node);
var jsDoc = this._parseJSDoc(node);
const existingEnumType = parent.members[node.name.text];
var existingEnumType = parent.members[node.name.text];
if (existingEnumType !== undefined) {
return;
}
const isPrivate = !ts.hasModifier(node, ts.ModifierFlags.Export);
var isPrivate = (node.flags & ts.NodeFlags.Export) !== ts.NodeFlags.Export;
const type = this._typeChecker.getTypeAtLocation(node);
var type = this._typeChecker.getTypeAtLocation(node);
const enumType = this._scope.enter(new AST.Enum(node.name.text, node, jsDoc.description, isPrivate));
var enumType = this._scope.enter(new AST.Enum(node.name.text, node, jsDoc.description, isPrivate));
parent.members[enumType.name] = enumType;
ts.forEachProperty(type.symbol.exports, symbol => {
this._visitEnumMember(symbol.declarations[0] as ts.EnumMember, enumType);
ts.forEachValue(type.symbol.exports, symbol => {
this._visitEnumMember(<ts.EnumMember>symbol.declarations[0], enumType);
});
this._scope.leave();
}
private _visitEnumMember(node: ts.EnumMember, parent: AST.Enum): void {
const jsDoc = this._parseJSDoc(node);
var jsDoc = this._parseJSDoc(node);
const value = (node.initializer === undefined) ? null : parseInt((node.initializer as ts.LiteralExpression).text);
var value = (node.initializer === undefined) ? null : parseInt((<ts.LiteralExpression>node.initializer).text);
const enumMember = this._scope.enter(new AST.EnumMember(ts.getTextOfNode(node.name), (jsDoc === null) ? "" : jsDoc.description, value));
var enumMember = this._scope.enter(new AST.EnumMember(ts.getTextOfNode(node.name), (jsDoc === null) ? "" : jsDoc.description, value));
parent.members.push(enumMember);
@@ -451,16 +452,16 @@ class Walker {
throw new Error("Default import is not supported.");
}
const moduleName = this._resolve((node.moduleSpecifier as ts.LiteralExpression).text, parent);
var moduleName = this._resolve((<ts.LiteralExpression>node.moduleSpecifier).text, parent);
if ((node.importClause.namedBindings as ts.NamespaceImport).name !== undefined) {
if ((<ts.NamespaceImport>node.importClause.namedBindings).name !== undefined) {
// import * as foo from "baz";
parent.members[(node.importClause.namedBindings as ts.NamespaceImport).name.text] = new AST.Reference(moduleName, "*", true);
parent.members[(<ts.NamespaceImport>node.importClause.namedBindings).name.text] = new AST.Reference(moduleName, "*", true);
}
else if ((node.importClause.namedBindings as ts.NamedImports).elements !== undefined) {
else if ((<ts.NamedImports>node.importClause.namedBindings).elements !== undefined) {
// import { foo, bar } from "baz";
for (const element of (node.importClause.namedBindings as ts.NamedImports).elements) {
const importedName = element.propertyName && element.propertyName.text || element.name.text;
for (const element of (<ts.NamedImports>node.importClause.namedBindings).elements) {
var importedName = element.propertyName && element.propertyName.text || element.name.text;
parent.members[element.name.text] = new AST.Reference(moduleName, importedName, true);
}
}
@@ -472,22 +473,22 @@ class Walker {
private _visitExportDeclaration(node: ts.ExportDeclaration, parent: AST.Module): void {
if (node.moduleSpecifier !== undefined) {
// export { foo } from "bar";
const moduleName = this._resolve((node.moduleSpecifier as ts.LiteralExpression).text, parent);
var moduleName = this._resolve((<ts.LiteralExpression>node.moduleSpecifier).text, parent);
for (const element of node.exportClause.elements) {
const importedName = element.propertyName && element.propertyName.text || element.name.text;
var 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) {
(parent.members[element.name.text] as AST.CanBePrivate).isPrivate = false;
(<AST.CanBePrivate><any>parent.members[element.name.text]).isPrivate = false;
}
}
}
private _resolve(relativeModuleName: string, currentModule: AST.Module): string {
let result = ts.normalizeSlashes(path.join(currentModule.name, `../${ relativeModuleName }`));
var result = ts.normalizeSlashes(path.join(currentModule.name, `../${ relativeModuleName }`));
if (result[0] !== ".") {
result = `./${ result }`;
@@ -497,7 +498,7 @@ class Walker {
}
private _parseJSDoc(node: ts.Node): JSDoc {
let comments = oldGetLeadingCommentRangesOfNodeFromText(node, this._currentSourceFile.text);
var comments = oldGetLeadingCommentRangesOfNode(node, this._currentSourceFile);
if (comments === undefined) {
comments = [];
@@ -507,41 +508,41 @@ class Walker {
comments = [comments[comments.length - 1]];
}
const comment =
var comment =
(comments.length === 0) ?
"" :
this._currentSourceFile.text.substring(comments[0].pos, comments[0].end);
const commentStartIndex = comment.indexOf("/**");
const commentEndIndex = comment.lastIndexOf("*/");
var commentStartIndex = comment.indexOf("/**");
var commentEndIndex = comment.lastIndexOf("*/");
const lines =
var lines =
(commentStartIndex === -1 || commentEndIndex === -1) ?
[] :
comment.substring(commentStartIndex + 2, commentEndIndex).split("\n").map(line => {
const match = line.match(/^[ \t]*\* (.*)/);
var match = line.match(/^[ \t]*\* (.*)/);
if (match === null) {
return "";
}
return match[1];
});
let rootDescription = "";
var rootDescription = "";
const parameters: { [name: string]: AST.Parameter } = Object.create(null);
var parameters: { [name: string]: AST.Parameter } = Object.create(null);
let typeAnnotation: string = null;
var typeAnnotation: string = null;
let returnType: AST.ReturnType = null;
var returnType: AST.ReturnType = null;
let isAbstract = false;
var isAbstract = false;
let lastRead: { description: string } = null;
var lastRead: { description: string } = null;
for (const line of lines) {
const firstWordMatch = line.match(/^\s*(\S+)(\s*)/);
const firstWord = (firstWordMatch !== null) ? firstWordMatch[1] : "";
let remainingLine = (firstWordMatch !== null) ? line.substring(firstWordMatch[0].length) : "";
var firstWordMatch = line.match(/^\s*(\S+)(\s*)/);
var firstWord = (firstWordMatch !== null) ? firstWordMatch[1] : "";
var remainingLine = (firstWordMatch !== null) ? line.substring(firstWordMatch[0].length) : "";
if (firstWord[0] === "@") {
lastRead = null;
@@ -552,33 +553,30 @@ class Walker {
isAbstract = true;
break;
case "@param": {
let type: string;
case "@param":
var type: string;
[type, remainingLine] = this._readType(remainingLine);
const [, name, description] = remainingLine.match(/(\S+)\s*(.*)/);
var [, name, description] = remainingLine.match(/(\S+)\s*(.*)/);
const subParameterMatch = name.match(/^(?:(.+)\.([^\.]+))|(?:(.+)\[("[^\[\]"]+")\])$/);
var subParameterMatch = name.match(/^(?:(.+)\.([^\.]+))|(?:(.+)\[("[^\[\]"]+")\])$/);
if (subParameterMatch === null) {
parameters[name] = lastRead = new AST.Parameter(name, description, type);
}
else {
const parentName = subParameterMatch[1] || subParameterMatch[3];
const childName = subParameterMatch[2] || subParameterMatch[4];
const parentParameter = parameters[parentName];
var parentName = subParameterMatch[1] || subParameterMatch[3];
var childName = subParameterMatch[2] || subParameterMatch[4];
var parentParameter = parameters[parentName];
parentParameter.subParameters.push(lastRead = new AST.Parameter(childName, description, type));
}
break;
}
case "@return": {
const [type, description] = this._readType(remainingLine);
case "@return":
var [type, description] = this._readType(remainingLine);
returnType = lastRead = new AST.ReturnType(description, type);
break;
}
case "@type":
[typeAnnotation] = this._readType(remainingLine);
@@ -609,9 +607,9 @@ class Walker {
return ["*", remainingLine];
}
let index = -1;
let numberOfUnterminatedBraces = 0;
for (let i = 0; i < remainingLine.length; i++) {
var index = -1;
var numberOfUnterminatedBraces = 0;
for (var i = 0; i < remainingLine.length; i++) {
if (remainingLine[i] === "{") {
numberOfUnterminatedBraces++;
}
@@ -629,7 +627,7 @@ class Walker {
throw new Error("Unterminated type specifier.");
}
const type = remainingLine.substr(1, index - 1);
var type = remainingLine.substr(1, index - 1);
remainingLine = remainingLine.substr(index + 1).replace(/^\s+/, "");
return [type, remainingLine];
@@ -643,24 +641,16 @@ class Walker {
return signatureDeclaration.typeParameters.map(typeParameter => typeParameter.name.text);
}
private _getGenericsOfTypeReferenceNode(typeReferenceNode: ts.ExpressionWithTypeArguments, intrinsicGenerics: string[]): (AST.UnresolvedType | AST.IntrinsicTypeReference)[] {
private _getGenericsOfTypeReferenceNode(typeReferenceNode: ts.ExpressionWithTypeArguments): (AST.UnresolvedType | AST.IntrinsicTypeReference)[] {
if (typeReferenceNode.typeArguments === undefined) {
return [];
}
const typeReference = this._typeChecker.getTypeAtLocation(typeReferenceNode) as ts.TypeReference;
var typeReference = <ts.TypeReference>this._typeChecker.getTypeAtLocation(typeReferenceNode);
return typeReference.typeArguments.map(typeArgument => {
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 }`);
if ((<ts.IntrinsicType>typeArgument).intrinsicName !== undefined) {
return new AST.IntrinsicTypeReference((<ts.IntrinsicType>typeArgument).intrinsicName);
}
return new AST.UnresolvedType(typeArgument.symbol, []);
@@ -679,12 +669,12 @@ class Walker {
private _connectParameters(astParameters: ts.ParameterDeclaration[], jsDocParameters: { [name: string]: AST.Parameter }, onMissingMessageCallback: (parameterName: string) => string) {
return astParameters.map(parameter => {
let parameterName = (parameter.name as ts.Identifier).text;
var parameterName = (<ts.Identifier>parameter.name).text;
if (parameterName[0] === "_") {
parameterName = parameterName.substr(1);
}
let jsDocParameter = jsDocParameters[parameterName];
var jsDocParameter = jsDocParameters[parameterName];
if (jsDocParameter === undefined) {
this._notifyIncorrectJsDoc(onMissingMessageCallback.call(this, parameterName));
@@ -696,8 +686,8 @@ class Walker {
}
private _notifyIncorrectJsDoc(message: string): void {
const fileName = path.basename(this._currentSourceFile.fileName);
if (fileName === "lib.es5.d.ts" || fileName === "lib.dom.d.ts") {
var fileName = path.basename(this._currentSourceFile.fileName);
if (fileName === "lib.core.d.ts" || fileName === "lib.dom.d.ts") {
return;
}
@@ -706,17 +696,17 @@ class Walker {
link(rootNamespaceName: string): void {
for (const moduleName of Object.keys(this.modules)) {
const module = this.modules[moduleName];
var module = this.modules[moduleName];
for (const memberName of Object.keys(module.members)) {
const member = module.members[memberName];
var member = module.members[memberName];
if (member instanceof AST.Class) {
if (member.unresolvedBaseType instanceof AST.UnresolvedType) {
member.baseType = this._resolveTypeReference(member.unresolvedBaseType);
member.baseType = this._resolveTypeReference(<AST.UnresolvedType>member.unresolvedBaseType);
}
else {
member.baseType = member.unresolvedBaseType;
member.baseType = <AST.TypeReference | AST.IntrinsicTypeReference>member.unresolvedBaseType;
}
member.interfaces = member.unresolvedInterfaces.map(interfase => {
@@ -724,7 +714,7 @@ class Walker {
return this._resolveTypeReference(interfase);
}
return interfase;
return <AST.TypeReference | AST.IntrinsicTypeReference>interfase;
});
}
@@ -734,12 +724,12 @@ class Walker {
return this._resolveTypeReference(baseType);
}
return baseType;
return <AST.TypeReference | AST.IntrinsicTypeReference>baseType;
});
}
else if (member instanceof AST.Enum) {
let value = 0;
var value = 0;
for (const enumMember of member.members) {
if (enumMember.value === null) {
enumMember.value = value;
@@ -761,17 +751,17 @@ class Walker {
private _moduleToNamespace(module: AST.Module): void {
for (const memberName of Object.keys(module.members)) {
let member = module.members[memberName];
var member = module.members[memberName];
if (member instanceof AST.Reference) {
if (member.isPrivate) {
if ((<AST.Reference>member).isPrivate) {
continue;
}
if (member.name === "*") {
const newNamespace = this._scope.enter(new AST.Namespace(memberName));
var newNamespace = this._scope.enter(new AST.Namespace(memberName));
const existingNamespace = this.namespaces[newNamespace.fullName];
var existingNamespace = this.namespaces[newNamespace.fullName];
if (existingNamespace !== undefined) {
this._scope.leave();
this._scope.enter(existingNamespace);
@@ -780,10 +770,10 @@ class Walker {
this.namespaces[newNamespace.fullName] = newNamespace;
}
let referencedModuleName = member.moduleName;
let referencedModule = this.modules[referencedModuleName];
var referencedModuleName = (<AST.Reference>member).moduleName;
var referencedModule = this.modules[referencedModuleName];
if (referencedModule === undefined && ((referencedModuleName + "/index") in this.modules)) {
member.moduleName = referencedModuleName = referencedModuleName + "/index";
(<AST.Reference>member).moduleName = referencedModuleName = referencedModuleName + "/index";
referencedModule = this.modules[referencedModuleName];
}
this._moduleToNamespace(referencedModule);
@@ -792,56 +782,56 @@ class Walker {
}
else {
while (member instanceof AST.Reference) {
member = this.modules[member.moduleName].members[member.name];
member = this.modules[(<AST.Reference>member).moduleName].members[member.name];
}
this._scope.enter(member);
this._scope.enter(<AST.NamespaceMember><any>member);
this._scope.leave();
(this._scope.current as AST.Namespace).members[member.name] = member;
(<AST.Namespace>this._scope.current).members[member.name] = <AST.NamespaceMember>member;
}
}
else if (!(member as AST.CanBePrivate).isPrivate) {
this._scope.enter(member);
else if (!(<AST.CanBePrivate><any>member).isPrivate) {
this._scope.enter(<AST.NamespaceMember>member);
this._scope.leave();
(this._scope.current as AST.Namespace).members[member.name] = member;
(<AST.Namespace>this._scope.current).members[member.name] = <AST.NamespaceMember>member;
}
}
}
private _resolveTypeReference(unresolvedType: AST.UnresolvedType): AST.TypeReference {
let node: ts.Node = unresolvedType.symbol.declarations[0];
var node: ts.Node = unresolvedType.symbol.declarations[0];
while (node.kind !== ts.SyntaxKind.SourceFile) {
node = node.parent;
}
const sourceFile = node as ts.SourceFile;
var sourceFile = <ts.SourceFile>node;
const moduleName = this._moduleNameFromFileName(sourceFile.fileName);
const module = this.modules[moduleName];
var moduleName = this._moduleNameFromFileName(sourceFile.fileName);
var module = this.modules[moduleName];
let result = module.members[unresolvedType.symbol.name];
var 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[result.moduleName].members[result.name];
result = this.modules[(<AST.Reference>result).moduleName].members[result.name];
}
const resultGenerics = unresolvedType.generics.map(generic => {
var resultGenerics = unresolvedType.generics.map(generic => {
if (generic instanceof AST.UnresolvedType) {
return this._resolveTypeReference(generic);
}
return generic;
return <AST.IntrinsicTypeReference>generic;
});
return new AST.TypeReference(result, resultGenerics);
return new AST.TypeReference(<AST.NamespaceMember><any>result, resultGenerics);
}
private _moduleNameFromFileName(fileName: string): string {
let result = ts.normalizeSlashes(path.relative(this._compiler.projectRoot, fileName));
var result = ts.normalizeSlashes(path.relative(this._compiler.projectRoot, fileName));
result = result.substr(0, result.length - ".ts".length);
@@ -854,14 +844,16 @@ class Walker {
}
export function walk(compiler: Compiler, root: string, rootNamespaceName: string) {
const sourceFiles = compiler.sourceFiles;
var sourceFiles = compiler.sourceFiles;
var rootFileName = ts.normalizeSlashes(path.resolve(root));
var rootSourceFile = sourceFiles.filter(sourceFile => sourceFile.fileName === rootFileName)[0];
const walker = new Walker(compiler);
var walker = new Walker(compiler);
// Walk
for (const sourceFile of sourceFiles) {
if (
path.basename(sourceFile.fileName) === "lib.es5.d.ts" ||
path.basename(sourceFile.fileName) === "lib.core.d.ts" ||
path.basename(sourceFile.fileName) === "lib.dom.d.ts" ||
sourceFile.fileName.substr(-"references.d.ts".length) === "references.d.ts"
) {
+211 -108
View File
@@ -26,52 +26,108 @@ var UglifyJS = require("uglify-js");
var FileTransform = require("async-build").FileTransform;
var Run = (function () {
function Run(outputLibraryName, unusedVarsToIgnore) {
function Run(entry, outputLibraryName, unusedVarsToIgnore) {
this._entry = path.resolve(entry).replace(/\\/g, "/");
this._outputLibraryName = outputLibraryName;
this._unusedVarsToIgnore = unusedVarsToIgnore;
this._root = UglifyJS.parse(fs.readFileSync(path.resolve(__filename, "..", "umd-wrapper.js"), "utf8"));
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.figure_out_scope({ screw_ie8: true });
this._toInsert = null;
this._licenseHeader = null;
this._rootSourceMap = 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: "" });
}
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":
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");
}
module.root = UglifyJS.parse(file.contents.toString(), {
filename: jsFilename,
toplevel: null,
});
throw ex;
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;
}
break;
case ".map":
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]);
});
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" }));
break;
}
};
@@ -79,72 +135,63 @@ 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));
// Splice in the TS output into the UMD wrapper.
var insertionParent = this._root.body[0].body.args[1].body;
moduleNames.forEach(function (moduleName, index) {
_this._modules[moduleName].id = index;
});
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;
// 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;
}
}
});
insertionParent.splice(-1, 0, node);
this._toInsert.splice(i, 1);
}
}
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.apply(insertionParent, [-1, 0].concat(this._toInsert));
this._root.figure_out_scope({ screw_ie8: true });
// 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;
// Set if there are any unused variables apart from the ones in unusedVarsToIgnore
var haveUnusedVars = false;
defineCall.expression.name = "def";
// Remove some things from the AST
var 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;
});
}
}));
nodesToRemove = [];
// Fixup anonymous functions to print a space after "function"
@@ -160,7 +207,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.value.indexOf("Copyright") !== -1) {
if (comment === _this._licenseHeader) {
return;
}
@@ -169,7 +216,7 @@ var Run = (function () {
return;
}
var indent = " "; // 5 spaces
var indent = " "; // 9 spaces
for (var i = 0; i < comment.col; i++) {
indent += " ";
}
@@ -183,16 +230,8 @@ 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()) {
@@ -227,6 +266,8 @@ var Run = (function () {
});
nodesToRemove = [];
this._root.figure_out_scope({ screw_ie8: true });
}
@@ -274,23 +315,39 @@ var Run = (function () {
// Output
var output = {
source_map: this._rootSourceMap,
ascii_only: true,
source_map: _this._rootSourceMap,
beautify: true,
comments: 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;
}
};
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
@@ -309,8 +366,8 @@ var Run = (function () {
})();
module.exports = {
build: function (outputLibraryName, unusedVarsToIgnore) {
var run = new Run(outputLibraryName, unusedVarsToIgnore);
build: function (entry, outputLibraryName, unusedVarsToIgnore) {
var run = new Run(entry, outputLibraryName, unusedVarsToIgnore);
return new FileTransform(function (file) {
run.addFile(file);
@@ -319,7 +376,7 @@ module.exports = {
});
},
watch: function (outputLibraryName, unusedVarsToIgnore) {
watch: function (entry, outputLibraryName, unusedVarsToIgnore) {
var files = Object.create(null);
return new FileTransform(function (file) {
@@ -327,7 +384,7 @@ module.exports = {
files[file.path] = file;
}
else {
var run = new Run(outputLibraryName, unusedVarsToIgnore);
var run = new Run(entry, outputLibraryName, unusedVarsToIgnore);
Object.keys(files).forEach(function (filename) {
run.addFile(files[filename]);
});
@@ -395,7 +452,6 @@ 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;
@@ -433,6 +489,53 @@ 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.
-65
View File
@@ -1,65 +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.
*
* @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");
});
-4
View File
@@ -98,7 +98,3 @@
.libjass-filters {
display: block;
}
.libjass-filters * {
color-interpolation-filters: sRGB;
}
+6 -5
View File
@@ -17,7 +17,7 @@
"main": "lib/libjass.js",
"scripts": {
"prepublish": "node ./build.js clean default",
"build": "tsc -p ./build/tsconfig.json",
"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",
"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,12 +26,13 @@
},
"devDependencies": {
"async": "1.x >=1.4",
"async-build": "0.3.1",
"intern": "3.x >=3.2.0",
"async-build": "0.2.0",
"firefox-profile": "0.3.x",
"intern": "3.x >=3.0.1",
"npm": "3.x",
"pngjs": "3.x",
"pngjs": "2.2.0",
"sax": "1.x",
"typescript": "next",
"typescript": "1.7.5",
"uglify-js": "2.x >=2.4.24"
},
"private": true
+5 -67
View File
@@ -42,8 +42,6 @@ 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";
@@ -52,88 +50,28 @@ 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: value => {
console.warn("Setter `libjass.debugMode = value` has been deprecated. Use `libjass.configure({ debugMode: value })` instead.");
settings.setDebugMode(value);
},
set: settings.setDebugMode,
},
verboseMode: {
get: () => settings.verboseMode,
set: value => {
console.warn("Setter `libjass.verboseMode = value` has been deprecated. Use `libjass.configure({ verboseMode: value })` instead.");
settings.setVerboseMode(value);
},
set: settings.setVerboseMode,
},
Set: {
get: () => set.Set,
set: value => {
console.warn("Setter `libjass.Set = value` has been deprecated. Use `libjass.configure({ Set: value })` instead.");
set.setImplementation(value);
},
set: set.setImplementation,
},
Map: {
get: () => map.Map,
set: value => {
console.warn("Setter `libjass.Map = value` has been deprecated. Use `libjass.configure({ Map: value })` instead.");
map.setImplementation(value);
},
set: map.setImplementation,
},
Promise: {
get: () => promise.Promise,
set: value => {
console.warn("Setter `libjass.Promise = value` has been deprecated. Use `libjass.configure({ Promise: value })` instead.");
promise.setImplementation(value);
},
set: promise.setImplementation,
},
});
+4 -4
View File
@@ -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 | null {
export function parseLineIntoProperty(line: string): Property {
const colonPos = line.indexOf(":");
if (colonPos === -1) {
return null;
@@ -45,9 +45,9 @@ export function parseLineIntoProperty(line: string): Property | null {
*
* @param {string} line
* @param {!Array.<string>} formatSpecifier
* @return {TypedTemplate}
* @return {!TypedTemplate}
*/
export function parseLineIntoTypedTemplate(line: string, formatSpecifier: string[]): TypedTemplate | null {
export function parseLineIntoTypedTemplate(line: string, formatSpecifier: string[]): TypedTemplate {
const property = parseLineIntoProperty(line);
if (property === null) {
return null;
+122 -125
View File
@@ -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 { result } = new ParserRun(input, rule);
const run = new ParserRun(input, rule);
if (result === null || result.end !== input.length) {
if (run.result === null || run.result.end !== input.length) {
if (debugMode) {
console.error("Parse failed. %s %s %o", rule, input, result);
console.error("Parse failed. %s %s %o", rule, input, run.result);
}
throw new Error("Parse failed.");
}
return result.value;
return run.result.value;
}
/**
@@ -58,20 +58,15 @@ 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 = ruleFunction.call(this, this._parseTree);
this._result = rules.get(rule).call(this, this._parseTree);
}
/**
* @type {ParseNode}
*/
get result(): ParseNode | null {
get result(): ParseNode {
return this._result;
}
@@ -94,10 +89,15 @@ 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 = 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);
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);
}
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(parse(part.value, "drawingInstructions") as parts.drawing.Instruction[]);
current.value[i] = new parts.DrawingInstructions(<parts.drawing.Instruction[]>parse(part.value, "drawingInstructions"));
}
});
@@ -124,7 +124,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_enclosedTags(parent: ParseNode): ParseNode | null {
parse_enclosedTags(parent: ParseNode): ParseNode {
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 = null;
let childNode: ParseNode = 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(
(current.value[current.value.length - 1] as parts.Comment).value +
(childNode.value as parts.Comment).value
(<parts.Comment>current.value[current.value.length - 1]).value +
(<parts.Comment>childNode.value).value
);
}
else {
@@ -237,7 +237,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_newline(parent: ParseNode): ParseNode | null {
parse_newline(parent: ParseNode): ParseNode {
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 | null {
parse_hardspace(parent: ParseNode): ParseNode {
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 | null {
parse_tag_a(parent: ParseNode): ParseNode {
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 = -1;
switch ((valueNode.value)) {
let value: number = null;
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 | null {
parse_tag_alpha(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -395,7 +395,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_an(parent: ParseNode): ParseNode | null {
parse_tag_an(parent: ParseNode): ParseNode {
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 | null {
parse_tag_b(parent: ParseNode): ParseNode {
const current = new ParseNode(parent);
if (this.read(current, "b") === null) {
@@ -429,7 +429,7 @@ class ParserRun {
return null;
}
let valueNode: ParseNode | null = null;
let valueNode: ParseNode = null;
let next = this._peek();
@@ -459,7 +459,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_be(parent: ParseNode): ParseNode | null {
parse_tag_be(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -467,7 +467,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_blur(parent: ParseNode): ParseNode | null {
parse_tag_blur(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -475,7 +475,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_bord(parent: ParseNode): ParseNode | null {
parse_tag_bord(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -483,7 +483,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_c(parent: ParseNode): ParseNode | null {
parse_tag_c(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -491,7 +491,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_clip(parent: ParseNode): ParseNode | null {
parse_tag_clip(parent: ParseNode): ParseNode {
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 | null {
parse_tag_fad(parent: ParseNode): ParseNode {
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 | null {
parse_tag_fade(parent: ParseNode): ParseNode {
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 | null {
parse_tag_fax(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -654,7 +654,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_fay(parent: ParseNode): ParseNode | null {
parse_tag_fay(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -662,7 +662,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_fn(parent: ParseNode): ParseNode | null {
parse_tag_fn(parent: ParseNode): ParseNode {
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 | null {
parse_tag_fr(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -698,7 +698,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_frx(parent: ParseNode): ParseNode | null {
parse_tag_frx(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -706,7 +706,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_fry(parent: ParseNode): ParseNode | null {
parse_tag_fry(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -714,7 +714,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_frz(parent: ParseNode): ParseNode | null {
parse_tag_frz(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -722,7 +722,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_fs(parent: ParseNode): ParseNode | null {
parse_tag_fs(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -730,7 +730,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_fsplus(parent: ParseNode): ParseNode | null {
parse_tag_fsplus(parent: ParseNode): ParseNode {
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 / 10);
current.value = new parts.FontSizePlus(valueNode.value);
return current;
}
@@ -754,7 +754,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_fsminus(parent: ParseNode): ParseNode | null {
parse_tag_fsminus(parent: ParseNode): ParseNode {
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 / 10);
current.value = new parts.FontSizeMinus(valueNode.value);
return current;
}
@@ -778,7 +778,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_fscx(parent: ParseNode): ParseNode | null {
parse_tag_fscx(parent: ParseNode): ParseNode {
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 | null {
parse_tag_fscy(parent: ParseNode): ParseNode {
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 | null {
parse_tag_fsp(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -834,7 +834,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_i(parent: ParseNode): ParseNode | null {
parse_tag_i(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -842,7 +842,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_iclip(parent: ParseNode): ParseNode | null {
parse_tag_iclip(parent: ParseNode): ParseNode {
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 | null {
parse_tag_k(parent: ParseNode): ParseNode {
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 | null {
parse_tag_K(parent: ParseNode): ParseNode {
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 | null {
parse_tag_kf(parent: ParseNode): ParseNode {
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 | null {
parse_tag_ko(parent: ParseNode): ParseNode {
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 | null {
parse_tag_move(parent: ParseNode): ParseNode {
const current = new ParseNode(parent);
if (this.read(current, "move") === null) {
@@ -998,8 +998,8 @@ class ParserRun {
return null;
}
let t1Node: ParseNode | null = null;
let t2Node: ParseNode | null = null;
let t1Node: ParseNode = null;
let t2Node: ParseNode = 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 | null {
parse_tag_org(parent: ParseNode): ParseNode {
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 | null {
parse_tag_p(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1089,7 +1089,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_pbo(parent: ParseNode): ParseNode | null {
parse_tag_pbo(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1097,7 +1097,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_pos(parent: ParseNode): ParseNode | null {
parse_tag_pos(parent: ParseNode): ParseNode {
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 | null {
parse_tag_q(parent: ParseNode): ParseNode {
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 | null {
parse_tag_r(parent: ParseNode): ParseNode {
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 | null {
parse_tag_s(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1203,7 +1203,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_shad(parent: ParseNode): ParseNode | null {
parse_tag_shad(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1211,7 +1211,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_t(parent: ParseNode): ParseNode | null {
parse_tag_t(parent: ParseNode): ParseNode {
const current = new ParseNode(parent);
if (this.read(current, "t") === null) {
@@ -1224,9 +1224,9 @@ class ParserRun {
return null;
}
let startNode: ParseNode | null = null;
let endNode: ParseNode | null = null;
let accelNode: ParseNode | null = null;
let startNode: ParseNode = null;
let endNode: ParseNode = null;
let accelNode: ParseNode = 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 = null;
let childNode: ParseNode = 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(
(transformTags[transformTags.length - 1] as parts.Comment).value +
(childNode.value as parts.Comment).value
(<parts.Comment>transformTags[transformTags.length - 1]).value +
(<parts.Comment>childNode.value).value
);
}
else {
@@ -1354,7 +1354,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_u(parent: ParseNode): ParseNode | null {
parse_tag_u(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1362,7 +1362,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_xbord(parent: ParseNode): ParseNode | null {
parse_tag_xbord(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1370,7 +1370,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_xshad(parent: ParseNode): ParseNode | null {
parse_tag_xshad(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1378,7 +1378,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_ybord(parent: ParseNode): ParseNode | null {
parse_tag_ybord(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1386,7 +1386,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_yshad(parent: ParseNode): ParseNode | null {
parse_tag_yshad(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1394,7 +1394,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_1a(parent: ParseNode): ParseNode | null {
parse_tag_1a(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1402,7 +1402,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_1c(parent: ParseNode): ParseNode | null {
parse_tag_1c(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1410,7 +1410,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_2a(parent: ParseNode): ParseNode | null {
parse_tag_2a(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1418,7 +1418,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_2c(parent: ParseNode): ParseNode | null {
parse_tag_2c(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1426,7 +1426,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_3a(parent: ParseNode): ParseNode | null {
parse_tag_3a(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1434,7 +1434,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_3c(parent: ParseNode): ParseNode | null {
parse_tag_3c(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1442,7 +1442,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_4a(parent: ParseNode): ParseNode | null {
parse_tag_4a(parent: ParseNode): ParseNode {
throw new Error("Method not implemented.");
}
@@ -1450,7 +1450,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_tag_4c(parent: ParseNode): ParseNode | null {
parse_tag_4c(parent: ParseNode): ParseNode {
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 = null;
let currentType: string = null;
const numberParts: ParseNode[] = [];
current.value = [];
@@ -1498,7 +1498,11 @@ class ParserRun {
}
const typePart = this.parse_text(current);
const newType = (typePart.value as parts.Text).value;
if (typePart === null) {
break;
}
const newType = (<parts.Text>typePart.value).value;
switch (newType) {
case "m":
case "l":
@@ -1518,7 +1522,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_decimalInt32(parent: ParseNode): ParseNode | null {
parse_decimalInt32(parent: ParseNode): ParseNode {
const current = new ParseNode(parent);
const isNegative = this.read(current, "-") !== null;
@@ -1550,7 +1554,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_hexInt32(parent: ParseNode): ParseNode | null {
parse_hexInt32(parent: ParseNode): ParseNode {
const current = new ParseNode(parent);
const isNegative = this.read(current, "-") !== null;
@@ -1590,13 +1594,13 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_decimalOrHexInt32(parent: ParseNode): ParseNode | null {
parse_decimalOrHexInt32(parent: ParseNode): ParseNode {
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();
@@ -1612,7 +1616,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_decimal(parent: ParseNode): ParseNode | null {
parse_decimal(parent: ParseNode): ParseNode {
const current = new ParseNode(parent);
const negative = (this.read(current, "-") !== null);
@@ -1637,12 +1641,12 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_unsignedDecimal(parent: ParseNode): ParseNode | null {
parse_unsignedDecimal(parent: ParseNode): ParseNode {
const current = new ParseNode(parent);
const characteristicNode = new ParseNode(current, "");
let mantissaNode: ParseNode | null = null;
let mantissaNode: ParseNode = null;
for (let next = this._peek(); this._haveMore() && next >= "0" && next <= "9"; next = this._peek()) {
characteristicNode.value += next;
@@ -1675,7 +1679,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_enableDisable(parent: ParseNode): ParseNode | null {
parse_enableDisable(parent: ParseNode): ParseNode {
const next = this._peek();
if (next === "0" || next === "1") {
@@ -1692,7 +1696,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_color(parent: ParseNode): ParseNode | null {
parse_color(parent: ParseNode): ParseNode {
const current = new ParseNode(parent);
while (this.read(current, "&") !== null || this.read(current, "H") !== null) { }
@@ -1720,7 +1724,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_alpha(parent: ParseNode): ParseNode | null {
parse_alpha(parent: ParseNode): ParseNode {
const current = new ParseNode(parent);
while (this.read(current, "&") !== null || this.read(current, "H") !== null) { }
@@ -1744,7 +1748,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
parse_colorWithAlpha(parent: ParseNode): ParseNode | null {
parse_colorWithAlpha(parent: ParseNode): ParseNode {
const current = new ParseNode(parent);
const valueNode = this.parse_decimalOrHexInt32(current);
@@ -1770,7 +1774,7 @@ class ParserRun {
* @param {string} next
* @return {ParseNode}
*/
read(parent: ParseNode, next: string): ParseNode | null {
read(parent: ParseNode, next: string): ParseNode {
if (this._peek(next.length) !== next) {
return null;
}
@@ -1801,7 +1805,7 @@ class ParserRun {
* @param {!ParseNode} parent
* @return {ParseNode}
*/
private _parse_tag_clip_or_iclip(tagName: "clip" | "iclip", parent: ParseNode): ParseNode | null {
private _parse_tag_clip_or_iclip(tagName: string, parent: ParseNode): ParseNode {
const current = new ParseNode(parent);
if (this.read(current, tagName) === null) {
@@ -1814,12 +1818,12 @@ class ParserRun {
return 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;
let x1Node: ParseNode = null;
let x2Node: ParseNode = null;
let y1Node: ParseNode = null;
let y2Node: ParseNode = null;
let scaleNode: ParseNode = null;
let commandsNode: ParseNode = null;
const firstNode = this.parse_decimal(current);
@@ -1847,10 +1851,6 @@ class ParserRun {
}
x2Node = this.parse_decimal(current);
if (x2Node === null) {
parent.pop();
return null;
}
if (this.read(current, ",") === null) {
parent.pop();
@@ -1858,10 +1858,6 @@ 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");
}
@@ -1872,7 +1868,7 @@ class ParserRun {
commandsNode.value += next;
}
current.value = new parts.VectorClip((scaleNode !== null) ? scaleNode.value : 1, parse(commandsNode.value, "drawingInstructions") as parts.drawing.Instruction[], tagName === "clip");
current.value = new parts.VectorClip((scaleNode !== null) ? scaleNode.value : 1, <parts.drawing.Instruction[]>parse(commandsNode.value, "drawingInstructions"), tagName === "clip");
}
if (this.read(current, ")") === null) {
@@ -1895,18 +1891,19 @@ class ParserRun {
function makeTagParserFunction(
tagName: string,
tagConstructor: { new (value: any): parts.Part },
valueParser: (current: ParseNode) => ParseNode | null,
valueParser: (current: ParseNode) => ParseNode,
required: boolean
): void {
(ParserRun.prototype as any)[`parse_tag_${ tagName }`] = function (this: ParserRun, parent: ParseNode): ParseNode | null {
(<any>ParserRun.prototype)[`parse_tag_${ tagName }`] = function (parent: ParseNode): ParseNode {
const self = <ParserRun>this;
const current = new ParseNode(parent);
if (this.read(current, tagName) === null) {
if (self.read(current, tagName) === null) {
parent.pop();
return null;
}
const valueNode: ParseNode | null = valueParser.call(this, current);
const valueNode = valueParser.call(self, current);
if (valueNode !== null) {
current.value = new tagConstructor(valueNode.value);
@@ -1956,8 +1953,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 (ParserRun.prototype as any)[key] === "function") {
rules.set(key.substr("parse_".length), (ParserRun.prototype as any)[key]);
if (key.indexOf("parse_") === 0 && typeof (<any>ParserRun.prototype)[key] === "function") {
rules.set(key.substr("parse_".length), (<any>ParserRun.prototype)[key]);
}
}
@@ -1974,7 +1971,7 @@ class ParseNode {
private _end: number;
private _value: any;
constructor(private _parent: ParseNode | null, value: any = null) {
constructor(private _parent: ParseNode, value: any = null) {
if (_parent !== null) {
_parent.children.push(this);
}
@@ -2006,7 +2003,7 @@ class ParseNode {
/**
* @type {ParseNode}
*/
get parent(): ParseNode | null {
get parent(): ParseNode {
return this._parent;
}
+8 -7
View File
@@ -24,6 +24,7 @@ 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";
@@ -54,7 +55,7 @@ export class StreamParser {
private _shouldSwallowBom: boolean = true;
private _currentSection: Section = Section.ScriptInfo;
private _currentAttachment: Attachment | null = null;
private _currentAttachment: Attachment = null;
constructor(private _stream: Stream) {
this._stream.nextLine().then(line => this._onNextLine(line), reason => {
@@ -118,7 +119,7 @@ export class StreamParser {
/**
* @param {string} line
*/
private _onNextLine(line: string | null): void {
private _onNextLine(line: string): void {
if (line === null) {
this.currentSection = Section.EOF;
return;
@@ -290,10 +291,10 @@ export class SrtStreamParser {
private _shouldSwallowBom: boolean = true;
private _currentDialogueNumber: string | null = null;
private _currentDialogueStart: string | null = null;
private _currentDialogueEnd: string | null = null;
private _currentDialogueText: string | null = null;
private _currentDialogueNumber: string = null;
private _currentDialogueStart: string = null;
private _currentDialogueEnd: string = null;
private _currentDialogueText: string = null;
constructor(private _stream: Stream) {
this._stream.nextLine().then(line => this._onNextLine(line), reason => {
@@ -319,7 +320,7 @@ export class SrtStreamParser {
/**
* @param {string} line
*/
private _onNextLine(line: string | null): void {
private _onNextLine(line: string): 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([
+17 -17
View File
@@ -27,7 +27,7 @@ export interface ReadableStream {
getReader(): ReadableStreamReader;
}
export interface ReadableStreamReader {
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 | null>;
nextLine(): Promise<string>;
}
/**
@@ -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 | null> {
let result: Promise<string | null>;
nextLine(): Promise<string> {
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>(null);
result = Promise.resolve<string>(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> | null = null;
private _failedError: ErrorEvent | null = null;
private _pendingDeferred: DeferredPromise<string> = null;
private _failedError: ErrorEvent = 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> | null = null;
private _pendingDeferred: DeferredPromise<string> = 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 = "";
}
+4 -7
View File
@@ -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 = null;
let nameTableRecord: TableRecord = 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,9 +111,6 @@ 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);
@@ -168,7 +165,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[] = (clazz as any).__fields;
const fields: StructMemberDefinition[] = (<any>clazz).__fields;
clazz.read = (reader: DataReader) => {
const result: any = new clazz();
@@ -208,7 +205,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 = [];
+36 -36
View File
@@ -183,17 +183,17 @@ export class Italic {
/**
* A bold tag {\b}
*
* @param {?boolean|?number} value {\b1} -> true, {\b0} -> false, {\b###} -> weight of the bold (number), {\b} -> null
* @param {*} value {\b1} -> true, {\b0} -> false, {\b###} -> weight of the bold (number), {\b} -> null
*/
export class Bold {
constructor(private _value: boolean | number | null) { }
constructor(private _value: Object) { }
/**
* The value of this bold tag.
*
* @type {?boolean|?number}
*/
get value(): boolean | number | null {
get value(): Object {
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 | null) { }
constructor(private _value: string) { }
/**
* The value of this font name tag.
*
* @type {?string}
*/
get value(): string | null {
get value(): string {
return this._value;
}
}
@@ -417,7 +417,7 @@ export class FontSize {
/**
* A font size increase tag {\fs+}
*
* @param {number} value {\fs+###} -> relative difference (number, percentage)
* @param {number} value {\fs+###} -> difference (number)
*/
export class FontSizePlus {
constructor(private _value: number) { }
@@ -435,7 +435,7 @@ export class FontSizePlus {
/**
* A font size decrease tag {\fs-}
*
* @param {number} value {\fs-###} -> relative difference (number, percentage)
* @param {number} value {\fs-###} -> difference (number)
*/
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 | null) { }
constructor(private _value: number) { }
/**
* The value of this horizontal font scaling tag.
*
* @type {?number}
*/
get value(): number | null {
get value(): number {
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 | null) { }
constructor(private _value: number) { }
/**
* The value of this vertical font scaling tag.
*
* @type {?number}
*/
get value(): number | null {
get value(): number {
return this._value;
}
}
@@ -571,7 +571,7 @@ export class SkewX {
*
* @type {?number}
*/
get value(): number {
get value() {
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 | null) { }
constructor(private _value: string) { }
/**
* The value of this style reset tag.
*
* @type {?string}
*/
get value(): string | null {
get value(): string {
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 | null, private _t2: number | null) { }
constructor(private _x1: number, private _y1: number, private _x2: number, private _y2: number, private _t1: number, private _t2: number) { }
/**
* 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 | null {
get t1(): number {
return this._t1;
}
/**
* The end time value of this move tag.
*
* @type {?number}
* @type {number}
*/
get t2(): number | null {
get t2(): number {
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 | null, private _end: number | null, private _accel: number | null, private _tags: Part[]) { }
constructor(private _start: number, private _end: number, private _accel: number, private _tags: Part[]) { }
/**
* The starting time of this transform tag.
*
* @type {?number}
*/
get start(): number | null {
get start(): number {
return this._start;
}
@@ -1122,7 +1122,7 @@ export class Transform {
*
* @type {?number}
*/
get end(): number | null {
get end(): number {
return this._end;
}
@@ -1131,7 +1131,7 @@ export class Transform {
*
* @type {?number}
*/
get accel(): number | null {
get accel(): number {
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 (this: any) {
ctor.prototype.toString = function () {
return (
ctorName + " { " +
propertyNames.map(name => `${ name }: ${ this[name] }`).join(", ") +
propertyNames.map(name => `${ name }: ${ (<any>this)[name] }`).join(", ") +
((propertyNames.length > 0) ? " " : "") +
"}"
);
};
}
}
};
import { registerClass } from "../serialization";
import { registerClassPrototype } from "../webworker/misc";
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);
registerClass(value);
registerClassPrototype(value.prototype);
}
}
for (const key of Object.keys(drawing)) {
const value: any = (drawing as any)[key];
const value: any = (<any>drawing)[key];
if (value instanceof Function) {
addToString(value, `Drawing${ key }`);
registerClass(value);
registerClassPrototype(value.prototype);
}
}
+4 -4
View File
@@ -41,10 +41,10 @@ import { ManualClock } from "./manual";
export class AutoClock implements Clock {
private _manualClock: ManualClock = new ManualClock();
private _nextAnimationFrameRequestId: number | null = null;
private _nextAnimationFrameRequestId: number = null;
private _lastKnownExternalTime: number | null = null;
private _lastKnownExternalTimeObtainedAt: number | null = null;
private _lastKnownExternalTime: number = null;
private _lastKnownExternalTimeObtainedAt: number = 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.seek(currentExternalTime);
this._manualClock.pause();
}
else {
this._manualClock.tick((timeStamp - this._lastKnownExternalTimeObtainedAt) / 1000 * this._manualClock.rate + this._lastKnownExternalTime);
+2 -1
View File
@@ -26,4 +26,5 @@ export { VideoClock } from "./clocks/video";
export { DefaultRenderer } from "./default";
export { NullRenderer } from "./null";
export { WebRenderer } from "./web/renderer";
export { RendererSettings } from "./settings";
export { RendererSettings, makeFontMapFromStyleElement } from "./settings";
+10 -17
View File
@@ -20,9 +20,9 @@
import { Clock, ClockEvent } from "./clocks/base";
import { RendererSettings } from "./settings";
import { RendererSettings, toRendererSettings } from "./settings";
import { debugMode, verboseMode } from "../settings";
import { 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 = RendererSettings.from(settings);
this._settings = toRendererSettings(settings);
this._clock.addEventListener(ClockEvent.Play, () => this._onClockPlay());
this._clock.addEventListener(ClockEvent.Tick, () => this._onClockTick());
@@ -159,21 +159,14 @@ export class NullRenderer {
}
for (const dialogue of this._ass.dialogues) {
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);
}
if (dialogue.end > currentTime) {
if (dialogue.start <= currentTime) {
// This dialogue is visible right now. Draw it.
this.draw(dialogue);
}
}
catch (ex) {
if (debugMode) {
console.error(`Rendering dialogue ${ dialogue.id } failed.`, ex);
else if (dialogue.start <= (currentTime + this._settings.preRenderTime)) {
// This dialogue will be visible soon. Pre-render it.
this.preRender(dialogue);
}
}
}
+63 -127
View File
@@ -18,13 +18,12 @@
* limitations under the License.
*/
import { debugMode } from "../settings";
import { Map } from "../utility/map";
/**
* Settings for the renderer.
*/
export class RendererSettings {
export interface 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.
*
@@ -38,14 +37,14 @@ export class 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.RendererSettings.makeFontMapFromStyleElement}
* If you have a <style> or <link> element on the page containing @font-face rules, you can use the {@link libjass.renderers.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[]> | null;
fontMap: Map<string, string | string[]>;
/**
* Subtitles will be pre-rendered for this amount of time (seconds).
@@ -97,77 +96,74 @@ export class RendererSettings {
* @type {boolean}
*/
useAttachedFonts: boolean;
}
/**
* 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>();
/**
* 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 = {};
}
const styleSheet = linkStyle.sheet as CSSStyleSheet;
for (let i = 0; i < styleSheet.cssRules.length; i++) {
const rule = styleSheet.cssRules[i];
const {
fontMap = null,
preRenderTime = 5,
preciseOutlines = false,
enableSvg = true,
fallbackFonts = 'Arial, Helvetica, sans-serif, "Segoe UI Symbol"',
useAttachedFonts = false
} = <RendererSettings>object;
if (isFontFaceRule(rule)) {
const name = rule.style.getPropertyValue("font-family").match(/^["']?(.*?)["']?$/)![1];
return {
fontMap,
preRenderTime,
preciseOutlines,
enableSvg,
fallbackFonts,
useAttachedFonts,
};
}
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];
}
/**
* 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>();
fontMap.set(name, src);
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];
}
}
return fontMap;
fontMap.set(name, src);
}
}
/**
* 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;
}
return fontMap;
}
/**
@@ -177,63 +173,3 @@ export class RendererSettings {
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;
}
+2 -6
View File
@@ -71,8 +71,8 @@ export class AnimationCollection {
* @param {!Array.<!libjass.renderers.Keyframe>} keyframes
*/
add(timingFunction: string, keyframes: Keyframe[]): void {
let start: number | null = null;
let end: number | null = null;
let start: number = null;
let end: number = null;
for (const keyframe of keyframes) {
if (start === null) {
@@ -82,10 +82,6 @@ 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) {
+15 -48
View File
@@ -54,76 +54,43 @@ 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 bboxMinX = Infinity;
let bboxMaxX = -Infinity;
let bboxMinY = Infinity;
let bboxMaxY = -Infinity;
let bboxWidth = 0;
let bboxHeight = 0;
for (const instruction of drawingInstructions.instructions) {
if (instruction instanceof parts.drawing.MoveInstruction) {
pathString += ` M ${ instruction.x.toFixed(3) } ${ (instruction.y + this._baselineOffset).toFixed(3) }`;
bboxMinX = Math.min(bboxMinX, instruction.x);
bboxMaxX = Math.max(bboxMaxX, instruction.x);
bboxMinY = Math.min(bboxMinY, instruction.y + this._baselineOffset);
bboxMaxY = Math.max(bboxMaxY, instruction.y + this._baselineOffset);
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);
}
else if (instruction instanceof parts.drawing.LineInstruction) {
pathString += ` L ${ instruction.x.toFixed(3) } ${ (instruction.y + this._baselineOffset).toFixed(3) }`;
bboxMinX = Math.min(bboxMinX, instruction.x);
bboxMaxX = Math.max(bboxMaxX, instruction.x);
bboxMinY = Math.min(bboxMinY, instruction.y + this._baselineOffset);
bboxMaxY = Math.max(bboxMaxY, instruction.y + this._baselineOffset);
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);
}
else if (instruction instanceof parts.drawing.CubicBezierCurveInstruction) {
pathString += ` C ${ instruction.x1.toFixed(3) } ${ (instruction.y1 + this._baselineOffset).toFixed(3) } ${ instruction.x2.toFixed(3) } ${ (instruction.y2 + this._baselineOffset).toFixed(3) } ${ instruction.x3.toFixed(3) } ${ (instruction.y3 + this._baselineOffset).toFixed(3) }`;
bboxMinX = Math.min(bboxMinX, instruction.x1, instruction.x2, instruction.x3);
bboxMaxX = Math.max(bboxMaxX, instruction.x1, instruction.x2, instruction.x3);
bboxMinY = Math.min(bboxMinY, instruction.y1 + this._baselineOffset, instruction.y2 + this._baselineOffset, instruction.y3 + this._baselineOffset);
bboxMaxY = Math.max(bboxMaxY, instruction.y1 + this._baselineOffset, instruction.y2 + this._baselineOffset, instruction.y3 + this._baselineOffset);
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);
}
}
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 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`;
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;
+91 -252
View File
@@ -111,8 +111,8 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
super(ass, clock, (() => {
if (!(_libjassSubsWrapper instanceof HTMLDivElement)) {
const temp = settings;
settings = _libjassSubsWrapper as any;
_libjassSubsWrapper = temp as any;
settings = <any>_libjassSubsWrapper;
_libjassSubsWrapper = <any>temp;
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>;
let ttfNames: Set<string> = null;
try {
ttfNames = getTtfNames(attachment);
}
@@ -215,7 +215,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
}
});
let fontFetchPromise: Promise<FontFace | null>;
let fontFetchPromise: Promise<FontFace>;
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 is string => url !== null);
}).filter(url => 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<null>((resolve, reject) => {
new Promise<void>((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(null) :
Promise.resolve<void>(null) :
Promise_first(thisFontFamilysFetchPromises).catch(reason => {
console.warn(`Fetching fonts for ${ fontFamily } at ${ urls.join(", ") } failed: %o`, reason);
return null;
@@ -354,7 +354,12 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
* @param {!libjass.Dialogue} dialogue
* @return {PreRenderedSub}
*/
preRender(dialogue: Dialogue): PreRenderedSub | null {
preRender(dialogue: Dialogue): PreRenderedSub {
const alreadyPreRenderedSub = this._preRenderedSubs.get(dialogue.id);
if (alreadyPreRenderedSub) {
return alreadyPreRenderedSub;
}
const currentTimeRelativeToDialogueStart = this.clock.currentTime - dialogue.start;
if (dialogue.containsTransformTag && currentTimeRelativeToDialogueStart < 0) {
@@ -362,11 +367,6 @@ 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 = null;
let currentSpan: HTMLSpanElement = null;
const currentSpanStyles = new SpanStyles(this, dialogue, this._scaleX, this._scaleY, this.settings, this._fontSizeElement, svgDefsElement, this._fontMetricsCache);
let currentAnimationCollection: AnimationCollection | null = null;
let currentAnimationCollection: AnimationCollection = 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 *= (1 + part.value);
currentSpanStyles.fontSize += part.value;
}
else if (part instanceof parts.FontSizeMinus) {
currentSpanStyles.fontSize *= (1 - part.value);
currentSpanStyles.fontSize -= 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,13 +587,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
}
else if (part instanceof parts.Reset) {
const newStyleName = part.value;
if (newStyleName === null) {
currentSpanStyles.reset(null);
}
else {
currentSpanStyles.reset(this.ass.styles.get(newStyleName));
}
currentSpanStyles.reset(this.ass.styles.get(part.value));
}
else if (part instanceof parts.Position) {
@@ -607,10 +601,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([
@@ -651,259 +645,124 @@ 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) {
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;
}
currentSpanStyles.outlineWidth += progression * (tag.value - currentSpanStyles.outlineWidth);
currentSpanStyles.outlineHeight += progression * (tag.value - currentSpanStyles.outlineHeight);
}
else if (tag instanceof parts.BorderX) {
if (tag.value !== null) {
currentSpanStyles.outlineWidth += progression * (tag.value - currentSpanStyles.outlineWidth);
}
else {
currentSpanStyles.outlineWidth = null;
}
currentSpanStyles.outlineWidth += progression * (tag.value - currentSpanStyles.outlineWidth);
}
else if (tag instanceof parts.BorderY) {
if (tag.value !== null) {
currentSpanStyles.outlineHeight += progression * (tag.value - currentSpanStyles.outlineHeight);
}
else {
currentSpanStyles.outlineHeight = null;
}
currentSpanStyles.outlineHeight += progression * (tag.value - currentSpanStyles.outlineHeight);
}
else if (tag instanceof parts.Shadow) {
if (tag.value !== null) {
currentSpanStyles.shadowDepthX += progression * (tag.value - currentSpanStyles.shadowDepthX);
currentSpanStyles.shadowDepthY += progression * (tag.value - currentSpanStyles.shadowDepthY);
}
else {
currentSpanStyles.shadowDepthX = null;
currentSpanStyles.shadowDepthY = null;
}
currentSpanStyles.shadowDepthX += progression * (tag.value - currentSpanStyles.shadowDepthX);
currentSpanStyles.shadowDepthY += progression * (tag.value - currentSpanStyles.shadowDepthY);
}
else if (tag instanceof parts.ShadowX) {
if (tag.value !== null) {
currentSpanStyles.shadowDepthX += progression * (tag.value - currentSpanStyles.shadowDepthX);
}
else {
currentSpanStyles.shadowDepthX = null;
}
currentSpanStyles.shadowDepthX += progression * (tag.value - currentSpanStyles.shadowDepthX);
}
else if (tag instanceof parts.ShadowY) {
if (tag.value !== null) {
currentSpanStyles.shadowDepthY += progression * (tag.value - currentSpanStyles.shadowDepthY);
}
else {
currentSpanStyles.shadowDepthY = null;
}
currentSpanStyles.shadowDepthY += progression * (tag.value - currentSpanStyles.shadowDepthY);
}
else if (tag instanceof parts.Blur) {
if (tag.value !== null) {
currentSpanStyles.blur += progression * (tag.value - currentSpanStyles.blur);
}
else {
currentSpanStyles.blur = null;
}
currentSpanStyles.blur += progression * (tag.value - currentSpanStyles.blur);
}
else if (tag instanceof parts.GaussianBlur) {
if (tag.value !== null) {
currentSpanStyles.gaussianBlur += progression * (tag.value - currentSpanStyles.gaussianBlur);
}
else {
currentSpanStyles.gaussianBlur = null;
}
currentSpanStyles.gaussianBlur += progression * (tag.value - currentSpanStyles.gaussianBlur);
}
else if (tag instanceof parts.FontSize) {
if (tag.value !== null) {
currentSpanStyles.fontSize += progression * (tag.value - currentSpanStyles.fontSize);
}
else {
currentSpanStyles.fontSize = null;
}
currentSpanStyles.fontSize += progression * (tag.value - currentSpanStyles.fontSize);
}
else if (tag instanceof parts.FontSizePlus) {
currentSpanStyles.fontSize *= (1 + progression * tag.value);
currentSpanStyles.fontSize += progression * tag.value;
}
else if (tag instanceof parts.FontSizeMinus) {
currentSpanStyles.fontSize *= (1 - progression * tag.value);
currentSpanStyles.fontSize -= progression * tag.value;
}
else if (tag instanceof parts.FontScaleX) {
if (tag.value !== null) {
currentSpanStyles.fontScaleX += progression * (tag.value - currentSpanStyles.fontScaleX);
}
else {
currentSpanStyles.fontScaleX = null;
}
currentSpanStyles.fontScaleX += progression * (tag.value - currentSpanStyles.fontScaleX);
}
else if (tag instanceof parts.FontScaleY) {
if (tag.value !== null) {
currentSpanStyles.fontScaleY += progression * (tag.value - currentSpanStyles.fontScaleY);
}
else {
currentSpanStyles.fontScaleY = null;
}
currentSpanStyles.fontScaleY += progression * (tag.value - currentSpanStyles.fontScaleY);
}
else if (tag instanceof parts.LetterSpacing) {
if (tag.value !== null) {
currentSpanStyles.letterSpacing += progression * (tag.value - currentSpanStyles.letterSpacing);
}
else {
currentSpanStyles.letterSpacing = null;
}
currentSpanStyles.letterSpacing += progression * (tag.value - currentSpanStyles.letterSpacing);
}
else if (tag instanceof parts.RotateX) {
if (tag.value !== null) {
currentSpanStyles.rotationX += progression * (tag.value - currentSpanStyles.rotationX);
}
else {
currentSpanStyles.rotationX = null;
}
currentSpanStyles.rotationX += progression * (tag.value - currentSpanStyles.rotationX);
}
else if (tag instanceof parts.RotateY) {
if (tag.value !== null) {
currentSpanStyles.rotationY += progression * (tag.value - currentSpanStyles.rotationY);
}
else {
currentSpanStyles.rotationY = null;
}
currentSpanStyles.rotationY += progression * (tag.value - currentSpanStyles.rotationY);
}
else if (tag instanceof parts.RotateZ) {
if (tag.value !== null) {
currentSpanStyles.rotationZ += progression * (tag.value - currentSpanStyles.rotationZ);
}
else {
currentSpanStyles.rotationZ = null;
}
currentSpanStyles.rotationZ += progression * (tag.value - currentSpanStyles.rotationZ);
}
else if (tag instanceof parts.SkewX) {
if (tag.value !== null) {
currentSpanStyles.skewX += progression * (tag.value - currentSpanStyles.skewX);
}
else {
currentSpanStyles.skewX = null;
}
currentSpanStyles.skewX += progression * (tag.value - currentSpanStyles.skewX);
}
else if (tag instanceof parts.SkewY) {
if (tag.value !== null) {
currentSpanStyles.skewY += progression * (tag.value - currentSpanStyles.skewY);
}
else {
currentSpanStyles.skewY = null;
}
currentSpanStyles.skewY += progression * (tag.value - currentSpanStyles.skewY);
}
else if (tag instanceof parts.PrimaryColor) {
if (tag.value !== null) {
currentSpanStyles.primaryColor = currentSpanStyles.primaryColor!.interpolate(tag.value, progression);
}
else {
currentSpanStyles.primaryColor = null;
}
currentSpanStyles.primaryColor = currentSpanStyles.primaryColor.interpolate(tag.value, progression);
}
else if (tag instanceof parts.SecondaryColor) {
if (tag.value !== null) {
currentSpanStyles.secondaryColor = currentSpanStyles.secondaryColor!.interpolate(tag.value, progression);
}
else {
currentSpanStyles.secondaryColor = null;
}
currentSpanStyles.secondaryColor = currentSpanStyles.secondaryColor.interpolate(tag.value, progression);
}
else if (tag instanceof parts.OutlineColor) {
if (tag.value !== null) {
currentSpanStyles.outlineColor = currentSpanStyles.outlineColor!.interpolate(tag.value, progression);
}
else {
currentSpanStyles.outlineColor = null;
}
currentSpanStyles.outlineColor = currentSpanStyles.outlineColor.interpolate(tag.value, progression);
}
else if (tag instanceof parts.ShadowColor) {
if (tag.value !== null) {
currentSpanStyles.shadowColor = currentSpanStyles.shadowColor!.interpolate(tag.value, progression);
}
else {
currentSpanStyles.shadowColor = null;
}
currentSpanStyles.shadowColor = currentSpanStyles.shadowColor.interpolate(tag.value, progression);
}
else if (tag instanceof parts.Alpha) {
if (tag.value !== null) {
currentSpanStyles.primaryAlpha += progression * (tag.value - currentSpanStyles.primaryAlpha);
currentSpanStyles.secondaryAlpha += progression * (tag.value - currentSpanStyles.secondaryAlpha);
currentSpanStyles.outlineAlpha += progression * (tag.value - currentSpanStyles.outlineAlpha);
currentSpanStyles.shadowAlpha += progression * (tag.value - currentSpanStyles.shadowAlpha);
}
else {
currentSpanStyles.primaryAlpha = null;
currentSpanStyles.secondaryAlpha = null;
currentSpanStyles.outlineAlpha = null;
currentSpanStyles.shadowAlpha = null;
}
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 if (tag instanceof parts.PrimaryAlpha) {
if (tag.value !== null) {
currentSpanStyles.primaryAlpha += progression * (tag.value - currentSpanStyles.primaryAlpha);
}
else {
currentSpanStyles.primaryAlpha = null;
}
currentSpanStyles.primaryAlpha += progression * (tag.value - currentSpanStyles.primaryAlpha);
}
else if (tag instanceof parts.SecondaryAlpha) {
if (tag.value !== null) {
currentSpanStyles.secondaryAlpha += progression * (tag.value - currentSpanStyles.secondaryAlpha);
}
else {
currentSpanStyles.secondaryAlpha = null;
}
currentSpanStyles.secondaryAlpha += progression * (tag.value - currentSpanStyles.secondaryAlpha);
}
else if (tag instanceof parts.OutlineAlpha) {
if (tag.value !== null) {
currentSpanStyles.outlineAlpha += progression * (tag.value - currentSpanStyles.outlineAlpha);
}
else {
currentSpanStyles.outlineAlpha = null;
}
currentSpanStyles.outlineAlpha += progression * (tag.value - currentSpanStyles.outlineAlpha);
}
else if (tag instanceof parts.ShadowAlpha) {
if (tag.value !== null) {
currentSpanStyles.shadowAlpha += progression * (tag.value - currentSpanStyles.shadowAlpha);
}
else {
currentSpanStyles.shadowAlpha = null;
}
currentSpanStyles.shadowAlpha += progression * (tag.value - currentSpanStyles.shadowAlpha);
}
}
}
@@ -919,17 +778,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);
}
@@ -938,35 +797,23 @@ 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];
divTransformStyle = `translate(${ -transformOrigin[0] }%, ${ -transformOrigin[1] }%) translate(-${ sub.style.marginLeft }, -${ sub.style.marginTop })`;
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;
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";
@@ -1029,23 +876,21 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
console.log(dialogue.toString());
}
let thePreRenderedSub = this._preRenderedSubs.get(dialogue.id);
let preRenderedSub = this._preRenderedSubs.get(dialogue.id);
if (thePreRenderedSub === undefined) {
thePreRenderedSub = this.preRender(dialogue)!;
if (preRenderedSub === undefined) {
preRenderedSub = this.preRender(dialogue);
if (debugMode) {
console.log(dialogue.toString());
}
}
const preRenderedSub = thePreRenderedSub;
const result = preRenderedSub.sub.cloneNode(true);
const result = <HTMLDivElement>preRenderedSub.sub.cloneNode(true);
const applyAnimationDelays = (node: HTMLElement) => {
const animationNames = node.style.animationName || node.style.webkitAnimationName || "";
if (animationNames !== "") {
const animationNames = node.style.animationName || node.style.webkitAnimationName;
if (animationNames !== undefined && animationNames !== "") {
const animationDelays = animationNames.split(",").map(name => {
name = name.trim();
const delay = preRenderedSub.animationDelays.get(name);
@@ -1055,11 +900,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(animatedDescendants[i] as HTMLElement);
applyAnimationDelays(<HTMLElement>animatedDescendants[i]);
}
const layer = dialogue.layer;
@@ -1071,7 +916,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 = null;
let insertBeforeElement: HTMLDivElement = null;
for (let insertBeforeLayer = layer + 1; insertBeforeLayer < this._layerWrappers.length && insertBeforeElement === null; insertBeforeLayer++) {
if (this._layerWrappers[insertBeforeLayer] !== undefined) {
insertBeforeElement = this._layerWrappers[insertBeforeLayer];
@@ -1084,14 +929,13 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
this._layerAlignmentWrappers[layer] = [];
}
let layerAlignmentWrapper = this._layerAlignmentWrappers[layer][alignment];
if (layerAlignmentWrapper === undefined) {
layerAlignmentWrapper = this._layerAlignmentWrappers[layer][alignment] = document.createElement("div");
if (this._layerAlignmentWrappers[layer][alignment] === undefined) {
const layerAlignmentWrapper = 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 = null;
let insertBeforeElement: HTMLDivElement = 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];
@@ -1099,23 +943,18 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
}
layerWrapper.insertBefore(layerAlignmentWrapper, insertBeforeElement);
this._layerAlignmentWrappers[layer][alignment] = layerAlignmentWrapper;
}
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);
}
this._layerAlignmentWrappers[layer][alignment].appendChild(result);
// Workaround for IE
const dialogueAnimationStylesElement = result.getElementsByTagName("style")[0];
if (dialogueAnimationStylesElement !== undefined) {
const sheet = dialogueAnimationStylesElement.sheet as CSSStyleSheet;
const sheet = <CSSStyleSheet>dialogueAnimationStylesElement.sheet;
if (sheet.cssRules.length === 0) {
sheet.cssText = dialogueAnimationStylesElement.textContent!;
sheet.cssText = dialogueAnimationStylesElement.textContent;
}
}
@@ -1177,7 +1016,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
*/
private _calculateFontMetricsAfterFetch(fontFamily: string, fontFetchPromise: Promise<any>): Promise<[number, number]> {
return fontFetchPromise.then(() => {
const fontSizeElement = this._fontSizeElement.cloneNode(true);
const fontSizeElement = <HTMLDivElement>this._fontSizeElement.cloneNode(true);
this._libjassSubsWrapper.appendChild(fontSizeElement);
return Promise_finally(calculateFontMetrics(fontFamily, this.settings.fallbackFonts, fontSizeElement), () => {
this._libjassSubsWrapper.removeChild(fontSizeElement);
@@ -1198,7 +1037,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]
@@ -1223,7 +1062,7 @@ export class WebRenderer extends NullRenderer implements EventSource<string> {
}
mixin(WebRenderer, [EventSource]);
export interface PreRenderedSub {
interface PreRenderedSub {
/** @type {!HTMLDivElement} */
sub: HTMLDivElement;
+206 -320
View File
@@ -51,7 +51,7 @@ export class SpanStyles {
private _defaultStyle: Style;
private _italic: boolean;
private _bold: boolean | number;
private _bold: Object;
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 | undefined | null): void {
reset(newStyle: Style): void {
if (newStyle === undefined || newStyle === null) {
newStyle = this._defaultStyle;
}
@@ -127,12 +127,12 @@ export class SpanStyles {
this.letterSpacing = newStyle.letterSpacing;
this.rotationX = 0;
this.rotationY = 0;
this.rotationZ = newStyle.rotationZ;
this._rotationX = null;
this._rotationY = null;
this._rotationZ = newStyle.rotationZ;
this.skewX = 0;
this.skewY = 0;
this._skewX = null;
this._skewY = null;
this.primaryColor = newStyle.primaryColor;
this.secondaryColor = newStyle.secondaryColor;
@@ -168,9 +168,11 @@ export class SpanStyles {
else if (this._bold !== false) {
fontStyleOrWeight += this._bold + " ";
}
const lineHeight = this._scaleY * (isTextOnlySpan ? this._fontScaleX : 1) * this._fontSize;
const fontSize = fontSizeForLineHeight(this._fontName, lineHeight, this._settings.fallbackFonts, this._fontSizeElement, this._fontMetricsCache);
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);
let fonts = this._fontName;
@@ -191,7 +193,7 @@ export class SpanStyles {
fonts += `, ${ this._settings.fallbackFonts }`;
}
span.style.font = `${ fontStyleOrWeight }${ fontSize.toFixed(3) }px/${ lineHeight.toFixed(3) }px ${ fonts }`;
span.style.font = `${ fontStyleOrWeight }${ fontSize }px/${ lineHeight }px ${ fonts }`;
let textDecoration = "";
if (this._underline) {
@@ -216,8 +218,19 @@ export class SpanStyles {
transform += `scaleY(${ this._fontScaleY }) `;
}
}
if (this._skewX !== 0 || this._skewY !== 0) {
transform += `matrix(1, ${ this._skewY }, ${ this._skewX }, 1, 0, 0) `;
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 (transform !== "") {
span.style.webkitTransform = transform;
@@ -231,8 +244,9 @@ export class SpanStyles {
const outlineWidth = this._scaleX * this._outlineWidth;
const outlineHeight = this._scaleY * this._outlineHeight;
const shadowDepthX = this._scaleX * this._shadowDepthX;
const shadowDepthY = this._scaleY * this._shadowDepthY;
const filterWrapperSpan = document.createElement("span");
filterWrapperSpan.appendChild(span);
let primaryColor = this._primaryColor.withAlpha(this._primaryAlpha);
let outlineColor = this._outlineColor.withAlpha(this._outlineAlpha);
@@ -254,74 +268,55 @@ export class SpanStyles {
span.style.color = primaryColor.toString();
if (this._settings.enableSvg) {
this._svg(
span,
outlineWidth, outlineHeight, outlineColor,
shadowDepthX, shadowDepthY, shadowColor
);
this._setSvgOutlineOnSpan(filterWrapperSpan, outlineWidth, outlineHeight, outlineColor, this._primaryAlpha);
}
else {
this._textShadow(
span,
outlineWidth, outlineHeight, outlineColor,
shadowDepthX, shadowDepthY, shadowColor
);
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;
}
}
if (this._rotationX !== 0 || this._rotationY !== 0) {
// Perspective needs to be set on a "transformable element"
span.style.display = "inline-block";
filterWrapperSpan.style.display = "inline-block";
}
span.style.webkitAnimation = animationCollection.animationStyle;
span.style.animation = animationCollection.animationStyle;
return span;
return filterWrapperSpan;
}
/**
* @param {!HTMLSpanElement} span
* @param {!HTMLSpanElement} filterWrapperSpan
* @param {number} outlineWidth
* @param {number} outlineHeight
* @param {!libjass.parts.Color} outlineColor
* @param {number} shadowDepthX
* @param {number} shadowDepthY
* @param {!libjass.parts.Color} shadowColor
* @param {number} primaryAlpha
*/
private _svg(
span: HTMLSpanElement,
outlineWidth: number, outlineHeight: number, outlineColor: Color,
shadowDepthX: number, shadowDepthY: number, shadowColor: Color
): void {
private _setSvgOutlineOnSpan(filterWrapperSpan: HTMLSpanElement, outlineWidth: number, outlineHeight: number, outlineColor: Color, primaryAlpha: number): void {
const filterId = `svg-filter-${ this._id }-${ this._nextFilterId++ }`;
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 || 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);
if (outlineWidth > 0 || outlineHeight > 0) {
/* 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;
@@ -356,61 +351,78 @@ export class SpanStyles {
}
}
})((x: number, y: number): void => {
const outlineId = `outline${ outlineNumber }`;
const outlineFilter = document.createElementNS("http://www.w3.org/2000/svg", "feMorphology");
filterElement.insertBefore(outlineFilter, mergedOutlines);
filterElement.appendChild(outlineFilter);
outlineFilter.in1.baseVal = "source";
outlineFilter.operator.baseVal = SVGFEMorphologyElement.SVG_MORPHOLOGY_OPERATOR_DILATE;
outlineFilter.radiusX.baseVal = x;
outlineFilter.radiusY.baseVal = y;
outlineFilter.result.baseVal = outlineId;
const outlineReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
mergedOutlines.appendChild(outlineReferenceNode);
outlineReferenceNode.in1.baseVal = outlineId;
outlineFilter.result.baseVal = `outline${ outlineNumber }`;
outlineNumber++;
});
((addOutline: (x: number, y: number) => void) => {
if ((outlineWidth % 1) > 0) {
addOutline(outlineWidth, 0);
addOutline(-outlineWidth, 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.insertBefore(source, filterElement.firstElementChild);
source.in1.baseVal = "SourceAlpha";
source.result.baseVal = "source";
if ((outlineHeight % 1) > 0) {
addOutline(0, outlineHeight);
addOutline(0, -outlineHeight);
}
})((x: number, y: number): void => {
const outlineId = `outline${ outlineNumber }`;
const sourceAlphaTransferNode = document.createElementNS("http://www.w3.org/2000/svg", "feFuncA");
source.appendChild(sourceAlphaTransferNode);
sourceAlphaTransferNode.type.baseVal = SVGComponentTransferFunctionElement.SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
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;
/* 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);
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 = outlineId;
outlineNumber++;
});
outlineReferenceNode.in1.baseVal = `outline${ i }`;
}
// Color it with the outline color
const coloredOutline = createComponentTransferFilter(outlineColor);
filterElement.appendChild(coloredOutline);
coloredOutline.in1.baseVal = "outline-alpha";
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;
// 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;
}
@@ -421,65 +433,21 @@ export class SpanStyles {
blurFilter.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
}
// 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";
// 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;
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";
}
// Merge the outline with the SourceGraphic
const mergedOutlineAndSourceGraphic = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
filterElement.appendChild(mergedOutlineAndSourceGraphic);
const outlineReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
mergedResult.appendChild(outlineReferenceNode);
outlineReferenceNode.in1.baseVal = "outline-colored";
mergedOutlineAndSourceGraphic.appendChild(outlineReferenceNode);
const sourceGraphicReferenceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
mergedResult.appendChild(sourceGraphicReferenceNode);
mergedOutlineAndSourceGraphic.appendChild(sourceGraphicReferenceNode);
sourceGraphicReferenceNode.in1.baseVal = "SourceGraphic";
}
else {
@@ -487,8 +455,6 @@ 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;
}
@@ -501,18 +467,10 @@ export class SpanStyles {
}
if (filterElement.childElementCount > 0) {
const filterId = `libjass-svg-filter-${ this._id }-${ this._nextFilterId++ }`;
this._svgDefsElement.appendChild(filterElement);
filterElement.id = filterId;
filterElement.x.baseVal.valueAsString = "-50%";
filterElement.width.baseVal.valueAsString = "200%";
filterElement.y.baseVal.valueAsString = "-50%";
filterElement.height.baseVal.valueAsString = "200%";
const filterProperty = `url("#${ filterId }")`;
span.style.webkitFilter = filterProperty;
span.style.filter = filterProperty;
filterWrapperSpan.style.webkitFilter = `url("#${ filterId }")`;
filterWrapperSpan.style.filter = `url("#${ filterId }")`;
}
}
@@ -521,18 +479,10 @@ 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 _textShadow(
span: HTMLSpanElement,
outlineWidth: number, outlineHeight: number, outlineColor: Color,
shadowDepthX: number, shadowDepthY: number, shadowColor: Color
): void {
private _setTextShadowOutlineOnSpan(span: HTMLSpanElement, outlineWidth: number, outlineHeight: number, outlineColor: Color): void {
if (outlineWidth > 0 || outlineHeight > 0) {
let outlineCssString = "";
let shadowCssString = "";
((addOutline: (x: number, y: number) => void) => {
for (let x = 0; x <= outlineWidth; x++) {
@@ -553,34 +503,11 @@ 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.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`;
}
outlineCssString += `, ${ outlineColor.toString() } ${ x }px ${ y }px ${ this._gaussianBlur.toFixed(3) }px`;
});
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;
}
span.style.textShadow = outlineCssString.substr(", ".length);
}
}
@@ -599,16 +526,16 @@ export class SpanStyles {
* @type {?boolean}
*/
set italic(value: boolean) {
this._italic = valueOrDefault(value, this._defaultStyle.italic);
this._italic = SpanStyles._valueOrDefault(value, this._defaultStyle.italic);
}
/**
* Sets the bold property. null defaults it to the default style's value.
*
* @type {(?boolean|?number)}
* @type {(?number|?boolean)}
*/
set bold(value: boolean | number | null) {
this._bold = valueOrDefault(value, this._defaultStyle.bold);
set bold(value: Object) {
this._bold = SpanStyles._valueOrDefault(value, this._defaultStyle.bold);
}
/**
@@ -617,7 +544,7 @@ export class SpanStyles {
* @type {?boolean}
*/
set underline(value: boolean) {
this._underline = valueOrDefault(value, this._defaultStyle.underline);
this._underline = SpanStyles._valueOrDefault(value, this._defaultStyle.underline);
}
/**
@@ -626,7 +553,7 @@ export class SpanStyles {
* @type {?boolean}
*/
set strikeThrough(value: boolean) {
this._strikeThrough = valueOrDefault(value, this._defaultStyle.strikeThrough);
this._strikeThrough = SpanStyles._valueOrDefault(value, this._defaultStyle.strikeThrough);
}
/**
@@ -634,7 +561,7 @@ export class SpanStyles {
*
* @type {number}
*/
get outlineWidth(): number | null {
get outlineWidth() {
return this._outlineWidth;
}
@@ -643,17 +570,17 @@ export class SpanStyles {
*
* @type {?number}
*/
set outlineWidth(value: number | null) {
this._outlineWidth = valueOrDefault(value, this._defaultStyle.outlineThickness);
set outlineWidth(value: number) {
this._outlineWidth = SpanStyles._valueOrDefault(value, this._defaultStyle.outlineThickness);
}
/**
* Gets the outline height property.
* Gets the outline width property.
*
* @type {number}
*/
get outlineHeight(): number | null {
return this._outlineHeight;
get outlineHeight() {
return this._outlineWidth;
}
/**
@@ -661,8 +588,8 @@ export class SpanStyles {
*
* @type {?number}
*/
set outlineHeight(value: number | null) {
this._outlineHeight = valueOrDefault(value, this._defaultStyle.outlineThickness);
set outlineHeight(value: number) {
this._outlineHeight = SpanStyles._valueOrDefault(value, this._defaultStyle.outlineThickness);
}
/**
@@ -670,7 +597,7 @@ export class SpanStyles {
*
* @type {number}
*/
get shadowDepthX(): number | null {
get shadowDepthX() {
return this._shadowDepthX;
}
@@ -679,8 +606,8 @@ export class SpanStyles {
*
* @type {?number}
*/
set shadowDepthX(value: number | null) {
this._shadowDepthX = valueOrDefault(value, this._defaultStyle.shadowDepth);
set shadowDepthX(value: number) {
this._shadowDepthX = SpanStyles._valueOrDefault(value, this._defaultStyle.shadowDepth);
}
/**
@@ -688,7 +615,7 @@ export class SpanStyles {
*
* @type {number}
*/
get shadowDepthY(): number | null {
get shadowDepthY() {
return this._shadowDepthY;
}
@@ -697,8 +624,8 @@ export class SpanStyles {
*
* @type {?number}
*/
set shadowDepthY(value: number | null) {
this._shadowDepthY = valueOrDefault(value, this._defaultStyle.shadowDepth);
set shadowDepthY(value: number) {
this._shadowDepthY = SpanStyles._valueOrDefault(value, this._defaultStyle.shadowDepth);
}
/**
@@ -706,7 +633,7 @@ export class SpanStyles {
*
* @type {number}
*/
get blur(): number | null {
get blur() {
return this._blur;
}
@@ -715,8 +642,8 @@ export class SpanStyles {
*
* @type {?number}
*/
set blur(value: number | null) {
this._blur = valueOrDefault(value, 0);
set blur(value: number) {
this._blur = SpanStyles._valueOrDefault(value, 0);
}
/**
@@ -724,7 +651,7 @@ export class SpanStyles {
*
* @type {number}
*/
get gaussianBlur(): number | null {
get gaussianBlur() {
return this._gaussianBlur;
}
@@ -733,8 +660,8 @@ export class SpanStyles {
*
* @type {?number}
*/
set gaussianBlur(value: number | null) {
this._gaussianBlur = valueOrDefault(value, 0);
set gaussianBlur(value: number) {
this._gaussianBlur = SpanStyles._valueOrDefault(value, 0);
}
/**
@@ -742,8 +669,8 @@ export class SpanStyles {
*
* @type {?string}
*/
set fontName(value: string | null) {
this._fontName = valueOrDefault(value, this._defaultStyle.fontName);
set fontName(value: string) {
this._fontName = SpanStyles._valueOrDefault(value, this._defaultStyle.fontName);
}
/**
@@ -751,7 +678,7 @@ export class SpanStyles {
*
* @type {number}
*/
get fontSize(): number | null {
get fontSize() {
return this._fontSize;
}
@@ -760,8 +687,8 @@ export class SpanStyles {
*
* @type {?number}
*/
set fontSize(value: number | null) {
this._fontSize = valueOrDefault(value, this._defaultStyle.fontSize);
set fontSize(value: number) {
this._fontSize = SpanStyles._valueOrDefault(value, this._defaultStyle.fontSize);
}
/**
@@ -769,7 +696,7 @@ export class SpanStyles {
*
* @type {number}
*/
get fontScaleX(): number | null {
get fontScaleX() {
return this._fontScaleX;
}
@@ -778,8 +705,8 @@ export class SpanStyles {
*
* @type {?number}
*/
set fontScaleX(value: number | null) {
this._fontScaleX = valueOrDefault(value, this._defaultStyle.fontScaleX);
set fontScaleX(value: number) {
this._fontScaleX = SpanStyles._valueOrDefault(value, this._defaultStyle.fontScaleX);
}
/**
@@ -787,7 +714,7 @@ export class SpanStyles {
*
* @type {number}
*/
get fontScaleY(): number | null {
get fontScaleY() {
return this._fontScaleY;
}
@@ -796,8 +723,8 @@ export class SpanStyles {
*
* @type {?number}
*/
set fontScaleY(value: number | null) {
this._fontScaleY = valueOrDefault(value, this._defaultStyle.fontScaleY);
set fontScaleY(value: number) {
this._fontScaleY = SpanStyles._valueOrDefault(value, this._defaultStyle.fontScaleY);
}
/**
@@ -805,7 +732,7 @@ export class SpanStyles {
*
* @type {number}
*/
get letterSpacing(): number | null {
get letterSpacing() {
return this._letterSpacing;
}
@@ -814,16 +741,16 @@ export class SpanStyles {
*
* @type {?number}
*/
set letterSpacing(value: number | null) {
this._letterSpacing = valueOrDefault(value, this._defaultStyle.letterSpacing);
set letterSpacing(value: number) {
this._letterSpacing = SpanStyles._valueOrDefault(value, this._defaultStyle.letterSpacing);
}
/**
* Gets the X-axis rotation property.
*
* @type {number}
* @type {?number}
*/
get rotationX(): number | null {
get rotationX() {
return this._rotationX;
}
@@ -832,16 +759,16 @@ export class SpanStyles {
*
* @type {?number}
*/
set rotationX(value: number | null) {
this._rotationX = valueOrDefault(value, 0);
set rotationX(value: number) {
this._rotationX = value;
}
/**
* Gets the Y-axis rotation property.
*
* @type {number}
* @type {?number}
*/
get rotationY(): number | null {
get rotationY() {
return this._rotationY;
}
@@ -850,16 +777,16 @@ export class SpanStyles {
*
* @type {?number}
*/
set rotationY(value: number | null) {
this._rotationY = valueOrDefault(value, 0);
set rotationY(value: number) {
this._rotationY = value;
}
/**
* Gets the Z-axis rotation property.
*
* @type {number}
* @type {?number}
*/
get rotationZ(): number | null {
get rotationZ() {
return this._rotationZ;
}
@@ -868,16 +795,16 @@ export class SpanStyles {
*
* @type {?number}
*/
set rotationZ(value: number | null) {
this._rotationZ = valueOrDefault(value, this._defaultStyle.rotationZ);
set rotationZ(value: number) {
this._rotationZ = SpanStyles._valueOrDefault(value, this._defaultStyle.rotationZ);
}
/**
* Gets the X-axis skew property.
*
* @type {number}
* @type {?number}
*/
get skewX(): number | null {
get skewX() {
return this._skewX;
}
@@ -886,16 +813,16 @@ export class SpanStyles {
*
* @type {?number}
*/
set skewX(value: number | null) {
this._skewX = valueOrDefault(value, 0);
set skewX(value: number) {
this._skewX = value;
}
/**
* Gets the Y-axis skew property.
*
* @type {number}
* @type {?number}
*/
get skewY(): number | null {
get skewY() {
return this._skewY;
}
@@ -904,8 +831,8 @@ export class SpanStyles {
*
* @type {?number}
*/
set skewY(value: number | null) {
this._skewY = valueOrDefault(value, 0);
set skewY(value: number) {
this._skewY = value;
}
/**
@@ -913,7 +840,7 @@ export class SpanStyles {
*
* @type {!libjass.Color}
*/
get primaryColor(): Color | null {
get primaryColor(): Color {
return this._primaryColor;
}
@@ -922,8 +849,8 @@ export class SpanStyles {
*
* @type {libjass.Color}
*/
set primaryColor(value: Color | null) {
this._primaryColor = valueOrDefault(value, this._defaultStyle.primaryColor);
set primaryColor(value: Color) {
this._primaryColor = SpanStyles._valueOrDefault(value, this._defaultStyle.primaryColor);
}
/**
@@ -931,7 +858,7 @@ export class SpanStyles {
*
* @type {!libjass.Color}
*/
get secondaryColor(): Color | null {
get secondaryColor(): Color {
return this._secondaryColor;
}
@@ -940,8 +867,8 @@ export class SpanStyles {
*
* @type {libjass.Color}
*/
set secondaryColor(value: Color | null) {
this._secondaryColor = valueOrDefault(value, this._defaultStyle.secondaryColor);
set secondaryColor(value: Color) {
this._secondaryColor = SpanStyles._valueOrDefault(value, this._defaultStyle.secondaryColor);
}
/**
@@ -949,7 +876,7 @@ export class SpanStyles {
*
* @type {!libjass.Color}
*/
get outlineColor(): Color | null {
get outlineColor(): Color {
return this._outlineColor;
}
@@ -958,8 +885,8 @@ export class SpanStyles {
*
* @type {libjass.Color}
*/
set outlineColor(value: Color | null) {
this._outlineColor = valueOrDefault(value, this._defaultStyle.outlineColor);
set outlineColor(value: Color) {
this._outlineColor = SpanStyles._valueOrDefault(value, this._defaultStyle.outlineColor);
}
/**
@@ -967,7 +894,7 @@ export class SpanStyles {
*
* @type {!libjass.Color}
*/
get shadowColor(): Color | null {
get shadowColor(): Color {
return this._shadowColor;
}
@@ -976,8 +903,8 @@ export class SpanStyles {
*
* @type {libjass.Color}
*/
set shadowColor(value: Color | null) {
this._shadowColor = valueOrDefault(value, this._defaultStyle.shadowColor);
set shadowColor(value: Color) {
this._shadowColor = SpanStyles._valueOrDefault(value, this._defaultStyle.shadowColor);
}
/**
@@ -985,7 +912,7 @@ export class SpanStyles {
*
* @type {number}
*/
get primaryAlpha(): number | null {
get primaryAlpha(): number {
return this._primaryAlpha;
}
@@ -994,8 +921,8 @@ export class SpanStyles {
*
* @type {?number}
*/
set primaryAlpha(value: number | null) {
this._primaryAlpha = valueOrDefault(value, this._defaultStyle.primaryColor.alpha);
set primaryAlpha(value: number) {
this._primaryAlpha = SpanStyles._valueOrDefault(value, this._defaultStyle.primaryColor.alpha);
}
/**
@@ -1003,7 +930,7 @@ export class SpanStyles {
*
* @type {number}
*/
get secondaryAlpha(): number | null {
get secondaryAlpha(): number {
return this._secondaryAlpha;
}
@@ -1012,8 +939,8 @@ export class SpanStyles {
*
* @type {?number}
*/
set secondaryAlpha(value: number | null) {
this._secondaryAlpha = valueOrDefault(value, this._defaultStyle.secondaryColor.alpha);
set secondaryAlpha(value: number) {
this._secondaryAlpha = SpanStyles._valueOrDefault(value, this._defaultStyle.secondaryColor.alpha);
}
/**
@@ -1021,7 +948,7 @@ export class SpanStyles {
*
* @type {number}
*/
get outlineAlpha(): number | null {
get outlineAlpha(): number {
return this._outlineAlpha;
}
@@ -1030,8 +957,8 @@ export class SpanStyles {
*
* @type {?number}
*/
set outlineAlpha(value: number | null) {
this._outlineAlpha = valueOrDefault(value, this._defaultStyle.outlineColor.alpha);
set outlineAlpha(value: number) {
this._outlineAlpha = SpanStyles._valueOrDefault(value, this._defaultStyle.outlineColor.alpha);
}
/**
@@ -1039,7 +966,7 @@ export class SpanStyles {
*
* @type {number}
*/
get shadowAlpha(): number | null {
get shadowAlpha(): number {
return this._shadowAlpha;
}
@@ -1048,50 +975,9 @@ export class SpanStyles {
*
* @type {?number}
*/
set shadowAlpha(value: number | null) {
this._shadowAlpha = valueOrDefault(value, this._defaultStyle.shadowColor.alpha);
set shadowAlpha(value: number) {
this._shadowAlpha = SpanStyles._valueOrDefault(value, this._defaultStyle.shadowColor.alpha);
}
}
/**
* @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);
private static _valueOrDefault = <T>(newValue: T, defaultValue: T): T => ((newValue !== null) ? newValue : defaultValue);
}
+17 -2
View File
@@ -18,8 +18,23 @@
* limitations under the License.
*/
interface Node {
cloneNode(deep?: boolean): this;
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 SVGFEComponentTransferElement {
-78
View File
@@ -1,78 +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.
*/
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;
});
}
+3 -17
View File
@@ -1,23 +1,9 @@
{
"compilerOptions": {
"lib": ["es5", "dom"],
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "CommonJS",
"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,
"inlineSources": true
"target": "ES5",
"experimentalDecorators": true
}
}
+23 -86
View File
@@ -25,35 +25,32 @@ import { ScriptProperties } from "./script-properties";
import { Format } from "./misc";
import { debugMode, verboseMode } from "../settings";
import { verboseMode } from "../settings";
import * as parser from "../parser";
import { parseLineIntoTypedTemplate } from "../parser/misc";
import { ReadableStream, TextDecoderConstructor } from "../parser/streams";
import { ReadableStream, TextDecoder, 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?: Function & { prototype: ReadableStream; };
ReadableStream?: { 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 = null;
private _dialoguesFormatSpecifier: string[] | null = null;
private _stylesFormatSpecifier: string[] = null;
private _dialoguesFormatSpecifier: string[] = null;
/**
* The properties of this script.
@@ -94,36 +91,36 @@ export class ASS {
/**
* The format specifier for the styles section.
*
* @type {Array.<string>}
* @type {!Array.<string>}
*/
get stylesFormatSpecifier(): string[] | null {
get stylesFormatSpecifier(): string[] {
return this._stylesFormatSpecifier;
}
/**
* The format specifier for the styles section.
*
* @type {Array.<string>}
* @type {!Array.<string>}
*/
get dialoguesFormatSpecifier(): string[] | null {
get dialoguesFormatSpecifier(): string[] {
return this._dialoguesFormatSpecifier;
}
/**
* The format specifier for the events section.
*
* @type {Array.<string>}
* @type {!Array.<string>}
*/
set stylesFormatSpecifier(value: string[] | null) {
set stylesFormatSpecifier(value: string[]) {
this._stylesFormatSpecifier = value;
}
/**
* The format specifier for the events section.
*
* @type {Array.<string>}
* @type {!Array.<string>}
*/
set dialoguesFormatSpecifier(value: string[] | null) {
set dialoguesFormatSpecifier(value: string[]) {
this._dialoguesFormatSpecifier = value;
}
@@ -142,10 +139,6 @@ 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;
@@ -170,10 +163,6 @@ 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;
@@ -200,37 +189,14 @@ 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|string)=0} type The type of the script. One of the {@link libjass.Format} constants, or one of the strings "ass" and "srt".
* @param {number=0} type The type of the script. One of the {@link libjass.Format} constants.
* @return {!Promise.<!libjass.ASS>}
*/
static fromString(raw: string, type: Format | "ass" | "srt" = Format.ASS): Promise<ASS> {
static fromString(raw: string, type: Format = Format.ASS): Promise<ASS> {
return ASS.fromStream(new parser.StringStream(raw), type);
}
@@ -238,19 +204,17 @@ 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|string)=0} type The type of the script. One of the {@link libjass.Format} constants, or one of the strings "ass" and "srt".
* @param {number=0} type The type of the script. One of the {@link libjass.Format} constants.
* @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 | "ass" | "srt" = Format.ASS): Promise<ASS> {
static fromStream(stream: parser.Stream, type: Format = 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(`Invalid value of type: ${ type }`);
throw new Error(`Illegal value of type: ${ type }`);
}
}
@@ -258,10 +222,10 @@ export class ASS {
* Creates an ASS object from the given URL.
*
* @param {string} url The URL of the script.
* @param {(number|string)=0} type The type of the script. One of the {@link libjass.Format} constants, or one of the strings "ass" and "srt".
* @param {number=0} type The type of the script. One of the {@link libjass.Format} constants.
* @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 | "ass" | "srt" = Format.ASS): Promise<ASS> {
static fromUrl(url: string, type: Format = Format.ASS): Promise<ASS> {
let fetchPromise: Promise<ASS>;
if (
@@ -282,9 +246,7 @@ export class ASS {
}
return fetchPromise.catch(reason => {
if (debugMode) {
console.log("fetch() failed, falling back to XHR: %o", reason);
}
console.warn("fetch() failed, falling back to XHR: %o", reason);
const xhr = new XMLHttpRequest();
const result = ASS.fromStream(new parser.XhrStream(xhr), type);
@@ -299,35 +261,10 @@ export class ASS {
*
* @param {!ReadableStream} stream
* @param {string="utf-8"} encoding
* @param {(number|string)=0} type The type of the script. One of the {@link libjass.Format} constants, or one of the strings "ass" and "srt".
* @param {number=0} type The type of the script. One of the {@link libjass.Format} constants.
* @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 | "ass" | "srt" = Format.ASS): Promise<ASS> {
static fromReadableStream(stream: ReadableStream, encoding: string = "utf-8", type: Format = 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;
}
}
-3
View File
@@ -18,8 +18,6 @@
* limitations under the License.
*/
import { registerClass as serializable } from "../serialization";
/**
* The type of an attachment.
*/
@@ -34,7 +32,6 @@ 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 = "";
+28 -40
View File
@@ -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,7 +44,6 @@ 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;
@@ -59,7 +58,7 @@ export class Dialogue {
private _alignment: number;
private _rawPartsString: string;
private _parts: parts.Part[] | null = null;
private _parts: parts.Part[] = null;
private _containsTransformTag: boolean = false;
@@ -75,45 +74,34 @@ export class Dialogue {
this._id = ++Dialogue._lastDialogueId;
let styleName = template.get("style");
if (typeof styleName === "string") {
if (styleName !== undefined && styleName !== null && styleName.constructor === String) {
styleName = styleName.replace(/^\*+/, "");
if (styleName.match(/^Default$/i) !== null) {
styleName = "Default";
}
}
let style = (styleName !== undefined) ? ass.styles.get(styleName) : undefined;
if (style === undefined) {
this._style = ass.styles.get(styleName);
if (this._style === undefined) {
if (debugMode) {
console.warn(`Unrecognized style ${ styleName }. Falling back to "Default"`);
}
style = ass.styles.get("Default");
if (style === undefined) {
throw new Error(`Unrecognized style ${ styleName }. Could not fall back to "Default" style since it doesn't exist.`);
}
this._style = ass.styles.get("Default");
}
if (this._style === undefined) {
throw new Error(`Unrecognized style ${ styleName }`);
}
this._style = style;
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._start = Dialogue._toTime(template.get("start"));
this._end = Dialogue._toTime(template.get("end"));
this._layer = Math.max(valueOrDefault(template, "layer", parseInt, value => !isNaN(value), "0"), 0);
const text = template.get("text");
if (typeof text !== "string") {
throw new Error(`Dialogue text ${ text } is not a string.`);
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.");
}
this._rawPartsString = text;
}
/**
@@ -184,7 +172,7 @@ export class Dialogue {
this._parsePartsString();
}
return this._parts!;
return this._parts;
}
/**
@@ -211,7 +199,7 @@ export class Dialogue {
* Parses this dialogue's parts from the raw parts string.
*/
private _parsePartsString(): void {
this._parts = parse(this._rawPartsString, "dialogueParts") as parts.Part[];
this._parts = <parts.Part[]>parse(this._rawPartsString, "dialogueParts");
this._alignment = this._style.alignment;
@@ -221,7 +209,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
@@ -230,7 +218,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,
@@ -257,14 +245,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}
*/
function 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}
*/
private static _toTime(str: string): number {
return str.split(":").reduce<number>((previousValue, currentValue) => previousValue * 60 + parseFloat(currentValue), 0);
}
}
+1 -1
View File
@@ -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) | null, defaultValue: string): T {
export function valueOrDefault<T>(template: Map<string, string>, key: string, converter: (str: string) => T, validator: (value: T) => boolean, defaultValue: string): T {
const value = template.get(key);
if (value === undefined) {
return converter(defaultValue);
-3
View File
@@ -18,14 +18,11 @@
* 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;
+9 -12
View File
@@ -24,8 +24,6 @@ import { parse } from "../parser/parse";
import { Color } from "../parts";
import { registerClass as serializable } from "../serialization";
import { Map } from "../utility/map";
/**
@@ -52,7 +50,6 @@ 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;
@@ -96,11 +93,11 @@ export class Style {
template = normalizedTemplate;
}
const name = template.get("name");
if (typeof name !== "string") {
throw new Error(`Style name ${ name } is not a string.`);
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.");
}
this._name = name.replace(/^\*+/, "");
this._name = this._name.replace(/^\*+/, "");
this._italic = !!valueOrDefault(template, "italic", parseFloat, value => !isNaN(value), "0");
this._bold = !!valueOrDefault(template, "bold", parseFloat, value => !isNaN(value), "0");
@@ -117,13 +114,13 @@ export class Style {
this._rotationZ = valueOrDefault(template, "angle", parseFloat, value => !isNaN(value), "0");
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._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._outlineThickness = valueOrDefault(template, "outline", parseFloat, value => value >= 0, "2");
this._borderStyle = valueOrDefault(template, "borderstyle", parseInt, value => (BorderStyle as any)[(BorderStyle as any)[value]] === value, "1");
this._borderStyle = valueOrDefault(template, "borderstyle", parseInt, value => (<any>BorderStyle)[(<any>BorderStyle)[value]] === value, "1");
this._shadowDepth = valueOrDefault(template, "shadow", parseFloat, value => value >= 0, "3");
+31 -33
View File
@@ -18,12 +18,16 @@
* 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 | undefined;
get(key: K): V;
/**
* @param {K} key
@@ -36,7 +40,7 @@ export interface Map<K, V> {
* @param {V} value
* @return {libjass.Map.<K, V>} This map
*/
set(key: K, value: V): this;
set(key: K, value?: V): Map<K, V>;
/**
* @param {K} key
@@ -52,7 +56,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: this) => void, thisArg?: any): void;
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void;
/**
* @type {number}
@@ -60,6 +64,18 @@ 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".
*
@@ -67,7 +83,7 @@ export interface Map<K, V> {
*
* @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> implements Map<K, V> {
class SimpleMap<K, V> {
private _keys: { [key: string]: K };
private _values: { [key: string]: V };
private _size: number;
@@ -90,9 +106,9 @@ class SimpleMap<K, V> implements Map<K, V> {
/**
* @param {K} key
* @return {?V}
* @return {V}
*/
get(key: K): V | undefined {
get(key: K): V {
const property = this._keyToProperty(key);
if (property === null) {
@@ -121,7 +137,7 @@ class SimpleMap<K, V> implements Map<K, V> {
* @param {V} value
* @return {libjass.Map.<K, V>} This map
*/
set(key: K, value: V): this {
set(key: K, value: V): Map<K, V> {
const property = this._keyToProperty(key);
if (property === null) {
@@ -172,7 +188,7 @@ class SimpleMap<K, V> implements 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: this) => void, thisArg?: any): void {
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void {
for (const property of Object.keys(this._keys)) {
callbackfn.call(thisArg, this._values[property], this._keys[property], this);
}
@@ -189,9 +205,9 @@ class SimpleMap<K, V> implements Map<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 | null {
private _keyToProperty(key: K): string {
if (typeof key === "number") {
return `#${ key }`;
}
@@ -200,29 +216,15 @@ class SimpleMap<K, V> implements Map<K, V> {
return `'${ key }`;
}
if ((key as any).id !== undefined) {
return `!${ (key as any).id }`;
if ((<any>key).id !== undefined) {
return `!${ (<any>key).id }`;
}
return null;
}
}
/**
* 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" || (() => {
if (Map === undefined || typeof Map.prototype.forEach !== "function" || (() => {
try {
return new Map([[1, "foo"], [2, "bar"]]).size !== 2;
}
@@ -233,16 +235,12 @@ if (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 | null): void {
export function setImplementation(value: typeof Map): void {
if (value !== null) {
Map = value;
}
+50 -49
View File
@@ -18,6 +18,15 @@
* 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>;
@@ -25,38 +34,53 @@ export interface Thenable<T> {
export interface ThenableThen<T> {
/** @type {function(this:!Thenable.<T>, function(T|!Thenable.<T>), function(*))} */
(this: Thenable<T>, resolve: ((resolution: T | Thenable<T>) => void) | undefined, reject: ((reason: any) => void) | undefined): void;
(resolve: (resolution: T | Thenable<T>) => void, reject: (reason: any) => void): 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> | undefined, onRejected?: (reason: any) => U | Thenable<U>): Promise<U>;
then<U>(onFulfilled?: (value: T) => Thenable<U>, 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 | undefined, onRejected?: (reason: any) => U | Thenable<U>): Promise<U>;
then<U>(onFulfilled?: (value: T) => U, 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) => {
process.nextTick(callback);
global.process.nextTick(callback);
};
}
else if (MutationObserver !== undefined) {
@@ -107,7 +131,7 @@ class SimplePromise<T> {
private _fulfillReactions: FulfilledPromiseReaction<T, any>[] = [];
private _rejectReactions: RejectedPromiseReaction<any>[] = [];
private _fulfilledValue: T | null = null;
private _fulfilledValue: T = null;
private _rejectedReason: any = null;
constructor(executor: (resolve: (resolution: T | Thenable<T>) => void, reject: (reason: any) => void) => void) {
@@ -129,11 +153,11 @@ class SimplePromise<T> {
* @param {?function(*):(U|!Thenable.<U>)} onRejected
* @return {!Promise.<U>}
*/
then<U>(onFulfilled: ((value: T) => U | Thenable<U>) | undefined, onRejected?: (reason: any) => U | Thenable<U>): Promise<U> {
then<U>(onFulfilled: (value: T) => U | Thenable<U>, onRejected: (reason: any) => U | Thenable<U>): Promise<U> {
const resultCapability = new DeferredPromise<U>();
if (typeof onFulfilled !== "function") {
onFulfilled = (value: T) => value as any as U;
onFulfilled = (value: T) => <U><any>value;
}
if (typeof onRejected !== "function") {
@@ -157,7 +181,7 @@ class SimplePromise<T> {
break;
case SimplePromiseState.FULFILLED:
this._enqueueFulfilledReactionJob(fulfillReaction, this._fulfilledValue!);
this._enqueueFulfilledReactionJob(fulfillReaction, this._fulfilledValue);
break;
case SimplePromiseState.REJECTED:
@@ -172,8 +196,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(undefined, onRejected);
catch(onRejected?: (reason: any) => T | Thenable<T>): Promise<T> {
return this.then(null, onRejected);
}
/**
@@ -252,12 +276,12 @@ class SimplePromise<T> {
}
if (resolution === null || (typeof resolution !== "object" && typeof resolution !== "function")) {
this._fulfill(resolution as T);
this._fulfill(<T>resolution);
return;
}
try {
var then = (resolution as Thenable<T>).then;
var then = (<Thenable<T>>resolution).then;
}
catch (ex) {
this._reject(ex);
@@ -265,11 +289,11 @@ class SimplePromise<T> {
}
if (typeof then !== "function") {
this._fulfill(resolution as T);
this._fulfill(<T>resolution);
return;
}
enqueueJob(() => this._resolveWithThenable(resolution as Thenable<T>, then));
enqueueJob(() => this._resolveWithThenable(<Thenable<T>>resolution, then));
};
const reject = (reason: any): void => {
@@ -307,8 +331,8 @@ class SimplePromise<T> {
const reactions = this._fulfillReactions;
this._fulfilledValue = value;
this._fulfillReactions = [];
this._rejectReactions = [];
this._fulfillReactions = undefined;
this._rejectReactions = undefined;
this._state = SimplePromiseState.FULFILLED;
for (const reaction of reactions) {
@@ -323,8 +347,8 @@ class SimplePromise<T> {
const reactions = this._rejectReactions;
this._rejectedReason = reason;
this._fulfillReactions = [];
this._rejectReactions = [];
this._fulfillReactions = undefined;
this._rejectReactions = undefined;
this._state = SimplePromiseState.REJECTED;
for (const reaction of reactions) {
@@ -377,32 +401,9 @@ class SimplePromise<T> {
}
}
/**
* 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;
}
};
if (Promise === undefined) {
Promise = SimplePromise;
}
interface FulfilledPromiseReaction<T, U> {
/** @type {!libjass.DeferredPromise.<U>} */
@@ -440,7 +441,7 @@ enum SimplePromiseState {
*
* @param {?function(new:Promise)} value
*/
export function setImplementation(value: typeof Promise | null): void {
export function setImplementation(value: typeof Promise): void {
if (value !== null) {
Promise = value;
}
+25 -27
View File
@@ -18,12 +18,16 @@
* 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): this;
add(value: T): Set<T>;
/**
*/
@@ -39,7 +43,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: this) => void, thisArg?: any): void;
forEach(callbackfn: (value: T, index: T, set: Set<T>) => void, thisArg?: any): void;
/**
* @type {number}
@@ -47,6 +51,18 @@ 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.
*
@@ -54,7 +70,7 @@ export interface Set<T> {
*
* @param {!Array.<T>=} iterable Only an array of values is supported.
*/
class SimpleSet<T> implements Set<T> {
class SimpleSet<T> {
private _elements: { [key: string]: T };
private _size: number;
@@ -78,7 +94,7 @@ class SimpleSet<T> implements Set<T> {
* @param {T} value
* @return {libjass.Set.<T>} This set
*/
add(value: T): this {
add(value: T): Set<T> {
const property = this._toProperty(value);
if (property === null) {
@@ -119,7 +135,7 @@ class SimpleSet<T> implements 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: this) => void, thisArg?: any): void {
forEach(callbackfn: (value: T, index: T, set: Set<T>) => void, thisArg?: any): void {
for (const property of Object.keys(this._elements)) {
const element = this._elements[property];
callbackfn.call(thisArg, element, element, this);
@@ -137,9 +153,9 @@ class SimpleSet<T> implements Set<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 | null {
private _toProperty(value: T): string {
if (typeof value === "number") {
return `#${ value }`;
}
@@ -152,21 +168,7 @@ class SimpleSet<T> implements Set<T> {
}
}
/**
* 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" || (() => {
if (Set === undefined || typeof Set.prototype.forEach !== "function" || (() => {
try {
return new Set([1, 2]).size !== 2;
}
@@ -177,16 +179,12 @@ if (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 | null): void {
export function setImplementation(value: typeof Set): void {
if (value !== null) {
Set = value;
}
+86
View File
@@ -0,0 +1,86 @@
/**
* 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);
}
}
+8 -9
View File
@@ -18,14 +18,13 @@
* 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, registerWorkerCommand } from "./misc";
import { getWorkerCommandHandler, serialize, deserialize, 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}
@@ -51,7 +50,7 @@ export interface WorkerCommandHandler {
/**
* The interface implemented by a communication channel to the other side.
*/
export interface WorkerCommunication {
interface WorkerCommunication {
/**
* @param {"message"} type
* @param {function(!MessageEvent): *} listener
@@ -136,7 +135,7 @@ export class WorkerChannelImpl implements WorkerChannel {
private _pendingRequests = new Map<number, DeferredPromise<any>>();
constructor(private _comm: WorkerCommunication) {
this._comm.addEventListener("message", ev => this._onMessage(ev.data as string), false);
this._comm.addEventListener("message", ev => this._onMessage(<string>ev.data), false);
}
/**
@@ -183,10 +182,10 @@ export class WorkerChannelImpl implements WorkerChannel {
* @param {string} rawMessage
*/
private _onMessage(rawMessage: string): void {
const message = deserialize(rawMessage) as { command: WorkerCommands };
const message = <{ command: WorkerCommands }>deserialize(rawMessage);
if (message.command === WorkerCommands.Response) {
const responseMessage = message as any as WorkerResponseMessage;
const responseMessage = <WorkerResponseMessage><any>message;
const deferred = this._pendingRequests.get(responseMessage.requestId);
if (deferred !== undefined) {
@@ -200,7 +199,7 @@ export class WorkerChannelImpl implements WorkerChannel {
}
}
else {
const requestMessage = message as WorkerRequestMessage;
const requestMessage = <WorkerRequestMessage>message;
const requestId = requestMessage.requestId;
const commandCallback = getWorkerCommandHandler(requestMessage.command);
@@ -217,4 +216,4 @@ export class WorkerChannelImpl implements WorkerChannel {
}
}
registerWorkerCommand(WorkerCommands.Ping, parameters => Promise.resolve(null));
registerWorkerCommand(WorkerCommands.Ping, parameters => new Promise<void>(resolve => resolve(null)));
+8 -10
View File
@@ -18,11 +18,17 @@
* 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.
*
@@ -30,7 +36,7 @@ export { WorkerCommands } from "./commands";
*/
export const supported = typeof Worker !== "undefined";
const _scriptNode = (typeof document !== "undefined" && document.currentScript !== undefined) ? (document.currentScript as HTMLScriptElement) : null;
const _scriptNode = (typeof document !== "undefined" && document.currentScript !== undefined) ? document.currentScript : null;
/**
* Create a new web worker and returns a {@link libjass.webworker.WorkerChannel} to it.
@@ -39,15 +45,7 @@ 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): 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;
}
export function createWorker(scriptPath: string = _scriptNode.src): WorkerChannel {
return new WorkerChannelImpl(new Worker(scriptPath));
}
+47 -1
View File
@@ -25,6 +25,8 @@ import { WorkerCommandHandler } from "./channel";
const workerCommands = new Map<WorkerCommands, WorkerCommandHandler>();
const classPrototypes = new Map<number, any>();
/**
* Registers a handler for the given worker command.
*
@@ -41,6 +43,50 @@ export function registerWorkerCommand(command: WorkerCommands, handler: WorkerCo
* @param {number} command
* @return {?function(*, function(*, *))}
*/
export function getWorkerCommandHandler(command: WorkerCommands): WorkerCommandHandler | undefined {
export function getWorkerCommandHandler(command: WorkerCommands): WorkerCommandHandler {
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;
});
}
+7
View File
@@ -18,6 +18,13 @@
* limitations under the License.
*/
interface Document {
/**
* @type {!HTMLScriptElement}
*/
currentScript: HTMLScriptElement;
}
interface WorkerGlobalScope {
/**
* @param {*} message
+3 -3
View File
@@ -2,8 +2,8 @@
Title:
ScriptType: v4.00+
WrapStyle: 0
PlayResX: 1280
PlayResY: 720
PlayResX: 256
PlayResY: 144
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,500,&H7F0000FF,&H00FFFFFF,&H7F000000,&H7F000000,0,0,0,0,100,100,0,0,1,10,0,2,75,75,75,1
Style: Default,Arial,100,&H7F0000FF,&H00FFFFFF,&H7F000000,&H7F000000,0,0,0,0,100,100,0,0,1,2,0,2,15,15,15,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
+2 -3
View File
@@ -21,11 +21,10 @@
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", 1280, 720, "rgb(47, 163, 254)");
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)");
return testPage
.prepare()
.then(function (testPage) { return testPage.seekAndCompareScreenshot(1, require.toUrl("./alpha-1.png")); })
.then(function (testPage) { return testPage.done(); });
.then(function (testPage) { return testPage.seekAndCompareScreenshot(1, require.toUrl("./alpha-1.png")); });
});
});
});
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

-32
View File
@@ -1,32 +0,0 @@
[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
-40
View File
@@ -1,40 +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.
*/
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(); });
});
});
});
Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

+3 -3
View File
@@ -2,8 +2,8 @@
Title:
ScriptType: v4.00+
WrapStyle: 0
PlayResX: 1280
PlayResY: 720
PlayResX: 256
PlayResY: 144
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,180,&H000000FF,&H00FFFFFF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,10,6,2,75,75,75,1
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
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
+2 -3
View File
@@ -21,11 +21,10 @@
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", 1280, 720);
var testPage = new TestPage(this.remote, require.toUrl("tests/support/browser-test-page.html"), "/tests/functional/fsc/fsc.ass", 256, 144);
return testPage
.prepare()
.then(function (testPage) { return testPage.seekAndCompareScreenshot(1, require.toUrl("./fsc-1.png")); })
.then(function (testPage) { return testPage.done(); });
.then(function (testPage) { return testPage.seekAndCompareScreenshot(1, require.toUrl("./fsc-1.png")); });
});
});
});
Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

+3 -3
View File
@@ -6,12 +6,12 @@ ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: None
PlayResX: 1280
PlayResY: 720
PlayResX: 256
PlayResY: 144
[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,180,&H00FFFFFF,&H000000FF,&H00000000,&H96000000,0,0,0,0,100,100,0,0,1,10,6,2,75,75,75,1
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
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
+2 -3
View File
@@ -21,7 +21,7 @@
define(["intern!tdd", "require", "tests/support/test-page"], function (tdd, require, TestPage) {
tdd.suite("kfx", function () {
tdd.test("Basic", function () {
var testPage = new TestPage(this.remote, require.toUrl("tests/support/browser-test-page.html"), "/tests/functional/kfx/kfx.ass", 1280, 720);
var testPage = new TestPage(this.remote, require.toUrl("tests/support/browser-test-page.html"), "/tests/functional/kfx/kfx.ass", 256, 144);
return testPage
.prepare()
.then(function (testPage) { return testPage.seekAndCompareScreenshot(1.5, require.toUrl("./kfx-1.png")); })
@@ -29,8 +29,7 @@ define(["intern!tdd", "require", "tests/support/test-page"], function (tdd, requ
.then(function (testPage) { return testPage.seekAndCompareScreenshot(3.5, require.toUrl("./kfx-3.png")); })
.then(function (testPage) { return testPage.seekAndCompareScreenshot(4.5, require.toUrl("./kfx-4.png")); })
.then(function (testPage) { return testPage.seekAndCompareScreenshot(5.5, require.toUrl("./kfx-5.png")); })
.then(function (testPage) { return testPage.seekAndCompareScreenshot(6.5, require.toUrl("./kfx-6.png")); })
.then(function (testPage) { return testPage.done(); });
.then(function (testPage) { return testPage.seekAndCompareScreenshot(6.5, require.toUrl("./kfx-6.png")); });
});
});
});

Some files were not shown because too many files have changed in this diff Show More