Source: core/text/TextStyle.js

core/text/TextStyle.js

  1. // disabling eslint for now, going to rewrite this in v5
  2. /* eslint-disable */
  3. import { TEXT_GRADIENT } from '../const';
  4. import { hex2string } from '../utils';
  5. const defaultStyle = {
  6. align: 'left',
  7. breakWords: false,
  8. dropShadow: false,
  9. dropShadowAlpha: 1,
  10. dropShadowAngle: Math.PI / 6,
  11. dropShadowBlur: 0,
  12. dropShadowColor: 'black',
  13. dropShadowDistance: 5,
  14. fill: 'black',
  15. fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL,
  16. fillGradientStops: [],
  17. fontFamily: 'Arial',
  18. fontSize: 26,
  19. fontStyle: 'normal',
  20. fontVariant: 'normal',
  21. fontWeight: 'normal',
  22. letterSpacing: 0,
  23. lineHeight: 0,
  24. lineJoin: 'miter',
  25. miterLimit: 10,
  26. padding: 0,
  27. stroke: 'black',
  28. strokeThickness: 0,
  29. textBaseline: 'alphabetic',
  30. trim: false,
  31. whiteSpace: 'pre',
  32. wordWrap: false,
  33. wordWrapWidth: 100,
  34. leading: 0,
  35. };
  36. const genericFontFamilies = [
  37. 'serif',
  38. 'sans-serif',
  39. 'monospace',
  40. 'cursive',
  41. 'fantasy',
  42. 'system-ui',
  43. ]
  44. /**
  45. * A TextStyle Object decorates a Text Object. It can be shared between
  46. * multiple Text objects. Changing the style will update all text objects using it.
  47. * It can be generated [here](https://pixijs.io/pixi-text-style).
  48. *
  49. * @class
  50. * @memberof PIXI
  51. */
  52. export default class TextStyle
  53. {
  54. /**
  55. * @param {object} [style] - The style parameters
  56. * @param {string} [style.align='left'] - Alignment for multiline text ('left', 'center' or 'right'),
  57. * does not affect single line text
  58. * @param {boolean} [style.breakWords=false] - Indicates if lines can be wrapped within words, it
  59. * needs wordWrap to be set to true
  60. * @param {boolean} [style.dropShadow=false] - Set a drop shadow for the text
  61. * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow
  62. * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow
  63. * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius
  64. * @param {string|number} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00'
  65. * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow
  66. * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas
  67. * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient
  68. * eg ['#000000','#FFFFFF']
  69. * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN}
  70. * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fill is an array of colours
  71. * to create a gradient, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT}
  72. * @param {number[]} [style.fillGradientStops] - If fill is an array of colours to create a gradient, this array can set
  73. * the stop points (numbers between 0 and 1) for the color, overriding the default behaviour of evenly spacing them.
  74. * @param {string|string[]} [style.fontFamily='Arial'] - The font family
  75. * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string,
  76. * equivalents are '26px','20pt','160%' or '1.6em')
  77. * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique')
  78. * @param {string} [style.fontVariant='normal'] - The font variant ('normal' or 'small-caps')
  79. * @param {string} [style.fontWeight='normal'] - The font weight ('normal', 'bold', 'bolder', 'lighter' and '100',
  80. * '200', '300', '400', '500', '600', '700', 800' or '900')
  81. * @param {number} [style.leading=0] - The space between lines
  82. * @param {number} [style.letterSpacing=0] - The amount of spacing between letters, default is 0
  83. * @param {number} [style.lineHeight] - The line height, a number that represents the vertical space that a letter uses
  84. * @param {string} [style.lineJoin='miter'] - The lineJoin property sets the type of corner created, it can resolve
  85. * spiked text issues. Possible values "miter" (creates a sharp corner), "round" (creates a round corner) or "bevel"
  86. * (creates a squared corner).
  87. * @param {number} [style.miterLimit=10] - The miter limit to use when using the 'miter' lineJoin mode. This can reduce
  88. * or increase the spikiness of rendered text.
  89. * @param {number} [style.padding=0] - Occasionally some fonts are cropped. Adding some padding will prevent this from
  90. * happening by adding padding to all sides of the text.
  91. * @param {string|number} [style.stroke='black'] - A canvas fillstyle that will be used on the text stroke
  92. * e.g 'blue', '#FCFF00'
  93. * @param {number} [style.strokeThickness=0] - A number that represents the thickness of the stroke.
  94. * Default is 0 (no stroke)
  95. * @param {boolean} [style.trim=false] - Trim transparent borders
  96. * @param {string} [style.textBaseline='alphabetic'] - The baseline of the text that is rendered.
  97. * @param {boolean} [style.whiteSpace='pre'] - Determines whether newlines & spaces are collapsed or preserved "normal"
  98. * (collapse, collapse), "pre" (preserve, preserve) | "pre-line" (preserve, collapse). It needs wordWrap to be set to true
  99. * @param {boolean} [style.wordWrap=false] - Indicates if word wrap should be used
  100. * @param {number} [style.wordWrapWidth=100] - The width at which text will wrap, it needs wordWrap to be set to true
  101. */
  102. constructor(style)
  103. {
  104. this.styleID = 0;
  105. this.reset();
  106. deepCopyProperties(this, style, style);
  107. }
  108. /**
  109. * Creates a new TextStyle object with the same values as this one.
  110. * Note that the only the properties of the object are cloned.
  111. *
  112. * @return {PIXI.TextStyle} New cloned TextStyle object
  113. */
  114. clone()
  115. {
  116. const clonedProperties = {};
  117. deepCopyProperties(clonedProperties, this, defaultStyle);
  118. return new TextStyle(clonedProperties);
  119. }
  120. /**
  121. * Resets all properties to the defaults specified in TextStyle.prototype._default
  122. */
  123. reset()
  124. {
  125. deepCopyProperties(this, defaultStyle, defaultStyle);
  126. }
  127. /**
  128. * Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text
  129. *
  130. * @member {string}
  131. */
  132. get align()
  133. {
  134. return this._align;
  135. }
  136. set align(align) // eslint-disable-line require-jsdoc
  137. {
  138. if (this._align !== align)
  139. {
  140. this._align = align;
  141. this.styleID++;
  142. }
  143. }
  144. /**
  145. * Indicates if lines can be wrapped within words, it needs wordWrap to be set to true
  146. *
  147. * @member {boolean}
  148. */
  149. get breakWords()
  150. {
  151. return this._breakWords;
  152. }
  153. set breakWords(breakWords) // eslint-disable-line require-jsdoc
  154. {
  155. if (this._breakWords !== breakWords)
  156. {
  157. this._breakWords = breakWords;
  158. this.styleID++;
  159. }
  160. }
  161. /**
  162. * Set a drop shadow for the text
  163. *
  164. * @member {boolean}
  165. */
  166. get dropShadow()
  167. {
  168. return this._dropShadow;
  169. }
  170. set dropShadow(dropShadow) // eslint-disable-line require-jsdoc
  171. {
  172. if (this._dropShadow !== dropShadow)
  173. {
  174. this._dropShadow = dropShadow;
  175. this.styleID++;
  176. }
  177. }
  178. /**
  179. * Set alpha for the drop shadow
  180. *
  181. * @member {number}
  182. */
  183. get dropShadowAlpha()
  184. {
  185. return this._dropShadowAlpha;
  186. }
  187. set dropShadowAlpha(dropShadowAlpha) // eslint-disable-line require-jsdoc
  188. {
  189. if (this._dropShadowAlpha !== dropShadowAlpha)
  190. {
  191. this._dropShadowAlpha = dropShadowAlpha;
  192. this.styleID++;
  193. }
  194. }
  195. /**
  196. * Set a angle of the drop shadow
  197. *
  198. * @member {number}
  199. */
  200. get dropShadowAngle()
  201. {
  202. return this._dropShadowAngle;
  203. }
  204. set dropShadowAngle(dropShadowAngle) // eslint-disable-line require-jsdoc
  205. {
  206. if (this._dropShadowAngle !== dropShadowAngle)
  207. {
  208. this._dropShadowAngle = dropShadowAngle;
  209. this.styleID++;
  210. }
  211. }
  212. /**
  213. * Set a shadow blur radius
  214. *
  215. * @member {number}
  216. */
  217. get dropShadowBlur()
  218. {
  219. return this._dropShadowBlur;
  220. }
  221. set dropShadowBlur(dropShadowBlur) // eslint-disable-line require-jsdoc
  222. {
  223. if (this._dropShadowBlur !== dropShadowBlur)
  224. {
  225. this._dropShadowBlur = dropShadowBlur;
  226. this.styleID++;
  227. }
  228. }
  229. /**
  230. * A fill style to be used on the dropshadow e.g 'red', '#00FF00'
  231. *
  232. * @member {string|number}
  233. */
  234. get dropShadowColor()
  235. {
  236. return this._dropShadowColor;
  237. }
  238. set dropShadowColor(dropShadowColor) // eslint-disable-line require-jsdoc
  239. {
  240. const outputColor = getColor(dropShadowColor);
  241. if (this._dropShadowColor !== outputColor)
  242. {
  243. this._dropShadowColor = outputColor;
  244. this.styleID++;
  245. }
  246. }
  247. /**
  248. * Set a distance of the drop shadow
  249. *
  250. * @member {number}
  251. */
  252. get dropShadowDistance()
  253. {
  254. return this._dropShadowDistance;
  255. }
  256. set dropShadowDistance(dropShadowDistance) // eslint-disable-line require-jsdoc
  257. {
  258. if (this._dropShadowDistance !== dropShadowDistance)
  259. {
  260. this._dropShadowDistance = dropShadowDistance;
  261. this.styleID++;
  262. }
  263. }
  264. /**
  265. * A canvas fillstyle that will be used on the text e.g 'red', '#00FF00'.
  266. * Can be an array to create a gradient eg ['#000000','#FFFFFF']
  267. * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN}
  268. *
  269. * @member {string|string[]|number|number[]|CanvasGradient|CanvasPattern}
  270. */
  271. get fill()
  272. {
  273. return this._fill;
  274. }
  275. set fill(fill) // eslint-disable-line require-jsdoc
  276. {
  277. const outputColor = getColor(fill);
  278. if (this._fill !== outputColor)
  279. {
  280. this._fill = outputColor;
  281. this.styleID++;
  282. }
  283. }
  284. /**
  285. * If fill is an array of colours to create a gradient, this can change the type/direction of the gradient.
  286. * See {@link PIXI.TEXT_GRADIENT}
  287. *
  288. * @member {number}
  289. */
  290. get fillGradientType()
  291. {
  292. return this._fillGradientType;
  293. }
  294. set fillGradientType(fillGradientType) // eslint-disable-line require-jsdoc
  295. {
  296. if (this._fillGradientType !== fillGradientType)
  297. {
  298. this._fillGradientType = fillGradientType;
  299. this.styleID++;
  300. }
  301. }
  302. /**
  303. * If fill is an array of colours to create a gradient, this array can set the stop points
  304. * (numbers between 0 and 1) for the color, overriding the default behaviour of evenly spacing them.
  305. *
  306. * @member {number[]}
  307. */
  308. get fillGradientStops()
  309. {
  310. return this._fillGradientStops;
  311. }
  312. set fillGradientStops(fillGradientStops) // eslint-disable-line require-jsdoc
  313. {
  314. if (!areArraysEqual(this._fillGradientStops,fillGradientStops))
  315. {
  316. this._fillGradientStops = fillGradientStops;
  317. this.styleID++;
  318. }
  319. }
  320. /**
  321. * The font family
  322. *
  323. * @member {string|string[]}
  324. */
  325. get fontFamily()
  326. {
  327. return this._fontFamily;
  328. }
  329. set fontFamily(fontFamily) // eslint-disable-line require-jsdoc
  330. {
  331. if (this.fontFamily !== fontFamily)
  332. {
  333. this._fontFamily = fontFamily;
  334. this.styleID++;
  335. }
  336. }
  337. /**
  338. * The font size
  339. * (as a number it converts to px, but as a string, equivalents are '26px','20pt','160%' or '1.6em')
  340. *
  341. * @member {number|string}
  342. */
  343. get fontSize()
  344. {
  345. return this._fontSize;
  346. }
  347. set fontSize(fontSize) // eslint-disable-line require-jsdoc
  348. {
  349. if (this._fontSize !== fontSize)
  350. {
  351. this._fontSize = fontSize;
  352. this.styleID++;
  353. }
  354. }
  355. /**
  356. * The font style
  357. * ('normal', 'italic' or 'oblique')
  358. *
  359. * @member {string}
  360. */
  361. get fontStyle()
  362. {
  363. return this._fontStyle;
  364. }
  365. set fontStyle(fontStyle) // eslint-disable-line require-jsdoc
  366. {
  367. if (this._fontStyle !== fontStyle)
  368. {
  369. this._fontStyle = fontStyle;
  370. this.styleID++;
  371. }
  372. }
  373. /**
  374. * The font variant
  375. * ('normal' or 'small-caps')
  376. *
  377. * @member {string}
  378. */
  379. get fontVariant()
  380. {
  381. return this._fontVariant;
  382. }
  383. set fontVariant(fontVariant) // eslint-disable-line require-jsdoc
  384. {
  385. if (this._fontVariant !== fontVariant)
  386. {
  387. this._fontVariant = fontVariant;
  388. this.styleID++;
  389. }
  390. }
  391. /**
  392. * The font weight
  393. * ('normal', 'bold', 'bolder', 'lighter' and '100', '200', '300', '400', '500', '600', '700', 800' or '900')
  394. *
  395. * @member {string}
  396. */
  397. get fontWeight()
  398. {
  399. return this._fontWeight;
  400. }
  401. set fontWeight(fontWeight) // eslint-disable-line require-jsdoc
  402. {
  403. if (this._fontWeight !== fontWeight)
  404. {
  405. this._fontWeight = fontWeight;
  406. this.styleID++;
  407. }
  408. }
  409. /**
  410. * The amount of spacing between letters, default is 0
  411. *
  412. * @member {number}
  413. */
  414. get letterSpacing()
  415. {
  416. return this._letterSpacing;
  417. }
  418. set letterSpacing(letterSpacing) // eslint-disable-line require-jsdoc
  419. {
  420. if (this._letterSpacing !== letterSpacing)
  421. {
  422. this._letterSpacing = letterSpacing;
  423. this.styleID++;
  424. }
  425. }
  426. /**
  427. * The line height, a number that represents the vertical space that a letter uses
  428. *
  429. * @member {number}
  430. */
  431. get lineHeight()
  432. {
  433. return this._lineHeight;
  434. }
  435. set lineHeight(lineHeight) // eslint-disable-line require-jsdoc
  436. {
  437. if (this._lineHeight !== lineHeight)
  438. {
  439. this._lineHeight = lineHeight;
  440. this.styleID++;
  441. }
  442. }
  443. /**
  444. * The space between lines
  445. *
  446. * @member {number}
  447. */
  448. get leading()
  449. {
  450. return this._leading;
  451. }
  452. set leading(leading) // eslint-disable-line require-jsdoc
  453. {
  454. if (this._leading !== leading)
  455. {
  456. this._leading = leading;
  457. this.styleID++;
  458. }
  459. }
  460. /**
  461. * The lineJoin property sets the type of corner created, it can resolve spiked text issues.
  462. * Default is 'miter' (creates a sharp corner).
  463. *
  464. * @member {string}
  465. */
  466. get lineJoin()
  467. {
  468. return this._lineJoin;
  469. }
  470. set lineJoin(lineJoin) // eslint-disable-line require-jsdoc
  471. {
  472. if (this._lineJoin !== lineJoin)
  473. {
  474. this._lineJoin = lineJoin;
  475. this.styleID++;
  476. }
  477. }
  478. /**
  479. * The miter limit to use when using the 'miter' lineJoin mode
  480. * This can reduce or increase the spikiness of rendered text.
  481. *
  482. * @member {number}
  483. */
  484. get miterLimit()
  485. {
  486. return this._miterLimit;
  487. }
  488. set miterLimit(miterLimit) // eslint-disable-line require-jsdoc
  489. {
  490. if (this._miterLimit !== miterLimit)
  491. {
  492. this._miterLimit = miterLimit;
  493. this.styleID++;
  494. }
  495. }
  496. /**
  497. * Occasionally some fonts are cropped. Adding some padding will prevent this from happening
  498. * by adding padding to all sides of the text.
  499. *
  500. * @member {number}
  501. */
  502. get padding()
  503. {
  504. return this._padding;
  505. }
  506. set padding(padding) // eslint-disable-line require-jsdoc
  507. {
  508. if (this._padding !== padding)
  509. {
  510. this._padding = padding;
  511. this.styleID++;
  512. }
  513. }
  514. /**
  515. * A canvas fillstyle that will be used on the text stroke
  516. * e.g 'blue', '#FCFF00'
  517. *
  518. * @member {string|number}
  519. */
  520. get stroke()
  521. {
  522. return this._stroke;
  523. }
  524. set stroke(stroke) // eslint-disable-line require-jsdoc
  525. {
  526. const outputColor = getColor(stroke);
  527. if (this._stroke !== outputColor)
  528. {
  529. this._stroke = outputColor;
  530. this.styleID++;
  531. }
  532. }
  533. /**
  534. * A number that represents the thickness of the stroke.
  535. * Default is 0 (no stroke)
  536. *
  537. * @member {number}
  538. */
  539. get strokeThickness()
  540. {
  541. return this._strokeThickness;
  542. }
  543. set strokeThickness(strokeThickness) // eslint-disable-line require-jsdoc
  544. {
  545. if (this._strokeThickness !== strokeThickness)
  546. {
  547. this._strokeThickness = strokeThickness;
  548. this.styleID++;
  549. }
  550. }
  551. /**
  552. * The baseline of the text that is rendered.
  553. *
  554. * @member {string}
  555. */
  556. get textBaseline()
  557. {
  558. return this._textBaseline;
  559. }
  560. set textBaseline(textBaseline) // eslint-disable-line require-jsdoc
  561. {
  562. if (this._textBaseline !== textBaseline)
  563. {
  564. this._textBaseline = textBaseline;
  565. this.styleID++;
  566. }
  567. }
  568. /**
  569. * Trim transparent borders
  570. *
  571. * @member {boolean}
  572. */
  573. get trim()
  574. {
  575. return this._trim;
  576. }
  577. set trim(trim) // eslint-disable-line require-jsdoc
  578. {
  579. if (this._trim !== trim)
  580. {
  581. this._trim = trim;
  582. this.styleID++;
  583. }
  584. }
  585. /**
  586. * How newlines and spaces should be handled.
  587. * Default is 'pre' (preserve, preserve).
  588. *
  589. * value | New lines | Spaces
  590. * --- | --- | ---
  591. * 'normal' | Collapse | Collapse
  592. * 'pre' | Preserve | Preserve
  593. * 'pre-line' | Preserve | Collapse
  594. *
  595. * @member {string}
  596. */
  597. get whiteSpace()
  598. {
  599. return this._whiteSpace;
  600. }
  601. set whiteSpace(whiteSpace) // eslint-disable-line require-jsdoc
  602. {
  603. if (this._whiteSpace !== whiteSpace)
  604. {
  605. this._whiteSpace = whiteSpace;
  606. this.styleID++;
  607. }
  608. }
  609. /**
  610. * Indicates if word wrap should be used
  611. *
  612. * @member {boolean}
  613. */
  614. get wordWrap()
  615. {
  616. return this._wordWrap;
  617. }
  618. set wordWrap(wordWrap) // eslint-disable-line require-jsdoc
  619. {
  620. if (this._wordWrap !== wordWrap)
  621. {
  622. this._wordWrap = wordWrap;
  623. this.styleID++;
  624. }
  625. }
  626. /**
  627. * The width at which text will wrap, it needs wordWrap to be set to true
  628. *
  629. * @member {number}
  630. */
  631. get wordWrapWidth()
  632. {
  633. return this._wordWrapWidth;
  634. }
  635. set wordWrapWidth(wordWrapWidth) // eslint-disable-line require-jsdoc
  636. {
  637. if (this._wordWrapWidth !== wordWrapWidth)
  638. {
  639. this._wordWrapWidth = wordWrapWidth;
  640. this.styleID++;
  641. }
  642. }
  643. /**
  644. * Generates a font style string to use for `TextMetrics.measureFont()`.
  645. *
  646. * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
  647. */
  648. toFontString()
  649. {
  650. // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
  651. const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
  652. // Clean-up fontFamily property by quoting each font name
  653. // this will support font names with spaces
  654. let fontFamilies = this.fontFamily;
  655. if (!Array.isArray(this.fontFamily))
  656. {
  657. fontFamilies = this.fontFamily.split(',');
  658. }
  659. for (let i = fontFamilies.length - 1; i >= 0; i--)
  660. {
  661. // Trim any extra white-space
  662. let fontFamily = fontFamilies[i].trim();
  663. // Check if font is already escaped in quotes except for CSS generic fonts
  664. if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily) && genericFontFamilies.indexOf(fontFamily) < 0)
  665. {
  666. fontFamily = `"${fontFamily}"`;
  667. }
  668. fontFamilies[i] = fontFamily;
  669. }
  670. return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
  671. }
  672. }
  673. /**
  674. * Utility function to convert hexadecimal colors to strings, and simply return the color if it's a string.
  675. * @private
  676. * @param {number|number[]} color
  677. * @return {string} The color as a string.
  678. */
  679. function getSingleColor(color)
  680. {
  681. if (typeof color === 'number')
  682. {
  683. return hex2string(color);
  684. }
  685. else if ( typeof color === 'string' )
  686. {
  687. if ( color.indexOf('0x') === 0 )
  688. {
  689. color = color.replace('0x', '#');
  690. }
  691. }
  692. return color;
  693. }
  694. /**
  695. * Utility function to convert hexadecimal colors to strings, and simply return the color if it's a string.
  696. * This version can also convert array of colors
  697. * @private
  698. * @param {number|number[]} color
  699. * @return {string} The color as a string.
  700. */
  701. function getColor(color)
  702. {
  703. if (!Array.isArray(color))
  704. {
  705. return getSingleColor(color);
  706. }
  707. else
  708. {
  709. for (let i = 0; i < color.length; ++i)
  710. {
  711. color[i] = getSingleColor(color[i]);
  712. }
  713. return color;
  714. }
  715. }
  716. /**
  717. * Utility function to convert hexadecimal colors to strings, and simply return the color if it's a string.
  718. * This version can also convert array of colors
  719. * @private
  720. * @param {Array} array1 First array to compare
  721. * @param {Array} array2 Second array to compare
  722. * @return {boolean} Do the arrays contain the same values in the same order
  723. */
  724. function areArraysEqual(array1, array2)
  725. {
  726. if (!Array.isArray(array1) || !Array.isArray(array2))
  727. {
  728. return false;
  729. }
  730. if (array1.length !== array2.length)
  731. {
  732. return false;
  733. }
  734. for (let i = 0; i < array1.length; ++i)
  735. {
  736. if (array1[i] !== array2[i])
  737. {
  738. return false;
  739. }
  740. }
  741. return true;
  742. }
  743. /**
  744. * Utility function to ensure that object properties are copied by value, and not by reference
  745. * @private
  746. * @param {Object} target Target object to copy properties into
  747. * @param {Object} source Source object for the proporties to copy
  748. * @param {string} propertyObj Object containing properties names we want to loop over
  749. */
  750. function deepCopyProperties(target, source, propertyObj) {
  751. for (const prop in propertyObj) {
  752. if (Array.isArray(source[prop])) {
  753. target[prop] = source[prop].slice();
  754. } else {
  755. target[prop] = source[prop];
  756. }
  757. }
  758. }