Compare commits
6
Commits
v0.8.0
...
new-rotation
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6053f59a1 | ||
|
|
73755e0c8a | ||
|
|
f951475103 | ||
|
|
77474771e6 | ||
|
|
55f9cb42fe | ||
|
|
a8addddbd6 |
+1
-1
@@ -81,7 +81,7 @@
|
||||
}
|
||||
|
||||
/* Filter wrapper span */
|
||||
.libjass-subs div[data-dialogue-id] > span {
|
||||
.libjass-subs div[data-dialogue-id] {
|
||||
-webkit-perspective-origin: center;
|
||||
-webkit-perspective: 200px;
|
||||
perspective-origin: center;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "libjass",
|
||||
"version": "0.8.0",
|
||||
"version": "0.9.0",
|
||||
"description": "A library to render ASS subtitles on HTML5 video in the browser.",
|
||||
"keywords": ["browser", "html5", "subtitles"],
|
||||
"homepage": "https://github.com/Arnavion/libjass",
|
||||
|
||||
@@ -78,13 +78,14 @@ module libjass.parser {
|
||||
parse_assScript(parent: ParseNode): ParseNode {
|
||||
var current = new ParseNode(parent);
|
||||
|
||||
current.value = Object.create(null);
|
||||
var sections = new Map<string, any>();
|
||||
current.value = sections;
|
||||
|
||||
while (this._haveMore()) {
|
||||
var scriptSectionNode = this.parse_assScriptSection(current);
|
||||
|
||||
if (scriptSectionNode !== null) {
|
||||
current.value[scriptSectionNode.value.name] = scriptSectionNode.value.contents;
|
||||
sections.set(scriptSectionNode.value.name, scriptSectionNode.value.contents);
|
||||
}
|
||||
else if (this.read(current, "\n") === null) {
|
||||
parent.pop();
|
||||
@@ -112,7 +113,9 @@ module libjass.parser {
|
||||
current.value.name = sectionHeaderNode.value;
|
||||
current.value.contents = null;
|
||||
|
||||
var formatSpecifier: string[] = null;
|
||||
var sectionTemplate: Map<string, string> = null;
|
||||
|
||||
var sectionTemplateArray: TypedTemplateArray = null;
|
||||
|
||||
while (this._haveMore() && this._peek() !== "[") {
|
||||
if (this.read(current, "\n") !== null) {
|
||||
@@ -126,30 +129,32 @@ module libjass.parser {
|
||||
var propertyNode = this.parse_assScriptProperty(current);
|
||||
|
||||
if (propertyNode !== null) {
|
||||
if (formatSpecifier === null) {
|
||||
if (sectionTemplateArray === null) {
|
||||
var property: { key: string; value: string } = propertyNode.value;
|
||||
|
||||
if (current.value.contents === null && property.key === "Format") {
|
||||
if (sectionTemplateArray === null && property.key === "Format") {
|
||||
// The first line of this section is a format specifier. This section will be an array of templates.
|
||||
formatSpecifier = property.value.split(",").map(formatPart => formatPart.trim());
|
||||
current.value.contents = <TypedTemplateArray>[];
|
||||
current.value.contents.formatSpecifier = formatSpecifier;
|
||||
sectionTemplateArray = <TypedTemplateArray>[];
|
||||
sectionTemplateArray.formatSpecifier = property.value.split(",").map(formatPart => formatPart.trim());
|
||||
|
||||
current.value.contents = sectionTemplateArray;
|
||||
}
|
||||
|
||||
else {
|
||||
// This section is a single template. Each line is a property of this template.
|
||||
if (current.value.contents === null) {
|
||||
current.value.contents = Object.create(null);
|
||||
if (sectionTemplate === null) {
|
||||
sectionTemplate = new Map<string, string>();
|
||||
current.value.contents = sectionTemplate;
|
||||
}
|
||||
|
||||
current.value.contents[property.key] = property.value;
|
||||
sectionTemplate.set(property.key, property.value);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
// Each line in this section is a template of a particular type, and the section is an array of these templates.
|
||||
|
||||
current.value.contents.push(propertyNode.value);
|
||||
sectionTemplateArray.push(<TypedTemplate>propertyNode.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,24 +255,26 @@ module libjass.parser {
|
||||
|
||||
current.value = property;
|
||||
|
||||
var scriptSectionContents = parent.value.contents;
|
||||
if (scriptSectionContents !== null) {
|
||||
var formatSpecifier = (<TypedTemplateArray>scriptSectionContents).formatSpecifier;
|
||||
if (formatSpecifier !== undefined) {
|
||||
// This line is a template of a particular type, and its parent section is an array of these templates.
|
||||
if (parent.value !== null) {
|
||||
var scriptSectionContents = parent.value.contents;
|
||||
if (scriptSectionContents !== undefined && scriptSectionContents !== null) {
|
||||
var formatSpecifier = (<TypedTemplateArray>scriptSectionContents).formatSpecifier;
|
||||
if (formatSpecifier !== undefined) {
|
||||
// This line is a template of a particular type, and its parent section is an array of these templates.
|
||||
|
||||
var template: Template = Object.create(null);
|
||||
var value = property.value.split(",");
|
||||
var template = new Map<string, string>();
|
||||
var value = property.value.split(",");
|
||||
|
||||
if (value.length > formatSpecifier.length) {
|
||||
value[formatSpecifier.length - 1] = value.slice(formatSpecifier.length - 1).join(",");
|
||||
if (value.length > formatSpecifier.length) {
|
||||
value[formatSpecifier.length - 1] = value.slice(formatSpecifier.length - 1).join(",");
|
||||
}
|
||||
|
||||
formatSpecifier.forEach((formatKey, index) => {
|
||||
template.set(formatKey, value[index]);
|
||||
});
|
||||
|
||||
current.value = <TypedTemplate>{ type: property.key, template: template };
|
||||
}
|
||||
|
||||
formatSpecifier.forEach((formatKey, index) => {
|
||||
template[formatKey] = value[index];
|
||||
});
|
||||
|
||||
current.value = <TypedTemplate>{ type: property.key, template: template };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2431,9 +2438,7 @@ module libjass.parser {
|
||||
this._start = ((_parent !== null) ? _parent.end : 0);
|
||||
this._end = this._start;
|
||||
|
||||
if (value !== null) {
|
||||
this.value = value;
|
||||
}
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2487,7 +2492,7 @@ module libjass.parser {
|
||||
set value(newValue: any) {
|
||||
this._value = newValue;
|
||||
|
||||
if (this._value.constructor === String && this._children.length === 0) {
|
||||
if (this._value !== null && this._value.constructor === String && this._children.length === 0) {
|
||||
this._setEnd(this._start + this._value.length);
|
||||
}
|
||||
}
|
||||
|
||||
+129
-79
@@ -46,7 +46,7 @@ module libjass.renderers {
|
||||
private _svgDefsElement: SVGDefsElement = null;
|
||||
|
||||
private _currentSubs: Map<Dialogue, HTMLDivElement> = new Map<Dialogue, HTMLDivElement>();
|
||||
private _preRenderedSubs: Map<number, HTMLDivElement> = new Map<number, HTMLDivElement>();
|
||||
private _preRenderedSubs: Map<number, { sub: HTMLDivElement; animationDelays: number[] }> = new Map<number, { sub: HTMLDivElement; animationDelays: number[] }>();
|
||||
|
||||
private _scaleX: number;
|
||||
private _scaleY: number;
|
||||
@@ -199,14 +199,35 @@ module libjass.renderers {
|
||||
|
||||
var currentSpan: HTMLSpanElement = null;
|
||||
var currentSpanStyles = new SpanStyles(this, dialogue, this._scaleX, this._scaleY, this.settings, this._fontSizeElement, this._svgDefsElement);
|
||||
var nextSpanParent: HTMLElement = sub;
|
||||
var rotationX = 0;
|
||||
var rotationY = 0;
|
||||
var rotationZ = 0;
|
||||
|
||||
var startNewSpan = (addNewLine: boolean): void => {
|
||||
if (currentSpan !== null && currentSpan.textContent !== "") {
|
||||
sub.appendChild(currentSpanStyles.setStylesOnSpan(currentSpan));
|
||||
var newSpan = currentSpanStyles.setStylesOnSpan(currentSpan, rotationX, rotationY, rotationZ);
|
||||
|
||||
if (currentSpanStyles.rotationX !== rotationX || currentSpanStyles.rotationY !== rotationY || currentSpanStyles.rotationZ !== rotationZ) {
|
||||
nextSpanParent.appendChild(newSpan);
|
||||
nextSpanParent = newSpan;
|
||||
|
||||
rotationX = currentSpanStyles.rotationX;
|
||||
rotationY = currentSpanStyles.rotationY;
|
||||
rotationZ = currentSpanStyles.rotationZ;
|
||||
}
|
||||
else {
|
||||
nextSpanParent.appendChild(newSpan.firstElementChild);
|
||||
}
|
||||
}
|
||||
|
||||
if (addNewLine) {
|
||||
sub.appendChild(document.createElement("br"));
|
||||
nextSpanParent = sub;
|
||||
nextSpanParent.appendChild(document.createElement("br"));
|
||||
|
||||
rotationX = 0;
|
||||
rotationY = 0;
|
||||
rotationZ = 0;
|
||||
}
|
||||
|
||||
currentSpan = document.createElement("span");
|
||||
@@ -367,7 +388,7 @@ module libjass.renderers {
|
||||
var newStyleName = (<parts.Reset>part).value;
|
||||
var newStyle: Style = null;
|
||||
if (newStyleName !== null) {
|
||||
newStyle = this.ass.styles[newStyleName];
|
||||
newStyle = this.ass.styles.get(newStyleName);
|
||||
}
|
||||
currentSpanStyles.reset(newStyle);
|
||||
}
|
||||
@@ -384,51 +405,51 @@ module libjass.renderers {
|
||||
var movePart = <parts.Move>part;
|
||||
|
||||
sub.style.position = "absolute";
|
||||
animationCollection.add("linear", [new Keyframe(0, {
|
||||
left: (this._scaleX * movePart.x1).toFixed(3) + "px",
|
||||
top: (this._scaleY * movePart.y1).toFixed(3) + "px"
|
||||
}), new Keyframe(movePart.t1, {
|
||||
left: (this._scaleX * movePart.x1).toFixed(3) + "px",
|
||||
top: (this._scaleY * movePart.y1).toFixed(3) + "px"
|
||||
}), new Keyframe(movePart.t2, {
|
||||
left: (this._scaleX * movePart.x2).toFixed(3) + "px",
|
||||
top: (this._scaleY * movePart.y2).toFixed(3) + "px"
|
||||
}), new Keyframe(dialogue.end - dialogue.start, {
|
||||
left: (this._scaleX * movePart.x2).toFixed(3) + "px",
|
||||
top: (this._scaleY * movePart.y2).toFixed(3) + "px"
|
||||
})]);
|
||||
animationCollection.add("linear", [new Keyframe(0, new Map<string, string>()
|
||||
.set("left", (this._scaleX * movePart.x1).toFixed(3) + "px")
|
||||
.set("top", (this._scaleY * movePart.y1).toFixed(3) + "px")
|
||||
), new Keyframe(movePart.t1, new Map<string, string>()
|
||||
.set("left", (this._scaleX * movePart.x1).toFixed(3) + "px")
|
||||
.set("top", (this._scaleY * movePart.y1).toFixed(3) + "px")
|
||||
), new Keyframe(movePart.t2, new Map<string, string>()
|
||||
.set("left", (this._scaleX * movePart.x2).toFixed(3) + "px")
|
||||
.set("top", (this._scaleY * movePart.y2).toFixed(3) + "px")
|
||||
), new Keyframe(dialogue.end - dialogue.start, new Map<string, string>()
|
||||
.set("left", (this._scaleX * movePart.x2).toFixed(3) + "px")
|
||||
.set("top", (this._scaleY * movePart.y2).toFixed(3) + "px")
|
||||
)]);
|
||||
}
|
||||
|
||||
else if (part instanceof parts.Fade) {
|
||||
var fadePart = <parts.Fade>part;
|
||||
|
||||
animationCollection.add("linear", [new Keyframe(0, {
|
||||
opacity: "0"
|
||||
}), new Keyframe(fadePart.start, {
|
||||
opacity: "1"
|
||||
}), new Keyframe(dialogue.end - dialogue.start - fadePart.end, {
|
||||
opacity: String("1")
|
||||
}), new Keyframe(dialogue.end - dialogue.start, {
|
||||
opacity: "0"
|
||||
})]);
|
||||
animationCollection.add("linear", [new Keyframe(0, new Map<string, string>()
|
||||
.set("opacity", "0")
|
||||
), new Keyframe(fadePart.start, new Map<string, string>()
|
||||
.set("opacity", "1")
|
||||
), new Keyframe(dialogue.end - dialogue.start - fadePart.end, new Map<string, string>()
|
||||
.set("opacity", "1")
|
||||
), new Keyframe(dialogue.end - dialogue.start, new Map<string, string>()
|
||||
.set("opacity", "0")
|
||||
)]);
|
||||
}
|
||||
|
||||
else if (part instanceof parts.ComplexFade) {
|
||||
var complexFadePart = <parts.ComplexFade>part;
|
||||
|
||||
animationCollection.add("linear", [new Keyframe(0, {
|
||||
opacity: String(complexFadePart.a1)
|
||||
}), new Keyframe(complexFadePart.t1, {
|
||||
opacity: String(complexFadePart.a1)
|
||||
}), new Keyframe(complexFadePart.t2, {
|
||||
opacity: String(complexFadePart.a2)
|
||||
}), new Keyframe(complexFadePart.t3, {
|
||||
opacity: String(complexFadePart.a2)
|
||||
}), new Keyframe(complexFadePart.t4, {
|
||||
opacity: String(complexFadePart.a3)
|
||||
}), new Keyframe(dialogue.end - dialogue.start, {
|
||||
opacity: String(complexFadePart.a3)
|
||||
})]);
|
||||
animationCollection.add("linear", [new Keyframe(0, new Map<string, string>()
|
||||
.set("opacity", complexFadePart.a1.toFixed(3))
|
||||
), new Keyframe(complexFadePart.t1, new Map<string, string>()
|
||||
.set("opacity", complexFadePart.a1.toFixed(3))
|
||||
), new Keyframe(complexFadePart.t2, new Map<string, string>()
|
||||
.set("opacity", complexFadePart.a2.toFixed(3))
|
||||
), new Keyframe(complexFadePart.t3, new Map<string, string>()
|
||||
.set("opacity", complexFadePart.a2.toFixed(3))
|
||||
), new Keyframe(complexFadePart.t4, new Map<string, string>()
|
||||
.set("opacity", complexFadePart.a3.toFixed(3))
|
||||
), new Keyframe(dialogue.end - dialogue.start, new Map<string, string>()
|
||||
.set("opacity", complexFadePart.a3.toFixed(3))
|
||||
)]);
|
||||
}
|
||||
|
||||
else if (part instanceof parts.DrawingMode) {
|
||||
@@ -516,7 +537,7 @@ module libjass.renderers {
|
||||
|
||||
sub.setAttribute("data-dialogue-id", this.id + "-" + dialogue.id);
|
||||
|
||||
this._preRenderedSubs.set(dialogue.id, sub);
|
||||
this._preRenderedSubs.set(dialogue.id, { sub: sub, animationDelays: animationCollection.animationDelays });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -549,22 +570,11 @@ module libjass.renderers {
|
||||
}
|
||||
}
|
||||
|
||||
var result = <HTMLDivElement>preRenderedSub.cloneNode(true);
|
||||
var result = <HTMLDivElement>preRenderedSub.sub.cloneNode(true);
|
||||
|
||||
var defaultAnimationDelay = result.style.webkitAnimationDelay;
|
||||
if (defaultAnimationDelay === undefined) {
|
||||
defaultAnimationDelay = result.style.animationDelay;
|
||||
}
|
||||
if (defaultAnimationDelay !== "") {
|
||||
var animationDelay =
|
||||
defaultAnimationDelay
|
||||
.split(",")
|
||||
.map(delay => (parseFloat(delay) + dialogue.start - this.clock.currentTime).toFixed(3) + "s")
|
||||
.join(",");
|
||||
|
||||
result.style.webkitAnimationDelay = animationDelay;
|
||||
result.style.animationDelay = animationDelay;
|
||||
}
|
||||
var animationDelay = preRenderedSub.animationDelays.map(delay => (delay + dialogue.start - this.clock.currentTime).toFixed(3) + "s").join(", ");
|
||||
result.style.webkitAnimationDelay = animationDelay;
|
||||
result.style.animationDelay = animationDelay;
|
||||
|
||||
var layer = dialogue.layer;
|
||||
var alignment = (result.style.position === "absolute") ? 0 : dialogue.alignment; // Alignment 0 is for absolutely-positioned subs
|
||||
@@ -675,10 +685,6 @@ module libjass.renderers {
|
||||
}
|
||||
mixin(WebRenderer, [EventSource]);
|
||||
|
||||
interface KeyframePropertiesMap {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents a single keyframe. It has a list of CSS properties (names and values) associated with a point in time. Multiple keyframes make up an animation.
|
||||
*
|
||||
@@ -686,7 +692,7 @@ module libjass.renderers {
|
||||
* @param {!Object.<string, string>} properties
|
||||
*/
|
||||
class Keyframe {
|
||||
constructor(private _time: number, private _properties: KeyframePropertiesMap) { }
|
||||
constructor(private _time: number, private _properties: Map<string, string>) { }
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
@@ -698,7 +704,7 @@ module libjass.renderers {
|
||||
/**
|
||||
* @type {!Object.<string, string>}
|
||||
*/
|
||||
get properties(): KeyframePropertiesMap {
|
||||
get properties(): Map<string, string> {
|
||||
return this._properties;
|
||||
}
|
||||
}
|
||||
@@ -717,6 +723,7 @@ module libjass.renderers {
|
||||
|
||||
private _cssText: string = "";
|
||||
private _animationStyle: string = "";
|
||||
private _animationDelays: number[] = [];
|
||||
private _numAnimations: number = 0;
|
||||
|
||||
constructor(renderer: NullRenderer, dialogue: Dialogue) {
|
||||
@@ -743,6 +750,13 @@ module libjass.renderers {
|
||||
return this._animationStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* This array should be used to set the "animation-delay" CSS property of the target element.
|
||||
*/
|
||||
get animationDelays(): number[] {
|
||||
return this._animationDelays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an animation to this collection. The given keyframes together make one animation.
|
||||
*
|
||||
@@ -764,8 +778,8 @@ module libjass.renderers {
|
||||
|
||||
ruleCssText += "\t" + (100 * keyframe.time / (this._end - this._start)).toFixed(3) + "% {\n";
|
||||
|
||||
Object.keys(keyframe.properties).forEach(propertyName => {
|
||||
ruleCssText += "\t\t" + propertyName + ": " + keyframe.properties[propertyName] + ";\n";
|
||||
keyframe.properties.forEach((value, name) => {
|
||||
ruleCssText += "\t\t" + name + ": " + value + ";\n";
|
||||
});
|
||||
|
||||
ruleCssText += "\t}\n";
|
||||
@@ -781,7 +795,8 @@ module libjass.renderers {
|
||||
this._animationStyle += ",";
|
||||
}
|
||||
|
||||
this._animationStyle += animationName + " " + (endTime - startTime).toFixed(3) + "s " + timingFunction + " " + startTime.toFixed(3) + "s";
|
||||
this._animationStyle += animationName + " " + (endTime - startTime).toFixed(3) + "s " + timingFunction;
|
||||
this._animationDelays.push(startTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -880,8 +895,8 @@ module libjass.renderers {
|
||||
|
||||
this.letterSpacing = newStyle.letterSpacing;
|
||||
|
||||
this._rotationX = null;
|
||||
this._rotationY = null;
|
||||
this._rotationX = 0;
|
||||
this._rotationY = 0;
|
||||
this._rotationZ = newStyle.rotationZ;
|
||||
|
||||
this._skewX = null;
|
||||
@@ -907,7 +922,7 @@ module libjass.renderers {
|
||||
* @param {!HTMLSpanElement} span
|
||||
* @return {!HTMLSpanElement} The resulting <span> with the CSS styles applied. This may be a wrapper around the input <span> if the styles were applied using SVG filters.
|
||||
*/
|
||||
setStylesOnSpan(span: HTMLSpanElement): HTMLSpanElement {
|
||||
setStylesOnSpan(span: HTMLSpanElement, previousRotationX: number, previousRotationY: number, previousRotationZ: number): HTMLSpanElement {
|
||||
var isTextOnlySpan = span.childNodes[0] instanceof Text;
|
||||
|
||||
var fontStyleOrWeight = "";
|
||||
@@ -953,15 +968,6 @@ module libjass.renderers {
|
||||
transform += "scaleY(" + this._fontScaleY + ") ";
|
||||
}
|
||||
}
|
||||
if (this._rotationY !== null) {
|
||||
transform += "rotateY(" + this._rotationY + "deg) ";
|
||||
}
|
||||
if (this._rotationX !== null) {
|
||||
transform += "rotateX(" + this._rotationX + "deg) ";
|
||||
}
|
||||
if (this._rotationZ !== 0) {
|
||||
transform += "rotateZ(" + (-1 * this._rotationZ) + "deg) ";
|
||||
}
|
||||
if (this._skewX !== null || this._skewY !== null) {
|
||||
var skewX = SpanStyles._valueOrDefault(this._skewX, 0);
|
||||
var skewY = SpanStyles._valueOrDefault(this._skewY, 0);
|
||||
@@ -1086,7 +1092,39 @@ module libjass.renderers {
|
||||
filterWrapperSpan.style.display = "inline-block";
|
||||
}
|
||||
|
||||
return filterWrapperSpan;
|
||||
var rotationWrapperSpan = document.createElement("span");
|
||||
rotationWrapperSpan.appendChild(filterWrapperSpan);
|
||||
|
||||
var rotationTransform = "";
|
||||
if (this._rotationX !== previousRotationX || this._rotationY !== previousRotationY || this._rotationZ !== previousRotationZ) {
|
||||
if (previousRotationZ !== 0) {
|
||||
rotationTransform += "rotateZ(" + (previousRotationZ) + "deg) ";
|
||||
}
|
||||
if (previousRotationX !== 0) {
|
||||
rotationTransform += "rotateX(" + (-previousRotationX) + "deg) ";
|
||||
}
|
||||
if (previousRotationY !== 0) {
|
||||
rotationTransform += "rotateY(" + (-previousRotationY) + "deg) ";
|
||||
}
|
||||
if (this._rotationY !== 0) {
|
||||
rotationTransform += "rotateY(" + this._rotationY + "deg) ";
|
||||
}
|
||||
if (this._rotationX !== 0) {
|
||||
rotationTransform += "rotateX(" + this._rotationX + "deg) ";
|
||||
}
|
||||
if (this._rotationZ !== 0) {
|
||||
rotationTransform += "rotateZ(" + (-this._rotationZ) + "deg) ";
|
||||
}
|
||||
}
|
||||
if (rotationTransform !== "") {
|
||||
rotationWrapperSpan.style.webkitTransform = rotationTransform;
|
||||
rotationWrapperSpan.style.webkitTransformOrigin = "50% 50%";
|
||||
rotationWrapperSpan.style.transform = rotationTransform;
|
||||
rotationWrapperSpan.style.transformOrigin = "50% 50%";
|
||||
rotationWrapperSpan.style.display = "inline-block";
|
||||
}
|
||||
|
||||
return rotationWrapperSpan;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1224,13 +1262,21 @@ module libjass.renderers {
|
||||
this._letterSpacing = SpanStyles._valueOrDefault(value, this._defaultStyle.letterSpacing);
|
||||
}
|
||||
|
||||
get rotationX() {
|
||||
return this._rotationX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the X-axis rotation property.
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
set rotationX(value: number) {
|
||||
this._rotationX = value;
|
||||
this._rotationX = SpanStyles._valueOrDefault(value, 0);
|
||||
}
|
||||
|
||||
get rotationY() {
|
||||
return this._rotationY;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1239,7 +1285,11 @@ module libjass.renderers {
|
||||
* @type {?number}
|
||||
*/
|
||||
set rotationY(value: number) {
|
||||
this._rotationY = value;
|
||||
this._rotationY = SpanStyles._valueOrDefault(value, 0);
|
||||
}
|
||||
|
||||
get rotationZ() {
|
||||
return this._rotationZ;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,16 +21,12 @@
|
||||
///<reference path="libjass.ts" />
|
||||
|
||||
module libjass {
|
||||
export interface StyleMap {
|
||||
[name: string]: Style;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents an ASS script. It contains the script properties, an array of Styles, and an array of Dialogues.
|
||||
*/
|
||||
export class ASS {
|
||||
private _properties: ScriptProperties = new ScriptProperties();
|
||||
private _styles: StyleMap = Object.create(null);
|
||||
private _styles: Map<string, Style> = new Map<string, Style>();
|
||||
private _dialogues: Dialogue[] = [];
|
||||
private _dialoguesFormatSpecifier: string[];
|
||||
|
||||
@@ -46,9 +42,9 @@ module libjass {
|
||||
/**
|
||||
* The styles in this script.
|
||||
*
|
||||
* @type {!Object.<string, !libjass.Style>}
|
||||
* @type {!Map.<string, !libjass.Style>}
|
||||
*/
|
||||
get styles(): StyleMap {
|
||||
get styles(): Map<string, Style> {
|
||||
return this._styles;
|
||||
}
|
||||
|
||||
@@ -115,25 +111,25 @@ module libjass {
|
||||
}
|
||||
|
||||
private static _fromASSString(rawASS: string): ASS {
|
||||
var script = parser.parse(rawASS, "assScript");
|
||||
var script = <Map<string, any>>parser.parse(rawASS, "assScript");
|
||||
|
||||
var result = new ASS();
|
||||
|
||||
// Get the script info template
|
||||
var infoTemplate: Template = script["Script Info"];
|
||||
var infoTemplate: Map<string, string> = script.get("Script Info");
|
||||
|
||||
if (libjass.verboseMode) {
|
||||
console.log("Read script info: " + JSON.stringify(infoTemplate), infoTemplate);
|
||||
}
|
||||
|
||||
// Parse the script properties
|
||||
result.properties.resolutionX = parseInt(infoTemplate["PlayResX"]);
|
||||
result.properties.resolutionY = parseInt(infoTemplate["PlayResY"]);
|
||||
result.properties.wrappingStyle = parseInt(infoTemplate["WrapStyle"]);
|
||||
result.properties.scaleBorderAndShadow = (infoTemplate["ScaledBorderAndShadow"] === "yes");
|
||||
result.properties.resolutionX = parseInt(infoTemplate.get("PlayResX"));
|
||||
result.properties.resolutionY = parseInt(infoTemplate.get("PlayResY"));
|
||||
result.properties.wrappingStyle = parseInt(infoTemplate.get("WrapStyle"));
|
||||
result.properties.scaleBorderAndShadow = (infoTemplate.get("ScaledBorderAndShadow") === "yes");
|
||||
|
||||
// Get styles from the styles section
|
||||
(<TypedTemplateArray>script["V4+ Styles"]).forEach(line => {
|
||||
(<TypedTemplateArray>script.get("V4+ Styles")).forEach(line => {
|
||||
if (line.type === "Style") {
|
||||
var styleTemplate = line.template;
|
||||
|
||||
@@ -143,12 +139,12 @@ module libjass {
|
||||
|
||||
// Create the style and add it to the styles map
|
||||
var newStyle = new Style(styleTemplate);
|
||||
result.styles[newStyle.name] = newStyle;
|
||||
result.styles.set(newStyle.name, newStyle);
|
||||
}
|
||||
});
|
||||
|
||||
// Get dialogues from the events section
|
||||
var events = <TypedTemplateArray>script["Events"];
|
||||
var events = <TypedTemplateArray>script.get("Events");
|
||||
result._dialoguesFormatSpecifier = events.formatSpecifier;
|
||||
events.forEach(line => result._addEvent(line));
|
||||
|
||||
@@ -166,36 +162,36 @@ module libjass {
|
||||
result.properties.wrappingStyle = 1;
|
||||
result.properties.scaleBorderAndShadow = true;
|
||||
|
||||
var newStyle = new Style({
|
||||
"Name": "Default",
|
||||
"Italic": "0", "Bold": "0", "Underline": "0", "StrikeOut": "0",
|
||||
"Fontname": "", "Fontsize": "50",
|
||||
"ScaleX": "100", "ScaleY": "100",
|
||||
"Spacing": "0",
|
||||
"Angle": "0",
|
||||
"PrimaryColour": "&H0000FFFF", "SecondaryColour": "&H00000000", "OutlineColour": "&H00000000", "BackColour": "&H00000000",
|
||||
"Outline": "1", "BorderStyle": "1",
|
||||
"Shadow": "1",
|
||||
"Alignment": "2",
|
||||
"MarginL": "80", "MarginR": "80", "MarginV": "35"
|
||||
});
|
||||
result.styles[newStyle.name] = newStyle;
|
||||
var newStyle = new Style(new Map<string, string>()
|
||||
.set("Name", "Default")
|
||||
.set("Italic", "0").set("Bold", "0").set("Underline", "0").set("StrikeOut", "0")
|
||||
.set("Fontname", "").set("Fontsize", "50")
|
||||
.set("ScaleX", "100").set("ScaleY", "100")
|
||||
.set("Spacing", "0")
|
||||
.set("Angle", "0")
|
||||
.set("PrimaryColour", "&H0000FFFF").set("SecondaryColour", "&H00000000").set("OutlineColour", "&H00000000").set("BackColour", "&H00000000")
|
||||
.set("Outline", "1").set("BorderStyle", "1")
|
||||
.set("Shadow", "1")
|
||||
.set("Alignment", "2")
|
||||
.set("MarginL", "80").set("MarginR", "80").set("MarginV", "35")
|
||||
);
|
||||
result.styles.set(newStyle.name, newStyle);
|
||||
|
||||
script.sort((line1, line2) => line1["number"] - line2["number"]).forEach(line => {
|
||||
result.dialogues.push(new Dialogue({
|
||||
"Style": "Default",
|
||||
"Start": line.start, "End": line.end,
|
||||
"Layer": "0",
|
||||
"Text":
|
||||
result.dialogues.push(new Dialogue(new Map<string, string>()
|
||||
.set("Style", "Default")
|
||||
.set("Start", line.start).set("End", line.end)
|
||||
.set("Layer", "0")
|
||||
.set("Text",
|
||||
(<string>line.text)
|
||||
.replace("<b>", "{\\b1}").replace("</b>", "{\\b0}")
|
||||
.replace("<i>", "{\\i1}").replace("</i>", "{\\i0}")
|
||||
.replace("<u>", "{\\u1}").replace("</u>", "{\\u0}")
|
||||
.replace(/<b>/g, "{\\b1}").replace(/<\/b>/g, "{\\b0}")
|
||||
.replace(/<i>/g, "{\\i1}").replace(/<\/i>/g, "{\\i0}")
|
||||
.replace(/<u>/g, "{\\u1}").replace(/<\/u>/g, "{\\u0}")
|
||||
.replace(
|
||||
/<font color="#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})">/g,
|
||||
(/* ujs:unreferenced */ substring: string, red: string, green: string, blue: string) => "{\c&H" + blue + green + red + "&}"
|
||||
).replace("</font>", "{\\c}")
|
||||
}, result));
|
||||
).replace(/<\/font>/g, "{\\c}")
|
||||
), result));
|
||||
});
|
||||
|
||||
return result;
|
||||
@@ -304,7 +300,7 @@ module libjass {
|
||||
/**
|
||||
* This class represents a single global style declaration in an ASS script. The styles can be obtained via the ASS.styles property.
|
||||
*
|
||||
* @param {!Object} template The template object that contains the style's properties. It is a map of the string values read from the ASS file.
|
||||
* @param {!Map.<string, string>} template The template object that contains the style's properties. It is a map of the string values read from the ASS file.
|
||||
* @param {string} template["Name"] The name of the style
|
||||
* @param {string} template["Italic"] -1 if the style is italicized
|
||||
* @param {string} template["Bold"] -1 if the style is bold
|
||||
@@ -359,39 +355,39 @@ module libjass {
|
||||
private _marginRight: number;
|
||||
private _marginVertical: number;
|
||||
|
||||
constructor(template: Template) {
|
||||
this._name = template["Name"];
|
||||
constructor(template: Map<string, string>) {
|
||||
this._name = template.get("Name");
|
||||
|
||||
this._italic = template["Italic"] === "-1";
|
||||
this._bold = template["Bold"] === "-1";
|
||||
this._underline = template["Underline"] === "-1";
|
||||
this._strikeThrough = template["StrikeOut"] === "-1";
|
||||
this._italic = template.get("Italic") === "-1";
|
||||
this._bold = template.get("Bold") === "-1";
|
||||
this._underline = template.get("Underline") === "-1";
|
||||
this._strikeThrough = template.get("StrikeOut") === "-1";
|
||||
|
||||
this._fontName = template["Fontname"];
|
||||
this._fontSize = parseFloat(template["Fontsize"]);
|
||||
this._fontName = template.get("Fontname");
|
||||
this._fontSize = parseFloat(template.get("Fontsize"));
|
||||
|
||||
this._fontScaleX = parseFloat(template["ScaleX"]) / 100;
|
||||
this._fontScaleY = parseFloat(template["ScaleY"]) / 100;
|
||||
this._fontScaleX = parseFloat(template.get("ScaleX")) / 100;
|
||||
this._fontScaleY = parseFloat(template.get("ScaleY")) / 100;
|
||||
|
||||
this._letterSpacing = parseFloat(template["Spacing"]);
|
||||
this._letterSpacing = parseFloat(template.get("Spacing"));
|
||||
|
||||
this._rotationZ = parseFloat(template["Angle"]);
|
||||
this._rotationZ = parseFloat(template.get("Angle"));
|
||||
|
||||
this._primaryColor = <parts.Color>parser.parse(template["PrimaryColour"], "colorWithAlpha");
|
||||
this._secondaryColor = <parts.Color>parser.parse(template["SecondaryColour"], "colorWithAlpha");
|
||||
this._outlineColor = <parts.Color>parser.parse(template["OutlineColour"], "colorWithAlpha");
|
||||
this._shadowColor = <parts.Color>parser.parse(template["BackColour"], "colorWithAlpha");
|
||||
this._primaryColor = <parts.Color>parser.parse(template.get("PrimaryColour"), "colorWithAlpha");
|
||||
this._secondaryColor = <parts.Color>parser.parse(template.get("SecondaryColour"), "colorWithAlpha");
|
||||
this._outlineColor = <parts.Color>parser.parse(template.get("OutlineColour"), "colorWithAlpha");
|
||||
this._shadowColor = <parts.Color>parser.parse(template.get("BackColour"), "colorWithAlpha");
|
||||
|
||||
this._outlineThickness = parseFloat(template["Outline"]);
|
||||
this._borderStyle = parseInt(template["BorderStyle"]);
|
||||
this._outlineThickness = parseFloat(template.get("Outline"));
|
||||
this._borderStyle = parseInt(template.get("BorderStyle"));
|
||||
|
||||
this._shadowDepth = parseFloat(template["Shadow"]);
|
||||
this._shadowDepth = parseFloat(template.get("Shadow"));
|
||||
|
||||
this._alignment = parseInt(template["Alignment"]);
|
||||
this._alignment = parseInt(template.get("Alignment"));
|
||||
|
||||
this._marginLeft = parseFloat(template["MarginL"]);
|
||||
this._marginRight = parseFloat(template["MarginR"]);
|
||||
this._marginVertical = parseFloat(template["MarginV"]);
|
||||
this._marginLeft = parseFloat(template.get("MarginL"));
|
||||
this._marginRight = parseFloat(template.get("MarginR"));
|
||||
this._marginVertical = parseFloat(template.get("MarginV"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -596,7 +592,7 @@ module libjass {
|
||||
/**
|
||||
* This class represents a dialogue in an ASS script.
|
||||
*
|
||||
* @param {!Object} template The template object that contains the dialogue's properties. It is a map of the string values read from the ASS file.
|
||||
* @param {!Map.<string, string>} template The template object that contains the dialogue's properties. It is a map of the string values read from the ASS file.
|
||||
* @param {string} template["Style"] The name of the default style of this dialogue
|
||||
* @param {string} template["Start"] The start time
|
||||
* @param {string} template["End"] The end time
|
||||
@@ -622,17 +618,17 @@ module libjass {
|
||||
|
||||
private _sub: HTMLDivElement = null;
|
||||
|
||||
constructor(template: Template, ass: ASS) {
|
||||
constructor(template: Map<string, string>, ass: ASS) {
|
||||
this._id = ++Dialogue._lastDialogueId;
|
||||
|
||||
this._style = ass.styles[template["Style"]];
|
||||
this._style = ass.styles.get(template.get("Style"));
|
||||
|
||||
this._start = Dialogue._toTime(template["Start"]);
|
||||
this._end = Dialogue._toTime(template["End"]);
|
||||
this._start = Dialogue._toTime(template.get("Start"));
|
||||
this._end = Dialogue._toTime(template.get("End"));
|
||||
|
||||
this._layer = Math.max(parseInt(template["Layer"]), 0);
|
||||
this._layer = Math.max(parseInt(template.get("Layer")), 0);
|
||||
|
||||
this._rawPartsString = template["Text"];
|
||||
this._rawPartsString = template.get("Text");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -772,19 +768,12 @@ module libjass {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A template object. It is a map of string keys and string values.
|
||||
*/
|
||||
export interface Template {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A template object with a particular type.
|
||||
*/
|
||||
export interface TypedTemplate {
|
||||
type: string;
|
||||
template: Template;
|
||||
template: Map<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user