(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.artplayerPluginAss = factory()); }(this, (function () { 'use strict'; function parseEffect(text) { var param = text .toLowerCase() .trim() .split(/\s*;\s*/); if (param[0] === 'banner') { return { name: param[0], delay: param[1] * 1 || 0, leftToRight: param[2] * 1 || 0, fadeAwayWidth: param[3] * 1 || 0, }; } if (/^scroll\s/.test(param[0])) { return { name: param[0], y1: Math.min(param[1] * 1, param[2] * 1), y2: Math.max(param[1] * 1, param[2] * 1), delay: param[3] * 1 || 0, fadeAwayHeight: param[4] * 1 || 0, }; } return null; } function parseDrawing(text) { return text .toLowerCase() // numbers .replace(/([+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)/g, ' $1 ') // commands .replace(/([mnlbspc])/g, ' $1 ') .trim() .replace(/\s+/g, ' ') .split(/\s(?=[mnlbspc])/) .map(function (cmd) { return ( cmd.split(' ') .filter(function (x, i) { return !(i && Number.isNaN(x * 1)); }) ); }); } var numTags = [ 'b', 'i', 'u', 's', 'fsp', 'k', 'K', 'kf', 'ko', 'kt', 'fe', 'q', 'p', 'pbo', 'a', 'an', 'fscx', 'fscy', 'fax', 'fay', 'frx', 'fry', 'frz', 'fr', 'be', 'blur', 'bord', 'xbord', 'ybord', 'shad', 'xshad', 'yshad' ]; var numRegexs = numTags.map(function (nt) { return ({ name: nt, regex: new RegExp(("^" + nt + "-?\\d")) }); }); function parseTag(text) { var assign; var tag = {}; for (var i = 0; i < numRegexs.length; i++) { var ref = numRegexs[i]; var name = ref.name; var regex = ref.regex; if (regex.test(text)) { tag[name] = text.slice(name.length) * 1; return tag; } } if (/^fn/.test(text)) { tag.fn = text.slice(2); } else if (/^r/.test(text)) { tag.r = text.slice(1); } else if (/^fs[\d+-]/.test(text)) { tag.fs = text.slice(2); } else if (/^\d?c&?H?[0-9a-f]+|^\d?c$/i.test(text)) { var ref$1 = text.match(/^(\d?)c&?H?(\w*)/); var num = ref$1[1]; var color = ref$1[2]; tag[("c" + (num || 1))] = color && ("000000" + color).slice(-6); } else if (/^\da&?H?[0-9a-f]+/i.test(text)) { var ref$2 = text.match(/^(\d)a&?H?(\w\w)/); var num$1 = ref$2[1]; var alpha = ref$2[2]; tag[("a" + num$1)] = alpha; } else if (/^alpha&?H?[0-9a-f]+/i.test(text)) { (assign = text.match(/^alpha&?H?([0-9a-f]+)/i), tag.alpha = assign[1]); tag.alpha = ("00" + (tag.alpha)).slice(-2); } else if (/^(?:pos|org|move|fad|fade)\(/.test(text)) { var ref$3 = text.match(/^(\w+)\((.*?)\)?$/); var key = ref$3[1]; var value = ref$3[2]; tag[key] = value .trim() .split(/\s*,\s*/) .map(Number); } else if (/^i?clip/.test(text)) { var p = text .match(/^i?clip\((.*?)\)?$/)[1] .trim() .split(/\s*,\s*/); tag.clip = { inverse: /iclip/.test(text), scale: 1, drawing: null, dots: null, }; if (p.length === 1) { tag.clip.drawing = parseDrawing(p[0]); } if (p.length === 2) { tag.clip.scale = p[0] * 1; tag.clip.drawing = parseDrawing(p[1]); } if (p.length === 4) { tag.clip.dots = p.map(Number); } } else if (/^t\(/.test(text)) { var p$1 = text .match(/^t\((.*?)\)?$/)[1] .trim() .replace(/\\.*/, function (x) { return x.replace(/,/g, '\n'); }) .split(/\s*,\s*/); if (!p$1[0]) { return tag; } tag.t = { t1: 0, t2: 0, accel: 1, tags: p$1[p$1.length - 1] .replace(/\n/g, ',') .split('\\') .slice(1) .map(parseTag), }; if (p$1.length === 2) { tag.t.accel = p$1[0] * 1; } if (p$1.length === 3) { tag.t.t1 = p$1[0] * 1; tag.t.t2 = p$1[1] * 1; } if (p$1.length === 4) { tag.t.t1 = p$1[0] * 1; tag.t.t2 = p$1[1] * 1; tag.t.accel = p$1[2] * 1; } } return tag; } function parseTags(text) { var tags = []; var depth = 0; var str = ''; for (var i = 0; i < text.length; i++) { var x = text[i]; if (x === '(') { depth++; } if (x === ')') { depth--; } if (depth < 0) { depth = 0; } if (!depth && x === '\\') { if (str) { tags.push(str); } str = ''; } else { str += x; } } tags.push(str); return tags.map(parseTag); } function parseText(text) { var pairs = text.split(/{([^{}]*?)}/); var parsed = []; if (pairs[0].length) { parsed.push({ tags: [], text: pairs[0], drawing: [] }); } for (var i = 1; i < pairs.length; i += 2) { var tags = parseTags(pairs[i]); var isDrawing = tags.reduce(function (v, tag) { return (tag.p === undefined ? v : !!tag.p); }, false); parsed.push({ tags: tags, text: isDrawing ? '' : pairs[i + 1], drawing: isDrawing ? parseDrawing(pairs[i + 1]) : [], }); } return { raw: text, combined: parsed.map(function (frag) { return frag.text; }).join(''), parsed: parsed, }; } function parseTime(time) { var t = time.split(':'); return t[0] * 3600 + t[1] * 60 + t[2] * 1; } function parseDialogue(text, format) { var fields = text.split(','); if (fields.length > format.length) { var textField = fields.slice(format.length - 1).join(); fields = fields.slice(0, format.length - 1); fields.push(textField); } var dia = {}; for (var i = 0; i < fields.length; i++) { var fmt = format[i]; var fld = fields[i].trim(); switch (fmt) { case 'Layer': case 'MarginL': case 'MarginR': case 'MarginV': dia[fmt] = fld * 1; break; case 'Start': case 'End': dia[fmt] = parseTime(fld); break; case 'Effect': dia[fmt] = parseEffect(fld); break; case 'Text': dia[fmt] = parseText(fld); break; default: dia[fmt] = fld; } } return dia; } function parseFormat(text) { return text.match(/Format\s*:\s*(.*)/i)[1].split(/\s*,\s*/); } function parseStyle(text) { return text.match(/Style\s*:\s*(.*)/i)[1].split(/\s*,\s*/); } function parse(text) { var tree = { info: {}, styles: { format: [], style: [] }, events: { format: [], comment: [], dialogue: [] }, }; var lines = text.split(/\r?\n/); var state = 0; for (var i = 0; i < lines.length; i++) { var line = lines[i].trim(); if (/^;/.test(line)) { continue; } if (/^\[Script Info\]/i.test(line)) { state = 1; } else if (/^\[V4\+? Styles\]/i.test(line)) { state = 2; } else if (/^\[Events\]/i.test(line)) { state = 3; } else if (/^\[.*\]/.test(line)) { state = 0; } if (state === 0) { continue; } if (state === 1) { if (/:/.test(line)) { var ref = line.match(/(.*?)\s*:\s*(.*)/); var key = ref[1]; var value = ref[2]; tree.info[key] = value; } } if (state === 2) { if (/^Format\s*:/i.test(line)) { tree.styles.format = parseFormat(line); } if (/^Style\s*:/i.test(line)) { tree.styles.style.push(parseStyle(line)); } } if (state === 3) { if (/^Format\s*:/i.test(line)) { tree.events.format = parseFormat(line); } if (/^(?:Comment|Dialogue)\s*:/i.test(line)) { var ref$1 = line.match(/^(\w+?)\s*:\s*(.*)/i); var key$1 = ref$1[1]; var value$1 = ref$1[2]; tree.events[key$1.toLowerCase()].push(parseDialogue(value$1, tree.events.format)); } } } return tree; } var assign = Object.assign || ( /* istanbul ignore next */ function assign(target) { var sources = [], len = arguments.length - 1; while ( len-- > 0 ) sources[ len ] = arguments[ len + 1 ]; for (var i = 0; i < sources.length; i++) { if (!sources[i]) { continue; } var keys = Object.keys(sources[i]); for (var j = 0; j < keys.length; j++) { // eslint-disable-next-line no-param-reassign target[keys[j]] = sources[i][keys[j]]; } } return target; } ); function createCommand(arr) { var cmd = { type: null, prev: null, next: null, points: [], }; if (/[mnlbs]/.test(arr[0])) { cmd.type = arr[0] .toUpperCase() .replace('N', 'L') .replace('B', 'C'); } for (var len = arr.length - !(arr.length & 1), i = 1; i < len; i += 2) { cmd.points.push({ x: arr[i] * 1, y: arr[i + 1] * 1 }); } return cmd; } function isValid(cmd) { if (!cmd.points.length || !cmd.type) { return false; } if (/C|S/.test(cmd.type) && cmd.points.length < 3) { return false; } return true; } function getViewBox(commands) { var ref; var minX = Infinity; var minY = Infinity; var maxX = -Infinity; var maxY = -Infinity; (ref = []).concat.apply(ref, commands.map(function (ref) { var points = ref.points; return points; })).forEach(function (ref) { var x = ref.x; var y = ref.y; minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x); maxY = Math.max(maxY, y); }); return { minX: minX, minY: minY, width: maxX - minX, height: maxY - minY, }; } /** * Convert S command to B command * Reference from https://github.com/d3/d3/blob/v3.5.17/src/svg/line.js#L259 * @param {Array} points points * @param {String} prev type of previous command * @param {String} next type of next command * @return {Array} converted commands */ function s2b(points, prev, next) { var results = []; var bb1 = [0, 2 / 3, 1 / 3, 0]; var bb2 = [0, 1 / 3, 2 / 3, 0]; var bb3 = [0, 1 / 6, 2 / 3, 1 / 6]; var dot4 = function (a, b) { return (a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]); }; var px = [points[points.length - 1].x, points[0].x, points[1].x, points[2].x]; var py = [points[points.length - 1].y, points[0].y, points[1].y, points[2].y]; results.push({ type: prev === 'M' ? 'M' : 'L', points: [{ x: dot4(bb3, px), y: dot4(bb3, py) }], }); for (var i = 3; i < points.length; i++) { px = [points[i - 3].x, points[i - 2].x, points[i - 1].x, points[i].x]; py = [points[i - 3].y, points[i - 2].y, points[i - 1].y, points[i].y]; results.push({ type: 'C', points: [ { x: dot4(bb1, px), y: dot4(bb1, py) }, { x: dot4(bb2, px), y: dot4(bb2, py) }, { x: dot4(bb3, px), y: dot4(bb3, py) } ], }); } if (next === 'L' || next === 'C') { var last = points[points.length - 1]; results.push({ type: 'L', points: [{ x: last.x, y: last.y }] }); } return results; } function toSVGPath(instructions) { return instructions.map(function (ref) { var type = ref.type; var points = ref.points; return ( type + points.map(function (ref) { var x = ref.x; var y = ref.y; return (x + "," + y); }).join(',') ); }).join(''); } function compileDrawing(rawCommands) { var ref$1; var commands = []; var i = 0; while (i < rawCommands.length) { var arr = rawCommands[i]; var cmd = createCommand(arr); if (isValid(cmd)) { if (cmd.type === 'S') { var ref = (commands[i - 1] || { points: [{ x: 0, y: 0 }] }).points.slice(-1)[0]; var x = ref.x; var y = ref.y; cmd.points.unshift({ x: x, y: y }); } if (i) { cmd.prev = commands[i - 1].type; commands[i - 1].next = cmd.type; } commands.push(cmd); i++; } else { if (i && commands[i - 1].type === 'S') { var additionPoints = { p: cmd.points, c: commands[i - 1].points.slice(0, 3), }; commands[i - 1].points = commands[i - 1].points.concat( (additionPoints[arr[0]] || []).map(function (ref) { var x = ref.x; var y = ref.y; return ({ x: x, y: y }); }) ); } rawCommands.splice(i, 1); } } var instructions = (ref$1 = []).concat.apply( ref$1, commands.map(function (ref) { var type = ref.type; var points = ref.points; var prev = ref.prev; var next = ref.next; return ( type === 'S' ? s2b(points, prev, next) : { type: type, points: points } ); }) ); return assign({ instructions: instructions, d: toSVGPath(instructions) }, getViewBox(commands)); } var tTags = [ 'fs', 'clip', 'c1', 'c2', 'c3', 'c4', 'a1', 'a2', 'a3', 'a4', 'alpha', 'fscx', 'fscy', 'fax', 'fay', 'frx', 'fry', 'frz', 'fr', 'be', 'blur', 'bord', 'xbord', 'ybord', 'shad', 'xshad', 'yshad' ]; function compileTag(tag, key, presets) { var obj, obj$1, obj$2; if ( presets === void 0 ) presets = {}; var value = tag[key]; if (value === undefined) { return null; } if (key === 'pos' || key === 'org') { return value.length === 2 ? ( obj = {}, obj[key] = { x: value[0], y: value[1] }, obj ) : null; } if (key === 'move') { var x1 = value[0]; var y1 = value[1]; var x2 = value[2]; var y2 = value[3]; var t1 = value[4]; if ( t1 === void 0 ) t1 = 0; var t2 = value[5]; if ( t2 === void 0 ) t2 = 0; return value.length === 4 || value.length === 6 ? { move: { x1: x1, y1: y1, x2: x2, y2: y2, t1: t1, t2: t2 } } : null; } if (key === 'fad' || key === 'fade') { if (value.length === 2) { var t1$1 = value[0]; var t2$1 = value[1]; return { fade: { type: 'fad', t1: t1$1, t2: t2$1 } }; } if (value.length === 7) { var a1 = value[0]; var a2 = value[1]; var a3 = value[2]; var t1$2 = value[3]; var t2$2 = value[4]; var t3 = value[5]; var t4 = value[6]; return { fade: { type: 'fade', a1: a1, a2: a2, a3: a3, t1: t1$2, t2: t2$2, t3: t3, t4: t4 } }; } return null; } if (key === 'clip') { var inverse = value.inverse; var scale = value.scale; var drawing = value.drawing; var dots = value.dots; if (drawing) { return { clip: { inverse: inverse, scale: scale, drawing: compileDrawing(drawing), dots: dots } }; } if (dots) { var x1$1 = dots[0]; var y1$1 = dots[1]; var x2$1 = dots[2]; var y2$1 = dots[3]; return { clip: { inverse: inverse, scale: scale, drawing: drawing, dots: { x1: x1$1, y1: y1$1, x2: x2$1, y2: y2$1 } } }; } return null; } if (/^[xy]?(bord|shad)$/.test(key)) { value = Math.max(value, 0); } if (key === 'bord') { return { xbord: value, ybord: value }; } if (key === 'shad') { return { xshad: value, yshad: value }; } if (/^c\d$/.test(key)) { return ( obj$1 = {}, obj$1[key] = value || presets[key], obj$1 ); } if (key === 'alpha') { return { a1: value, a2: value, a3: value, a4: value }; } if (key === 'fr') { return { frz: value }; } if (key === 'fs') { return { fs: /^\+|-/.test(value) ? (value * 1 > -10 ? (1 + value / 10) : 1) * presets.fs : value * 1, }; } if (key === 't') { var t1$3 = value.t1; var accel = value.accel; var tags = value.tags; var t2$3 = value.t2 || (presets.end - presets.start) * 1e3; var compiledTag = {}; tags.forEach(function (t) { var k = Object.keys(t)[0]; if (~tTags.indexOf(k) && !(k === 'clip' && !t[k].dots)) { assign(compiledTag, compileTag(t, k, presets)); } }); return { t: { t1: t1$3, t2: t2$3, accel: accel, tag: compiledTag } }; } return ( obj$2 = {}, obj$2[key] = value, obj$2 ); } var a2an = [ null, 1, 2, 3, null, 7, 8, 9, null, 4, 5, 6 ]; var globalTags = ['r', 'a', 'an', 'pos', 'org', 'move', 'fade', 'fad', 'clip']; function createSlice(name, styles) { return { name: name, borderStyle: styles[name].style.BorderStyle, tag: styles[name].tag, fragments: [], }; } function compileText(ref) { var styles = ref.styles; var name = ref.name; var parsed = ref.parsed; var start = ref.start; var end = ref.end; var alignment; var pos; var org; var move; var fade; var clip; var slices = []; var slice = createSlice(name, styles); var prevTag = {}; for (var i = 0; i < parsed.length; i++) { var ref$1 = parsed[i]; var tags = ref$1.tags; var text = ref$1.text; var drawing = ref$1.drawing; var reset = (void 0); for (var j = 0; j < tags.length; j++) { var tag = tags[j]; reset = tag.r === undefined ? reset : tag.r; } var fragment = { tag: reset === undefined ? JSON.parse(JSON.stringify(prevTag)) : {}, text: text, drawing: drawing.length ? compileDrawing(drawing) : null, }; for (var j$1 = 0; j$1 < tags.length; j$1++) { var tag$1 = tags[j$1]; alignment = alignment || a2an[tag$1.a || 0] || tag$1.an; pos = pos || compileTag(tag$1, 'pos'); org = org || compileTag(tag$1, 'org'); move = move || compileTag(tag$1, 'move'); fade = fade || compileTag(tag$1, 'fade') || compileTag(tag$1, 'fad'); clip = compileTag(tag$1, 'clip') || clip; var key = Object.keys(tag$1)[0]; if (key && !~globalTags.indexOf(key)) { var ref$2 = slice.tag; var c1 = ref$2.c1; var c2 = ref$2.c2; var c3 = ref$2.c3; var c4 = ref$2.c4; var fs = prevTag.fs || slice.tag.fs; var compiledTag = compileTag(tag$1, key, { start: start, end: end, c1: c1, c2: c2, c3: c3, c4: c4, fs: fs }); if (key === 't') { fragment.tag.t = fragment.tag.t || []; fragment.tag.t.push(compiledTag.t); } else { assign(fragment.tag, compiledTag); } } } prevTag = fragment.tag; if (reset !== undefined) { slices.push(slice); slice = createSlice(styles[reset] ? reset : name, styles); } if (fragment.text || fragment.drawing) { var prev = slice.fragments[slice.fragments.length - 1] || {}; if (prev.text && fragment.text && !Object.keys(fragment.tag).length) { // merge fragment to previous if its tag is empty prev.text += fragment.text; } else { slice.fragments.push(fragment); } } } slices.push(slice); return assign({ alignment: alignment, slices: slices }, pos, org, move, fade, clip); } function compileDialogues(ref) { var styles = ref.styles; var dialogues = ref.dialogues; var minLayer = Infinity; var results = []; for (var i = 0; i < dialogues.length; i++) { var dia = dialogues[i]; if (dia.Start >= dia.End) { continue; } if (!styles[dia.Style]) { dia.Style = 'Default'; } var stl = styles[dia.Style].style; var compiledText = compileText({ styles: styles, name: dia.Style, parsed: dia.Text.parsed, start: dia.Start, end: dia.End, }); var alignment = compiledText.alignment || stl.Alignment; minLayer = Math.min(minLayer, dia.Layer); results.push(assign({ layer: dia.Layer, start: dia.Start, end: dia.End, // reset style by `\r` will not effect margin and alignment margin: { left: dia.MarginL || stl.MarginL, right: dia.MarginR || stl.MarginR, vertical: dia.MarginV || stl.MarginV, }, effect: dia.Effect, }, compiledText, { alignment: alignment })); } for (var i$1 = 0; i$1 < results.length; i$1++) { results[i$1].layer -= minLayer; } return results.sort(function (a, b) { return a.start - b.start || a.end - b.end; }); } // same as Aegisub // https://github.com/Aegisub/Aegisub/blob/master/src/ass_style.h var DEFAULT_STYLE = { Name: 'Default', Fontname: 'Arial', Fontsize: '20', PrimaryColour: '&H00FFFFFF&', SecondaryColour: '&H000000FF&', OutlineColour: '&H00000000&', BackColour: '&H00000000&', Bold: '0', Italic: '0', Underline: '0', StrikeOut: '0', ScaleX: '100', ScaleY: '100', Spacing: '0', Angle: '0', BorderStyle: '1', Outline: '2', Shadow: '2', Alignment: '2', MarginL: '10', MarginR: '10', MarginV: '10', Encoding: '1', }; /** * @param {String} color * @returns {Array} [AA, BBGGRR] */ function parseStyleColor(color) { if (/^(&|H|&H)[0-9a-f]{6,}/i.test(color)) { var ref = color.match(/&?H?([0-9a-f]{2})?([0-9a-f]{6})/i); var a = ref[1]; var c = ref[2]; return [a || '00', c]; } var num = parseInt(color, 10); if (!Number.isNaN(num)) { var min = -2147483648; var max = 2147483647; if (num < min) { return ['00', '000000']; } var aabbggrr = (min <= num && num <= max) ? ("00000000" + ((num < 0 ? num + 4294967296 : num).toString(16))).slice(-8) : String(num).slice(0, 8); return [aabbggrr.slice(0, 2), aabbggrr.slice(2)]; } return ['00', '000000']; } function compileStyles(ref) { var info = ref.info; var style = ref.style; var format = ref.format; var defaultStyle = ref.defaultStyle; var result = {}; var styles = [ assign({}, DEFAULT_STYLE, defaultStyle, { Name: 'Default' }) ].concat( style.map(function (stl) { var s = {}; for (var i = 0; i < format.length; i++) { s[format[i]] = stl[i]; } return s; }) ); var loop = function ( i ) { var s = styles[i]; // this behavior is same as Aegisub by black-box testing if (/^(\*+)Default$/.test(s.Name)) { s.Name = 'Default'; } Object.keys(s).forEach(function (key) { if (key !== 'Name' && key !== 'Fontname' && !/Colour/.test(key)) { s[key] *= 1; } }); var ref$1 = parseStyleColor(s.PrimaryColour); var a1 = ref$1[0]; var c1 = ref$1[1]; var ref$2 = parseStyleColor(s.SecondaryColour); var a2 = ref$2[0]; var c2 = ref$2[1]; var ref$3 = parseStyleColor(s.OutlineColour); var a3 = ref$3[0]; var c3 = ref$3[1]; var ref$4 = parseStyleColor(s.BackColour); var a4 = ref$4[0]; var c4 = ref$4[1]; var tag = { fn: s.Fontname, fs: s.Fontsize, c1: c1, a1: a1, c2: c2, a2: a2, c3: c3, a3: a3, c4: c4, a4: a4, b: Math.abs(s.Bold), i: Math.abs(s.Italic), u: Math.abs(s.Underline), s: Math.abs(s.StrikeOut), fscx: s.ScaleX, fscy: s.ScaleY, fsp: s.Spacing, frz: s.Angle, xbord: s.Outline, ybord: s.Outline, xshad: s.Shadow, yshad: s.Shadow, q: /^[0-3]$/.test(info.WrapStyle) ? info.WrapStyle * 1 : 2, }; result[s.Name] = { style: s, tag: tag }; }; for (var i = 0; i < styles.length; i++) loop( i ); return result; } function compile(text, options) { if ( options === void 0 ) options = {}; var tree = parse(text); var styles = compileStyles({ info: tree.info, style: tree.styles.style, format: tree.styles.format, defaultStyle: options.defaultStyle || {}, }); return { info: tree.info, width: tree.info.PlayResX * 1 || null, height: tree.info.PlayResY * 1 || null, collisions: tree.info.Collisions || 'Normal', styles: styles, dialogues: compileDialogues({ styles: styles, dialogues: tree.events.dialogue, }), }; } var raf = ( window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || (function (cb) { return setTimeout(cb, 50 / 3); }) ); var caf = ( window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || clearTimeout ); function color2rgba(c) { var t = c.match(/(\w\w)(\w\w)(\w\w)(\w\w)/); var a = 1 - ("0x" + (t[1])) / 255; var b = +("0x" + (t[2])); var g = +("0x" + (t[3])); var r = +("0x" + (t[4])); return ("rgba(" + r + "," + g + "," + b + "," + a + ")"); } function uuid() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0; var v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } function createSVGEl(name, attrs) { if ( attrs === void 0 ) attrs = []; var $el = document.createElementNS('http://www.w3.org/2000/svg', name); for (var i = 0; i < attrs.length; i++) { var attr = attrs[i]; $el.setAttributeNS( attr[0] === 'xlink:href' ? 'http://www.w3.org/1999/xlink' : null, attr[0], attr[1] ); } return $el; } function getVendor(prop) { var ref = document.body; var style = ref.style; var Prop = prop.replace(/^\w/, function (x) { return x.toUpperCase(); }); if (prop in style) { return ''; } if (("webkit" + Prop) in style) { return '-webkit-'; } if (("moz" + Prop) in style) { return '-moz-'; } return ''; } var vendor = { transform: getVendor('transform'), animation: getVendor('animation'), clipPath: getVendor('clipPath'), }; function getStyleRoot(container) { var rootNode = container.getRootNode ? container.getRootNode() : document; return rootNode === document ? rootNode.head : rootNode; } var strokeTags = ['c3', 'a3', 'c4', 'a4', 'xbord', 'ybord', 'xshad', 'yshad', 'blur', 'be']; var transformTags = ['fscx', 'fscy', 'frx', 'fry', 'frz', 'fax', 'fay']; function createClipPath(clip) { var sw = this._.scriptRes.width; var sh = this._.scriptRes.height; var d = ''; if (clip.dots !== null) { var ref = clip.dots; var x1 = ref.x1; var y1 = ref.y1; var x2 = ref.x2; var y2 = ref.y2; x1 /= sw; y1 /= sh; x2 /= sw; y2 /= sh; d = "M" + x1 + "," + y1 + "L" + x1 + "," + y2 + "," + x2 + "," + y2 + "," + x2 + "," + y1 + "Z"; } if (clip.drawing !== null) { d = clip.drawing.instructions.map(function (ref) { var type = ref.type; var points = ref.points; return ( type + points.map(function (ref) { var x = ref.x; var y = ref.y; return ((x / sw) + "," + (y / sh)); }).join(',') ); }).join(''); } var scale = 1 / (1 << (clip.scale - 1)); if (clip.inverse) { d += "M0,0L0," + scale + "," + scale + "," + scale + "," + scale + ",0,0,0Z"; } var id = "ASS-" + (uuid()); var $clipPath = createSVGEl('clipPath', [ ['id', id], ['clipPathUnits', 'objectBoundingBox'] ]); $clipPath.appendChild(createSVGEl('path', [ ['d', d], ['transform', ("scale(" + scale + ")")], ['clip-rule', 'evenodd'] ])); this._.$defs.appendChild($clipPath); return { $clipPath: $clipPath, cssText: ((vendor.clipPath) + "clip-path:url(#" + id + ");"), }; } function setClipPath(dialogue) { if (!dialogue.clip) { return; } var $fobb = document.createElement('div'); this._.$stage.insertBefore($fobb, dialogue.$div); $fobb.appendChild(dialogue.$div); $fobb.className = 'ASS-fix-objectBoundingBox'; var ref = createClipPath.call(this, dialogue.clip); var cssText = ref.cssText; var $clipPath = ref.$clipPath; this._.$defs.appendChild($clipPath); $fobb.style.cssText = cssText; assign(dialogue, { $div: $fobb, $clipPath: $clipPath }); } var $fixFontSize = document.createElement('div'); $fixFontSize.className = 'ASS-fix-font-size'; $fixFontSize.textContent = 'M'; var cache = Object.create(null); function getRealFontSize(fn, fs) { var key = fn + "-" + fs; if (!cache[key]) { $fixFontSize.style.cssText = "line-height:normal;font-size:" + fs + "px;font-family:\"" + fn + "\",Arial;"; cache[key] = fs * fs / $fixFontSize.clientHeight; } return cache[key]; } function createSVGStroke(tag, id, scale) { var hasBorder = tag.xbord || tag.ybord; var hasShadow = tag.xshad || tag.yshad; var isOpaque = tag.a1 !== 'FF'; var blur = tag.blur || tag.be || 0; var $filter = createSVGEl('filter', [['id', id]]); $filter.appendChild(createSVGEl('feGaussianBlur', [ ['stdDeviation', hasBorder ? 0 : blur], ['in', 'SourceGraphic'], ['result', 'sg_b'] ])); $filter.appendChild(createSVGEl('feFlood', [ ['flood-color', color2rgba(tag.a1 + tag.c1)], ['result', 'c1'] ])); $filter.appendChild(createSVGEl('feComposite', [ ['operator', 'in'], ['in', 'c1'], ['in2', 'sg_b'], ['result', 'main'] ])); if (hasBorder) { $filter.appendChild(createSVGEl('feMorphology', [ ['radius', ((tag.xbord * scale) + " " + (tag.ybord * scale))], ['operator', 'dilate'], ['in', 'SourceGraphic'], ['result', 'dil'] ])); $filter.appendChild(createSVGEl('feGaussianBlur', [ ['stdDeviation', blur], ['in', 'dil'], ['result', 'dil_b'] ])); $filter.appendChild(createSVGEl('feComposite', [ ['operator', 'out'], ['in', 'dil_b'], ['in2', 'SourceGraphic'], ['result', 'dil_b_o'] ])); $filter.appendChild(createSVGEl('feFlood', [ ['flood-color', color2rgba(tag.a3 + tag.c3)], ['result', 'c3'] ])); $filter.appendChild(createSVGEl('feComposite', [ ['operator', 'in'], ['in', 'c3'], ['in2', 'dil_b_o'], ['result', 'border'] ])); } if (hasShadow && (hasBorder || isOpaque)) { $filter.appendChild(createSVGEl('feOffset', [ ['dx', tag.xshad * scale], ['dy', tag.yshad * scale], ['in', hasBorder ? 'dil' : 'SourceGraphic'], ['result', 'off'] ])); $filter.appendChild(createSVGEl('feGaussianBlur', [ ['stdDeviation', blur], ['in', 'off'], ['result', 'off_b'] ])); if (!isOpaque) { $filter.appendChild(createSVGEl('feOffset', [ ['dx', tag.xshad * scale], ['dy', tag.yshad * scale], ['in', 'SourceGraphic'], ['result', 'sg_off'] ])); $filter.appendChild(createSVGEl('feComposite', [ ['operator', 'out'], ['in', 'off_b'], ['in2', 'sg_off'], ['result', 'off_b_o'] ])); } $filter.appendChild(createSVGEl('feFlood', [ ['flood-color', color2rgba(tag.a4 + tag.c4)], ['result', 'c4'] ])); $filter.appendChild(createSVGEl('feComposite', [ ['operator', 'in'], ['in', 'c4'], ['in2', isOpaque ? 'off_b' : 'off_b_o'], ['result', 'shadow'] ])); } var $merge = createSVGEl('feMerge', []); if (hasShadow && (hasBorder || isOpaque)) { $merge.appendChild(createSVGEl('feMergeNode', [['in', 'shadow']])); } if (hasBorder) { $merge.appendChild(createSVGEl('feMergeNode', [['in', 'border']])); } $merge.appendChild(createSVGEl('feMergeNode', [['in', 'main']])); $filter.appendChild($merge); return $filter; } function createCSSStroke(tag, scale) { var arr = []; var oc = color2rgba(tag.a3 + tag.c3); var ox = tag.xbord * scale; var oy = tag.ybord * scale; var sc = color2rgba(tag.a4 + tag.c4); var sx = tag.xshad * scale; var sy = tag.yshad * scale; var blur = tag.blur || tag.be || 0; if (!(ox + oy + sx + sy)) { return 'none'; } if (ox || oy) { for (var i = -1; i <= 1; i++) { for (var j = -1; j <= 1; j++) { for (var x = 1; x < ox; x++) { for (var y = 1; y < oy; y++) { if (i || j) { arr.push((oc + " " + (i * x) + "px " + (j * y) + "px " + blur + "px")); } } } arr.push((oc + " " + (i * ox) + "px " + (j * oy) + "px " + blur + "px")); } } } if (sx || sy) { var pnx = sx > 0 ? 1 : -1; var pny = sy > 0 ? 1 : -1; sx = Math.abs(sx); sy = Math.abs(sy); for (var x$1 = Math.max(ox, sx - ox); x$1 < sx + ox; x$1++) { for (var y$1 = Math.max(oy, sy - oy); y$1 < sy + oy; y$1++) { arr.push((sc + " " + (x$1 * pnx) + "px " + (y$1 * pny) + "px " + blur + "px")); } } arr.push((sc + " " + ((sx + ox) * pnx) + "px " + ((sy + oy) * pny) + "px " + blur + "px")); } return arr.join(); } function createTransform(tag) { return [ // TODO: I don't know why perspective is 314, it just performances well. 'perspective(314px)', ("rotateY(" + (tag.fry || 0) + "deg)"), ("rotateX(" + (tag.frx || 0) + "deg)"), ("rotateZ(" + (-tag.frz || 0) + "deg)"), ("scale(" + (tag.p ? 1 : (tag.fscx || 100) / 100) + "," + (tag.p ? 1 : (tag.fscy || 100) / 100) + ")"), ("skew(" + (tag.fax || 0) + "rad," + (tag.fay || 0) + "rad)") ].join(' '); } function setTransformOrigin(dialogue) { var alignment = dialogue.alignment; var width = dialogue.width; var height = dialogue.height; var x = dialogue.x; var y = dialogue.y; var $div = dialogue.$div; var org = dialogue.org; if (!org) { org = { x: 0, y: 0 }; if (alignment % 3 === 1) { org.x = x; } if (alignment % 3 === 2) { org.x = x + width / 2; } if (alignment % 3 === 0) { org.x = x + width; } if (alignment <= 3) { org.y = y + height; } if (alignment >= 4 && alignment <= 6) { org.y = y + height / 2; } if (alignment >= 7) { org.y = y; } } for (var i = $div.childNodes.length - 1; i >= 0; i--) { var node = $div.childNodes[i]; if (node.dataset.hasRotate === 'true') { // It's not extremely precise for offsets are round the value to an integer. var tox = org.x - x - node.offsetLeft; var toy = org.y - y - node.offsetTop; node.style.cssText += (vendor.transform) + "transform-origin:" + tox + "px " + toy + "px;"; } } } function getKeyframeString(name, list) { return ("@" + (vendor.animation) + "keyframes " + name + " {" + list + "}\n"); } var KeyframeBlockList = function KeyframeBlockList() { this.obj = {}; }; KeyframeBlockList.prototype.set = function set (keyText, prop, value) { if (!this.obj[keyText]) { this.obj[keyText] = {}; } this.obj[keyText][prop] = value; }; KeyframeBlockList.prototype.setT = function setT (ref) { var t1 = ref.t1; var t2 = ref.t2; var duration = ref.duration; var prop = ref.prop; var from = ref.from; var to = ref.to; this.set('0.000%', prop, from); if (t1 < duration) { this.set((((t1 / duration * 100).toFixed(3)) + "%"), prop, from); } if (t2 < duration) { this.set((((t2 / duration * 100).toFixed(3)) + "%"), prop, to); } this.set('100.000%', prop, to); }; KeyframeBlockList.prototype.toString = function toString () { var this$1 = this; return Object.keys(this.obj) .map(function (keyText) { return ( (keyText + "{" + (Object.keys(this$1.obj[keyText]) .map(function (prop) { return ("" + (vendor[prop] || '') + prop + ":" + (this$1.obj[keyText][prop]) + ";"); }) .join('')) + "}") ); }) .join(''); }; // TODO: multi \t can't be merged directly function mergeT(ts) { return ts.reduceRight(function (results, t) { var merged = false; return results .map(function (r) { merged = t.t1 === r.t1 && t.t2 === r.t2 && t.accel === r.accel; return assign({}, r, merged ? { tag: assign({}, r.tag, t.tag) } : {}); }) .concat(merged ? [] : t); }, []); } function getKeyframes() { var this$1 = this; var keyframes = ''; this.dialogues.forEach(function (dialogue) { var start = dialogue.start; var end = dialogue.end; var effect = dialogue.effect; var move = dialogue.move; var fade = dialogue.fade; var slices = dialogue.slices; var duration = (end - start) * 1000; var diaKbl = new KeyframeBlockList(); // TODO: when effect and move both exist, its behavior is weird, for now only move works. if (effect && !move) { var name = effect.name; var delay = effect.delay; var lefttoright = effect.lefttoright; var y1 = effect.y1; var y2 = effect.y2 || this$1._.resampledRes.height; if (name === 'banner') { var tx = this$1.scale * (duration / delay) * (lefttoright ? 1 : -1); diaKbl.set('0.000%', 'transform', 'translateX(0)'); diaKbl.set('100.000%', 'transform', ("translateX(" + tx + "px)")); } if (/^scroll/.test(name)) { var updown = /up/.test(name) ? -1 : 1; var tFrom = "translateY(" + (this$1.scale * y1 * updown) + "px)"; var tTo = "translateY(" + (this$1.scale * y2 * updown) + "px)"; var dp = (y2 - y1) / (duration / delay) * 100; diaKbl.set('0.000%', 'transform', tFrom); if (dp < 100) { diaKbl.set(((dp.toFixed(3)) + "%"), 'transform', tTo); } diaKbl.set('100.000%', 'transform', tTo); } } if (move) { var x1 = move.x1; var y1$1 = move.y1; var x2 = move.x2; var y2$1 = move.y2; var t1 = move.t1; var t2 = move.t2 || duration; var pos = dialogue.pos || { x: 0, y: 0 }; var values = [{ x: x1, y: y1$1 }, { x: x2, y: y2$1 }].map(function (ref) { var x = ref.x; var y = ref.y; return ( ("translate(" + (this$1.scale * (x - pos.x)) + "px, " + (this$1.scale * (y - pos.y)) + "px)") ); }); diaKbl.setT({ t1: t1, t2: t2, duration: duration, prop: 'transform', from: values[0], to: values[1] }); } if (fade) { if (fade.type === 'fad') { var t1$1 = fade.t1; var t2$1 = fade.t2; diaKbl.set('0.000%', 'opacity', 0); if (t1$1 < duration) { diaKbl.set((((t1$1 / duration * 100).toFixed(3)) + "%"), 'opacity', 1); if (t1$1 + t2$1 < duration) { diaKbl.set(((((duration - t2$1) / duration * 100).toFixed(3)) + "%"), 'opacity', 1); } diaKbl.set('100.000%', 'opacity', 0); } else { diaKbl.set('100.000%', 'opacity', duration / t1$1); } } else { var a1 = fade.a1; var a2 = fade.a2; var a3 = fade.a3; var t1$2 = fade.t1; var t2$2 = fade.t2; var t3 = fade.t3; var t4 = fade.t4; var keyTexts = [t1$2, t2$2, t3, t4].map(function (t) { return (((t / duration * 100).toFixed(3)) + "%"); }); var values$1 = [a1, a2, a3].map(function (a) { return 1 - a / 255; }); diaKbl.set('0.000%', 'opacity', values$1[0]); if (t1$2 < duration) { diaKbl.set(keyTexts[0], 'opacity', values$1[0]); } if (t2$2 < duration) { diaKbl.set(keyTexts[1], 'opacity', values$1[1]); } if (t3 < duration) { diaKbl.set(keyTexts[2], 'opacity', values$1[1]); } if (t4 < duration) { diaKbl.set(keyTexts[3], 'opacity', values$1[2]); } diaKbl.set('100.000%', 'opacity', values$1[2]); } } var diaList = diaKbl.toString(); if (diaList) { assign(dialogue, { animationName: ("ASS-" + (uuid())) }); keyframes += getKeyframeString(dialogue.animationName, diaList); } slices.forEach(function (slice) { slice.fragments.forEach(function (fragment) { if (!fragment.tag.t || !fragment.tag.t.length) { return; } var kbl = new KeyframeBlockList(); var fromTag = assign({}, slice.tag, fragment.tag); // TODO: accel is not implemented yet mergeT(fragment.tag.t).forEach(function (ref) { var t1 = ref.t1; var t2 = ref.t2; var tag = ref.tag; if (tag.fs) { var from = (this$1.scale * getRealFontSize(fromTag.fn, fromTag.fs)) + "px"; var to = (this$1.scale * getRealFontSize(tag.fn, fromTag.fs)) + "px"; kbl.setT({ t1: t1, t2: t2, duration: duration, prop: 'font-size', from: from, to: to }); } if (tag.fsp) { var from$1 = (this$1.scale * fromTag.fsp) + "px"; var to$1 = (this$1.scale * tag.fsp) + "px"; kbl.setT({ t1: t1, t2: t2, duration: duration, prop: 'letter-spacing', from: from$1, to: to$1 }); } var hasAlpha = ( tag.a1 !== undefined && tag.a1 === tag.a2 && tag.a2 === tag.a3 && tag.a3 === tag.a4 ); if (tag.c1 || (tag.a1 && !hasAlpha)) { var from$2 = color2rgba(fromTag.a1 + fromTag.c1); var to$2 = color2rgba((tag.a1 || fromTag.a1) + (tag.c1 || fromTag.c1)); kbl.setT({ t1: t1, t2: t2, duration: duration, prop: 'color', from: from$2, to: to$2 }); } if (hasAlpha) { var from$3 = 1 - parseInt(fromTag.a1, 16) / 255; var to$3 = 1 - parseInt(tag.a1, 16) / 255; kbl.setT({ t1: t1, t2: t2, duration: duration, prop: 'opacity', from: from$3, to: to$3 }); } var hasStroke = strokeTags.some(function (x) { return ( tag[x] !== undefined && tag[x] !== (fragment.tag[x] || slice.tag[x]) ); }); if (hasStroke) { var scale = /Yes/i.test(this$1.info.ScaledBorderAndShadow) ? this$1.scale : 1; var from$4 = createCSSStroke(fromTag, scale); var to$4 = createCSSStroke(assign({}, fromTag, tag), scale); kbl.setT({ t1: t1, t2: t2, duration: duration, prop: 'text-shadow', from: from$4, to: to$4 }); } var hasTransfrom = transformTags.some(function (x) { return ( tag[x] !== undefined && tag[x] !== (fragment.tag[x] || slice.tag[x]) ); }); if (hasTransfrom) { var toTag = assign({}, fromTag, tag); if (fragment.drawing) { // scales will be handled inside svg assign(toTag, { p: 0, fscx: ((tag.fscx || fromTag.fscx) / fromTag.fscx) * 100, fscy: ((tag.fscy || fromTag.fscy) / fromTag.fscy) * 100, }); assign(fromTag, { fscx: 100, fscy: 100 }); } var from$5 = createTransform(fromTag); var to$5 = createTransform(toTag); kbl.setT({ t1: t1, t2: t2, duration: duration, prop: 'transform', from: from$5, to: to$5 }); } }); var list = kbl.toString(); assign(fragment, { animationName: ("ASS-" + (uuid())) }); keyframes += getKeyframeString(fragment.animationName, list); }); }); }); return keyframes; } function createAnimation(name, duration, delay) { var va = vendor.animation; return ( va + "animation-name:" + name + ";" + va + "animation-duration:" + duration + "s;" + va + "animation-delay:" + delay + "s;" + va + "animation-timing-function:linear;" + va + "animation-iteration-count:1;" + va + "animation-fill-mode:forwards;" ); } function createDrawing(fragment, styleTag) { var tag = assign({}, styleTag, fragment.tag); var ref = fragment.drawing; var minX = ref.minX; var minY = ref.minY; var width = ref.width; var height = ref.height; var baseScale = this.scale / (1 << (tag.p - 1)); var scaleX = (tag.fscx ? tag.fscx / 100 : 1) * baseScale; var scaleY = (tag.fscy ? tag.fscy / 100 : 1) * baseScale; var blur = tag.blur || tag.be || 0; var vbx = tag.xbord + (tag.xshad < 0 ? -tag.xshad : 0) + blur; var vby = tag.ybord + (tag.yshad < 0 ? -tag.yshad : 0) + blur; var vbw = width * scaleX + 2 * tag.xbord + Math.abs(tag.xshad) + 2 * blur; var vbh = height * scaleY + 2 * tag.ybord + Math.abs(tag.yshad) + 2 * blur; var $svg = createSVGEl('svg', [ ['width', vbw], ['height', vbh], ['viewBox', ((-vbx) + " " + (-vby) + " " + vbw + " " + vbh)] ]); var strokeScale = /Yes/i.test(this.info.ScaledBorderAndShadow) ? this.scale : 1; var filterId = "ASS-" + (uuid()); var $defs = createSVGEl('defs'); $defs.appendChild(createSVGStroke(tag, filterId, strokeScale)); $svg.appendChild($defs); var symbolId = "ASS-" + (uuid()); var $symbol = createSVGEl('symbol', [ ['id', symbolId], ['viewBox', (minX + " " + minY + " " + width + " " + height)] ]); $symbol.appendChild(createSVGEl('path', [['d', fragment.drawing.d]])); $svg.appendChild($symbol); $svg.appendChild(createSVGEl('use', [ ['width', width * scaleX], ['height', height * scaleY], ['xlink:href', ("#" + symbolId)], ['filter', ("url(#" + filterId + ")")] ])); $svg.style.cssText = ( 'position:absolute;' + "left:" + (minX * scaleX - vbx) + "px;" + "top:" + (minY * scaleY - vby) + "px;" ); return { $svg: $svg, cssText: ("position:relative;width:" + (width * scaleX) + "px;height:" + (height * scaleY) + "px;"), }; } function encodeText(text, q) { return text .replace(//g, '>') .replace(/\s/g, ' ') .replace(/\\h/g, ' ') .replace(/\\N/g, '
') .replace(/\\n/g, q === 2 ? '
' : ' '); } function createDialogue(dialogue) { var this$1 = this; var $div = document.createElement('div'); $div.className = 'ASS-dialogue'; var df = document.createDocumentFragment(); var slices = dialogue.slices; var start = dialogue.start; var end = dialogue.end; slices.forEach(function (slice) { var borderStyle = slice.borderStyle; slice.fragments.forEach(function (fragment) { var text = fragment.text; var drawing = fragment.drawing; var animationName = fragment.animationName; var tag = assign({}, slice.tag, fragment.tag); var cssText = 'display:inline-block;'; var vct = this$1.video.currentTime; if (!drawing) { cssText += "font-family:\"" + (tag.fn) + "\",Arial;"; cssText += "font-size:" + (this$1.scale * getRealFontSize(tag.fn, tag.fs)) + "px;"; cssText += "color:" + (color2rgba(tag.a1 + tag.c1)) + ";"; var scale = /Yes/i.test(this$1.info.ScaledBorderAndShadow) ? this$1.scale : 1; if (borderStyle === 1) { cssText += "text-shadow:" + (createCSSStroke(tag, scale)) + ";"; } if (borderStyle === 3) { cssText += ( "background-color:" + (color2rgba(tag.a3 + tag.c3)) + ";" + "box-shadow:" + (createCSSStroke(tag, scale)) + ";" ); } cssText += tag.b ? ("font-weight:" + (tag.b === 1 ? 'bold' : tag.b) + ";") : ''; cssText += tag.i ? 'font-style:italic;' : ''; cssText += (tag.u || tag.s) ? ("text-decoration:" + (tag.u ? 'underline' : '') + " " + (tag.s ? 'line-through' : '') + ";") : ''; cssText += tag.fsp ? ("letter-spacing:" + (tag.fsp) + "px;") : ''; // TODO: (tag.q === 0) and (tag.q === 3) are not implemented yet, // for now just handle it as (tag.q === 1) if (tag.q === 1 || tag.q === 0 || tag.q === 3) { cssText += 'word-break:break-all;white-space:normal;'; } if (tag.q === 2) { cssText += 'word-break:normal;white-space:nowrap;'; } } var hasTransfrom = transformTags.some(function (x) { return ( /^fsc[xy]$/.test(x) ? tag[x] !== 100 : !!tag[x] ); }); if (hasTransfrom) { cssText += (vendor.transform) + "transform:" + (createTransform(tag)) + ";"; if (!drawing) { cssText += 'transform-style:preserve-3d;word-break:normal;white-space:nowrap;'; } } if (animationName) { cssText += createAnimation(animationName, end - start, Math.min(0, start - vct)); } if (drawing && tag.pbo) { var pbo = this$1.scale * -tag.pbo * (tag.fscy || 100) / 100; cssText += "vertical-align:" + pbo + "px;"; } var hasRotate = /"fr[xyz]":[^0]/.test(JSON.stringify(tag)); encodeText(text, tag.q).split('
').forEach(function (html, idx) { var $span = document.createElement('span'); $span.dataset.hasRotate = hasRotate; if (drawing) { var obj = createDrawing.call(this$1, fragment, slice.tag); $span.style.cssText = obj.cssText; $span.appendChild(obj.$svg); } else { if (idx) { df.appendChild(document.createElement('br')); } if (!html) { return; } $span.innerHTML = html; } // TODO: maybe it can be optimized $span.style.cssText += cssText; df.appendChild($span); }); }); }); $div.appendChild(df); return $div; } function allocate(dialogue) { var layer = dialogue.layer; var margin = dialogue.margin; var width = dialogue.width; var height = dialogue.height; var alignment = dialogue.alignment; var end = dialogue.end; var stageWidth = this.width - (this.scale * (margin.left + margin.right) | 0); var stageHeight = this.height; var vertical = this.scale * margin.vertical | 0; var vct = this.video.currentTime * 100; this._.space[layer] = this._.space[layer] || { left: { width: new Uint16Array(stageHeight + 1), end: new Uint16Array(stageHeight + 1) }, center: { width: new Uint16Array(stageHeight + 1), end: new Uint16Array(stageHeight + 1) }, right: { width: new Uint16Array(stageHeight + 1), end: new Uint16Array(stageHeight + 1) }, }; var channel = this._.space[layer]; var align = ['right', 'left', 'center'][alignment % 3]; var willCollide = function (y) { var lw = channel.left.width[y]; var cw = channel.center.width[y]; var rw = channel.right.width[y]; var le = channel.left.end[y]; var ce = channel.center.end[y]; var re = channel.right.end[y]; return ( (align === 'left' && ( (le > vct && lw) || (ce > vct && cw && 2 * width + cw > stageWidth) || (re > vct && rw && width + rw > stageWidth) )) || (align === 'center' && ( (le > vct && lw && 2 * lw + width > stageWidth) || (ce > vct && cw) || (re > vct && rw && 2 * rw + width > stageWidth) )) || (align === 'right' && ( (le > vct && lw && lw + width > stageWidth) || (ce > vct && cw && 2 * width + cw > stageWidth) || (re > vct && rw) )) ); }; var count = 0; var result = 0; var find = function (y) { count = willCollide(y) ? 0 : count + 1; if (count >= height) { result = y; return true; } return false; }; if (alignment <= 3) { for (var i = stageHeight - vertical - 1; i > vertical; i--) { if (find(i)) { break; } } } else if (alignment >= 7) { for (var i$1 = vertical + 1; i$1 < stageHeight - vertical; i$1++) { if (find(i$1)) { break; } } } else { for (var i$2 = (stageHeight - height) >> 1; i$2 < stageHeight - vertical; i$2++) { if (find(i$2)) { break; } } } if (alignment > 3) { result -= height - 1; } for (var i$3 = result; i$3 < result + height; i$3++) { channel[align].width[i$3] = width; channel[align].end[i$3] = end * 100; } return result; } function getPosition(dialogue) { var effect = dialogue.effect; var move = dialogue.move; var alignment = dialogue.alignment; var width = dialogue.width; var height = dialogue.height; var margin = dialogue.margin; var slices = dialogue.slices; var x = 0; var y = 0; if (effect) { if (effect.name === 'banner') { if (alignment <= 3) { y = this.height - height - margin.vertical; } if (alignment >= 4 && alignment <= 6) { y = (this.height - height) / 2; } if (alignment >= 7) { y = margin.vertical; } x = effect.lefttoright ? -width : this.width; } } else if (dialogue.pos || move) { var pos = dialogue.pos || { x: 0, y: 0 }; if (alignment % 3 === 1) { x = this.scale * pos.x; } if (alignment % 3 === 2) { x = this.scale * pos.x - width / 2; } if (alignment % 3 === 0) { x = this.scale * pos.x - width; } if (alignment <= 3) { y = this.scale * pos.y - height; } if (alignment >= 4 && alignment <= 6) { y = this.scale * pos.y - height / 2; } if (alignment >= 7) { y = this.scale * pos.y; } } else { if (alignment % 3 === 1) { x = 0; } if (alignment % 3 === 2) { x = (this.width - width) / 2; } if (alignment % 3 === 0) { x = this.width - width - this.scale * margin.right; } var hasT = slices.some(function (slice) { return ( slice.fragments.some(function (ref) { var animationName = ref.animationName; return animationName; }) ); }); if (hasT) { if (alignment <= 3) { y = this.height - height - margin.vertical; } if (alignment >= 4 && alignment <= 6) { y = (this.height - height) / 2; } if (alignment >= 7) { y = margin.vertical; } } else { y = allocate.call(this, dialogue); } } return { x: x, y: y }; } function createStyle(dialogue) { var layer = dialogue.layer; var start = dialogue.start; var end = dialogue.end; var alignment = dialogue.alignment; var effect = dialogue.effect; var pos = dialogue.pos; var margin = dialogue.margin; var animationName = dialogue.animationName; var width = dialogue.width; var height = dialogue.height; var x = dialogue.x; var y = dialogue.y; var vct = this.video.currentTime; var cssText = ''; if (layer) { cssText += "z-index:" + layer + ";"; } if (animationName) { cssText += createAnimation(animationName, end - start, Math.min(0, start - vct)); } cssText += "text-align:" + (['right', 'left', 'center'][alignment % 3]) + ";"; if (!effect) { var mw = this.width - this.scale * (margin.left + margin.right); cssText += "max-width:" + mw + "px;"; if (!pos) { if (alignment % 3 === 1) { cssText += "margin-left:" + (this.scale * margin.left) + "px;"; } if (alignment % 3 === 0) { cssText += "margin-right:" + (this.scale * margin.right) + "px;"; } if (width > this.width - this.scale * (margin.left + margin.right)) { cssText += "margin-left:" + (this.scale * margin.left) + "px;"; cssText += "margin-right:" + (this.scale * margin.right) + "px;"; } } } cssText += "width:" + width + "px;height:" + height + "px;left:" + x + "px;top:" + y + "px;"; return cssText; } function renderer(dialogue) { var $div = createDialogue.call(this, dialogue); assign(dialogue, { $div: $div }); this._.$stage.appendChild($div); var ref = $div.getBoundingClientRect(); var width = ref.width; var height = ref.height; assign(dialogue, { width: width, height: height }); assign(dialogue, getPosition.call(this, dialogue)); $div.style.cssText = createStyle.call(this, dialogue); setTransformOrigin(dialogue); setClipPath.call(this, dialogue); return dialogue; } function framing() { var vct = this.video.currentTime; for (var i = this._.stagings.length - 1; i >= 0; i--) { var dia = this._.stagings[i]; var end = dia.end; if (dia.effect && /scroll/.test(dia.effect.name)) { var ref = dia.effect; var y1 = ref.y1; var y2 = ref.y2; var delay = ref.delay; var duration = ((y2 || this._.resampledRes.height) - y1) / (1000 / delay); end = Math.min(end, dia.start + duration); } if (end < vct) { this._.$stage.removeChild(dia.$div); if (dia.$clipPath) { this._.$defs.removeChild(dia.$clipPath); } this._.stagings.splice(i, 1); } } var dias = this.dialogues; while ( this._.index < dias.length && vct >= dias[this._.index].start ) { if (vct < dias[this._.index].end) { var dia$1 = renderer.call(this, dias[this._.index]); this._.stagings.push(dia$1); } ++this._.index; } } function play() { var this$1 = this; var frame = function () { framing.call(this$1); this$1._.requestId = raf(frame); }; caf(this._.requestId); this._.requestId = raf(frame); this._.$stage.classList.remove('ASS-animation-paused'); return this; } function pause() { caf(this._.requestId); this._.requestId = 0; this._.$stage.classList.add('ASS-animation-paused'); return this; } function clear() { while (this._.$stage.lastChild) { this._.$stage.removeChild(this._.$stage.lastChild); } while (this._.$defs.lastChild) { this._.$defs.removeChild(this._.$defs.lastChild); } this._.stagings = []; this._.space = []; } function seek() { var vct = this.video.currentTime; var dias = this.dialogues; clear.call(this); this._.index = (function () { var from = 0; var to = dias.length - 1; while (from + 1 < to && vct > dias[(to + from) >> 1].end) { from = (to + from) >> 1; } if (!from) { return 0; } for (var i = from; i < to; i++) { if ( dias[i].end > vct && vct >= dias[i].start || (i && dias[i - 1].end < vct && vct < dias[i].start) ) { return i; } } return to; })(); framing.call(this); } function bindEvents() { var l = this._.listener; l.play = play.bind(this); l.pause = pause.bind(this); l.seeking = seek.bind(this); this.video.addEventListener('play', l.play); this.video.addEventListener('pause', l.pause); this.video.addEventListener('seeking', l.seeking); } function unbindEvents() { var l = this._.listener; this.video.removeEventListener('play', l.play); this.video.removeEventListener('pause', l.pause); this.video.removeEventListener('seeking', l.seeking); l.play = null; l.pause = null; l.seeking = null; } function resize() { var cw = this.video.clientWidth; var ch = this.video.clientHeight; var vw = this.video.videoWidth || cw; var vh = this.video.videoHeight || ch; var sw = this._.scriptRes.width; var sh = this._.scriptRes.height; var rw = sw; var rh = sh; var videoScale = Math.min(cw / vw, ch / vh); if (this.resampling === 'video_width') { rh = sw / vw * vh; } if (this.resampling === 'video_height') { rw = sh / vh * vw; } this.scale = Math.min(cw / rw, ch / rh); if (this.resampling === 'script_width') { this.scale = videoScale * (vw / rw); } if (this.resampling === 'script_height') { this.scale = videoScale * (vh / rh); } this.width = this.scale * rw; this.height = this.scale * rh; this._.resampledRes = { width: rw, height: rh }; this.container.style.cssText = "width:" + cw + "px;height:" + ch + "px;"; var cssText = ( "width:" + (this.width) + "px;" + "height:" + (this.height) + "px;" + "top:" + ((ch - this.height) / 2) + "px;" + "left:" + ((cw - this.width) / 2) + "px;" ); this._.$stage.style.cssText = cssText; this._.$svg.style.cssText = cssText; this._.$svg.setAttributeNS(null, 'viewBox', ("0 0 " + sw + " " + sh)); this._.$animation.innerHTML = getKeyframes.call(this); seek.call(this); return this; } var GLOBAL_CSS = '.ASS-container,.ASS-stage{position:relative;overflow:hidden}.ASS-container video{position:absolute;top:0;left:0}.ASS-stage{pointer-events:none;position:absolute}.ASS-dialogue{font-size:0;position:absolute}.ASS-fix-font-size{position:absolute;visibility:hidden}.ASS-fix-objectBoundingBox{width:100%;height:100%;position:absolute;top:0;left:0}.ASS-animation-paused *{-webkit-animation-play-state:paused!important;animation-play-state:paused!important}'; function init(source, video, options) { if ( options === void 0 ) options = {}; this.scale = 1; // private variables this._ = { index: 0, stagings: [], space: [], listener: {}, $svg: createSVGEl('svg'), $defs: createSVGEl('defs'), $stage: document.createElement('div'), $animation: document.createElement('style'), }; this._.$svg.appendChild(this._.$defs); this._.$stage.className = 'ASS-stage ASS-animation-paused'; this._.resampling = options.resampling || 'video_height'; this.container = options.container || document.createElement('div'); this.container.classList.add('ASS-container'); this.container.appendChild($fixFontSize); this.container.appendChild(this._.$svg); this._.hasInitContainer = !!options.container; this.video = video; bindEvents.call(this); if (!this._.hasInitContainer) { var isPlaying = !video.paused; video.parentNode.insertBefore(this.container, video); this.container.appendChild(video); if (isPlaying && video.paused) { video.play(); } } this.container.appendChild(this._.$stage); var ref = compile(source); var info = ref.info; var width = ref.width; var height = ref.height; var dialogues = ref.dialogues; this.info = info; this._.scriptRes = { width: width || video.videoWidth, height: height || video.videoHeight, }; this.dialogues = dialogues; var styleRoot = getStyleRoot(this.container); var $style = styleRoot.querySelector('#ASS-global-style'); if (!$style) { $style = document.createElement('style'); $style.type = 'text/css'; $style.id = 'ASS-global-style'; $style.appendChild(document.createTextNode(GLOBAL_CSS)); styleRoot.appendChild($style); } this._.$animation.type = 'text/css'; this._.$animation.className = 'ASS-animation'; styleRoot.appendChild(this._.$animation); resize.call(this); if (!this.video.paused) { seek.call(this); play.call(this); } return this; } function show() { this._.$stage.style.visibility = 'visible'; return this; } function hide() { this._.$stage.style.visibility = 'hidden'; return this; } function destroy() { pause.call(this); clear.call(this); unbindEvents.call(this, this._.listener); var styleRoot = getStyleRoot(this.container); if (!this._.hasInitContainer) { var isPlay = !this.video.paused; this.container.parentNode.insertBefore(this.video, this.container); this.container.parentNode.removeChild(this.container); if (isPlay && this.video.paused) { this.video.play(); } } styleRoot.removeChild(this._.$animation); // eslint-disable-next-line no-restricted-syntax for (var key in this) { if (Object.prototype.hasOwnProperty.call(this, key)) { this[key] = null; } } return this; } var regex = /^(video|script)_(width|height)$/; function getter() { return regex.test(this._.resampling) ? this._.resampling : 'video_height'; } function setter(r) { if (r === this._.resampling) { return r; } if (regex.test(r)) { this._.resampling = r; this.resize(); } return this._.resampling; } var ASS = function ASS(source, video, options) { if (typeof source !== 'string') { return this; } return init.call(this, source, video, options); }; var prototypeAccessors = { resampling: { configurable: true } }; ASS.prototype.resize = function resize$1 () { return resize.call(this); }; ASS.prototype.show = function show$1 () { return show.call(this); }; ASS.prototype.hide = function hide$1 () { return hide.call(this); }; ASS.prototype.destroy = function destroy$1 () { return destroy.call(this); }; prototypeAccessors.resampling.get = function () { return getter.call(this); }; prototypeAccessors.resampling.set = function (r) { return setter.call(this, r); }; Object.defineProperties( ASS.prototype, prototypeAccessors ); function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css_248z = ".artplayer-plugin-ass{bottom:0!important}\n/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLnNjc3MiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsc0JBQ0Usa0JBQXNCIiwiZmlsZSI6InN0eWxlLnNjc3MiLCJzb3VyY2VzQ29udGVudCI6WyIuYXJ0cGxheWVyLXBsdWdpbi1hc3Mge1xuICBib3R0b206IDAgIWltcG9ydGFudDsgfVxuIl19 */"; styleInject(css_248z); function artplayerPluginAss(url) { return function (art) { var ass = null; fetch(url).then(function (res) { return res.text(); }).then(function (text) { var _art$template = art.template, $video = _art$template.$video, $subtitle = _art$template.$subtitle; $subtitle.classList.add('artplayer-plugin-ass'); art.once('ready', function () { ass = new ASS(text, $video, { container: $subtitle, resampling: 'video_width' }); ass.resize(); art.emit('artplayerPluginAss', ass); art.on('resize', function () { return ass.resize(); }); art.on('destroy', function () { return ass.destroy(); }); }); }); return { name: 'artplayerPluginAss', ass: ass }; }; } return artplayerPluginAss; }))); //# sourceMappingURL=artplayer-plugin-ass.js.map