|
1 /** |
|
2 * plugin.js |
|
3 * |
|
4 * Copyright, Moxiecode Systems AB |
|
5 * Released under LGPL License. |
|
6 * |
|
7 * License: http://www.tinymce.com/license |
|
8 * Contributing: http://www.tinymce.com/contributing |
|
9 */ |
|
10 |
|
11 /*global tinymce:true */ |
|
12 |
|
13 tinymce.PluginManager.add('link', function(editor) { |
|
14 function createLinkList(callback) { |
|
15 return function() { |
|
16 var linkList = editor.settings.link_list; |
|
17 |
|
18 if (typeof linkList == "string") { |
|
19 tinymce.util.XHR.send({ |
|
20 url: linkList, |
|
21 success: function(text) { |
|
22 callback(tinymce.util.JSON.parse(text)); |
|
23 } |
|
24 }); |
|
25 } else if (typeof linkList == "function") { |
|
26 linkList(callback); |
|
27 } else { |
|
28 callback(linkList); |
|
29 } |
|
30 }; |
|
31 } |
|
32 |
|
33 function buildListItems(inputList, itemCallback, startItems) { |
|
34 function appendItems(values, output) { |
|
35 output = output || []; |
|
36 |
|
37 tinymce.each(values, function(item) { |
|
38 var menuItem = {text: item.text || item.title}; |
|
39 |
|
40 if (item.menu) { |
|
41 menuItem.menu = appendItems(item.menu); |
|
42 } else { |
|
43 menuItem.value = item.value; |
|
44 |
|
45 if (itemCallback) { |
|
46 itemCallback(menuItem); |
|
47 } |
|
48 } |
|
49 |
|
50 output.push(menuItem); |
|
51 }); |
|
52 |
|
53 return output; |
|
54 } |
|
55 |
|
56 return appendItems(inputList, startItems || []); |
|
57 } |
|
58 |
|
59 function showDialog(linkList) { |
|
60 var data = {}, selection = editor.selection, dom = editor.dom, selectedElm, anchorElm, initialText; |
|
61 var win, onlyText, textListCtrl, linkListCtrl, relListCtrl, targetListCtrl, classListCtrl, linkTitleCtrl, value; |
|
62 |
|
63 function linkListChangeHandler(e) { |
|
64 var textCtrl = win.find('#text'); |
|
65 |
|
66 if (!textCtrl.value() || (e.lastControl && textCtrl.value() == e.lastControl.text())) { |
|
67 textCtrl.value(e.control.text()); |
|
68 } |
|
69 |
|
70 win.find('#href').value(e.control.value()); |
|
71 } |
|
72 |
|
73 function buildAnchorListControl(url) { |
|
74 var anchorList = []; |
|
75 |
|
76 tinymce.each(editor.dom.select('a:not([href])'), function(anchor) { |
|
77 var id = anchor.name || anchor.id; |
|
78 |
|
79 if (id) { |
|
80 anchorList.push({ |
|
81 text: id, |
|
82 value: '#' + id, |
|
83 selected: url.indexOf('#' + id) != -1 |
|
84 }); |
|
85 } |
|
86 }); |
|
87 |
|
88 if (anchorList.length) { |
|
89 anchorList.unshift({text: 'None', value: ''}); |
|
90 |
|
91 return { |
|
92 name: 'anchor', |
|
93 type: 'listbox', |
|
94 label: 'Anchors', |
|
95 values: anchorList, |
|
96 onselect: linkListChangeHandler |
|
97 }; |
|
98 } |
|
99 } |
|
100 |
|
101 function updateText() { |
|
102 if (!initialText && data.text.length === 0 && onlyText) { |
|
103 this.parent().parent().find('#text')[0].value(this.value()); |
|
104 } |
|
105 } |
|
106 |
|
107 function urlChange(e) { |
|
108 var meta = e.meta || {}; |
|
109 |
|
110 if (linkListCtrl) { |
|
111 linkListCtrl.value(editor.convertURL(this.value(), 'href')); |
|
112 } |
|
113 |
|
114 tinymce.each(e.meta, function(value, key) { |
|
115 win.find('#' + key).value(value); |
|
116 }); |
|
117 |
|
118 if (!meta.text) { |
|
119 updateText.call(this); |
|
120 } |
|
121 } |
|
122 |
|
123 function isOnlyTextSelected(anchorElm) { |
|
124 var html = selection.getContent(); |
|
125 |
|
126 // Partial html and not a fully selected anchor element |
|
127 if (/</.test(html) && (!/^<a [^>]+>[^<]+<\/a>$/.test(html) || html.indexOf('href=') == -1)) { |
|
128 return false; |
|
129 } |
|
130 |
|
131 if (anchorElm) { |
|
132 var nodes = anchorElm.childNodes, i; |
|
133 |
|
134 if (nodes.length === 0) { |
|
135 return false; |
|
136 } |
|
137 |
|
138 for (i = nodes.length - 1; i >= 0; i--) { |
|
139 if (nodes[i].nodeType != 3) { |
|
140 return false; |
|
141 } |
|
142 } |
|
143 } |
|
144 |
|
145 return true; |
|
146 } |
|
147 |
|
148 selectedElm = selection.getNode(); |
|
149 anchorElm = dom.getParent(selectedElm, 'a[href]'); |
|
150 onlyText = isOnlyTextSelected(); |
|
151 |
|
152 data.text = initialText = anchorElm ? (anchorElm.innerText || anchorElm.textContent) : selection.getContent({format: 'text'}); |
|
153 data.href = anchorElm ? dom.getAttrib(anchorElm, 'href') : ''; |
|
154 |
|
155 if (anchorElm) { |
|
156 data.target = dom.getAttrib(anchorElm, 'target'); |
|
157 } else if (editor.settings.default_link_target) { |
|
158 data.target = editor.settings.default_link_target; |
|
159 } |
|
160 |
|
161 if ((value = dom.getAttrib(anchorElm, 'rel'))) { |
|
162 data.rel = value; |
|
163 } |
|
164 |
|
165 if ((value = dom.getAttrib(anchorElm, 'class'))) { |
|
166 data['class'] = value; |
|
167 } |
|
168 |
|
169 if ((value = dom.getAttrib(anchorElm, 'title'))) { |
|
170 data.title = value; |
|
171 } |
|
172 |
|
173 if (onlyText) { |
|
174 textListCtrl = { |
|
175 name: 'text', |
|
176 type: 'textbox', |
|
177 size: 40, |
|
178 label: 'Text to display', |
|
179 onchange: function() { |
|
180 data.text = this.value(); |
|
181 } |
|
182 }; |
|
183 } |
|
184 |
|
185 if (linkList) { |
|
186 linkListCtrl = { |
|
187 type: 'listbox', |
|
188 label: 'Link list', |
|
189 values: buildListItems( |
|
190 linkList, |
|
191 function(item) { |
|
192 item.value = editor.convertURL(item.value || item.url, 'href'); |
|
193 }, |
|
194 [{text: 'None', value: ''}] |
|
195 ), |
|
196 onselect: linkListChangeHandler, |
|
197 value: editor.convertURL(data.href, 'href'), |
|
198 onPostRender: function() { |
|
199 linkListCtrl = this; |
|
200 } |
|
201 }; |
|
202 } |
|
203 |
|
204 if (editor.settings.target_list !== false) { |
|
205 if (!editor.settings.target_list) { |
|
206 editor.settings.target_list = [ |
|
207 {text: 'None', value: ''}, |
|
208 {text: 'New window', value: '_blank'} |
|
209 ]; |
|
210 } |
|
211 |
|
212 targetListCtrl = { |
|
213 name: 'target', |
|
214 type: 'listbox', |
|
215 label: 'Target', |
|
216 values: buildListItems(editor.settings.target_list) |
|
217 }; |
|
218 } |
|
219 |
|
220 if (editor.settings.rel_list) { |
|
221 relListCtrl = { |
|
222 name: 'rel', |
|
223 type: 'listbox', |
|
224 label: 'Rel', |
|
225 values: buildListItems(editor.settings.rel_list) |
|
226 }; |
|
227 } |
|
228 |
|
229 if (editor.settings.link_class_list) { |
|
230 classListCtrl = { |
|
231 name: 'class', |
|
232 type: 'listbox', |
|
233 label: 'Class', |
|
234 values: buildListItems( |
|
235 editor.settings.link_class_list, |
|
236 function(item) { |
|
237 if (item.value) { |
|
238 item.textStyle = function() { |
|
239 return editor.formatter.getCssText({inline: 'a', classes: [item.value]}); |
|
240 }; |
|
241 } |
|
242 } |
|
243 ) |
|
244 }; |
|
245 } |
|
246 |
|
247 if (editor.settings.link_title !== false) { |
|
248 linkTitleCtrl = { |
|
249 name: 'title', |
|
250 type: 'textbox', |
|
251 label: 'Title', |
|
252 value: data.title |
|
253 }; |
|
254 } |
|
255 |
|
256 win = editor.windowManager.open({ |
|
257 title: 'Insert link', |
|
258 data: data, |
|
259 body: [ |
|
260 { |
|
261 name: 'href', |
|
262 type: 'filepicker', |
|
263 filetype: 'file', |
|
264 size: 40, |
|
265 autofocus: true, |
|
266 label: 'Url', |
|
267 onchange: urlChange, |
|
268 onkeyup: updateText |
|
269 }, |
|
270 textListCtrl, |
|
271 linkTitleCtrl, |
|
272 buildAnchorListControl(data.href), |
|
273 linkListCtrl, |
|
274 relListCtrl, |
|
275 targetListCtrl, |
|
276 classListCtrl |
|
277 ], |
|
278 onSubmit: function(e) { |
|
279 /*eslint dot-notation: 0*/ |
|
280 var href; |
|
281 |
|
282 data = tinymce.extend(data, e.data); |
|
283 href = data.href; |
|
284 |
|
285 // Delay confirm since onSubmit will move focus |
|
286 function delayedConfirm(message, callback) { |
|
287 var rng = editor.selection.getRng(); |
|
288 |
|
289 window.setTimeout(function() { |
|
290 editor.windowManager.confirm(message, function(state) { |
|
291 editor.selection.setRng(rng); |
|
292 callback(state); |
|
293 }); |
|
294 }, 0); |
|
295 } |
|
296 |
|
297 function insertLink() { |
|
298 var linkAttrs = { |
|
299 href: href, |
|
300 target: data.target ? data.target : null, |
|
301 rel: data.rel ? data.rel : null, |
|
302 "class": data["class"] ? data["class"] : null, |
|
303 title: data.title ? data.title : null |
|
304 }; |
|
305 |
|
306 if (anchorElm) { |
|
307 editor.focus(); |
|
308 |
|
309 if (onlyText && data.text != initialText) { |
|
310 if ("innerText" in anchorElm) { |
|
311 anchorElm.innerText = data.text; |
|
312 } else { |
|
313 anchorElm.textContent = data.text; |
|
314 } |
|
315 } |
|
316 |
|
317 dom.setAttribs(anchorElm, linkAttrs); |
|
318 |
|
319 selection.select(anchorElm); |
|
320 editor.undoManager.add(); |
|
321 } else { |
|
322 if (onlyText) { |
|
323 editor.insertContent(dom.createHTML('a', linkAttrs, dom.encode(data.text))); |
|
324 } else { |
|
325 editor.execCommand('mceInsertLink', false, linkAttrs); |
|
326 } |
|
327 } |
|
328 } |
|
329 |
|
330 if (!href) { |
|
331 editor.execCommand('unlink'); |
|
332 return; |
|
333 } |
|
334 |
|
335 // Is email and not //user@domain.com |
|
336 if (href.indexOf('@') > 0 && href.indexOf('//') == -1 && href.indexOf('mailto:') == -1) { |
|
337 delayedConfirm( |
|
338 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?', |
|
339 function(state) { |
|
340 if (state) { |
|
341 href = 'mailto:' + href; |
|
342 } |
|
343 |
|
344 insertLink(); |
|
345 } |
|
346 ); |
|
347 |
|
348 return; |
|
349 } |
|
350 |
|
351 // Is not protocol prefixed |
|
352 if ((editor.settings.link_assume_external_targets && !/^\w+:/i.test(href)) || |
|
353 (!editor.settings.link_assume_external_targets && /^\s*www\./i.test(href))) { |
|
354 delayedConfirm( |
|
355 'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?', |
|
356 function(state) { |
|
357 if (state) { |
|
358 href = 'http://' + href; |
|
359 } |
|
360 |
|
361 insertLink(); |
|
362 } |
|
363 ); |
|
364 |
|
365 return; |
|
366 } |
|
367 |
|
368 insertLink(); |
|
369 } |
|
370 }); |
|
371 } |
|
372 |
|
373 editor.addButton('link', { |
|
374 icon: 'link', |
|
375 tooltip: 'Insert/edit link', |
|
376 shortcut: 'Meta+K', |
|
377 onclick: createLinkList(showDialog), |
|
378 stateSelector: 'a[href]' |
|
379 }); |
|
380 |
|
381 editor.addButton('unlink', { |
|
382 icon: 'unlink', |
|
383 tooltip: 'Remove link', |
|
384 cmd: 'unlink', |
|
385 stateSelector: 'a[href]' |
|
386 }); |
|
387 |
|
388 editor.addShortcut('Meta+K', '', createLinkList(showDialog)); |
|
389 editor.addCommand('mceLink', createLinkList(showDialog)); |
|
390 |
|
391 this.showDialog = showDialog; |
|
392 |
|
393 editor.addMenuItem('link', { |
|
394 icon: 'link', |
|
395 text: 'Insert/edit link', |
|
396 shortcut: 'Meta+K', |
|
397 onclick: createLinkList(showDialog), |
|
398 stateSelector: 'a[href]', |
|
399 context: 'insert', |
|
400 prependToContext: true |
|
401 }); |
|
402 }); |