@dangl/angular-material-shared 2.9.0-beta0037 → 2.9.0-beta0040

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/fesm2022/dangl-angular-material-shared-tiny-mce.mjs +29 -35
  2. package/fesm2022/dangl-angular-material-shared-tiny-mce.mjs.map +1 -1
  3. package/fesm2022/dangl-angular-material-shared.mjs +36 -51
  4. package/fesm2022/dangl-angular-material-shared.mjs.map +1 -1
  5. package/package.json +11 -10
  6. package/styles/material-theme.scss +1 -1
  7. package/styles/material-variables.scss +5 -5
  8. package/tinymce-assets/icons/default/icons.js +46 -9
  9. package/tinymce-assets/icons/default/icons.min.js +1 -1
  10. package/tinymce-assets/models/dom/model.js +8198 -7244
  11. package/tinymce-assets/models/dom/model.min.js +1 -4
  12. package/tinymce-assets/plugins/accordion/plugin.js +1174 -879
  13. package/tinymce-assets/plugins/accordion/plugin.min.js +1 -4
  14. package/tinymce-assets/plugins/advlist/plugin.js +412 -200
  15. package/tinymce-assets/plugins/advlist/plugin.min.js +1 -4
  16. package/tinymce-assets/plugins/anchor/plugin.js +189 -166
  17. package/tinymce-assets/plugins/anchor/plugin.min.js +1 -4
  18. package/tinymce-assets/plugins/autolink/plugin.js +310 -220
  19. package/tinymce-assets/plugins/autolink/plugin.min.js +1 -4
  20. package/tinymce-assets/plugins/autoresize/plugin.js +180 -149
  21. package/tinymce-assets/plugins/autoresize/plugin.min.js +1 -4
  22. package/tinymce-assets/plugins/autosave/plugin.js +208 -189
  23. package/tinymce-assets/plugins/autosave/plugin.min.js +1 -4
  24. package/tinymce-assets/plugins/charmap/plugin.js +905 -1566
  25. package/tinymce-assets/plugins/charmap/plugin.min.js +1 -4
  26. package/tinymce-assets/plugins/code/plugin.js +73 -61
  27. package/tinymce-assets/plugins/code/plugin.min.js +0 -3
  28. package/tinymce-assets/plugins/codesample/plugin.js +3593 -2402
  29. package/tinymce-assets/plugins/codesample/plugin.min.js +8 -3
  30. package/tinymce-assets/plugins/directionality/plugin.js +545 -304
  31. package/tinymce-assets/plugins/directionality/plugin.min.js +1 -4
  32. package/tinymce-assets/plugins/emoticons/js/emojiimages.min.js +0 -2
  33. package/tinymce-assets/plugins/emoticons/js/emojis.min.js +0 -1
  34. package/tinymce-assets/plugins/emoticons/plugin.js +702 -488
  35. package/tinymce-assets/plugins/emoticons/plugin.min.js +1 -4
  36. package/tinymce-assets/plugins/fullscreen/plugin.js +1409 -1049
  37. package/tinymce-assets/plugins/fullscreen/plugin.min.js +1 -4
  38. package/tinymce-assets/plugins/help/js/i18n/keynav/ar.js +4 -1
  39. package/tinymce-assets/plugins/help/js/i18n/keynav/bg_BG.js +4 -1
  40. package/tinymce-assets/plugins/help/js/i18n/keynav/ca.js +4 -1
  41. package/tinymce-assets/plugins/help/js/i18n/keynav/cs.js +4 -1
  42. package/tinymce-assets/plugins/help/js/i18n/keynav/da.js +4 -1
  43. package/tinymce-assets/plugins/help/js/i18n/keynav/de.js +4 -1
  44. package/tinymce-assets/plugins/help/js/i18n/keynav/el.js +4 -1
  45. package/tinymce-assets/plugins/help/js/i18n/keynav/en.js +4 -1
  46. package/tinymce-assets/plugins/help/js/i18n/keynav/es.js +4 -1
  47. package/tinymce-assets/plugins/help/js/i18n/keynav/eu.js +4 -1
  48. package/tinymce-assets/plugins/help/js/i18n/keynav/fa.js +4 -1
  49. package/tinymce-assets/plugins/help/js/i18n/keynav/fi.js +4 -1
  50. package/tinymce-assets/plugins/help/js/i18n/keynav/fr_FR.js +4 -1
  51. package/tinymce-assets/plugins/help/js/i18n/keynav/he_IL.js +4 -1
  52. package/tinymce-assets/plugins/help/js/i18n/keynav/hi.js +4 -1
  53. package/tinymce-assets/plugins/help/js/i18n/keynav/hr.js +4 -1
  54. package/tinymce-assets/plugins/help/js/i18n/keynav/hu_HU.js +4 -1
  55. package/tinymce-assets/plugins/help/js/i18n/keynav/id.js +4 -1
  56. package/tinymce-assets/plugins/help/js/i18n/keynav/it.js +4 -1
  57. package/tinymce-assets/plugins/help/js/i18n/keynav/ja.js +4 -1
  58. package/tinymce-assets/plugins/help/js/i18n/keynav/kk.js +4 -1
  59. package/tinymce-assets/plugins/help/js/i18n/keynav/ko_KR.js +4 -1
  60. package/tinymce-assets/plugins/help/js/i18n/keynav/ms.js +4 -1
  61. package/tinymce-assets/plugins/help/js/i18n/keynav/nb_NO.js +4 -1
  62. package/tinymce-assets/plugins/help/js/i18n/keynav/nl.js +4 -1
  63. package/tinymce-assets/plugins/help/js/i18n/keynav/pl.js +4 -1
  64. package/tinymce-assets/plugins/help/js/i18n/keynav/pt_BR.js +4 -1
  65. package/tinymce-assets/plugins/help/js/i18n/keynav/pt_PT.js +4 -1
  66. package/tinymce-assets/plugins/help/js/i18n/keynav/ro.js +4 -1
  67. package/tinymce-assets/plugins/help/js/i18n/keynav/ru.js +4 -1
  68. package/tinymce-assets/plugins/help/js/i18n/keynav/sk.js +4 -1
  69. package/tinymce-assets/plugins/help/js/i18n/keynav/sl_SI.js +5 -2
  70. package/tinymce-assets/plugins/help/js/i18n/keynav/sv_SE.js +4 -1
  71. package/tinymce-assets/plugins/help/js/i18n/keynav/th_TH.js +4 -1
  72. package/tinymce-assets/plugins/help/js/i18n/keynav/tr.js +4 -1
  73. package/tinymce-assets/plugins/help/js/i18n/keynav/uk.js +4 -1
  74. package/tinymce-assets/plugins/help/js/i18n/keynav/vi.js +4 -1
  75. package/tinymce-assets/plugins/help/js/i18n/keynav/zh_CN.js +8 -5
  76. package/tinymce-assets/plugins/help/js/i18n/keynav/zh_TW.js +4 -1
  77. package/tinymce-assets/plugins/help/plugin.js +739 -809
  78. package/tinymce-assets/plugins/help/plugin.min.js +1 -4
  79. package/tinymce-assets/plugins/image/plugin.js +1475 -1291
  80. package/tinymce-assets/plugins/image/plugin.min.js +1 -4
  81. package/tinymce-assets/plugins/importcss/plugin.js +328 -269
  82. package/tinymce-assets/plugins/importcss/plugin.min.js +1 -4
  83. package/tinymce-assets/plugins/insertdatetime/plugin.js +140 -141
  84. package/tinymce-assets/plugins/insertdatetime/plugin.min.js +1 -4
  85. package/tinymce-assets/plugins/link/plugin.js +1419 -1084
  86. package/tinymce-assets/plugins/link/plugin.min.js +1 -4
  87. package/tinymce-assets/plugins/lists/plugin.js +2177 -1805
  88. package/tinymce-assets/plugins/lists/plugin.min.js +1 -4
  89. package/tinymce-assets/plugins/media/plugin.js +1305 -1047
  90. package/tinymce-assets/plugins/media/plugin.min.js +1 -4
  91. package/tinymce-assets/plugins/nonbreaking/plugin.js +93 -88
  92. package/tinymce-assets/plugins/nonbreaking/plugin.min.js +0 -3
  93. package/tinymce-assets/plugins/pagebreak/plugin.js +91 -85
  94. package/tinymce-assets/plugins/pagebreak/plugin.min.js +0 -3
  95. package/tinymce-assets/plugins/preview/plugin.js +742 -74
  96. package/tinymce-assets/plugins/preview/plugin.min.js +1 -4
  97. package/tinymce-assets/plugins/quickbars/plugin.js +582 -375
  98. package/tinymce-assets/plugins/quickbars/plugin.min.js +1 -4
  99. package/tinymce-assets/plugins/save/plugin.js +100 -82
  100. package/tinymce-assets/plugins/save/plugin.min.js +1 -4
  101. package/tinymce-assets/plugins/searchreplace/plugin.js +1183 -906
  102. package/tinymce-assets/plugins/searchreplace/plugin.min.js +1 -4
  103. package/tinymce-assets/plugins/table/plugin.js +3651 -3094
  104. package/tinymce-assets/plugins/table/plugin.min.js +1 -4
  105. package/tinymce-assets/plugins/visualblocks/plugin.js +71 -63
  106. package/tinymce-assets/plugins/visualblocks/plugin.min.js +1 -4
  107. package/tinymce-assets/plugins/visualchars/plugin.js +685 -436
  108. package/tinymce-assets/plugins/visualchars/plugin.min.js +1 -4
  109. package/tinymce-assets/plugins/wordcount/plugin.js +382 -307
  110. package/tinymce-assets/plugins/wordcount/plugin.min.js +1 -4
  111. package/tinymce-assets/skins/content/dark/content.css +9 -0
  112. package/tinymce-assets/skins/content/dark/content.js +10 -2
  113. package/tinymce-assets/skins/content/dark/content.min.css +9 -0
  114. package/tinymce-assets/skins/content/default/content.css +9 -0
  115. package/tinymce-assets/skins/content/default/content.js +10 -2
  116. package/tinymce-assets/skins/content/default/content.min.css +9 -0
  117. package/tinymce-assets/skins/content/document/content.css +9 -0
  118. package/tinymce-assets/skins/content/document/content.js +10 -2
  119. package/tinymce-assets/skins/content/document/content.min.css +9 -0
  120. package/tinymce-assets/skins/content/tinymce-5/content.css +9 -0
  121. package/tinymce-assets/skins/content/tinymce-5/content.js +10 -2
  122. package/tinymce-assets/skins/content/tinymce-5/content.min.css +9 -0
  123. package/tinymce-assets/skins/content/tinymce-5-dark/content.css +9 -0
  124. package/tinymce-assets/skins/content/tinymce-5-dark/content.js +10 -2
  125. package/tinymce-assets/skins/content/tinymce-5-dark/content.min.css +9 -0
  126. package/tinymce-assets/skins/content/writer/content.css +9 -0
  127. package/tinymce-assets/skins/content/writer/content.js +10 -2
  128. package/tinymce-assets/skins/content/writer/content.min.css +9 -0
  129. package/tinymce-assets/skins/ui/oxide/content.css +157 -49
  130. package/tinymce-assets/skins/ui/oxide/content.inline.css +157 -49
  131. package/tinymce-assets/skins/ui/oxide/content.inline.js +10 -2
  132. package/tinymce-assets/skins/ui/oxide/content.inline.min.css +10 -1
  133. package/tinymce-assets/skins/ui/oxide/content.js +10 -2
  134. package/tinymce-assets/skins/ui/oxide/content.min.css +10 -1
  135. package/tinymce-assets/skins/ui/oxide/skin.css +1592 -214
  136. package/tinymce-assets/skins/ui/oxide/skin.js +1 -2
  137. package/tinymce-assets/skins/ui/oxide/skin.min.css +1 -1
  138. package/tinymce-assets/skins/ui/oxide/skin.shadowdom.js +1 -2
  139. package/tinymce-assets/skins/ui/oxide-dark/content.css +157 -42
  140. package/tinymce-assets/skins/ui/oxide-dark/content.inline.css +157 -49
  141. package/tinymce-assets/skins/ui/oxide-dark/content.inline.js +10 -2
  142. package/tinymce-assets/skins/ui/oxide-dark/content.inline.min.css +10 -1
  143. package/tinymce-assets/skins/ui/oxide-dark/content.js +10 -2
  144. package/tinymce-assets/skins/ui/oxide-dark/content.min.css +10 -1
  145. package/tinymce-assets/skins/ui/oxide-dark/skin.css +1595 -217
  146. package/tinymce-assets/skins/ui/oxide-dark/skin.js +1 -2
  147. package/tinymce-assets/skins/ui/oxide-dark/skin.min.css +1 -1
  148. package/tinymce-assets/skins/ui/oxide-dark/skin.shadowdom.js +1 -2
  149. package/tinymce-assets/skins/ui/tinymce-5/content.css +157 -49
  150. package/tinymce-assets/skins/ui/tinymce-5/content.inline.css +157 -49
  151. package/tinymce-assets/skins/ui/tinymce-5/content.inline.js +10 -2
  152. package/tinymce-assets/skins/ui/tinymce-5/content.inline.min.css +10 -1
  153. package/tinymce-assets/skins/ui/tinymce-5/content.js +10 -2
  154. package/tinymce-assets/skins/ui/tinymce-5/content.min.css +10 -1
  155. package/tinymce-assets/skins/ui/tinymce-5/skin.css +1606 -203
  156. package/tinymce-assets/skins/ui/tinymce-5/skin.js +1 -2
  157. package/tinymce-assets/skins/ui/tinymce-5/skin.min.css +1 -1
  158. package/tinymce-assets/skins/ui/tinymce-5/skin.shadowdom.js +1 -2
  159. package/tinymce-assets/skins/ui/tinymce-5-dark/content.css +157 -42
  160. package/tinymce-assets/skins/ui/tinymce-5-dark/content.inline.css +157 -49
  161. package/tinymce-assets/skins/ui/tinymce-5-dark/content.inline.js +10 -2
  162. package/tinymce-assets/skins/ui/tinymce-5-dark/content.inline.min.css +10 -1
  163. package/tinymce-assets/skins/ui/tinymce-5-dark/content.js +10 -2
  164. package/tinymce-assets/skins/ui/tinymce-5-dark/content.min.css +10 -1
  165. package/tinymce-assets/skins/ui/tinymce-5-dark/skin.css +1607 -204
  166. package/tinymce-assets/skins/ui/tinymce-5-dark/skin.js +1 -2
  167. package/tinymce-assets/skins/ui/tinymce-5-dark/skin.min.css +1 -1
  168. package/tinymce-assets/skins/ui/tinymce-5-dark/skin.shadowdom.js +1 -2
  169. package/tinymce-assets/themes/silver/theme.js +32786 -28738
  170. package/tinymce-assets/themes/silver/theme.min.js +1 -4
  171. package/types/dangl-angular-material-shared-tiny-mce.d.ts +12 -13
  172. package/types/dangl-angular-material-shared.d.ts +26 -26
  173. package/tinymce-assets/plugins/template/index.js +0 -7
  174. package/tinymce-assets/plugins/template/plugin.js +0 -567
  175. package/tinymce-assets/plugins/template/plugin.min.js +0 -4
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TinyMCE version 6.8.3 (2024-02-08)
2
+ * TinyMCE version 7.9.3 (2026-05-19)
3
3
  */
