insert.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /**
  2. * @file
  3. * JavaScript to activate "Insert" buttons on file and image fields.
  4. */
  5. (function ($) {
  6. /**
  7. * Behavior to add "Insert" buttons.
  8. */
  9. Drupal.behaviors.insert = {};
  10. Drupal.behaviors.insert.attach = function(context) {
  11. if (typeof(insertTextarea) == 'undefined') {
  12. insertTextarea = $('#edit-body textarea.text-full').get(0) || false;
  13. }
  14. // Keep track of the last active textarea (if not using WYSIWYG).
  15. $('textarea:not([name$="[data][title]"]):not(.insert-processed)', context).addClass('insert-processed').focus(insertSetActive).blur(insertRemoveActive);
  16. // Add the click handler to the insert button.
  17. $('.insert-button:not(.insert-processed)', context).addClass('insert-processed').click(insert);
  18. function insertSetActive() {
  19. insertTextarea = this;
  20. this.insertHasFocus = true;
  21. }
  22. function insertRemoveActive() {
  23. if (insertTextarea == this) {
  24. var thisTextarea = this;
  25. setTimeout(function() {
  26. thisTextarea.insertHasFocus = false;
  27. }, 1000);
  28. }
  29. }
  30. function insert() {
  31. var widgetType = $(this).attr('rel');
  32. var settings = Drupal.settings.insert.widgets[widgetType];
  33. var wrapper = $(this).parents(settings.wrapper).filter(':first').get(0);
  34. var style = $('.insert-style', wrapper).val();
  35. var content = $('input.insert-template[name$="[' + style + ']"]', wrapper).val();
  36. var filename = $('input.insert-filename', wrapper).val();
  37. var options = {
  38. widgetType: widgetType,
  39. filename: filename,
  40. style: style,
  41. fields: {}
  42. };
  43. // Update replacements.
  44. for (var fieldName in settings.fields) {
  45. var fieldValue = $(settings.fields[fieldName], wrapper).val();
  46. if (fieldValue) {
  47. fieldValue = fieldValue
  48. .replace(/&/g, '&')
  49. .replace(/"/g, '"')
  50. .replace(/'/g, ''')
  51. .replace(/</g, '&lt;')
  52. .replace(/>/g, '&gt;');
  53. }
  54. options['fields'][fieldName] = fieldValue;
  55. if (fieldValue) {
  56. var fieldRegExp = new RegExp('__' + fieldName + '(_or_filename)?__', 'g');
  57. content = content.replace(fieldRegExp, fieldValue);
  58. }
  59. else {
  60. var fieldRegExp = new RegExp('__' + fieldName + '_or_filename__', 'g');
  61. content = content.replace(fieldRegExp, filename);
  62. }
  63. }
  64. // File name replacement.
  65. var fieldRegExp = new RegExp('__filename__', 'g');
  66. content = content.replace(fieldRegExp, filename);
  67. // Check for a maximum dimension and scale down the width if necessary.
  68. // This is intended for use with Image Resize Filter.
  69. var widthMatches = content.match(/width[ ]*=[ ]*"(\d*)"/i);
  70. var heightMatches = content.match(/height[ ]*=[ ]*"(\d*)"/i);
  71. if (settings.maxWidth && widthMatches && parseInt(widthMatches[1]) > settings.maxWidth) {
  72. var insertRatio = settings.maxWidth / widthMatches[1];
  73. var width = settings.maxWidth;
  74. content = content.replace(/width[ ]*=[ ]*"?(\d*)"?/i, 'width="' + width + '"');
  75. if (heightMatches) {
  76. var height = Math.round(heightMatches[1] * insertRatio);
  77. content = content.replace(/height[ ]*=[ ]*"?(\d*)"?/i, 'height="' + height + '"');
  78. }
  79. }
  80. // Allow other modules to perform replacements.
  81. options['content'] = content;
  82. $.event.trigger('insertIntoActiveEditor', [options]);
  83. content = options['content'];
  84. // Cleanup unused replacements.
  85. content = content.replace(/"__([a-z0-9_]+)__"/g, '""');
  86. // Cleanup empty attributes (other than alt).
  87. content = content.replace(/([a-z]+)[ ]*=[ ]*""/g, function(match, tagName) {
  88. return (tagName === 'alt') ? match : '';
  89. });
  90. // Insert the text.
  91. Drupal.insert.insertIntoActiveEditor(content);
  92. }
  93. };
  94. // General Insert API functions.
  95. Drupal.insert = {
  96. /**
  97. * Insert content into the current (or last active) editor on the page. This
  98. * should work with most WYSIWYGs as well as plain textareas.
  99. *
  100. * @param content
  101. */
  102. insertIntoActiveEditor: function(content) {
  103. var editorElement;
  104. // Always work in normal text areas that currently have focus.
  105. if (insertTextarea && insertTextarea.insertHasFocus) {
  106. editorElement = insertTextarea;
  107. Drupal.insert.insertAtCursor(insertTextarea, content);
  108. }
  109. // Direct tinyMCE support.
  110. else if (typeof(tinyMCE) != 'undefined' && tinyMCE.activeEditor) {
  111. editorElement = document.getElementById(tinyMCE.activeEditor.editorId);
  112. Drupal.insert.activateTabPane(editorElement);
  113. tinyMCE.activeEditor.execCommand('mceInsertContent', false, content);
  114. }
  115. // WYSIWYG support, should work in all editors if available.
  116. else if (Drupal.wysiwyg && Drupal.wysiwyg.activeId) {
  117. editorElement = document.getElementById(Drupal.wysiwyg.activeId);
  118. Drupal.insert.activateTabPane(editorElement);
  119. Drupal.wysiwyg.instances[Drupal.wysiwyg.activeId].insert(content)
  120. }
  121. // FCKeditor module support.
  122. else if (typeof(FCKeditorAPI) != 'undefined' && typeof(fckActiveId) != 'undefined') {
  123. editorElement = document.getElementById(fckActiveId);
  124. Drupal.insert.activateTabPane(editorElement);
  125. FCKeditorAPI.Instances[fckActiveId].InsertHtml(content);
  126. }
  127. // Direct FCKeditor support (only body field supported).
  128. else if (typeof(FCKeditorAPI) != 'undefined') {
  129. // Try inserting into the body.
  130. if (FCKeditorAPI.Instances[insertTextarea.id]) {
  131. editorElement = insertTextarea;
  132. Drupal.insert.activateTabPane(editorElement);
  133. FCKeditorAPI.Instances[insertTextarea.id].InsertHtml(content);
  134. }
  135. // Try inserting into the first instance we find (may occur with very
  136. // old versions of FCKeditor).
  137. else {
  138. for (var n in FCKeditorAPI.Instances) {
  139. editorElement = document.getElementById(n);
  140. Drupal.insert.activateTabPane(editorElement);
  141. FCKeditorAPI.Instances[n].InsertHtml(content);
  142. break;
  143. }
  144. }
  145. }
  146. // CKeditor module support.
  147. else if (typeof(CKEDITOR) != 'undefined' && typeof(Drupal.ckeditorActiveId) != 'undefined') {
  148. editorElement = document.getElementById(Drupal.ckeditorActiveId);
  149. Drupal.insert.activateTabPane(editorElement);
  150. CKEDITOR.instances[Drupal.ckeditorActiveId].insertHtml(content);
  151. }
  152. // Direct CKeditor support (only body field supported).
  153. else if (typeof(CKEDITOR) != 'undefined' && CKEDITOR.instances[insertTextarea.id]) {
  154. editorElement = insertTextarea;
  155. Drupal.insert.activateTabPane(editorElement);
  156. CKEDITOR.instances[insertTextarea.id].insertHtml(content);
  157. }
  158. else if (insertTextarea) {
  159. editorElement = insertTextarea;
  160. Drupal.insert.activateTabPane(editorElement);
  161. Drupal.insert.insertAtCursor(insertTextarea, content);
  162. }
  163. if (editorElement) {
  164. Drupal.insert.contentWarning(editorElement, content);
  165. }
  166. return false;
  167. },
  168. /**
  169. * Check for vertical tabs and activate the pane containing the editor.
  170. *
  171. * @param editor
  172. * The DOM object of the editor that will be checked.
  173. */
  174. activateTabPane: function(editor) {
  175. var $pane = $(editor).parents('.vertical-tabs-pane:first');
  176. var $panes = $pane.parent('.vertical-tabs-panes');
  177. var $tabs = $panes.parents('.vertical-tabs:first').find('ul.vertical-tabs-list:first li a');
  178. if ($pane.size() && $pane.is(':hidden') && $panes.size() && $tabs.size()) {
  179. var index = $panes.children().index($pane);
  180. $tabs.eq(index).click();
  181. }
  182. },
  183. /**
  184. * Warn users when attempting to insert an image into an unsupported field.
  185. *
  186. * This function is only a 90% use-case, as it doesn't support when the filter
  187. * tip are hidden, themed, or when only one format is available. However it
  188. * should fail silently in these situations.
  189. */
  190. contentWarning: function(editorElement, content) {
  191. if (!content.match(/<img /)) return;
  192. var $wrapper = $(editorElement).parents('div.text-format-wrapper:first');
  193. if (!$wrapper.length) return;
  194. $wrapper.find('.filter-guidelines-item:visible li').each(function(index, element) {
  195. var expression = new RegExp(Drupal.t('Allowed HTML tags'));
  196. if (expression.exec(element.textContent) && !element.textContent.match(/<img>/)) {
  197. alert(Drupal.t("The selected text format will not allow it to display images. The text format will need to be changed for this image to display properly when saved."));
  198. }
  199. });
  200. },
  201. /**
  202. * Insert content into a textarea at the current cursor position.
  203. *
  204. * @param editor
  205. * The DOM object of the textarea that will receive the text.
  206. * @param content
  207. * The string to be inserted.
  208. */
  209. insertAtCursor: function(editor, content) {
  210. // Record the current scroll position.
  211. var scroll = editor.scrollTop;
  212. // IE support.
  213. if (document.selection) {
  214. editor.focus();
  215. sel = document.selection.createRange();
  216. sel.text = content;
  217. }
  218. // Mozilla/Firefox/Netscape 7+ support.
  219. else if (editor.selectionStart || editor.selectionStart == '0') {
  220. var startPos = editor.selectionStart;
  221. var endPos = editor.selectionEnd;
  222. editor.value = editor.value.substring(0, startPos) + content + editor.value.substring(endPos, editor.value.length);
  223. }
  224. // Fallback, just add to the end of the content.
  225. else {
  226. editor.value += content;
  227. }
  228. // Ensure the textarea does not unexpectedly scroll.
  229. editor.scrollTop = scroll;
  230. }
  231. };
  232. })(jQuery);