-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathIntegration.ts
More file actions
150 lines (129 loc) · 4.59 KB
/
Integration.ts
File metadata and controls
150 lines (129 loc) · 4.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { Editor, RawEditorOptions, TinyMCE as TinyMCEGlobal } from 'tinymce';
import { getJquery } from './JQuery';
import { patchJQueryFunctions } from './Patch';
import { loadTinymce, getTinymceInstance } from './TinyMCE';
export interface RawEditorExtendedSettings extends RawEditorOptions {
script_url?: string;
channel?: string;
api_key?: string;
selector?: undefined;
target?: undefined;
script_loaded?: () => void;
oninit?: string | AllInitFn;
}
declare global {
interface JQuery<TElement = HTMLElement> extends Iterable<TElement> {
tinymce(): Editor;
tinymce(settings: RawEditorExtendedSettings): Promise<Editor[]>;
}
}
type AllInitFn = (editors: Editor[]) => void;
export const getScriptSrc = (settings: RawEditorExtendedSettings): string => {
if (typeof settings.script_url === 'string') {
return settings.script_url;
} else {
const channel = typeof settings.channel === 'string' ? settings.channel : '8';
const apiKey = typeof settings.api_key === 'string' ? settings.api_key : 'no-api-key';
return `https://cdn.tiny.cloud/1/${apiKey}/tinymce/${channel}/tinymce.min.js`;
}
};
const getEditors = (tinymce: TinyMCEGlobal, self: JQuery<HTMLElement>): Editor[] => {
const out: Editor[] = [];
self.each((i, ele) => {
const ed = tinymce.get(ele.id);
if (ed != null) {
out.push(ed);
}
});
return out;
};
const resolveFunction = <F extends Function> (tiny: TinyMCEGlobal, fnOrStr: unknown): F | null => {
if (typeof fnOrStr === 'string') {
const func: unknown = tiny.resolve(fnOrStr);
if (typeof func === 'function') {
const scope = (fnOrStr.indexOf('.') === -1) ? tiny : tiny.resolve(fnOrStr.replace(/\.\w+$/, ''));
return (func as F).bind(scope);
}
} else if (typeof fnOrStr === 'function') {
return fnOrStr.bind(tiny);
}
return null;
};
let patchApplied = false;
const tinymceFn = function (this: JQuery<HTMLElement>, settings?: RawEditorExtendedSettings): Editor | undefined | Promise<Editor[]> {
// No match then just ignore the call
if (!this.length) {
return !settings ? undefined : Promise.resolve([]);
}
// Get editor instance
if (!settings) {
return getTinymceInstance(this[0]) ?? undefined;
}
// Hide textarea to avoid flicker
this.css('visibility', 'hidden');
return new Promise<Editor[]>((resolve) => {
// Load tinymce
loadTinymce(getScriptSrc(settings), (tinymce, loadedFromProvidedUrl) => {
// Execute callback after tinymce has been loaded and before the initialization occurs
if (loadedFromProvidedUrl && settings.script_loaded) {
settings.script_loaded();
}
// Apply patches to the jQuery object, only once
if (!patchApplied) {
patchApplied = true;
patchJQueryFunctions(getJquery());
}
// track how many editors have initialized so we can run a callback
let initCount = 0;
const allInitCallback = resolveFunction<AllInitFn>(tinymce, settings.oninit);
const allInitialized = () => {
const editors = getEditors(tinymce, this);
if (allInitCallback) {
allInitCallback(editors);
}
resolve(editors);
};
// Create an editor instance for each matched node
this.each((_i, elm) => {
// Generate unique id for target element if needed
if (!elm.id) {
elm.id = tinymce.DOM.uniqueId();
}
// Only init the editor once
if (tinymce.get(elm.id)) {
initCount++;
return;
}
const initInstanceCallback = (editor: Editor) => {
this.css('visibility', '');
initCount++;
const origFn = settings.init_instance_callback;
if (typeof origFn === 'function') {
origFn.call(editor, editor);
}
if (initCount === this.length) {
allInitialized();
}
};
// Create editor instance and render it
tinymce.init({
...settings,
selector: undefined,
target: elm,
init_instance_callback: initInstanceCallback
});
}); // this.each
if (initCount === this.length) {
allInitialized();
}
}); // load tinymce
});
};
export const setupIntegration = () => {
const jq = getJquery();
// Add :tinymce pseudo selector this will select elements that has been converted into editor instances
// it's now possible to use things like $('*:tinymce') to get all TinyMCE bound elements.
jq.expr.pseudos.tinymce = (e: Element) => !!getTinymceInstance(e);
// Add a tinymce function for creating editors
(jq.fn as any).tinymce = tinymceFn;
};