4
4
 
5
5
  (function () {
@@ -7,278 +7,548 @@
7
7
 
8
8
  var global$5 = tinymce.util.Tools.resolve('tinymce.PluginManager');
9
9
 
10
+ /* eslint-disable @typescript-eslint/no-wrapper-object-types */
10
11
  const hasProto = (v, constructor, predicate) => {
11
- var _a;
12
- if (predicate(v, constructor.prototype)) {
13
- return true;
14
- } else {
15
- return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
16
- }
17
- };
18
- const typeOf = x => {
19
- const t = typeof x;
20
- if (x === null) {
21
- return 'null';
22
- } else if (t === 'object' && Array.isArray(x)) {
23
- return 'array';
24
- } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
25
- return 'string';
26
- } else {
27
- return t;
28
- }
12
+ var _a;
13
+ if (predicate(v, constructor.prototype)) {
14
+ return true;
15
+ }
16
+ else {
17
+ // String-based fallback time
18
+ return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
19
+ }
20
+ };
21
+ const typeOf = (x) => {
22
+ const t = typeof x;
23
+ if (x === null) {
24
+ return 'null';
25
+ }
26
+ else if (t === 'object' && Array.isArray(x)) {
27
+ return 'array';
28
+ }
29
+ else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
30
+ return 'string';
31
+ }
32
+ else {
33
+ return t;
34
+ }
29
35
  };
30
- const isType = type => value => typeOf(value) === type;
31
- const isSimpleType = type => value => typeof value === type;
32
- const eq = t => a => t === a;
36
+ const isType = (type) => (value) => typeOf(value) === type;
37
+ const isSimpleType = (type) => (value) => typeof value === type;
38
+ const eq = (t) => (a) => t === a;
33
39
  const isString = isType('string');
34
40
  const isObject = isType('object');
35
41
  const isArray = isType('array');
36
42
  const isNull = eq(null);
37
43
  const isBoolean = isSimpleType('boolean');
38
- const isNullable = a => a === null || a === undefined;
39
- const isNonNullable = a => !isNullable(a);
44
+ const isNullable = (a) => a === null || a === undefined;
45
+ const isNonNullable = (a) => !isNullable(a);
40
46
  const isFunction = isSimpleType('function');
