plugin.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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. /*global tinymce:true */
  11. tinymce.PluginManager.add('link', function(editor) {
  12. function createLinkList(callback) {
  13. return function() {
  14. var linkList = editor.settings.link_list;
  15. if (typeof(linkList) == "string") {
  16. tinymce.util.XHR.send({
  17. url: linkList,
  18. success: function(text) {
  19. callback(tinymce.util.JSON.parse(text));
  20. }
  21. });
  22. } else {
  23. callback(linkList);
  24. }
  25. };
  26. }
  27. function showDialog(linkList) {
  28. var data = {}, selection = editor.selection, dom = editor.dom, selectedElm, anchorElm, initialText;
  29. var win, onlyText, textListCtrl, linkListCtrl, relListCtrl, targetListCtrl, classListCtrl, linkTitleCtrl;
  30. function linkListChangeHandler(e) {
  31. var textCtrl = win.find('#text');
  32. if (!textCtrl.value() || (e.lastControl && textCtrl.value() == e.lastControl.text())) {
  33. textCtrl.value(e.control.text());
  34. }
  35. win.find('#href').value(e.control.value());
  36. }
  37. function buildLinkList() {
  38. var linkListItems = [{text: 'None', value: ''}];
  39. tinymce.each(linkList, function(link) {
  40. linkListItems.push({
  41. text: link.text || link.title,
  42. value: editor.convertURL(link.value || link.url, 'href'),
  43. menu: link.menu
  44. });
  45. });
  46. return linkListItems;
  47. }
  48. function applyPreview(items) {
  49. tinymce.each(items, function(item) {
  50. item.textStyle = function() {
  51. return editor.formatter.getCssText({inline: 'a', classes: [item.value]});
  52. };
  53. });
  54. return items;
  55. }
  56. function buildValues(listSettingName, dataItemName, defaultItems) {
  57. var selectedItem, items = [];
  58. tinymce.each(editor.settings[listSettingName] || defaultItems, function(target) {
  59. var item = {
  60. text: target.text || target.title,
  61. value: target.value
  62. };
  63. items.push(item);
  64. if (data[dataItemName] === target.value || (!selectedItem && target.selected)) {
  65. selectedItem = item;
  66. }
  67. });
  68. if (selectedItem && !data[dataItemName]) {
  69. data[dataItemName] = selectedItem.value;
  70. selectedItem.selected = true;
  71. }
  72. return items;
  73. }
  74. function buildAnchorListControl(url) {
  75. var anchorList = [];
  76. tinymce.each(editor.dom.select('a:not([href])'), function(anchor) {
  77. var id = anchor.name || anchor.id;
  78. if (id) {
  79. anchorList.push({
  80. text: id,
  81. value: '#' + id,
  82. selected: url.indexOf('#' + id) != -1
  83. });
  84. }
  85. });
  86. if (anchorList.length) {
  87. anchorList.unshift({text: 'None', value: ''});
  88. return {
  89. name: 'anchor',
  90. type: 'listbox',
  91. label: 'Anchors',
  92. values: anchorList,
  93. onselect: linkListChangeHandler
  94. };
  95. }
  96. }
  97. function urlChange() {
  98. if (linkListCtrl) {
  99. linkListCtrl.value(editor.convertURL(this.value(), 'href'));
  100. }
  101. if (!initialText && data.text.length === 0 && onlyText) {
  102. this.parent().parent().find('#text')[0].value(this.value());
  103. }
  104. }
  105. function isOnlyTextSelected(anchorElm) {
  106. var html = selection.getContent();
  107. // Partial html and not a fully selected anchor element
  108. if (/</.test(html) && (!/^<a [^>]+>[^<]+<\/a>$/.test(html) || html.indexOf('href=') == -1)) {
  109. return false;
  110. }
  111. if (anchorElm) {
  112. var nodes = anchorElm.childNodes, i;
  113. if (nodes.length === 0) {
  114. return false;
  115. }
  116. for (i = nodes.length - 1; i >= 0; i--) {
  117. if (nodes[i].nodeType != 3) {
  118. return false;
  119. }
  120. }
  121. }
  122. return true;
  123. }
  124. selectedElm = selection.getNode();
  125. anchorElm = dom.getParent(selectedElm, 'a[href]');
  126. onlyText = isOnlyTextSelected();
  127. data.text = initialText = anchorElm ? (anchorElm.innerText || anchorElm.textContent) : selection.getContent({format: 'text'});
  128. data.href = anchorElm ? dom.getAttrib(anchorElm, 'href') : '';
  129. data.target = anchorElm ? dom.getAttrib(anchorElm, 'target') : (editor.settings.default_link_target || null);
  130. data.rel = anchorElm ? dom.getAttrib(anchorElm, 'rel') : null;
  131. data['class'] = anchorElm ? dom.getAttrib(anchorElm, 'class') : null;
  132. data.title = anchorElm ? dom.getAttrib(anchorElm, 'title') : '';
  133. if (onlyText) {
  134. textListCtrl = {
  135. name: 'text',
  136. type: 'textbox',
  137. size: 40,
  138. label: 'Text to display',
  139. onchange: function() {
  140. data.text = this.value();
  141. }
  142. };
  143. }
  144. if (linkList) {
  145. linkListCtrl = {
  146. type: 'listbox',
  147. label: 'Link list',
  148. values: buildLinkList(),
  149. onselect: linkListChangeHandler,
  150. value: editor.convertURL(data.href, 'href'),
  151. onPostRender: function() {
  152. linkListCtrl = this;
  153. }
  154. };
  155. }
  156. if (editor.settings.target_list !== false) {
  157. targetListCtrl = {
  158. name: 'target',
  159. type: 'listbox',
  160. label: 'Target',
  161. values: buildValues('target_list', 'target', [{text: 'None', value: ''}, {text: 'New window', value: '_blank'}])
  162. };
  163. }
  164. if (editor.settings.rel_list) {
  165. relListCtrl = {
  166. name: 'rel',
  167. type: 'listbox',
  168. label: 'Rel',
  169. values: buildValues('rel_list', 'rel', [{text: 'None', value: ''}])
  170. };
  171. }
  172. if (editor.settings.link_class_list) {
  173. classListCtrl = {
  174. name: 'class',
  175. type: 'listbox',
  176. label: 'Class',
  177. values: applyPreview(buildValues('link_class_list', 'class'))
  178. };
  179. }
  180. if (editor.settings.link_title !== false) {
  181. linkTitleCtrl = {
  182. name: 'title',
  183. type: 'textbox',
  184. label: 'Title',
  185. value: data.title
  186. };
  187. }
  188. win = editor.windowManager.open({
  189. title: 'Insert link',
  190. data: data,
  191. body: [
  192. {
  193. name: 'href',
  194. type: 'filepicker',
  195. filetype: 'file',
  196. size: 40,
  197. autofocus: true,
  198. label: 'Url',
  199. onchange: urlChange,
  200. onkeyup: urlChange
  201. },
  202. textListCtrl,
  203. linkTitleCtrl,
  204. buildAnchorListControl(data.href),
  205. linkListCtrl,
  206. relListCtrl,
  207. targetListCtrl,
  208. classListCtrl
  209. ],
  210. onSubmit: function(e) {
  211. var href;
  212. data = tinymce.extend(data, e.data);
  213. href = data.href;
  214. // Delay confirm since onSubmit will move focus
  215. function delayedConfirm(message, callback) {
  216. var rng = editor.selection.getRng();
  217. window.setTimeout(function() {
  218. editor.windowManager.confirm(message, function(state) {
  219. editor.selection.setRng(rng);
  220. callback(state);
  221. });
  222. }, 0);
  223. }
  224. function insertLink() {
  225. var linkAttrs = {
  226. href: href,
  227. target: data.target ? data.target : null,
  228. rel: data.rel ? data.rel : null,
  229. "class": data["class"] ? data["class"] : null,
  230. title: data.title ? data.title : null
  231. };
  232. if (anchorElm) {
  233. editor.focus();
  234. if (onlyText && data.text != initialText) {
  235. if ("innerText" in anchorElm) {
  236. anchorElm.innerText = data.text;
  237. } else {
  238. anchorElm.textContent = data.text;
  239. }
  240. }
  241. dom.setAttribs(anchorElm, linkAttrs);
  242. selection.select(anchorElm);
  243. editor.undoManager.add();
  244. } else {
  245. if (onlyText) {
  246. editor.insertContent(dom.createHTML('a', linkAttrs, dom.encode(data.text)));
  247. } else {
  248. editor.execCommand('mceInsertLink', false, linkAttrs);
  249. }
  250. }
  251. }
  252. if (!href) {
  253. editor.execCommand('unlink');
  254. return;
  255. }
  256. // Is email and not //user@domain.com
  257. if (href.indexOf('@') > 0 && href.indexOf('//') == -1 && href.indexOf('mailto:') == -1) {
  258. delayedConfirm(
  259. 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?',
  260. function(state) {
  261. if (state) {
  262. href = 'mailto:' + href;
  263. }
  264. insertLink();
  265. }
  266. );
  267. return;
  268. }
  269. // Is www. prefixed
  270. if (/^\s*www\./i.test(href)) {
  271. delayedConfirm(
  272. 'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?',
  273. function(state) {
  274. if (state) {
  275. href = 'http://' + href;
  276. }
  277. insertLink();
  278. }
  279. );
  280. return;
  281. }
  282. insertLink();
  283. }
  284. });
  285. }
  286. editor.addButton('link', {
  287. icon: 'link',
  288. tooltip: 'Insert/edit link',
  289. shortcut: 'Ctrl+K',
  290. onclick: createLinkList(showDialog),
  291. stateSelector: 'a[href]'
  292. });
  293. editor.addButton('unlink', {
  294. icon: 'unlink',
  295. tooltip: 'Remove link',
  296. cmd: 'unlink',
  297. stateSelector: 'a[href]'
  298. });
  299. editor.addShortcut('Ctrl+K', '', createLinkList(showDialog));
  300. this.showDialog = showDialog;
  301. editor.addMenuItem('link', {
  302. icon: 'link',
  303. text: 'Insert link',
  304. shortcut: 'Ctrl+K',
  305. onclick: createLinkList(showDialog),
  306. stateSelector: 'a[href]',
  307. context: 'insert',
  308. prependToContext: true
  309. });
  310. });