Source: lib/text/cue.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.text.Cue');
  7. goog.provide('shaka.text.CueRegion');
  8. goog.require('shaka.util.ArrayUtils');
  9. /**
  10. * @implements {shaka.extern.Cue}
  11. * @export
  12. */
  13. shaka.text.Cue = class {
  14. /**
  15. * @param {number} startTime
  16. * @param {number} endTime
  17. * @param {string} payload
  18. */
  19. constructor(startTime, endTime, payload) {
  20. const Cue = shaka.text.Cue;
  21. /**
  22. * @override
  23. * @exportInterface
  24. */
  25. this.startTime = startTime;
  26. /**
  27. * @override
  28. * @exportInterface
  29. */
  30. this.direction = Cue.direction.HORIZONTAL_LEFT_TO_RIGHT;
  31. /**
  32. * @override
  33. * @exportInterface
  34. */
  35. this.endTime = endTime;
  36. // The explicit type here is to work around an apparent compiler bug where
  37. // the compiler seems to get confused about the type. Since we are
  38. // overriding the interface type, it shouldn't be necessary to do this.
  39. // Compiler version 20200517.
  40. /**
  41. * @override
  42. * @exportInterface
  43. * @type {string}
  44. */
  45. this.payload = payload;
  46. /**
  47. * @override
  48. * @exportInterface
  49. */
  50. this.region = new shaka.text.CueRegion();
  51. /**
  52. * @override
  53. * @exportInterface
  54. */
  55. this.position = null;
  56. /**
  57. * @override
  58. * @exportInterface
  59. */
  60. this.positionAlign = Cue.positionAlign.AUTO;
  61. /**
  62. * @override
  63. * @exportInterface
  64. */
  65. this.size = 0;
  66. /**
  67. * @override
  68. * @exportInterface
  69. */
  70. this.textAlign = Cue.textAlign.CENTER;
  71. /**
  72. * @override
  73. * @exportInterface
  74. */
  75. this.writingMode = Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM;
  76. /**
  77. * @override
  78. * @exportInterface
  79. */
  80. this.lineInterpretation = Cue.lineInterpretation.LINE_NUMBER;
  81. /**
  82. * @override
  83. * @exportInterface
  84. */
  85. this.line = null;
  86. /**
  87. * @override
  88. * @exportInterface
  89. */
  90. this.lineHeight = '';
  91. /**
  92. * Line Alignment is set to start by default.
  93. * @override
  94. * @exportInterface
  95. */
  96. this.lineAlign = Cue.lineAlign.START;
  97. /**
  98. * Set the captions at the bottom of the text container by default.
  99. * @override
  100. * @exportInterface
  101. */
  102. this.displayAlign = Cue.displayAlign.AFTER;
  103. /**
  104. * @override
  105. * @exportInterface
  106. */
  107. this.color = '';
  108. /**
  109. * @override
  110. * @exportInterface
  111. */
  112. this.backgroundColor = '';
  113. /**
  114. * @override
  115. * @exportInterface
  116. */
  117. this.backgroundImage = '';
  118. /**
  119. * @override
  120. * @exportInterface
  121. */
  122. this.border = '';
  123. /**
  124. * @override
  125. * @exportInterface
  126. */
  127. this.fontSize = '';
  128. /**
  129. * @override
  130. * @exportInterface
  131. */
  132. this.fontWeight = Cue.fontWeight.NORMAL;
  133. /**
  134. * @override
  135. * @exportInterface
  136. */
  137. this.fontStyle = Cue.fontStyle.NORMAL;
  138. /**
  139. * @override
  140. * @exportInterface
  141. */
  142. this.fontFamily = '';
  143. /**
  144. * @override
  145. * @exportInterface
  146. */
  147. this.letterSpacing = '';
  148. /**
  149. * @override
  150. * @exportInterface
  151. */
  152. this.linePadding = '';
  153. /**
  154. * @override
  155. * @exportInterface
  156. */
  157. this.opacity = 1;
  158. /**
  159. * @override
  160. * @exportInterface
  161. */
  162. this.textDecoration = [];
  163. /**
  164. * @override
  165. * @exportInterface
  166. */
  167. this.wrapLine = true;
  168. /**
  169. * @override
  170. * @exportInterface
  171. */
  172. this.id = '';
  173. /**
  174. * @override
  175. * @exportInterface
  176. */
  177. this.nestedCues = [];
  178. /**
  179. * @override
  180. * @exportInterface
  181. */
  182. this.lineBreak = false;
  183. /**
  184. * @override
  185. * @exportInterface
  186. */
  187. this.spacer = false;
  188. /**
  189. * @override
  190. * @exportInterface
  191. */
  192. this.cellResolution = {
  193. columns: 32,
  194. rows: 15,
  195. };
  196. }
  197. /**
  198. * @param {number} start
  199. * @param {number} end
  200. * @return {!shaka.text.Cue}
  201. */
  202. static lineBreak(start, end) {
  203. const cue = new shaka.text.Cue(start, end, '');
  204. cue.lineBreak = true;
  205. return cue;
  206. }
  207. /**
  208. * Create a copy of the cue with the same properties.
  209. * @return {!shaka.text.Cue}
  210. * @suppress {checkTypes} since we must use [] and "in" with a struct type.
  211. */
  212. clone() {
  213. const clone = new shaka.text.Cue(0, 0, '');
  214. for (const k in this) {
  215. clone[k] = this[k];
  216. // Make copies of array fields, but only one level deep. That way, if we
  217. // change, for instance, textDecoration on the clone, we don't affect the
  218. // original.
  219. if (clone[k] && clone[k].constructor == Array) {
  220. clone[k] = /** @type {!Array} */(clone[k]).slice();
  221. }
  222. }
  223. return clone;
  224. }
  225. /**
  226. * Check if two Cues have all the same values in all properties.
  227. * @param {!shaka.text.Cue} cue1
  228. * @param {!shaka.text.Cue} cue2
  229. * @return {boolean}
  230. * @suppress {checkTypes} since we must use [] and "in" with a struct type.
  231. */
  232. static equal(cue1, cue2) {
  233. // Compare the start time, end time and payload of the cues first for
  234. // performance optimization. We can avoid the more expensive recursive
  235. // checks if the top-level properties don't match.
  236. // See: https://github.com/google/shaka-player/issues/3018
  237. if (cue1.startTime != cue2.startTime || cue1.endTime != cue2.endTime ||
  238. cue1.payload != cue2.payload) {
  239. return false;
  240. }
  241. for (const k in cue1) {
  242. if (k == 'startTime' || k == 'endTime' || k == 'payload') {
  243. // Already compared.
  244. } else if (k == 'nestedCues') {
  245. // This uses shaka.text.Cue.equal rather than just this.equal, since
  246. // otherwise recursing here will unbox the method and cause "this" to be
  247. // undefined in deeper recursion.
  248. if (!shaka.util.ArrayUtils.equal(
  249. cue1.nestedCues, cue2.nestedCues, shaka.text.Cue.equal)) {
  250. return false;
  251. }
  252. } else if (k == 'region' || k == 'cellResolution') {
  253. for (const k2 in cue1[k]) {
  254. if (cue1[k][k2] != cue2[k][k2]) {
  255. return false;
  256. }
  257. }
  258. } else if (Array.isArray(cue1[k])) {
  259. if (!shaka.util.ArrayUtils.equal(cue1[k], cue2[k])) {
  260. return false;
  261. }
  262. } else {
  263. if (cue1[k] != cue2[k]) {
  264. return false;
  265. }
  266. }
  267. }
  268. return true;
  269. }
  270. };
  271. /**
  272. * @enum {string}
  273. * @export
  274. */
  275. shaka.text.Cue.positionAlign = {
  276. 'LEFT': 'line-left',
  277. 'RIGHT': 'line-right',
  278. 'CENTER': 'center',
  279. 'AUTO': 'auto',
  280. };
  281. /**
  282. * @enum {string}
  283. * @export
  284. */
  285. shaka.text.Cue.textAlign = {
  286. 'LEFT': 'left',
  287. 'RIGHT': 'right',
  288. 'CENTER': 'center',
  289. 'START': 'start',
  290. 'END': 'end',
  291. };
  292. /**
  293. * Vertical alignments of the cues within their extents.
  294. * 'BEFORE' means displaying at the top of the captions container box, 'CENTER'
  295. * means in the middle, 'AFTER' means at the bottom.
  296. * @enum {string}
  297. * @export
  298. */
  299. shaka.text.Cue.displayAlign = {
  300. 'BEFORE': 'before',
  301. 'CENTER': 'center',
  302. 'AFTER': 'after',
  303. };
  304. /**
  305. * @enum {string}
  306. * @export
  307. */
  308. shaka.text.Cue.direction = {
  309. 'HORIZONTAL_LEFT_TO_RIGHT': 'ltr',
  310. 'HORIZONTAL_RIGHT_TO_LEFT': 'rtl',
  311. };
  312. /**
  313. * @enum {string}
  314. * @export
  315. */
  316. shaka.text.Cue.writingMode = {
  317. 'HORIZONTAL_TOP_TO_BOTTOM': 'horizontal-tb',
  318. 'VERTICAL_LEFT_TO_RIGHT': 'vertical-lr',
  319. 'VERTICAL_RIGHT_TO_LEFT': 'vertical-rl',
  320. };
  321. /**
  322. * @enum {number}
  323. * @export
  324. */
  325. shaka.text.Cue.lineInterpretation = {
  326. 'LINE_NUMBER': 0,
  327. 'PERCENTAGE': 1,
  328. };
  329. /**
  330. * @enum {string}
  331. * @export
  332. */
  333. shaka.text.Cue.lineAlign = {
  334. 'CENTER': 'center',
  335. 'START': 'start',
  336. 'END': 'end',
  337. };
  338. /**
  339. * Default text color according to
  340. * https://w3c.github.io/webvtt/#default-text-color
  341. * @enum {string}
  342. * @export
  343. */
  344. shaka.text.Cue.defaultTextColor = {
  345. 'white': '#FFF',
  346. 'lime': '#0F0',
  347. 'cyan': '#0FF',
  348. 'red': '#F00',
  349. 'yellow': '#FF0',
  350. 'magenta': '#F0F',
  351. 'blue': '#00F',
  352. 'black': '#000',
  353. };
  354. /**
  355. * Default text background color according to
  356. * https://w3c.github.io/webvtt/#default-text-background
  357. * @enum {string}
  358. * @export
  359. */
  360. shaka.text.Cue.defaultTextBackgroundColor = {
  361. 'bg_white': '#FFF',
  362. 'bg_lime': '#0F0',
  363. 'bg_cyan': '#0FF',
  364. 'bg_red': '#F00',
  365. 'bg_yellow': '#FF0',
  366. 'bg_magenta': '#F0F',
  367. 'bg_blue': '#00F',
  368. 'bg_black': '#000',
  369. };
  370. /**
  371. * In CSS font weight can be a number, where 400 is normal and 700 is bold.
  372. * Use these values for the enum for consistency.
  373. * @enum {number}
  374. * @export
  375. */
  376. shaka.text.Cue.fontWeight = {
  377. 'NORMAL': 400,
  378. 'BOLD': 700,
  379. };
  380. /**
  381. * @enum {string}
  382. * @export
  383. */
  384. shaka.text.Cue.fontStyle = {
  385. 'NORMAL': 'normal',
  386. 'ITALIC': 'italic',
  387. 'OBLIQUE': 'oblique',
  388. };
  389. /**
  390. * @enum {string}
  391. * @export
  392. */
  393. shaka.text.Cue.textDecoration = {
  394. 'UNDERLINE': 'underline',
  395. 'LINE_THROUGH': 'lineThrough',
  396. 'OVERLINE': 'overline',
  397. };
  398. /**
  399. * @implements {shaka.extern.CueRegion}
  400. * @struct
  401. * @export
  402. */
  403. shaka.text.CueRegion = class {
  404. /** */
  405. constructor() {
  406. const CueRegion = shaka.text.CueRegion;
  407. /**
  408. * @override
  409. * @exportInterface
  410. */
  411. this.id = '';
  412. /**
  413. * @override
  414. * @exportInterface
  415. */
  416. this.viewportAnchorX = 0;
  417. /**
  418. * @override
  419. * @exportInterface
  420. */
  421. this.viewportAnchorY = 0;
  422. /**
  423. * @override
  424. * @exportInterface
  425. */
  426. this.regionAnchorX = 0;
  427. /**
  428. * @override
  429. * @exportInterface
  430. */
  431. this.regionAnchorY = 0;
  432. /**
  433. * @override
  434. * @exportInterface
  435. */
  436. this.width = 100;
  437. /**
  438. * @override
  439. * @exportInterface
  440. */
  441. this.height = 100;
  442. /**
  443. * @override
  444. * @exportInterface
  445. */
  446. this.heightUnits = CueRegion.units.PERCENTAGE;
  447. /**
  448. * @override
  449. * @exportInterface
  450. */
  451. this.widthUnits = CueRegion.units.PERCENTAGE;
  452. /**
  453. * @override
  454. * @exportInterface
  455. */
  456. this.viewportAnchorUnits = CueRegion.units.PERCENTAGE;
  457. /**
  458. * @override
  459. * @exportInterface
  460. */
  461. this.scroll = CueRegion.scrollMode.NONE;
  462. }
  463. };
  464. /**
  465. * @enum {number}
  466. * @export
  467. */
  468. shaka.text.CueRegion.units = {
  469. 'PX': 0,
  470. 'PERCENTAGE': 1,
  471. 'LINES': 2,
  472. };
  473. /**
  474. * @enum {string}
  475. * @export
  476. */
  477. shaka.text.CueRegion.scrollMode = {
  478. 'NONE': '',
  479. 'UP': 'up',
  480. };