41
47
  const isArrayOf = (value, pred) => {
42
- if (isArray(value)) {
43
- for (let i = 0, len = value.length; i < len; ++i) {
44
- if (!pred(value[i])) {
45
- return false;
46
- }
48
+ if (isArray(value)) {
49
+ for (let i = 0, len = value.length; i < len; ++i) {
50
+ if (!(pred(value[i]))) {
51
+ return false;
52
+ }
53
+ }
54
+ return true;
47
55
  }
48
- return true;
49
- }
50
- return false;
56
+ return false;
51
57
  };
52
58
 
53
- const noop = () => {
54
- };
55
- const constant = value => {
56
- return () => {
57
- return value;
58
- };
59
+ const noop = () => { };
60
+ const constant = (value) => {
61
+ return () => {
62
+ return value;
63
+ };
59
64
  };
60
65
  const tripleEquals = (a, b) => {
61
- return a === b;
66
+ return a === b;
62
67
  };
63
68
 
69
+ /**
70
+ * The `Optional` type represents a value (of any type) that potentially does
71
+ * not exist. Any `Optional<T>` can either be a `Some<T>` (in which case the
72
+ * value does exist) or a `None` (in which case the value does not exist). This
73
+ * module defines a whole lot of FP-inspired utility functions for dealing with
74
+ * `Optional` objects.
75
+ *
76
+ * Comparison with null or undefined:
77
+ * - We don't get fancy null coalescing operators with `Optional`
78
+ * - We do get fancy helper functions with `Optional`
79
+ * - `Optional` support nesting, and allow for the type to still be nullable (or
80
+ * another `Optional`)
81
+ * - There is no option to turn off strict-optional-checks like there is for
82
+ * strict-null-checks
83
+ */
64
84
  class Optional {
65
- constructor(tag, value) {
66
- this.tag = tag;
67
- this.value = value;
68
- }
69
- static some(value) {
70
- return new Optional(true, value);
71
- }
72
- static none() {
73
- return Optional.singletonNone;
74
- }
75
- fold(onNone, onSome) {
76
- if (this.tag) {
77
- return onSome(this.value);
78
- } else {
79
- return onNone();
80
- }
81
- }
82
- isSome() {
83
- return this.tag;
84
- }
85
- isNone() {
86
- return !this.tag;
87
- }
88
- map(mapper) {
89
- if (this.tag) {
90
- return Optional.some(mapper(this.value));
91
- } else {
92
- return Optional.none();
93
- }
94
- }
95
- bind(binder) {
96
- if (this.tag) {
97
- return binder(this.value);
98
- } else {
99
- return Optional.none();
100
- }
101
- }
102
- exists(predicate) {
103
- return this.tag && predicate(this.value);
104
- }
105
- forall(predicate) {
106
- return !this.tag || predicate(this.value);
107
- }
108
- filter(predicate) {
109
- if (!this.tag || predicate(this.value)) {
110
- return this;
111
- } else {
112
- return Optional.none();
113
- }
114
- }
115
- getOr(replacement) {
116
- return this.tag ? this.value : replacement;
117
- }
118
- or(replacement) {
119
- return this.tag ? this : replacement;
120
- }
121
- getOrThunk(thunk) {
122
- return this.tag ? this.value : thunk();
123
- }
124
- orThunk(thunk) {
125
- return this.tag ? this : thunk();
126
- }
127
- getOrDie(message) {
128
- if (!this.tag) {
129
- throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
130
- } else {
131
- return this.value;
132
- }
133
- }
134
- static from(value) {
135
- return isNonNullable(value) ? Optional.some(value) : Optional.none();
136
- }
137
- getOrNull() {
138
- return this.tag ? this.value : null;
139
- }
140
- getOrUndefined() {
141
- return this.value;
142
- }
143
- each(worker) {
144
- if (this.tag) {
145
- worker(this.value);
146
- }
147
- }
148
- toArray() {
149
- return this.tag ? [this.value] : [];
150
- }
151
- toString() {
152
- return this.tag ? `some(${ this.value })` : 'none()';
153
- }
85
+ // The internal representation has a `tag` and a `value`, but both are
86
+ // private: able to be console.logged, but not able to be accessed by code
87
+ constructor(tag, value) {
88
+ this.tag = tag;
89
+ this.value = value;
90
+ }
91
+ // --- Identities ---
92
+ /**
93
+ * Creates a new `Optional<T>` that **does** contain a value.
94
+ */
95
+ static some(value) {
96
+ return new Optional(true, value);
97
+ }
98
+ /**
99
+ * Create a new `Optional<T>` that **does not** contain a value. `T` can be
100
+ * any type because we don't actually have a `T`.
101
+ */
102
+ static none() {
103
+ return Optional.singletonNone;
104
+ }
105
+ /**
106
+ * Perform a transform on an `Optional` type. Regardless of whether this
107
+ * `Optional` contains a value or not, `fold` will return a value of type `U`.
108
+ * If this `Optional` does not contain a value, the `U` will be created by
109
+ * calling `onNone`. If this `Optional` does contain a value, the `U` will be
110
+ * created by calling `onSome`.
111
+ *
112
+ * For the FP enthusiasts in the room, this function:
113
+ * 1. Could be used to implement all of the functions below
114
+ * 2. Forms a catamorphism
115
+ */
116
+ fold(onNone, onSome) {
117
+ if (this.tag) {
118
+ return onSome(this.value);
119
+ }
120
+ else {
121
+ return onNone();
122
+ }
123
+ }
124
+ /**
125
+ * Determine if this `Optional` object contains a value.
126
+ */
127
+ isSome() {
128
+ return this.tag;
129
+ }
130
+ /**
131
+ * Determine if this `Optional` object **does not** contain a value.
132
+ */
133
+ isNone() {
134
+ return !this.tag;
135
+ }
136
+ // --- Functor (name stolen from Haskell / maths) ---
137
+ /**
138
+ * Perform a transform on an `Optional` object, **if** there is a value. If
139
+ * you provide a function to turn a T into a U, this is the function you use
140
+ * to turn an `Optional<T>` into an `Optional<U>`. If this **does** contain
141
+ * a value then the output will also contain a value (that value being the
142
+ * output of `mapper(this.value)`), and if this **does not** contain a value
143
+ * then neither will the output.
144
+ */
145
+ map(mapper) {
146
+ if (this.tag) {
147
+ return Optional.some(mapper(this.value));
148
+ }
149
+ else {
150
+ return Optional.none();
151
+ }
152
+ }
153
+ // --- Monad (name stolen from Haskell / maths) ---
154
+ /**
155
+ * Perform a transform on an `Optional` object, **if** there is a value.
156
+ * Unlike `map`, here the transform itself also returns an `Optional`.
157
+ */
158
+ bind(binder) {
159
+ if (this.tag) {
160
+ return binder(this.value);
161
+ }
162
+ else {
163
+ return Optional.none();
164
+ }
165
+ }
166
+ // --- Traversable (name stolen from Haskell / maths) ---
167
+ /**
168
+ * For a given predicate, this function finds out if there **exists** a value
169
+ * inside this `Optional` object that meets the predicate. In practice, this
170
+ * means that for `Optional`s that do not contain a value it returns false (as
171
+ * no predicate-meeting value exists).
172
+ */
173
+ exists(predicate) {
174
+ return this.tag && predicate(this.value);
175
+ }
176
+ /**
177
+ * For a given predicate, this function finds out if **all** the values inside
178
+ * this `Optional` object meet the predicate. In practice, this means that
179
+ * for `Optional`s that do not contain a value it returns true (as all 0
180
+ * objects do meet the predicate).
181
+ */
182
+ forall(predicate) {
183
+ return !this.tag || predicate(this.value);
184
+ }
185
+ filter(predicate) {
186
+ if (!this.tag || predicate(this.value)) {
187
+ return this;
188
+ }
189
+ else {
190
+ return Optional.none();
191
+ }
192
+ }
193
+ // --- Getters ---
194
+ /**
195
+ * Get the value out of the inside of the `Optional` object, using a default
196
+ * `replacement` value if the provided `Optional` object does not contain a
197
+ * value.
198
+ */
199
+ getOr(replacement) {
200
+ return this.tag ? this.value : replacement;
201
+ }
202
+ /**
203
+ * Get the value out of the inside of the `Optional` object, using a default
204
+ * `replacement` value if the provided `Optional` object does not contain a
205
+ * value. Unlike `getOr`, in this method the `replacement` object is also
206
+ * `Optional` - meaning that this method will always return an `Optional`.
207
+ */
208
+ or(replacement) {
209
+ return this.tag ? this : replacement;
210
+ }
211
+ /**
212
+ * Get the value out of the inside of the `Optional` object, using a default
213
+ * `replacement` value if the provided `Optional` object does not contain a
214
+ * value. Unlike `getOr`, in this method the `replacement` value is
215
+ * "thunked" - that is to say that you don't pass a value to `getOrThunk`, you
216
+ * pass a function which (if called) will **return** the `value` you want to
217
+ * use.
218
+ */
219
+ getOrThunk(thunk) {
220
+ return this.tag ? this.value : thunk();
221
+ }
222
+ /**
223
+ * Get the value out of the inside of the `Optional` object, using a default
224
+ * `replacement` value if the provided Optional object does not contain a
225
+ * value.
226
+ *
227
+ * Unlike `or`, in this method the `replacement` value is "thunked" - that is
228
+ * to say that you don't pass a value to `orThunk`, you pass a function which
229
+ * (if called) will **return** the `value` you want to use.
230
+ *
231
+ * Unlike `getOrThunk`, in this method the `replacement` value is also
232
+ * `Optional`, meaning that this method will always return an `Optional`.
233
+ */
234
+ orThunk(thunk) {
235
+ return this.tag ? this : thunk();
236
+ }
237
+ /**
238
+ * Get the value out of the inside of the `Optional` object, throwing an
239
+ * exception if the provided `Optional` object does not contain a value.
240
+ *
241
+ * WARNING:
242
+ * You should only be using this function if you know that the `Optional`
243
+ * object **is not** empty (otherwise you're throwing exceptions in production
244
+ * code, which is bad).
245
+ *
246
+ * In tests this is more acceptable.
247
+ *
248
+ * Prefer other methods to this, such as `.each`.
249
+ */
250
+ getOrDie(message) {
251
+ if (!this.tag) {
252
+ throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
253
+ }
254
+ else {
255
+ return this.value;
256
+ }
257
+ }
258
+ // --- Interop with null and undefined ---
259
+ /**
260
+ * Creates an `Optional` value from a nullable (or undefined-able) input.
261
+ * Null, or undefined, is converted to `None`, and anything else is converted
262
+ * to `Some`.
263
+ */
264
+ static from(value) {
265
+ return isNonNullable(value) ? Optional.some(value) : Optional.none();
266
+ }
267
+ /**
268
+ * Converts an `Optional` to a nullable type, by getting the value if it
269
+ * exists, or returning `null` if it does not.
270
+ */
271
+ getOrNull() {
272
+ return this.tag ? this.value : null;
273
+ }
274
+ /**
275
+ * Converts an `Optional` to an undefined-able type, by getting the value if
276
+ * it exists, or returning `undefined` if it does not.
277
+ */
278
+ getOrUndefined() {
279
+ return this.value;
280
+ }
281
+ // --- Utilities ---
282
+ /**
283
+ * If the `Optional` contains a value, perform an action on that value.
284
+ * Unlike the rest of the methods on this type, `.each` has side-effects. If
285
+ * you want to transform an `Optional<T>` **into** something, then this is not
286
+ * the method for you. If you want to use an `Optional<T>` to **do**
287
+ * something, then this is the method for you - provided you're okay with not
288
+ * doing anything in the case where the `Optional` doesn't have a value inside
289
+ * it. If you're not sure whether your use-case fits into transforming
290
+ * **into** something or **doing** something, check whether it has a return
291
+ * value. If it does, you should be performing a transform.
292
+ */
293
+ each(worker) {
294
+ if (this.tag) {
295
+ worker(this.value);
296
+ }
297
+ }
298
+ /**
299
+ * Turn the `Optional` object into an array that contains all of the values
300
+ * stored inside the `Optional`. In practice, this means the output will have
301
+ * either 0 or 1 elements.
302
+ */
303
+ toArray() {
304
+ return this.tag ? [this.value] : [];
305
+ }
306
+ /**
307
+ * Turn the `Optional` object into a string for debugging or printing. Not
308
+ * recommended for production code, but good for debugging. Also note that
309
+ * these days an `Optional` object can be logged to the console directly, and
310
+ * its inner value (if it exists) will be visible.
311
+ */
312
+ toString() {
313
+ return this.tag ? `some(${this.value})` : 'none()';
314
+ }
154
315
  }
316
+ // Sneaky optimisation: every instance of Optional.none is identical, so just
317
+ // reuse the same object
155
318
  Optional.singletonNone = new Optional(false);
156
319
 
320
+ /* eslint-disable @typescript-eslint/unbound-method */
321
+ const nativeSlice = Array.prototype.slice;
157
322
  const nativeIndexOf = Array.prototype.indexOf;
158
323
  const nativePush = Array.prototype.push;
324
+ /* eslint-enable */
159
325
  const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
160
326
  const contains = (xs, x) => rawIndexOf(xs, x) > -1;
161
327
  const map = (xs, f) => {
162
- const len = xs.length;
163
- const r = new Array(len);
164
- for (let i = 0; i < len; i++) {
165
- const x = xs[i];
166
- r[i] = f(x, i);
167
- }
168
- return r;
328
+ // pre-allocating array size when it's guaranteed to be known
329
+ // http://jsperf.com/push-allocated-vs-dynamic/22
330
+ const len = xs.length;
331
+ const r = new Array(len);
332
+ for (let i = 0; i < len; i++) {
333
+ const x = xs[i];
334
+ r[i] = f(x, i);
335
+ }
336
+ return r;
169
337
  };
338
+ // Unwound implementing other functions in terms of each.
339
+ // The code size is roughly the same, and it should allow for better optimisation.
340
+ // const each = function<T, U>(xs: T[], f: (x: T, i?: number, xs?: T[]) => void): void {
170
341
  const each$1 = (xs, f) => {
171
- for (let i = 0, len = xs.length; i < len; i++) {
172
- const x = xs[i];
173
- f(x, i);
174
- }
342
+ for (let i = 0, len = xs.length; i < len; i++) {
343
+ const x = xs[i];
344
+ f(x, i);
345
+ }
175
346
  };
176
347
  const foldl = (xs, f, acc) => {
177
- each$1(xs, (x, i) => {
178
- acc = f(acc, x, i);
179
- });
180
- return acc;
348
+ each$1(xs, (x, i) => {
349
+ acc = f(acc, x, i);
350
+ });
351
+ return acc;
181
352
  };
182
- const flatten = xs => {
183
- const r = [];
184
- for (let i = 0, len = xs.length; i < len; ++i) {
185
- if (!isArray(xs[i])) {
186
- throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
353
+ const flatten = (xs) => {
354
+ // Note, this is possible because push supports multiple arguments:
355
+ // http://jsperf.com/concat-push/6
356
+ // Note that in the past, concat() would silently work (very slowly) for array-like objects.
357
+ // With this change it will throw an error.
358
+ const r = [];
359
+ for (let i = 0, len = xs.length; i < len; ++i) {
360
+ // Ensure that each value is an array itself
361
+ if (!isArray(xs[i])) {
362
+ throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
363
+ }
364
+ nativePush.apply(r, xs[i]);
187
365
  }
188
- nativePush.apply(r, xs[i]);
189
- }
190
- return r;
366
+ return r;
191
367
  };
192
368
  const bind = (xs, f) => flatten(map(xs, f));
369
+ isFunction(Array.from) ? Array.from : (x) => nativeSlice.call(x);
193
370
  const findMap = (arr, f) => {
194
- for (let i = 0; i < arr.length; i++) {
195
- const r = f(arr[i], i);
196
- if (r.isSome()) {
197
- return r;
371
+ for (let i = 0; i < arr.length; i++) {
372
+ const r = f(arr[i], i);
373
+ if (r.isSome()) {
374
+ return r;
375
+ }
198
376
  }
199
- }
200
- return Optional.none();
377
+ return Optional.none();
378
+ };
379
+
380
+ // There are many variations of Object iteration that are faster than the 'for-in' style:
381
+ // http://jsperf.com/object-keys-iteration/107
382
+ //
383
+ // Use the native keys if it is available (IE9+), otherwise fall back to manually filtering
384
+ const keys = Object.keys;
385
+ // eslint-disable-next-line @typescript-eslint/unbound-method
386
+ const hasOwnProperty = Object.hasOwnProperty;
387
+ const each = (obj, f) => {
388
+ const props = keys(obj);
389
+ for (let k = 0, len = props.length; k < len; k++) {
390
+ const i = props[k];
391
+ const x = obj[i];
392
+ f(x, i);
393
+ }
394
+ };
395
+ const objAcc = (r) => (x, i) => {
396
+ r[i] = x;
201
397
  };
398
+ const internalFilter = (obj, pred, onTrue, onFalse) => {
399
+ each(obj, (x, i) => {
400
+ (pred(x, i) ? onTrue : onFalse)(x, i);
401
+ });
402
+ };
403
+ const filter = (obj, pred) => {
404
+ const t = {};
405
+ internalFilter(obj, pred, objAcc(t), noop);
406
+ return t;
407
+ };
408
+ const has = (obj, key) => hasOwnProperty.call(obj, key);
409
+ const hasNonNullableKey = (obj, key) => has(obj, key) && obj[key] !== undefined && obj[key] !== null;
202
410
 
203
- const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));
204
- const cat = arr => {
205
- const r = [];
206
- const push = x => {
207
- r.push(x);
208
- };
209
- for (let i = 0; i < arr.length; i++) {
210
- arr[i].each(push);
211
- }
212
- return r;
411
+ const Cell = (initial) => {
412
+ let value = initial;
413
+ const get = () => {
414
+ return value;
415
+ };
416
+ const set = (v) => {
417
+ value = v;
418
+ };
419
+ return {
420
+ get,
421
+ set
422
+ };
213
423
  };
424
+
425
+ /**
426
+ * **Is** the value stored inside this Optional object equal to `rhs`?
427
+ */
428
+ const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists((left) => comparator(left, rhs));
429
+ const cat = (arr) => {
430
+ const r = [];
431
+ const push = (x) => {
432
+ r.push(x);
433
+ };
434
+ for (let i = 0; i < arr.length; i++) {
435
+ arr[i].each(push);
436
+ }
437
+ return r;
438
+ };
439
+ // This can help with type inference, by specifying the type param on the none case, so the caller doesn't have to.
214
440
  const someIf = (b, a) => b ? Optional.some(a) : Optional.none();
215
441
 
216
- const option = name => editor => editor.options.get(name);
217
- const register$1 = editor => {
218
- const registerOption = editor.options.register;
219
- registerOption('link_assume_external_targets', {
220
- processor: value => {
221
- const valid = isString(value) || isBoolean(value);
222
- if (valid) {
223
- if (value === true) {
224
- return {
225
- value: 1,
226
- valid
227
- };
228
- } else if (value === 'http' || value === 'https') {
229
- return {
230
- value,
231
- valid
232
- };
233
- } else {
234
- return {
235
- value: 0,
236
- valid
237
- };
238
- }
239
- } else {
240
- return {
241
- valid: false,
242
- message: 'Must be a string or a boolean.'
243
- };
244
- }
245
- },
246
- default: false
247
- });
248
- registerOption('link_context_toolbar', {
249
- processor: 'boolean',
250
- default: false
251
- });
252
- registerOption('link_list', { processor: value => isString(value) || isFunction(value) || isArrayOf(value, isObject) });
253
- registerOption('link_default_target', { processor: 'string' });
254
- registerOption('link_default_protocol', {
255
- processor: 'string',
256
- default: 'https'
257
- });
258
- registerOption('link_target_list', {
259
- processor: value => isBoolean(value) || isArrayOf(value, isObject),
260
- default: true
261
- });
262
- registerOption('link_rel_list', {
263
- processor: 'object[]',
264
- default: []
265
- });
266
- registerOption('link_class_list', {
267
- processor: 'object[]',
268
- default: []
269
- });
270
- registerOption('link_title', {
271
- processor: 'boolean',
272
- default: true
273
- });
274
- registerOption('allow_unsafe_link_target', {
275
- processor: 'boolean',
276
- default: false
277
- });
278
- registerOption('link_quicklink', {
279
- processor: 'boolean',
280
- default: false
281
- });
442
+ const singleton = (doRevoke) => {
443
+ const subject = Cell(Optional.none());
444
+ const revoke = () => subject.get().each(doRevoke);
445
+ const clear = () => {
446
+ revoke();
447
+ subject.set(Optional.none());
448
+ };
449
+ const isSet = () => subject.get().isSome();
450
+ const get = () => subject.get();
451
+ const set = (s) => {
452
+ revoke();
453
+ subject.set(Optional.some(s));
454
+ };
455
+ return {
456
+ clear,
457
+ isSet,
458
+ get,
459
+ set
460
+ };
461
+ };
462
+ const value = () => {
463
+ const subject = singleton(noop);
464
+ const on = (f) => subject.get().each(f);
465
+ return {
466
+ ...subject,
467
+ on
468
+ };
469
+ };
470
+
471
+ const removeFromStart = (str, numChars) => {
472
+ return str.substring(numChars);
473
+ };
474
+
475
+ const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
476
+ const removeLeading = (str, prefix) => {
477
+ return startsWith(str, prefix) ? removeFromStart(str, prefix.length) : str;
478
+ };
479
+ /** Does 'str' start with 'prefix'?
480
+ * Note: all strings start with the empty string.
481
+ * More formally, for all strings x, startsWith(x, "").
482
+ * This is so that for all strings x and y, startsWith(y + x, y)
483
+ */
484
+ const startsWith = (str, prefix) => {
485
+ return checkRange(str, prefix, 0);
486
+ };
487
+
488
+ const option = (name) => (editor) => editor.options.get(name);
489
+ const register$1 = (editor) => {
490
+ const registerOption = editor.options.register;
491
+ registerOption('link_assume_external_targets', {
492
+ processor: (value) => {
493
+ const valid = isString(value) || isBoolean(value);
494
+ if (valid) {
495
+ if (value === true) {
496
+ return { value: 1 /* AssumeExternalTargets.WARN */, valid };
497
+ }
498
+ else if (value === "http" /* AssumeExternalTargets.ALWAYS_HTTP */ || value === "https" /* AssumeExternalTargets.ALWAYS_HTTPS */) {
499
+ return { value, valid };
500
+ }
501
+ else {
502
+ return { value: 0 /* AssumeExternalTargets.OFF */, valid };
503
+ }
504
+ }
505
+ else {
506
+ return { valid: false, message: 'Must be a string or a boolean.' };
507
+ }
508
+ },
509
+ default: false
510
+ });
511
+ registerOption('link_context_toolbar', {
512
+ processor: 'boolean',
513
+ default: false
514
+ });
515
+ registerOption('link_list', {
516
+ processor: (value) => isString(value) || isFunction(value) || isArrayOf(value, isObject)
517
+ });
518
+ registerOption('link_default_target', {
519
+ processor: 'string'
520
+ });
521
+ registerOption('link_default_protocol', {
522
+ processor: 'string',
523
+ default: 'https'
524
+ });
525
+ registerOption('link_target_list', {
526
+ processor: (value) => isBoolean(value) || isArrayOf(value, isObject),
527
+ default: true
528
+ });
529
+ registerOption('link_rel_list', {
530
+ processor: 'object[]',
531
+ default: []
532
+ });
533
+ registerOption('link_class_list', {
534
+ processor: 'object[]',
535
+ default: []
536
+ });
537
+ registerOption('link_title', {
538
+ processor: 'boolean',
539
+ default: true
540
+ });
541
+ registerOption('allow_unsafe_link_target', {
542
+ processor: 'boolean',
543
+ default: false
544
+ });
545
+ registerOption('link_quicklink', {
546
+ processor: 'boolean',
547
+ default: false
548
+ });
549
+ registerOption('link_attributes_postprocess', {
550
+ processor: 'function',
551
+ });
282
552
  };
283
553
  const assumeExternalTargets = option('link_assume_external_targets');
284
554
  const hasContextToolbar = option('link_context_toolbar');
@@ -291,952 +561,1017 @@
291
561
  const shouldShowLinkTitle = option('link_title');
292
562
  const allowUnsafeLinkTarget = option('allow_unsafe_link_target');
293
563
  const useQuickLink = option('link_quicklink');
564
+ const attributesPostProcess = option('link_attributes_postprocess');
294
565
 
295
- var global$4 = tinymce.util.Tools.resolve('tinymce.util.Tools');
296
-
297
- const getValue = item => isString(item.value) ? item.value : '';
298
- const getText = item => {
299
- if (isString(item.text)) {
300
- return item.text;
301
- } else if (isString(item.title)) {
302
- return item.title;
303
- } else {
304
- return '';
305
- }
306
- };
307
- const sanitizeList = (list, extractValue) => {
308
- const out = [];
309
- global$4.each(list, item => {
310
- const text = getText(item);
311
- if (item.menu !== undefined) {
312
- const items = sanitizeList(item.menu, extractValue);
313
- out.push({
314
- text,
315
- items
316
- });
317
- } else {
318
- const value = extractValue(item);
319
- out.push({
320
- text,
321
- value
322
- });
323
- }
324
- });
325
- return out;
326
- };
327
- const sanitizeWith = (extracter = getValue) => list => Optional.from(list).map(list => sanitizeList(list, extracter));
328
- const sanitize = list => sanitizeWith(getValue)(list);
329
- const createUi = (name, label) => items => ({
330
- name,
331
- type: 'listbox',
332
- label,
333
- items
334
- });
335
- const ListOptions = {
336
- sanitize,
337
- sanitizeWith,
338
- createUi,
339
- getValue
340
- };
341
-
342
- const keys = Object.keys;
343
- const hasOwnProperty = Object.hasOwnProperty;
344
- const each = (obj, f) => {
345
- const props = keys(obj);
346
- for (let k = 0, len = props.length; k < len; k++) {
347
- const i = props[k];
348
- const x = obj[i];
349
- f(x, i);
350
- }
351
- };
352
- const objAcc = r => (x, i) => {
353
- r[i] = x;
354
- };
355
- const internalFilter = (obj, pred, onTrue, onFalse) => {
356
- each(obj, (x, i) => {
357
- (pred(x, i) ? onTrue : onFalse)(x, i);
358
- });
359
- };
360
- const filter = (obj, pred) => {
361
- const t = {};
362
- internalFilter(obj, pred, objAcc(t), noop);
363
- return t;
364
- };
365
- const has = (obj, key) => hasOwnProperty.call(obj, key);
366
- const hasNonNullableKey = (obj, key) => has(obj, key) && obj[key] !== undefined && obj[key] !== null;
566
+ var global$4 = tinymce.util.Tools.resolve('tinymce.util.URI');
367
567
 
368
568
  var global$3 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');
369
569
 
370
- var global$2 = tinymce.util.Tools.resolve('tinymce.util.URI');
570
+ var global$2 = tinymce.util.Tools.resolve('tinymce.util.Tools');
371
571
 
372
- const isAnchor = elm => isNonNullable(elm) && elm.nodeName.toLowerCase() === 'a';
373
- const isLink = elm => isAnchor(elm) && !!getHref(elm);
572
+ const isAnchor = (elm) => isNonNullable(elm) && elm.nodeName.toLowerCase() === 'a';
573
+ const isLink = (elm) => isAnchor(elm) && !!getHref(elm);
374
574
  const collectNodesInRange = (rng, predicate) => {
375
- if (rng.collapsed) {
376
- return [];
377
- } else {
378
- const contents = rng.cloneContents();
379
- const firstChild = contents.firstChild;
380
- const walker = new global$3(firstChild, contents);
381
- const elements = [];
382
- let current = firstChild;
383
- do {
384
- if (predicate(current)) {
385
- elements.push(current);
386
- }
387
- } while (current = walker.next());
388
- return elements;
389
- }
390
- };
391
- const hasProtocol = url => /^\w+:/i.test(url);
392
- const getHref = elm => {
393
- var _a, _b;
394
- return (_b = (_a = elm.getAttribute('data-mce-href')) !== null && _a !== void 0 ? _a : elm.getAttribute('href')) !== null && _b !== void 0 ? _b : '';
575
+ if (rng.collapsed) {
576
+ return [];
577
+ }
578
+ else {
579
+ const contents = rng.cloneContents();
580
+ const firstChild = contents.firstChild;
581
+ const walker = new global$3(firstChild, contents);
582
+ const elements = [];
583
+ let current = firstChild;
584
+ do {
585
+ if (predicate(current)) {
586
+ elements.push(current);
587
+ }
588
+ } while ((current = walker.next()));
589
+ return elements;
590
+ }
591
+ };
592
+ const hasProtocol = (url) => /^\w+:/i.test(url);
593
+ const getHref = (elm) => {
594
+ var _a, _b;
595
+ // Returns the real href value not the resolved a.href value
596
+ return (_b = (_a = elm.getAttribute('data-mce-href')) !== null && _a !== void 0 ? _a : elm.getAttribute('href')) !== null && _b !== void 0 ? _b : '';
395
597
  };
396
598
  const applyRelTargetRules = (rel, isUnsafe) => {
397
- const rules = ['noopener'];
398
- const rels = rel ? rel.split(/\s+/) : [];
399
- const toString = rels => global$4.trim(rels.sort().join(' '));
400
- const addTargetRules = rels => {
401
- rels = removeTargetRules(rels);
402
- return rels.length > 0 ? rels.concat(rules) : rules;
403
- };
404
- const removeTargetRules = rels => rels.filter(val => global$4.inArray(rules, val) === -1);
405
- const newRels = isUnsafe ? addTargetRules(rels) : removeTargetRules(rels);
406
- return newRels.length > 0 ? toString(newRels) : '';
407
- };
408
- const trimCaretContainers = text => text.replace(/\uFEFF/g, '');
599
+ const rules = ['noopener'];
600
+ const rels = rel ? rel.split(/\s+/) : [];
601
+ const toString = (rels) => global$2.trim(rels.sort().join(' '));
602
+ const addTargetRules = (rels) => {
603
+ rels = removeTargetRules(rels);
604
+ return rels.length > 0 ? rels.concat(rules) : rules;
605
+ };
606
+ const removeTargetRules = (rels) => rels.filter((val) => global$2.inArray(rules, val) === -1);
607
+ const newRels = isUnsafe ? addTargetRules(rels) : removeTargetRules(rels);
608
+ return newRels.length > 0 ? toString(newRels) : '';
609
+ };
610
+ const trimCaretContainers = (text) => text.replace(/\uFEFF/g, '');
409
611
  const getAnchorElement = (editor, selectedElm) => {
410
- selectedElm = selectedElm || getLinksInSelection(editor.selection.getRng())[0] || editor.selection.getNode();
411
- if (isImageFigure(selectedElm)) {
412
- return Optional.from(editor.dom.select('a[href]', selectedElm)[0]);
413
- } else {
414
- return Optional.from(editor.dom.getParent(selectedElm, 'a[href]'));
415
- }
612
+ selectedElm = selectedElm || getLinksInSelection(editor.selection.getRng())[0] || editor.selection.getNode();
613
+ if (isImageFigure(selectedElm)) {
614
+ // for an image contained in a figure we look for a link inside the selected element
615
+ return Optional.from(editor.dom.select('a[href]', selectedElm)[0]);
616
+ }
617
+ else {
618
+ return Optional.from(editor.dom.getParent(selectedElm, 'a[href]'));
619
+ }
416
620
  };
417
621
  const isInAnchor = (editor, selectedElm) => getAnchorElement(editor, selectedElm).isSome();
418
622
  const getAnchorText = (selection, anchorElm) => {
419
- const text = anchorElm.fold(() => selection.getContent({ format: 'text' }), anchorElm => anchorElm.innerText || anchorElm.textContent || '');
420
- return trimCaretContainers(text);
421
- };
422
- const getLinksInSelection = rng => collectNodesInRange(rng, isLink);
423
- const getLinks$1 = elements => global$4.grep(elements, isLink);
424
- const hasLinks = elements => getLinks$1(elements).length > 0;
425
- const hasLinksInSelection = rng => getLinksInSelection(rng).length > 0;
426
- const isOnlyTextSelected = editor => {
427
- const inlineTextElements = editor.schema.getTextInlineElements();
428
- const isElement = elm => elm.nodeType === 1 && !isAnchor(elm) && !has(inlineTextElements, elm.nodeName.toLowerCase());
429
- const isInBlockAnchor = getAnchorElement(editor).exists(anchor => anchor.hasAttribute('data-mce-block'));
430
- if (isInBlockAnchor) {
431
- return false;
432
- }
433
- const rng = editor.selection.getRng();
434
- if (!rng.collapsed) {
435
- const elements = collectNodesInRange(rng, isElement);
436
- return elements.length === 0;
437
- } else {
438
- return true;
439
- }
440
- };
441
- const isImageFigure = elm => isNonNullable(elm) && elm.nodeName === 'FIGURE' && /\bimage\b/i.test(elm.className);
442
- const getLinkAttrs = data => {
443
- const attrs = [
444
- 'title',
445
- 'rel',
446
- 'class',
447
- 'target'
448
- ];
449
- return foldl(attrs, (acc, key) => {
450
- data[key].each(value => {
451
- acc[key] = value.length > 0 ? value : null;
623
+ const text = anchorElm.fold(() => selection.getContent({ format: 'text' }), (anchorElm) => anchorElm.innerText || anchorElm.textContent || '');
624
+ return trimCaretContainers(text);
625
+ };
626
+ const getLinksInSelection = (rng) => collectNodesInRange(rng, isLink);
627
+ const getLinks$1 = (elements) => global$2.grep(elements, isLink);
628
+ const hasLinks = (elements) => getLinks$1(elements).length > 0;
629
+ const hasLinksInSelection = (rng) => getLinksInSelection(rng).length > 0;
630
+ const isOnlyTextSelected = (editor) => {
631
+ // Allow anchor and inline text elements to be in the selection but nothing else
632
+ const inlineTextElements = editor.schema.getTextInlineElements();
633
+ const isElement = (elm) => elm.nodeType === 1 && !isAnchor(elm) && !has(inlineTextElements, elm.nodeName.toLowerCase());
634
+ // If selection is inside a block anchor then always treat it as non text only
635
+ const isInBlockAnchor = getAnchorElement(editor).exists((anchor) => anchor.hasAttribute('data-mce-block'));
636
+ if (isInBlockAnchor) {
637
+ return false;
638
+ }
639
+ const rng = editor.selection.getRng();
640
+ if (!rng.collapsed) {
641
+ // Collect all non inline text elements in the range and make sure no elements were found
642
+ const elements = collectNodesInRange(rng, isElement);
643
+ return elements.length === 0;
644
+ }
645
+ else {
646
+ return true;
647
+ }
648
+ };
649
+ const isImageFigure = (elm) => isNonNullable(elm) && elm.nodeName === 'FIGURE' && /\bimage\b/i.test(elm.className);
650
+
651
+ const getLinkAttrs = (data) => {
652
+ const attrs = ['title', 'rel', 'class', 'target'];
653
+ return foldl(attrs, (acc, key) => {
654
+ data[key].each((value) => {
655
+ // If dealing with an empty string, then treat that as being null so the attribute is removed
656
+ acc[key] = value.length > 0 ? value : null;
657
+ });
658
+ return acc;
659
+ }, {
660
+ href: data.href
452
661
  });
453
- return acc;
454
- }, { href: data.href });
455
662
  };
456
663
  const handleExternalTargets = (href, assumeExternalTargets) => {
457
- if ((assumeExternalTargets === 'http' || assumeExternalTargets === 'https') && !hasProtocol(href)) {
458
- return assumeExternalTargets + '://' + href;
459
- }
460
- return href;
664
+ if ((assumeExternalTargets === "http" /* AssumeExternalTargets.ALWAYS_HTTP */
665
+ || assumeExternalTargets === "https" /* AssumeExternalTargets.ALWAYS_HTTPS */)
666
+ && !hasProtocol(href)) {
667
+ return assumeExternalTargets + '://' + href;
668
+ }
669
+ return href;
461
670
  };
462
671
  const applyLinkOverrides = (editor, linkAttrs) => {
463
- const newLinkAttrs = { ...linkAttrs };
464
- if (getRelList(editor).length === 0 && !allowUnsafeLinkTarget(editor)) {
465
- const newRel = applyRelTargetRules(newLinkAttrs.rel, newLinkAttrs.target === '_blank');
466
- newLinkAttrs.rel = newRel ? newRel : null;
467
- }
468
- if (Optional.from(newLinkAttrs.target).isNone() && getTargetList(editor) === false) {
469
- newLinkAttrs.target = getDefaultLinkTarget(editor);
470
- }
471
- newLinkAttrs.href = handleExternalTargets(newLinkAttrs.href, assumeExternalTargets(editor));
472
- return newLinkAttrs;
672
+ const newLinkAttrs = { ...linkAttrs };
673
+ if (getRelList(editor).length === 0 && !allowUnsafeLinkTarget(editor)) {
674
+ const newRel = applyRelTargetRules(newLinkAttrs.rel, newLinkAttrs.target === '_blank');
675
+ newLinkAttrs.rel = newRel ? newRel : null;
676
+ }
677
+ if (Optional.from(newLinkAttrs.target).isNone() && getTargetList(editor) === false) {
678
+ newLinkAttrs.target = getDefaultLinkTarget(editor);
679
+ }
680
+ newLinkAttrs.href = handleExternalTargets(newLinkAttrs.href, assumeExternalTargets(editor));
681
+ return newLinkAttrs;
473
682
  };
474
683
  const updateLink = (editor, anchorElm, text, linkAttrs) => {
475
- text.each(text => {
476
- if (has(anchorElm, 'innerText')) {
477
- anchorElm.innerText = text;
478
- } else {
479
- anchorElm.textContent = text;
480
- }
481
- });
482
- editor.dom.setAttribs(anchorElm, linkAttrs);
483
- editor.selection.select(anchorElm);
684
+ // If we have text, then update the anchor elements text content
685
+ text.each((text) => {
686
+ if (has(anchorElm, 'innerText')) {
687
+ anchorElm.innerText = text;
688
+ }
689
+ else {
690
+ anchorElm.textContent = text;
691
+ }
692
+ });
693
+ editor.dom.setAttribs(anchorElm, linkAttrs);
694
+ // Move the cursor behind the updated link, so the user can go on typing.
695
+ const rng = editor.dom.createRng();
696
+ rng.setStartAfter(anchorElm);
697
+ rng.setEndAfter(anchorElm);
698
+ editor.selection.setRng(rng);
484
699
  };
485
700
  const createLink = (editor, selectedElm, text, linkAttrs) => {
486
- const dom = editor.dom;
487
- if (isImageFigure(selectedElm)) {
488
- linkImageFigure(dom, selectedElm, linkAttrs);
489
- } else {
490
- text.fold(() => {
491
- editor.execCommand('mceInsertLink', false, linkAttrs);
492
- }, text => {
493
- editor.insertContent(dom.createHTML('a', linkAttrs, dom.encode(text)));
494
- });
495
- }
701
+ const dom = editor.dom;
702
+ if (isImageFigure(selectedElm)) {
703
+ linkImageFigure(dom, selectedElm, linkAttrs);
704
+ }
705
+ else {
706
+ text.fold(() => {
707
+ editor.execCommand('mceInsertLink', false, linkAttrs);
708
+ // Now the newly inserted link is selected. Move the cursor behind the new link, so the user can go on typing.
709
+ const end = editor.selection.getEnd();
710
+ const rng = dom.createRng();
711
+ rng.setStartAfter(end);
712
+ rng.setEndAfter(end);
713
+ editor.selection.setRng(rng);
714
+ }, (text) => {
715
+ editor.insertContent(dom.createHTML('a', linkAttrs, dom.encode(text)));
716
+ });
717
+ }
496
718
  };
497
719
  const linkDomMutation = (editor, attachState, data) => {
498
- const selectedElm = editor.selection.getNode();
499
- const anchorElm = getAnchorElement(editor, selectedElm);
500
- const linkAttrs = applyLinkOverrides(editor, getLinkAttrs(data));
501
- editor.undoManager.transact(() => {
502
- if (data.href === attachState.href) {
503
- attachState.attach();
504
- }
505
- anchorElm.fold(() => {
506
- createLink(editor, selectedElm, data.text, linkAttrs);
507
- }, elm => {
508
- editor.focus();
509
- updateLink(editor, elm, data.text, linkAttrs);
720
+ const selectedElm = editor.selection.getNode();
721
+ const anchorElm = getAnchorElement(editor, selectedElm);
722
+ const linkAttrs = applyLinkOverrides(editor, getLinkAttrs(data));
723
+ const attributesPostProcess$1 = attributesPostProcess(editor);
724
+ if (isNonNullable(attributesPostProcess$1)) {
725
+ attributesPostProcess$1(linkAttrs);
726
+ }
727
+ editor.undoManager.transact(() => {
728
+ if (data.href === attachState.href) {
729
+ attachState.attach();
730
+ }
731
+ anchorElm.fold(() => {
732
+ createLink(editor, selectedElm, data.text, linkAttrs);
733
+ }, (elm) => {
734
+ editor.focus();
735
+ updateLink(editor, elm, data.text, linkAttrs);
736
+ });
510
737
  });
511
- });
512
- };
513
- const unlinkSelection = editor => {
514
- const dom = editor.dom, selection = editor.selection;
515
- const bookmark = selection.getBookmark();
516
- const rng = selection.getRng().cloneRange();
517
- const startAnchorElm = dom.getParent(rng.startContainer, 'a[href]', editor.getBody());
518
- const endAnchorElm = dom.getParent(rng.endContainer, 'a[href]', editor.getBody());
519
- if (startAnchorElm) {
520
- rng.setStartBefore(startAnchorElm);
521
- }
522
- if (endAnchorElm) {
523
- rng.setEndAfter(endAnchorElm);
524
- }
525
- selection.setRng(rng);
526
- editor.execCommand('unlink');
527
- selection.moveToBookmark(bookmark);
528
- };
529
- const unlinkDomMutation = editor => {
530
- editor.undoManager.transact(() => {
531
- const node = editor.selection.getNode();
532
- if (isImageFigure(node)) {
533
- unlinkImageFigure(editor, node);
534
- } else {
535
- unlinkSelection(editor);
536
- }
537
- editor.focus();
538
- });
539
- };
540
- const unwrapOptions = data => {
541
- const {
542
- class: cls,
543
- href,
544
- rel,
545
- target,
546
- text,
547
- title
548
- } = data;
549
- return filter({
550
- class: cls.getOrNull(),
551
- href,
552
- rel: rel.getOrNull(),
553
- target: target.getOrNull(),
554
- text: text.getOrNull(),
555
- title: title.getOrNull()
556
- }, (v, _k) => isNull(v) === false);
738
+ };
739
+ const unlinkSelection = (editor) => {
740
+ const dom = editor.dom, selection = editor.selection;
741
+ const bookmark = selection.getBookmark();
742
+ const rng = selection.getRng().cloneRange();
743
+ // Extend the selection out to the entire anchor element
744
+ const startAnchorElm = dom.getParent(rng.startContainer, 'a[href]', editor.getBody());
745
+ const endAnchorElm = dom.getParent(rng.endContainer, 'a[href]', editor.getBody());
746
+ if (startAnchorElm) {
747
+ rng.setStartBefore(startAnchorElm);
748
+ }
749
+ if (endAnchorElm) {
750
+ rng.setEndAfter(endAnchorElm);
751
+ }
752
+ selection.setRng(rng);
753
+ // Remove the link
754
+ editor.execCommand('unlink');
755
+ selection.moveToBookmark(bookmark);
756
+ };
757
+ const unlinkDomMutation = (editor) => {
758
+ editor.undoManager.transact(() => {
759
+ const node = editor.selection.getNode();
760
+ if (isImageFigure(node)) {
761
+ unlinkImageFigure(editor, node);
762
+ }
763
+ else {
764
+ unlinkSelection(editor);
765
+ }
766
+ editor.focus();
767
+ });
768
+ };
769
+ /*
770
+ * RTC uses unwrapped options.
771
+ *
772
+ * To best simulate this, we unwrap to null and filter out empty values.
773
+ */
774
+ const unwrapOptions = (data) => {
775
+ const { class: cls, href, rel, target, text, title } = data;
776
+ return filter({
777
+ class: cls.getOrNull(),
778
+ href,
779
+ rel: rel.getOrNull(),
780
+ target: target.getOrNull(),
781
+ text: text.getOrNull(),
782
+ title: title.getOrNull()
783
+ }, (v, _k) => isNull(v) === false);
557
784
  };
558
785
  const sanitizeData = (editor, data) => {
559
- const getOption = editor.options.get;
560
- const uriOptions = {
561
- allow_html_data_urls: getOption('allow_html_data_urls'),
562
- allow_script_urls: getOption('allow_script_urls'),
563
- allow_svg_data_urls: getOption('allow_svg_data_urls')
564
- };
565
- const href = data.href;
566
- return {
567
- ...data,
568
- href: global$2.isDomSafe(href, 'a', uriOptions) ? href : ''
569
- };
786
+ const getOption = editor.options.get;
787
+ const uriOptions = {
788
+ allow_html_data_urls: getOption('allow_html_data_urls'),
789
+ allow_script_urls: getOption('allow_script_urls'),
790
+ allow_svg_data_urls: getOption('allow_svg_data_urls')
791
+ };
792
+ // Sanitize the URL
793
+ const href = data.href;
794
+ return {
795
+ ...data,
796
+ href: global$4.isDomSafe(href, 'a', uriOptions) ? href : ''
797
+ };
570
798
  };
571
799
  const link = (editor, attachState, data) => {
572
- const sanitizedData = sanitizeData(editor, data);
573
- editor.hasPlugin('rtc', true) ? editor.execCommand('createlink', false, unwrapOptions(sanitizedData)) : linkDomMutation(editor, attachState, sanitizedData);
800
+ const sanitizedData = sanitizeData(editor, data);
801
+ editor.hasPlugin('rtc', true) ? editor.execCommand('createlink', false, unwrapOptions(sanitizedData)) : linkDomMutation(editor, attachState, sanitizedData);
574
802
  };
575
- const unlink = editor => {
576
- editor.hasPlugin('rtc', true) ? editor.execCommand('unlink') : unlinkDomMutation(editor);
803
+ const unlink = (editor) => {
804
+ editor.hasPlugin('rtc', true) ? editor.execCommand('unlink') : unlinkDomMutation(editor);
577
805
  };
578
806
  const unlinkImageFigure = (editor, fig) => {
579
- var _a;
580
- const img = editor.dom.select('img', fig)[0];
581
- if (img) {
582
- const a = editor.dom.getParents(img, 'a[href]', fig)[0];
583
- if (a) {
584
- (_a = a.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(img, a);
585
- editor.dom.remove(a);
807
+ var _a;
808
+ const img = editor.dom.select('img', fig)[0];
809
+ if (img) {
810
+ const a = editor.dom.getParents(img, 'a[href]', fig)[0];
811
+ if (a) {
812
+ (_a = a.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(img, a);
813
+ editor.dom.remove(a);
814
+ }
586
815
  }
587
- }
588
816
  };
589
817
  const linkImageFigure = (dom, fig, attrs) => {
590
- var _a;
591
- const img = dom.select('img', fig)[0];
592
- if (img) {
593
- const a = dom.create('a', attrs);
594
- (_a = img.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(a, img);
595
- a.appendChild(img);
596
- }
818
+ var _a;
819
+ const img = dom.select('img', fig)[0];
820
+ if (img) {
821
+ const a = dom.create('a', attrs);
822
+ (_a = img.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(a, img);
823
+ a.appendChild(img);
824
+ }
825
+ };
826
+
827
+ const getValue = (item) => isString(item.value) ? item.value : '';
828
+ const getText = (item) => {
829
+ if (isString(item.text)) {
830
+ return item.text;
831
+ }
832
+ else if (isString(item.title)) {
833
+ return item.title;
834
+ }
835
+ else {
836
+ return '';
837
+ }
838
+ };
839
+ const sanitizeList = (list, extractValue) => {
840
+ const out = [];
841
+ global$2.each(list, (item) => {
842
+ const text = getText(item);
843
+ if (item.menu !== undefined) {
844
+ const items = sanitizeList(item.menu, extractValue);
845
+ out.push({ text, items }); // list group
846
+ }
847
+ else {
848
+ const value = extractValue(item);
849
+ out.push({ text, value }); // list value
850
+ }
851
+ });
852
+ return out;
853
+ };
854
+ const sanitizeWith = (extracter = getValue) => (list) => Optional.from(list).map((list) => sanitizeList(list, extracter));
855
+ const sanitize = (list) => sanitizeWith(getValue)(list);
856
+ // NOTE: May need to care about flattening.
857
+ const createUi = (name, label) => (items) => ({
858
+ name,
859
+ type: 'listbox',
860
+ label,
861
+ items
862
+ });
863
+ const ListOptions = {
864
+ sanitize,
865
+ sanitizeWith,
866
+ createUi,
867
+ getValue
597
868
  };
598
869
 
599
- const isListGroup = item => hasNonNullableKey(item, 'items');
600
- const findTextByValue = (value, catalog) => findMap(catalog, item => {
601
- if (isListGroup(item)) {
602
- return findTextByValue(value, item.items);
603
- } else {
604
- return someIf(item.value === value, item);
605
- }
870
+ const isListGroup = (item) => hasNonNullableKey(item, 'items');
871
+ const findTextByValue = (value, catalog) => findMap(catalog, (item) => {
872
+ if (isListGroup(item)) {
873
+ return findTextByValue(value, item.items);
874
+ }
875
+ else {
876
+ return someIf(item.value === value, item);
877
+ }
606
878
  });
607
879
  const getDelta = (persistentText, fieldName, catalog, data) => {
608
- const value = data[fieldName];
609
- const hasPersistentText = persistentText.length > 0;
610
- return value !== undefined ? findTextByValue(value, catalog).map(i => ({
611
- url: {
612
- value: i.value,
613
- meta: {
614
- text: hasPersistentText ? persistentText : i.text,
615
- attach: noop
616
- }
617
- },
618
- text: hasPersistentText ? persistentText : i.text
619
- })) : Optional.none();
880
+ const value = data[fieldName];
881
+ const hasPersistentText = persistentText.length > 0;
882
+ return value !== undefined ? findTextByValue(value, catalog).map((i) => ({
883
+ url: {
884
+ value: i.value,
885
+ meta: {
886
+ text: hasPersistentText ? persistentText : i.text,
887
+ attach: noop
888
+ }
889
+ },
890
+ text: hasPersistentText ? persistentText : i.text
891
+ })) : Optional.none();
620
892
  };
621
893
  const findCatalog = (catalogs, fieldName) => {
622
- if (fieldName === 'link') {
623
- return catalogs.link;
624
- } else if (fieldName === 'anchor') {
625
- return catalogs.anchor;
626
- } else {
627
- return Optional.none();
628
- }
894
+ if (fieldName === 'link') {
895
+ return catalogs.link;
896
+ }
897
+ else if (fieldName === 'anchor') {
898
+ return catalogs.anchor;
899
+ }
900
+ else {
901
+ return Optional.none();
902
+ }
629
903
  };
630
904
  const init = (initialData, linkCatalog) => {
631
- const persistentData = {
632
- text: initialData.text,
633
- title: initialData.title
634
- };
635
- const getTitleFromUrlChange = url => {
636
- var _a;
637
- return someIf(persistentData.title.length <= 0, Optional.from((_a = url.meta) === null || _a === void 0 ? void 0 : _a.title).getOr(''));
638
- };
639
- const getTextFromUrlChange = url => {
640
- var _a;
641
- return someIf(persistentData.text.length <= 0, Optional.from((_a = url.meta) === null || _a === void 0 ? void 0 : _a.text).getOr(url.value));
642
- };
643
- const onUrlChange = data => {
644
- const text = getTextFromUrlChange(data.url);
645
- const title = getTitleFromUrlChange(data.url);
646
- if (text.isSome() || title.isSome()) {
647
- return Optional.some({
648
- ...text.map(text => ({ text })).getOr({}),
649
- ...title.map(title => ({ title })).getOr({})
650
- });
651
- } else {
652
- return Optional.none();
653
- }
654
- };
655
- const onCatalogChange = (data, change) => {
656
- const catalog = findCatalog(linkCatalog, change).getOr([]);
657
- return getDelta(persistentData.text, change, catalog, data);
658
- };
659
- const onChange = (getData, change) => {
660
- const name = change.name;
661
- if (name === 'url') {
662
- return onUrlChange(getData());
663
- } else if (contains([
664
- 'anchor',
665
- 'link'
666
- ], name)) {
667
- return onCatalogChange(getData(), name);
668
- } else if (name === 'text' || name === 'title') {
669
- persistentData[name] = getData()[name];
670
- return Optional.none();
671
- } else {
672
- return Optional.none();
673
- }
674
- };
675
- return { onChange };
905
+ const persistentData = {
906
+ text: initialData.text,
907
+ title: initialData.title
908
+ };
909
+ const getTitleFromUrlChange = (url) => { var _a; return someIf(persistentData.title.length <= 0, Optional.from((_a = url.meta) === null || _a === void 0 ? void 0 : _a.title).getOr('')); };
910
+ const getTextFromUrlChange = (url) => { var _a; return someIf(persistentData.text.length <= 0, Optional.from((_a = url.meta) === null || _a === void 0 ? void 0 : _a.text).getOr(url.value)); };
911
+ const onUrlChange = (data) => {
912
+ const text = getTextFromUrlChange(data.url);
913
+ const title = getTitleFromUrlChange(data.url);
914
+ // We are going to change the text/title because it has not been manually entered by the user.
915
+ if (text.isSome() || title.isSome()) {
916
+ return Optional.some({
917
+ ...text.map((text) => ({ text })).getOr({}),
918
+ ...title.map((title) => ({ title })).getOr({})
919
+ });
920
+ }
921
+ else {
922
+ return Optional.none();
923
+ }
924
+ };
925
+ const onCatalogChange = (data, change) => {
926
+ const catalog = findCatalog(linkCatalog, change).getOr([]);
927
+ return getDelta(persistentData.text, change, catalog, data);
928
+ };
929
+ const onChange = (getData, change) => {
930
+ const name = change.name;
931
+ if (name === 'url') {
932
+ return onUrlChange(getData());
933
+ }
934
+ else if (contains(['anchor', 'link'], name)) {
935
+ return onCatalogChange(getData(), name);
936
+ }
937
+ else if (name === 'text' || name === 'title') {
938
+ // Update the persistent text/title state, as a user has input custom text
939
+ persistentData[name] = getData()[name];
940
+ return Optional.none();
941
+ }
942
+ else {
943
+ return Optional.none();
944
+ }
945
+ };
946
+ return {
947
+ onChange
948
+ };
676
949
  };
677
950
  const DialogChanges = {
678
- init,
679
- getDelta
951
+ init,
952
+ getDelta
680
953
  };
681
954
 
682
955
  var global$1 = tinymce.util.Tools.resolve('tinymce.util.Delay');
683
956
 
957
+ // Delay confirm since onSubmit will move focus
684
958
  const delayedConfirm = (editor, message, callback) => {
685
- const rng = editor.selection.getRng();
686
- global$1.setEditorTimeout(editor, () => {
687
- editor.windowManager.confirm(message, state => {
688
- editor.selection.setRng(rng);
689
- callback(state);
959
+ const rng = editor.selection.getRng();
960
+ global$1.setEditorTimeout(editor, () => {
961
+ editor.windowManager.confirm(message, (state) => {
962
+ editor.selection.setRng(rng);
963
+ callback(state);
964
+ });
965
+ });
966
+ };
967
+ const tryEmailTransform = (data) => {
968
+ const url = data.href;
969
+ const suggestMailTo = url.indexOf('@') > 0 && url.indexOf('/') === -1 && url.indexOf('mailto:') === -1;
970
+ return suggestMailTo ? Optional.some({
971
+ message: 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?',
972
+ preprocess: (oldData) => ({ ...oldData, href: 'mailto:' + url })
973
+ }) : Optional.none();
974
+ };
975
+ const tryProtocolTransform = (assumeExternalTargets, defaultLinkProtocol) => (data) => {
976
+ const url = data.href;
977
+ const suggestProtocol = (assumeExternalTargets === 1 /* AssumeExternalTargets.WARN */ && !hasProtocol(url) ||
978
+ assumeExternalTargets === 0 /* AssumeExternalTargets.OFF */ && /^\s*www(\.|\d\.)/i.test(url));
979
+ return suggestProtocol ? Optional.some({
980
+ message: `The URL you entered seems to be an external link. Do you want to add the required ${defaultLinkProtocol}:// prefix?`,
981
+ preprocess: (oldData) => ({ ...oldData, href: defaultLinkProtocol + '://' + url })
982
+ }) : Optional.none();
983
+ };
984
+ const preprocess = (editor, data) => findMap([tryEmailTransform, tryProtocolTransform(assumeExternalTargets(editor), getDefaultLinkProtocol(editor))], (f) => f(data)).fold(() => Promise.resolve(data), (transform) => new Promise((callback) => {
985
+ delayedConfirm(editor, transform.message, (state) => {
986
+ callback(state ? transform.preprocess(data) : data);
690
987
  });
691
- });
692
- };
693
- const tryEmailTransform = data => {
694
- const url = data.href;
695
- const suggestMailTo = url.indexOf('@') > 0 && url.indexOf('/') === -1 && url.indexOf('mailto:') === -1;
696
- return suggestMailTo ? Optional.some({
697
- message: 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?',
698
- preprocess: oldData => ({
699
- ...oldData,
700
- href: 'mailto:' + url
701
- })
702
- }) : Optional.none();
703
- };
704
- const tryProtocolTransform = (assumeExternalTargets, defaultLinkProtocol) => data => {
705
- const url = data.href;
706
- const suggestProtocol = assumeExternalTargets === 1 && !hasProtocol(url) || assumeExternalTargets === 0 && /^\s*www(\.|\d\.)/i.test(url);
707
- return suggestProtocol ? Optional.some({
708
- message: `The URL you entered seems to be an external link. Do you want to add the required ${ defaultLinkProtocol }:// prefix?`,
709
- preprocess: oldData => ({
710
- ...oldData,
711
- href: defaultLinkProtocol + '://' + url
712
- })
713
- }) : Optional.none();
714
- };
715
- const preprocess = (editor, data) => findMap([
716
- tryEmailTransform,
717
- tryProtocolTransform(assumeExternalTargets(editor), getDefaultLinkProtocol(editor))
718
- ], f => f(data)).fold(() => Promise.resolve(data), transform => new Promise(callback => {
719
- delayedConfirm(editor, transform.message, state => {
720
- callback(state ? transform.preprocess(data) : data);
721
- });
722
988
  }));
723
- const DialogConfirms = { preprocess };
724
-
725
- const getAnchors = editor => {
726
- const anchorNodes = editor.dom.select('a:not([href])');
727
- const anchors = bind(anchorNodes, anchor => {
728
- const id = anchor.name || anchor.id;
729
- return id ? [{
730
- text: id,
731
- value: '#' + id
732
- }] : [];
733
- });
734
- return anchors.length > 0 ? Optional.some([{
735
- text: 'None',
736
- value: ''
737
- }].concat(anchors)) : Optional.none();
738
- };
739
- const AnchorListOptions = { getAnchors };
989
+ const DialogConfirms = {
990
+ preprocess
991
+ };
740
992
 
741
- const getClasses = editor => {
742
- const list = getLinkClassList(editor);
743
- if (list.length > 0) {
744
- return ListOptions.sanitize(list);
745
- }
746
- return Optional.none();
747
- };
748
- const ClassListOptions = { getClasses };
993
+ // NOTE: you currently need anchors in the content for this field to appear
994
+ const getAnchors = (editor) => {
995
+ const anchorNodes = editor.dom.select('a:not([href])');
996
+ const anchors = bind(anchorNodes, (anchor) => {
997
+ const id = anchor.name || anchor.id;
998
+ return id ? [{ text: id, value: '#' + id }] : [];
999
+ });
1000
+ return anchors.length > 0 ? Optional.some([{ text: 'None', value: '' }].concat(anchors)) : Optional.none();
1001
+ };
1002
+ const AnchorListOptions = {
1003
+ getAnchors
1004
+ };
749
1005
 
750
- const parseJson = text => {
751
- try {
752
- return Optional.some(JSON.parse(text));
753
- } catch (err) {
1006
+ // Looks like tinymce currently renders menus, but doesn't
1007
+ // let you choose from one.
1008
+ const getClasses = (editor) => {
1009
+ const list = getLinkClassList(editor);
1010
+ if (list.length > 0) {
1011
+ return ListOptions.sanitize(list);
1012
+ }
754
1013
  return Optional.none();
755
- }
756
- };
757
- const getLinks = editor => {
758
- const extractor = item => editor.convertURL(item.value || item.url || '', 'href');
759
- const linkList = getLinkList(editor);
760
- return new Promise(resolve => {
761
- if (isString(linkList)) {
762
- fetch(linkList).then(res => res.ok ? res.text().then(parseJson) : Promise.reject()).then(resolve, () => resolve(Optional.none()));
763
- } else if (isFunction(linkList)) {
764
- linkList(output => resolve(Optional.some(output)));
765
- } else {
766
- resolve(Optional.from(linkList));
767
- }
768
- }).then(optItems => optItems.bind(ListOptions.sanitizeWith(extractor)).map(items => {
769
- if (items.length > 0) {
770
- const noneItem = [{
771
- text: 'None',
772
- value: ''
773
- }];
774
- return noneItem.concat(items);
775
- } else {
776
- return items;
777
- }
778
- }));
779
- };
780
- const LinkListOptions = { getLinks };
1014
+ };
1015
+ const ClassListOptions = {
1016
+ getClasses
1017
+ };
1018
+
1019
+ const parseJson = (text) => {
1020
+ // Do some proper modelling.
1021
+ try {
1022
+ return Optional.some(JSON.parse(text));
1023
+ }
1024
+ catch (_a) {
1025
+ return Optional.none();
1026
+ }
1027
+ };
1028
+ const getLinks = (editor) => {
1029
+ const extractor = (item) => editor.convertURL(item.value || item.url || '', 'href');
1030
+ const linkList = getLinkList(editor);
1031
+ return new Promise((resolve) => {
1032
+ // TODO - better handling of failure
1033
+ if (isString(linkList)) {
1034
+ fetch(linkList)
1035
+ .then((res) => res.ok ? res.text().then(parseJson) : Promise.reject())
1036
+ .then(resolve, () => resolve(Optional.none()));
1037
+ }
1038
+ else if (isFunction(linkList)) {
1039
+ linkList((output) => resolve(Optional.some(output)));
1040
+ }
1041
+ else {
1042
+ resolve(Optional.from(linkList));
1043
+ }
1044
+ }).then((optItems) => optItems.bind(ListOptions.sanitizeWith(extractor)).map((items) => {
1045
+ if (items.length > 0) {
1046
+ const noneItem = [{ text: 'None', value: '' }];
1047
+ return noneItem.concat(items);
1048
+ }
1049
+ else {
1050
+ return items;
1051
+ }
1052
+ }));
1053
+ };
1054
+ const LinkListOptions = {
1055
+ getLinks
1056
+ };
781
1057
 
782
1058
  const getRels = (editor, initialTarget) => {
783
- const list = getRelList(editor);
784
- if (list.length > 0) {
785
- const isTargetBlank = is(initialTarget, '_blank');
786
- const enforceSafe = allowUnsafeLinkTarget(editor) === false;
787
- const safeRelExtractor = item => applyRelTargetRules(ListOptions.getValue(item), isTargetBlank);
788
- const sanitizer = enforceSafe ? ListOptions.sanitizeWith(safeRelExtractor) : ListOptions.sanitize;
789
- return sanitizer(list);
790
- }
791
- return Optional.none();
792
- };
793
- const RelOptions = { getRels };
1059
+ const list = getRelList(editor);
1060
+ if (list.length > 0) {
1061
+ const isTargetBlank = is(initialTarget, '_blank');
1062
+ const enforceSafe = allowUnsafeLinkTarget(editor) === false;
1063
+ const safeRelExtractor = (item) => applyRelTargetRules(ListOptions.getValue(item), isTargetBlank);
1064
+ const sanitizer = enforceSafe ? ListOptions.sanitizeWith(safeRelExtractor) : ListOptions.sanitize;
1065
+ return sanitizer(list);
1066
+ }
1067
+ return Optional.none();
1068
+ };
1069
+ const RelOptions = {
1070
+ getRels
1071
+ };
794
1072
 
1073
+ // In current tinymce, targets can be nested menus.
1074
+ // Do we really want to support that?
795
1075
  const fallbacks = [
796
- {
797
- text: 'Current window',
798
- value: ''
799
- },
800
- {
801
- text: 'New window',
802
- value: '_blank'
803
- }
1076
+ { text: 'Current window', value: '' },
1077
+ { text: 'New window', value: '_blank' }
804
1078
  ];
805
- const getTargets = editor => {
806
- const list = getTargetList(editor);
807
- if (isArray(list)) {
808
- return ListOptions.sanitize(list).orThunk(() => Optional.some(fallbacks));
809
- } else if (list === false) {
810
- return Optional.none();
811
- }
812
- return Optional.some(fallbacks);
1079
+ const getTargets = (editor) => {
1080
+ const list = getTargetList(editor);
1081
+ if (isArray(list)) {
1082
+ return ListOptions.sanitize(list).orThunk(() => Optional.some(fallbacks));
1083
+ }
1084
+ else if (list === false) {
1085
+ return Optional.none();
1086
+ }
1087
+ return Optional.some(fallbacks);
1088
+ };
1089
+ const TargetOptions = {
1090
+ getTargets
813
1091
  };
814
- const TargetOptions = { getTargets };
815
1092
 
816
1093
  const nonEmptyAttr = (dom, elem, name) => {
817
- const val = dom.getAttrib(elem, name);
818
- return val !== null && val.length > 0 ? Optional.some(val) : Optional.none();
1094
+ const val = dom.getAttrib(elem, name);
1095
+ return val !== null && val.length > 0 ? Optional.some(val) : Optional.none();
819
1096
  };
820
1097
  const extractFromAnchor = (editor, anchor) => {
821
- const dom = editor.dom;
822
- const onlyText = isOnlyTextSelected(editor);
823
- const text = onlyText ? Optional.some(getAnchorText(editor.selection, anchor)) : Optional.none();
824
- const url = anchor.bind(anchorElm => Optional.from(dom.getAttrib(anchorElm, 'href')));
825
- const target = anchor.bind(anchorElm => Optional.from(dom.getAttrib(anchorElm, 'target')));
826
- const rel = anchor.bind(anchorElm => nonEmptyAttr(dom, anchorElm, 'rel'));
827
- const linkClass = anchor.bind(anchorElm => nonEmptyAttr(dom, anchorElm, 'class'));
828
- const title = anchor.bind(anchorElm => nonEmptyAttr(dom, anchorElm, 'title'));
829
- return {
830
- url,
831
- text,
832
- title,
833
- target,
834
- rel,
835
- linkClass
836
- };
837
- };
838
- const collect = (editor, linkNode) => LinkListOptions.getLinks(editor).then(links => {
839
- const anchor = extractFromAnchor(editor, linkNode);
840
- return {
841
- anchor,
842
- catalogs: {
843
- targets: TargetOptions.getTargets(editor),
844
- rels: RelOptions.getRels(editor, anchor.target),
845
- classes: ClassListOptions.getClasses(editor),
846
- anchor: AnchorListOptions.getAnchors(editor),
847
- link: links
848
- },
849
- optNode: linkNode,
850
- flags: { titleEnabled: shouldShowLinkTitle(editor) }
851
- };
1098
+ const dom = editor.dom;
1099
+ const onlyText = isOnlyTextSelected(editor);
1100
+ const text = onlyText ? Optional.some(getAnchorText(editor.selection, anchor)) : Optional.none();
1101
+ const url = anchor.bind((anchorElm) => Optional.from(dom.getAttrib(anchorElm, 'href')));
1102
+ const target = anchor.bind((anchorElm) => Optional.from(dom.getAttrib(anchorElm, 'target')));
1103
+ const rel = anchor.bind((anchorElm) => nonEmptyAttr(dom, anchorElm, 'rel'));
1104
+ const linkClass = anchor.bind((anchorElm) => nonEmptyAttr(dom, anchorElm, 'class'));
1105
+ const title = anchor.bind((anchorElm) => nonEmptyAttr(dom, anchorElm, 'title'));
1106
+ return {
1107
+ url,
1108
+ text,
1109
+ title,
1110
+ target,
1111
+ rel,
1112
+ linkClass
1113
+ };
1114
+ };
1115
+ const collect = (editor, linkNode) => LinkListOptions.getLinks(editor).then((links) => {
1116
+ const anchor = extractFromAnchor(editor, linkNode);
1117
+ return {
1118
+ anchor,
1119
+ catalogs: {
1120
+ targets: TargetOptions.getTargets(editor),
1121
+ // This should be initial target. Is anchor.target that?
1122
+ rels: RelOptions.getRels(editor, anchor.target),
1123
+ classes: ClassListOptions.getClasses(editor),
1124
+ anchor: AnchorListOptions.getAnchors(editor),
1125
+ link: links
1126
+ },
1127
+ optNode: linkNode,
1128
+ flags: {
1129
+ titleEnabled: shouldShowLinkTitle(editor)
1130
+ }
1131
+ };
852
1132
  });
853
- const DialogInfo = { collect };
1133
+ const DialogInfo = {
1134
+ collect
1135
+ };
854
1136
 
855
- const handleSubmit = (editor, info) => api => {
856
- const data = api.getData();
857
- if (!data.url.value) {
858
- unlink(editor);
1137
+ const handleSubmit = (editor, info) => (api) => {
1138
+ const data = api.getData();
1139
+ if (!data.url.value) {
1140
+ unlink(editor);
1141
+ // Temporary fix. TODO: TINY-2811
1142
+ api.close();
1143
+ return;
1144
+ }
1145
+ // Check if a key is defined, meaning it was a field in the dialog. If it is,
1146
+ // then check if it's changed and return none if nothing has changed.
1147
+ const getChangedValue = (key) => Optional.from(data[key]).filter((value) => !is(info.anchor[key], value));
1148
+ const changedData = {
1149
+ href: data.url.value,
1150
+ text: getChangedValue('text'),
1151
+ target: getChangedValue('target'),
1152
+ rel: getChangedValue('rel'),
1153
+ class: getChangedValue('linkClass'),
1154
+ title: getChangedValue('title')
1155
+ };
1156
+ const attachState = {
1157
+ href: data.url.value,
1158
+ attach: data.url.meta !== undefined && data.url.meta.attach ? data.url.meta.attach : noop
1159
+ };
1160
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
1161
+ DialogConfirms.preprocess(editor, changedData).then((pData) => {
1162
+ link(editor, attachState, pData);
1163
+ });
859
1164
  api.close();
860
- return;
861
- }
862
- const getChangedValue = key => Optional.from(data[key]).filter(value => !is(info.anchor[key], value));
863
- const changedData = {
864
- href: data.url.value,
865
- text: getChangedValue('text'),
866
- target: getChangedValue('target'),
867
- rel: getChangedValue('rel'),
868
- class: getChangedValue('linkClass'),
869
- title: getChangedValue('title')
870
- };
871
- const attachState = {
872
- href: data.url.value,
873
- attach: data.url.meta !== undefined && data.url.meta.attach ? data.url.meta.attach : noop
874
- };
875
- DialogConfirms.preprocess(editor, changedData).then(pData => {
876
- link(editor, attachState, pData);
877
- });
878
- api.close();
879
- };
880
- const collectData = editor => {
881
- const anchorNode = getAnchorElement(editor);
882
- return DialogInfo.collect(editor, anchorNode);
1165
+ };
1166
+ const collectData = (editor) => {
1167
+ const anchorNode = getAnchorElement(editor);
1168
+ return DialogInfo.collect(editor, anchorNode);
883
1169
  };
884
1170
  const getInitialData = (info, defaultTarget) => {
885
- const anchor = info.anchor;
886
- const url = anchor.url.getOr('');
887
- return {
888
- url: {
889
- value: url,
890
- meta: { original: { value: url } }
891
- },
892
- text: anchor.text.getOr(''),
893
- title: anchor.title.getOr(''),
894
- anchor: url,
895
- link: url,
896
- rel: anchor.rel.getOr(''),
897
- target: anchor.target.or(defaultTarget).getOr(''),
898
- linkClass: anchor.linkClass.getOr('')
899
- };
1171
+ const anchor = info.anchor;
1172
+ const url = anchor.url.getOr('');
1173
+ return {
1174
+ url: {
1175
+ value: url,
1176
+ meta: {
1177
+ original: {
1178
+ value: url
1179
+ }
1180
+ }
1181
+ },
1182
+ text: anchor.text.getOr(''),
1183
+ title: anchor.title.getOr(''),
1184
+ anchor: url,
1185
+ link: url,
1186
+ rel: anchor.rel.getOr(''),
1187
+ target: anchor.target.or(defaultTarget).getOr(''),
1188
+ linkClass: anchor.linkClass.getOr('')
1189
+ };
900
1190
  };
901
1191
  const makeDialog = (settings, onSubmit, editor) => {
902
- const urlInput = [{
903
- name: 'url',
904
- type: 'urlinput',
905
- filetype: 'file',
906
- label: 'URL',
907
- picker_text: 'Browse links'
908
- }];
909
- const displayText = settings.anchor.text.map(() => ({
910
- name: 'text',
911
- type: 'input',
912
- label: 'Text to display'
913
- })).toArray();
914
- const titleText = settings.flags.titleEnabled ? [{
915
- name: 'title',
916
- type: 'input',
917
- label: 'Title'
918
- }] : [];
919
- const defaultTarget = Optional.from(getDefaultLinkTarget(editor));
920
- const initialData = getInitialData(settings, defaultTarget);
921
- const catalogs = settings.catalogs;
922
- const dialogDelta = DialogChanges.init(initialData, catalogs);
923
- const body = {
924
- type: 'panel',
925
- items: flatten([
926
- urlInput,
927
- displayText,
928
- titleText,
929
- cat([
930
- catalogs.anchor.map(ListOptions.createUi('anchor', 'Anchors')),
931
- catalogs.rels.map(ListOptions.createUi('rel', 'Rel')),
932
- catalogs.targets.map(ListOptions.createUi('target', 'Open link in...')),
933
- catalogs.link.map(ListOptions.createUi('link', 'Link list')),
934
- catalogs.classes.map(ListOptions.createUi('linkClass', 'Class'))
935
- ])
936
- ])
937
- };
938
- return {
939
- title: 'Insert/Edit Link',
940
- size: 'normal',
941
- body,
942
- buttons: [
943
- {
944
- type: 'cancel',
945
- name: 'cancel',
946
- text: 'Cancel'
947
- },
948
- {
949
- type: 'submit',
950
- name: 'save',
951
- text: 'Save',
952
- primary: true
953
- }
954
- ],
955
- initialData,
956
- onChange: (api, {name}) => {
957
- dialogDelta.onChange(api.getData, { name }).each(newData => {
958
- api.setData(newData);
959
- });
960
- },
961
- onSubmit
962
- };
963
- };
964
- const open$1 = editor => {
965
- const data = collectData(editor);
966
- data.then(info => {
967
- const onSubmit = handleSubmit(editor, info);
968
- return makeDialog(info, onSubmit, editor);
969
- }).then(spec => {
970
- editor.windowManager.open(spec);
971
- });
1192
+ const urlInput = [
1193
+ {
1194
+ name: 'url',
1195
+ type: 'urlinput',
1196
+ filetype: 'file',
1197
+ label: 'URL',
1198
+ picker_text: 'Browse links'
1199
+ }
1200
+ ];
1201
+ const displayText = settings.anchor.text.map(() => ({
1202
+ name: 'text',
1203
+ type: 'input',
1204
+ label: 'Text to display'
1205
+ })).toArray();
1206
+ const titleText = settings.flags.titleEnabled ? [
1207
+ {
1208
+ name: 'title',
1209
+ type: 'input',
1210
+ label: 'Title'
1211
+ }
1212
+ ] : [];
1213
+ const defaultTarget = Optional.from(getDefaultLinkTarget(editor));
1214
+ const initialData = getInitialData(settings, defaultTarget);
1215
+ const catalogs = settings.catalogs;
1216
+ const dialogDelta = DialogChanges.init(initialData, catalogs);
1217
+ const body = {
1218
+ type: 'panel',
1219
+ items: flatten([
1220
+ urlInput,
1221
+ displayText,
1222
+ titleText,
1223
+ cat([
1224
+ catalogs.anchor.map(ListOptions.createUi('anchor', 'Anchors')),
1225
+ catalogs.rels.map(ListOptions.createUi('rel', 'Rel')),
1226
+ catalogs.targets.map(ListOptions.createUi('target', 'Open link in...')),
1227
+ catalogs.link.map(ListOptions.createUi('link', 'Link list')),
1228
+ catalogs.classes.map(ListOptions.createUi('linkClass', 'Class'))
1229
+ ])
1230
+ ])
1231
+ };
1232
+ return {
1233
+ title: 'Insert/Edit Link',
1234
+ size: 'normal',
1235
+ body,
1236
+ buttons: [
1237
+ {
1238
+ type: 'cancel',
1239
+ name: 'cancel',
1240
+ text: 'Cancel'
1241
+ },
1242
+ {
1243
+ type: 'submit',
1244
+ name: 'save',
1245
+ text: 'Save',
1246
+ primary: true
1247
+ }
1248
+ ],
1249
+ initialData,
1250
+ onChange: (api, { name }) => {
1251
+ dialogDelta.onChange(api.getData, { name }).each((newData) => {
1252
+ api.setData(newData);
1253
+ });
1254
+ },
1255
+ onSubmit
1256
+ };
1257
+ };
1258
+ const open = (editor) => {
1259
+ const data = collectData(editor);
1260
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
1261
+ data.then((info) => {
1262
+ const onSubmit = handleSubmit(editor, info);
1263
+ return makeDialog(info, onSubmit, editor);
1264
+ }).then((spec) => {
1265
+ editor.windowManager.open(spec);
1266
+ });
972
1267
  };
973
1268
 
974
- const register = editor => {
975
- editor.addCommand('mceLink', (_ui, value) => {
976
- if ((value === null || value === void 0 ? void 0 : value.dialog) === true || !useQuickLink(editor)) {
977
- open$1(editor);
978
- } else {
979
- editor.dispatch('contexttoolbar-show', { toolbarKey: 'quicklink' });
980
- }
981
- });
1269
+ const register = (editor) => {
1270
+ editor.addCommand('mceLink', (_ui, value) => {
1271
+ if ((value === null || value === void 0 ? void 0 : value.dialog) === true || !useQuickLink(editor)) {
1272
+ open(editor);
1273
+ }
1274
+ else {
1275
+ editor.dispatch('contexttoolbar-show', {
1276
+ toolbarKey: 'quicklink'
1277
+ });
1278
+ }
1279
+ });
1280
+ };
1281
+
1282
+ const setup$2 = (editor) => {
1283
+ editor.addShortcut('Meta+K', '', () => {
1284
+ editor.execCommand('mceLink');
1285
+ });
982
1286
  };
983
1287
 
984
1288
  var global = tinymce.util.Tools.resolve('tinymce.util.VK');
985
1289
 
986
1290
  const appendClickRemove = (link, evt) => {
987
- document.body.appendChild(link);
988
- link.dispatchEvent(evt);
989
- document.body.removeChild(link);
990
- };
991
- const open = url => {
992
- const link = document.createElement('a');
993
- link.target = '_blank';
994
- link.href = url;
995
- link.rel = 'noreferrer noopener';
996
- const evt = document.createEvent('MouseEvents');
997
- evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
998
- appendClickRemove(link, evt);
1291
+ document.body.appendChild(link);
1292
+ link.dispatchEvent(evt);
1293
+ document.body.removeChild(link);
1294
+ };
1295
+ const openLink = (url) => {
1296
+ const link = document.createElement('a');
1297
+ link.target = '_blank';
1298
+ link.href = url;
1299
+ link.rel = 'noreferrer noopener';
1300
+ const evt = new MouseEvent('click', {
1301
+ bubbles: true,
1302
+ cancelable: true,
1303
+ view: window
1304
+ });
1305
+ document.dispatchEvent(evt);
1306
+ appendClickRemove(link, evt);
999
1307
  };
1000
-
1001
- const getLink = (editor, elm) => editor.dom.getParent(elm, 'a[href]');
1002
- const getSelectedLink = editor => getLink(editor, editor.selection.getStart());
1003
- const hasOnlyAltModifier = e => {
1004
- return e.altKey === true && e.shiftKey === false && e.ctrlKey === false && e.metaKey === false;
1308
+ const hasOnlyAltModifier = (e) => {
1309
+ return e.altKey === true && e.shiftKey === false && e.ctrlKey === false && e.metaKey === false;
1005
1310
  };
1006
1311
  const gotoLink = (editor, a) => {
1007
- if (a) {
1008
- const href = getHref(a);
1009
- if (/^#/.test(href)) {
1010
- const targetEl = editor.dom.select(href);
1011
- if (targetEl.length) {
1012
- editor.selection.scrollIntoView(targetEl[0], true);
1013
- }
1014
- } else {
1015
- open(a.href);
1016
- }
1017
- }
1018
- };
1019
- const openDialog = editor => () => {
1020
- editor.execCommand('mceLink', false, { dialog: true });
1021
- };
1022
- const gotoSelectedLink = editor => () => {
1023
- gotoLink(editor, getSelectedLink(editor));
1024
- };
1025
- const setupGotoLinks = editor => {
1026
- editor.on('click', e => {
1027
- const link = getLink(editor, e.target);
1028
- if (link && global.metaKeyPressed(e)) {
1029
- e.preventDefault();
1030
- gotoLink(editor, link);
1031
- }
1032
- });
1033
- editor.on('keydown', e => {
1034
- if (!e.isDefaultPrevented() && e.keyCode === 13 && hasOnlyAltModifier(e)) {
1035
- const link = getSelectedLink(editor);
1036
- if (link) {
1037
- e.preventDefault();
1038
- gotoLink(editor, link);
1039
- }
1040
- }
1041
- });
1312
+ if (a) {
1313
+ const href = getHref(a);
1314
+ if (/^#/.test(href)) {
1315
+ const targetEl = editor.dom.select(`${href},[name="${removeLeading(href, '#')}"]`);
1316
+ if (targetEl.length) {
1317
+ editor.selection.scrollIntoView(targetEl[0], true);
1318
+ }
1319
+ }
1320
+ else {
1321
+ openLink(a.href);
1322
+ }
1323
+ }
1042
1324
  };
1043
- const toggleState = (editor, toggler) => {
1044
- editor.on('NodeChange', toggler);
1045
- return () => editor.off('NodeChange', toggler);
1046
- };
1047
- const toggleLinkState = editor => api => {
1048
- const updateState = () => {
1049
- api.setActive(!editor.mode.isReadOnly() && isInAnchor(editor, editor.selection.getNode()));
1050
- api.setEnabled(editor.selection.isEditable());
1051
- };
1052
- updateState();
1053
- return toggleState(editor, updateState);
1054
- };
1055
- const toggleLinkMenuState = editor => api => {
1056
- const updateState = () => {
1057
- api.setEnabled(editor.selection.isEditable());
1058
- };
1059
- updateState();
1060
- return toggleState(editor, updateState);
1061
- };
1062
- const hasExactlyOneLinkInSelection = editor => {
1063
- const links = editor.selection.isCollapsed() ? getLinks$1(editor.dom.getParents(editor.selection.getStart())) : getLinksInSelection(editor.selection.getRng());
1064
- return links.length === 1;
1065
- };
1066
- const toggleGotoLinkState = editor => api => {
1067
- const updateState = () => api.setEnabled(hasExactlyOneLinkInSelection(editor));
1068
- updateState();
1069
- return toggleState(editor, updateState);
1070
- };
1071
- const toggleUnlinkState = editor => api => {
1072
- const hasLinks$1 = parents => hasLinks(parents) || hasLinksInSelection(editor.selection.getRng());
1073
- const parents = editor.dom.getParents(editor.selection.getStart());
1074
- const updateEnabled = parents => {
1075
- api.setEnabled(hasLinks$1(parents) && editor.selection.isEditable());
1076
- };
1077
- updateEnabled(parents);
1078
- return toggleState(editor, e => updateEnabled(e.parents));
1325
+ const isSelectionOnImageWithEmbeddedLink = (editor) => {
1326
+ const rng = editor.selection.getRng();
1327
+ const node = rng.startContainer;
1328
+ // Handle a case where an image embedded with a link is selected
1329
+ return isLink(node) && rng.startContainer === rng.endContainer && editor.dom.select('img', node).length === 1;
1330
+ };
1331
+ const getLinkFromElement = (editor, element) => {
1332
+ const links = getLinks$1(editor.dom.getParents(element));
1333
+ return someIf(links.length === 1, links[0]);
1334
+ };
1335
+ const getLinkInSelection = (editor) => {
1336
+ const links = getLinksInSelection(editor.selection.getRng());
1337
+ return someIf(links.length > 0, links[0]).or(getLinkFromElement(editor, editor.selection.getNode()));
1338
+ };
1339
+ const getLinkFromSelection = (editor) => editor.selection.isCollapsed() || isSelectionOnImageWithEmbeddedLink(editor)
1340
+ ? getLinkFromElement(editor, editor.selection.getStart())
1341
+ : getLinkInSelection(editor);
1342
+ const setup$1 = (editor) => {
1343
+ const selectedLink = value();
1344
+ const getSelectedLink = () => selectedLink.get().or(getLinkFromSelection(editor));
1345
+ const gotoSelectedLink = () => getSelectedLink().each((link) => gotoLink(editor, link));
1346
+ editor.on('contextmenu', (e) => {
1347
+ getLinkFromElement(editor, e.target).each(selectedLink.set);
1348
+ });
1349
+ editor.on('SelectionChange', () => {
1350
+ if (!selectedLink.isSet()) {
1351
+ getLinkFromSelection(editor).each(selectedLink.set);
1352
+ }
1353
+ });
1354
+ editor.on('click', (e) => {
1355
+ selectedLink.clear();
1356
+ const links = getLinks$1(editor.dom.getParents(e.target));
1357
+ if (links.length === 1 && global.metaKeyPressed(e)) {
1358
+ e.preventDefault();
1359
+ gotoLink(editor, links[0]);
1360
+ }
1361
+ });
1362
+ editor.on('keydown', (e) => {
1363
+ selectedLink.clear();
1364
+ if (!e.isDefaultPrevented() && e.keyCode === 13 && hasOnlyAltModifier(e)) {
1365
+ getSelectedLink().each((link) => {
1366
+ e.preventDefault();
1367
+ gotoLink(editor, link);
1368
+ });
1369
+ }
1370
+ });
1371
+ return {
1372
+ gotoSelectedLink
1373
+ };
1079
1374
  };
1080
1375
 
1081
- const setup = editor => {
1082
- editor.addShortcut('Meta+K', '', () => {
1083
- editor.execCommand('mceLink');
1084
- });
1376
+ const openDialog = (editor) => () => {
1377
+ editor.execCommand('mceLink', false, { dialog: true });
1085
1378
  };
1086
-
1087
- const setupButtons = editor => {
1088
- editor.ui.registry.addToggleButton('link', {
1089
- icon: 'link',
1090
- tooltip: 'Insert/edit link',
1091
- onAction: openDialog(editor),
1092
- onSetup: toggleLinkState(editor)
1093
- });
1094
- editor.ui.registry.addButton('openlink', {
1095
- icon: 'new-tab',
1096
- tooltip: 'Open link',
1097
- onAction: gotoSelectedLink(editor),
1098
- onSetup: toggleGotoLinkState(editor)
1099
- });
1100
- editor.ui.registry.addButton('unlink', {
1101
- icon: 'unlink',
1102
- tooltip: 'Remove link',
1103
- onAction: () => unlink(editor),
1104
- onSetup: toggleUnlinkState(editor)
1105
- });
1106
- };
1107
- const setupMenuItems = editor => {
1108
- editor.ui.registry.addMenuItem('openlink', {
1109
- text: 'Open link',
1110
- icon: 'new-tab',
1111
- onAction: gotoSelectedLink(editor),
1112
- onSetup: toggleGotoLinkState(editor)
1113
- });
1114
- editor.ui.registry.addMenuItem('link', {
1115
- icon: 'link',
1116
- text: 'Link...',
1117
- shortcut: 'Meta+K',
1118
- onSetup: toggleLinkMenuState(editor),
1119
- onAction: openDialog(editor)
1120
- });
1121
- editor.ui.registry.addMenuItem('unlink', {
1122
- icon: 'unlink',
1123
- text: 'Remove link',
1124
- onAction: () => unlink(editor),
1125
- onSetup: toggleUnlinkState(editor)
1126
- });
1127
- };
1128
- const setupContextMenu = editor => {
1129
- const inLink = 'link unlink openlink';
1130
- const noLink = 'link';
1131
- editor.ui.registry.addContextMenu('link', {
1132
- update: element => {
1133
- const isEditable = editor.dom.isEditable(element);
1134
- if (!isEditable) {
1135
- return '';
1136
- }
1137
- return hasLinks(editor.dom.getParents(element, 'a')) ? inLink : noLink;
1138
- }
1139
- });
1140
- };
1141
- const setupContextToolbars = editor => {
1142
- const collapseSelectionToEnd = editor => {
1143
- editor.selection.collapse(false);
1144
- };
1145
- const onSetupLink = buttonApi => {
1146
- const node = editor.selection.getNode();
1147
- buttonApi.setEnabled(isInAnchor(editor, node));
1148
- return noop;
1149
- };
1150
- const getLinkText = value => {
1151
- const anchor = getAnchorElement(editor);
1152
- const onlyText = isOnlyTextSelected(editor);
1153
- if (anchor.isNone() && onlyText) {
1154
- const text = getAnchorText(editor.selection, anchor);
1155
- return someIf(text.length === 0, value);
1156
- } else {
1157
- return Optional.none();
1158
- }
1159
- };
1160
- editor.ui.registry.addContextForm('quicklink', {
1161
- launch: {
1162
- type: 'contextformtogglebutton',
1163
- icon: 'link',
1164
- tooltip: 'Link',
1165
- onSetup: toggleLinkState(editor)
1166
- },
1167
- label: 'Link',
1168
- predicate: node => hasContextToolbar(editor) && isInAnchor(editor, node),
1169
- initValue: () => {
1170
- const elm = getAnchorElement(editor);
1171
- return elm.fold(constant(''), getHref);
1172
- },
1173
- commands: [
1174
- {
1175
- type: 'contextformtogglebutton',
1379
+ const toggleState = (editor, toggler) => {
1380
+ editor.on('NodeChange', toggler);
1381
+ return () => editor.off('NodeChange', toggler);
1382
+ };
1383
+ const toggleLinkState = (editor) => (api) => {
1384
+ const updateState = () => {
1385
+ api.setActive(!editor.mode.isReadOnly() && isInAnchor(editor, editor.selection.getNode()));
1386
+ api.setEnabled(editor.selection.isEditable());
1387
+ };
1388
+ updateState();
1389
+ return toggleState(editor, updateState);
1390
+ };
1391
+ const toggleLinkMenuState = (editor) => (api) => {
1392
+ const updateState = () => {
1393
+ api.setEnabled(editor.selection.isEditable());
1394
+ };
1395
+ updateState();
1396
+ return toggleState(editor, updateState);
1397
+ };
1398
+ const toggleRequiresLinkState = (editor) => (api) => {
1399
+ const hasLinks$1 = (parents) => hasLinks(parents) || hasLinksInSelection(editor.selection.getRng());
1400
+ const parents = editor.dom.getParents(editor.selection.getStart());
1401
+ const updateEnabled = (parents) => {
1402
+ api.setEnabled(hasLinks$1(parents) && editor.selection.isEditable());
1403
+ };
1404
+ updateEnabled(parents);
1405
+ return toggleState(editor, (e) => updateEnabled(e.parents));
1406
+ };
1407
+ const setupButtons = (editor, openLink) => {
1408
+ editor.ui.registry.addToggleButton('link', {
1176
1409
  icon: 'link',
1177
- tooltip: 'Link',
1178
- primary: true,
1179
- onSetup: buttonApi => {
1180
- const node = editor.selection.getNode();
1181
- buttonApi.setActive(isInAnchor(editor, node));
1182
- return toggleLinkState(editor)(buttonApi);
1183
- },
1184
- onAction: formApi => {
1185
- const value = formApi.getValue();
1186
- const text = getLinkText(value);
1187
- const attachState = {
1188
- href: value,
1189
- attach: noop
1190
- };
1191
- link(editor, attachState, {
1192
- href: value,
1193
- text,
1194
- title: Optional.none(),
1195
- rel: Optional.none(),
1196
- target: Optional.none(),
1197
- class: Optional.none()
1198
- });
1199
- collapseSelectionToEnd(editor);
1200
- formApi.hide();
1201
- }
1202
- },
1203
- {
1204
- type: 'contextformbutton',
1410
+ tooltip: 'Insert/edit link',
1411
+ shortcut: 'Meta+K',
1412
+ onAction: openDialog(editor),
1413
+ onSetup: toggleLinkState(editor)
1414
+ });
1415
+ editor.ui.registry.addButton('openlink', {
1416
+ icon: 'new-tab',
1417
+ tooltip: 'Open link',
1418
+ onAction: openLink.gotoSelectedLink,
1419
+ onSetup: toggleRequiresLinkState(editor)
1420
+ });
1421
+ editor.ui.registry.addButton('unlink', {
1205
1422
  icon: 'unlink',
1206
1423
  tooltip: 'Remove link',
1207
- onSetup: onSetupLink,
1208
- onAction: formApi => {
1209
- unlink(editor);
1210
- formApi.hide();
1211
- }
1212
- },
1213
- {
1214
- type: 'contextformbutton',
1424
+ onAction: () => unlink(editor),
1425
+ onSetup: toggleRequiresLinkState(editor)
1426
+ });
1427
+ };
1428
+ const setupMenuItems = (editor, openLink) => {
1429
+ editor.ui.registry.addMenuItem('openlink', {
1430
+ text: 'Open link',
1215
1431
  icon: 'new-tab',
1216
- tooltip: 'Open link',
1217
- onSetup: onSetupLink,
1218
- onAction: formApi => {
1219
- gotoSelectedLink(editor)();
1220
- formApi.hide();
1432
+ onAction: openLink.gotoSelectedLink,
1433
+ onSetup: toggleRequiresLinkState(editor)
1434
+ });
1435
+ editor.ui.registry.addMenuItem('link', {
1436
+ icon: 'link',
1437
+ text: 'Link...',
1438
+ shortcut: 'Meta+K',
1439
+ onAction: openDialog(editor),
1440
+ onSetup: toggleLinkMenuState(editor)
1441
+ });
1442
+ editor.ui.registry.addMenuItem('unlink', {
1443
+ icon: 'unlink',
1444
+ text: 'Remove link',
1445
+ onAction: () => unlink(editor),
1446
+ onSetup: toggleRequiresLinkState(editor)
1447
+ });
1448
+ };
1449
+ const setupContextMenu = (editor) => {
1450
+ const inLink = 'link unlink openlink';
1451
+ const noLink = 'link';
1452
+ editor.ui.registry.addContextMenu('link', {
1453
+ update: (element) => {
1454
+ const isEditable = editor.dom.isEditable(element);
1455
+ if (!isEditable) {
1456
+ return '';
1457
+ }
1458
+ return hasLinks(editor.dom.getParents(element, 'a')) ? inLink : noLink;
1459
+ }
1460
+ });
1461
+ };
1462
+ const setupContextToolbars = (editor, openLink) => {
1463
+ const collapseSelectionToEnd = (editor) => {
1464
+ editor.selection.collapse(false);
1465
+ };
1466
+ const onSetupLink = (buttonApi) => {
1467
+ const node = editor.selection.getNode();
1468
+ buttonApi.setEnabled(isInAnchor(editor, node) && editor.selection.isEditable());
1469
+ return noop;
1470
+ };
1471
+ /**
1472
+ * if we're editing a link, don't change the text.
1473
+ * if anything other than text is selected, don't change the text.
1474
+ * TINY-9593: If there is a text selection return `Optional.none`
1475
+ * because `mceInsertLink` command will handle the selection.
1476
+ */
1477
+ const getLinkText = (value) => {
1478
+ const anchor = getAnchorElement(editor);
1479
+ const onlyText = isOnlyTextSelected(editor);
1480
+ if (anchor.isNone() && onlyText) {
1481
+ const text = getAnchorText(editor.selection, anchor);
1482
+ return someIf(text.length === 0, value);
1221
1483
  }
1222
- }
1223
- ]
1224
- });
1484
+ else {
1485
+ return Optional.none();
1486
+ }
1487
+ };
1488
+ editor.ui.registry.addContextForm('quicklink', {
1489
+ launch: {
1490
+ type: 'contextformtogglebutton',
1491
+ icon: 'link',
1492
+ tooltip: 'Link',
1493
+ onSetup: toggleLinkState(editor)
1494
+ },
1495
+ label: 'Link',
1496
+ predicate: (node) => hasContextToolbar(editor) && isInAnchor(editor, node),
1497
+ initValue: () => {
1498
+ const elm = getAnchorElement(editor);
1499
+ return elm.fold(constant(''), getHref);
1500
+ },
1501
+ commands: [
1502
+ {
1503
+ type: 'contextformtogglebutton',
1504
+ icon: 'link',
1505
+ tooltip: 'Link',
1506
+ primary: true,
1507
+ onSetup: (buttonApi) => {
1508
+ const node = editor.selection.getNode();
1509
+ // TODO: Make a test for this later.
1510
+ buttonApi.setActive(isInAnchor(editor, node));
1511
+ return toggleLinkState(editor)(buttonApi);
1512
+ },
1513
+ onAction: (formApi) => {
1514
+ const value = formApi.getValue();
1515
+ const text = getLinkText(value);
1516
+ const attachState = { href: value, attach: noop };
1517
+ link(editor, attachState, {
1518
+ href: value,
1519
+ text,
1520
+ title: Optional.none(),
1521
+ rel: Optional.none(),
1522
+ target: Optional.from(getDefaultLinkTarget(editor)),
1523
+ class: Optional.none()
1524
+ });
1525
+ collapseSelectionToEnd(editor);
1526
+ formApi.hide();
1527
+ }
1528
+ },
1529
+ {
1530
+ type: 'contextformbutton',
1531
+ icon: 'unlink',
1532
+ tooltip: 'Remove link',
1533
+ onSetup: onSetupLink,
1534
+ // TODO: The original inlite action was quite complex. Are we missing something with this?
1535
+ onAction: (formApi) => {
1536
+ unlink(editor);
1537
+ formApi.hide();
1538
+ }
1539
+ },
1540
+ {
1541
+ type: 'contextformbutton',
1542
+ icon: 'new-tab',
1543
+ tooltip: 'Open link',
1544
+ onSetup: onSetupLink,
1545
+ onAction: (formApi) => {
1546
+ openLink.gotoSelectedLink();
1547
+ formApi.hide();
1548
+ }
1549
+ }
1550
+ ]
1551
+ });
1552
+ };
1553
+ const setup = (editor) => {
1554
+ const openLink = setup$1(editor);
1555
+ setupButtons(editor, openLink);
1556
+ setupMenuItems(editor, openLink);
1557
+ setupContextMenu(editor);
1558
+ setupContextToolbars(editor, openLink);
1225
1559
  };
1226
1560
 
1227
1561
  var Plugin = () => {
1228
- global$5.add('link', editor => {
1229
- register$1(editor);
1230
- setupButtons(editor);
1231
- setupMenuItems(editor);
1232
- setupContextMenu(editor);
1233
- setupContextToolbars(editor);
1234
- setupGotoLinks(editor);
1235
- register(editor);
1236
- setup(editor);
1237
- });
1562
+ global$5.add('link', (editor) => {
1563
+ register$1(editor);
1564
+ register(editor);
1565
+ setup(editor);
1566
+ setup$2(editor);
1567
+ });
1238
1568
  };
1239
1569
 
1240
1570
  Plugin();
1571
+ /** *****
1572
+ * DO NOT EXPORT ANYTHING
1573
+ *
1574
+ * IF YOU DO ROLLUP WILL LEAVE A GLOBAL ON THE PAGE
1575
+ *******/
1241
1576
 
1242
1577
  })();