You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

392 lines
12 KiB

  1. ( function( $ ) {
  2. function SwitchEditors() {
  3. var tinymce, $$,
  4. exports = {};
  5. function init() {
  6. if ( ! tinymce && window.tinymce ) {
  7. tinymce = window.tinymce;
  8. $$ = tinymce.$;
  9. $$( document ).on( 'click', function( event ) {
  10. var id, mode,
  11. target = $$( event.target );
  12. if ( target.hasClass( 'wp-switch-editor' ) ) {
  13. id = target.attr( 'data-wp-editor-id' );
  14. mode = target.hasClass( 'switch-tmce' ) ? 'tmce' : 'html';
  15. switchEditor( id, mode );
  16. }
  17. });
  18. }
  19. }
  20. function getToolbarHeight( editor ) {
  21. var node = $$( '.mce-toolbar-grp', editor.getContainer() )[0],
  22. height = node && node.clientHeight;
  23. if ( height && height > 10 && height < 200 ) {
  24. return parseInt( height, 10 );
  25. }
  26. return 30;
  27. }
  28. function switchEditor( id, mode ) {
  29. id = id || 'content';
  30. mode = mode || 'toggle';
  31. var editorHeight, toolbarHeight, iframe,
  32. editor = tinymce.get( id ),
  33. wrap = $$( '#wp-' + id + '-wrap' ),
  34. $textarea = $$( '#' + id ),
  35. textarea = $textarea[0];
  36. if ( 'toggle' === mode ) {
  37. if ( editor && ! editor.isHidden() ) {
  38. mode = 'html';
  39. } else {
  40. mode = 'tmce';
  41. }
  42. }
  43. if ( 'tmce' === mode || 'tinymce' === mode ) {
  44. if ( editor && ! editor.isHidden() ) {
  45. return false;
  46. }
  47. if ( typeof( window.QTags ) !== 'undefined' ) {
  48. window.QTags.closeAllTags( id );
  49. }
  50. editorHeight = parseInt( textarea.style.height, 10 ) || 0;
  51. if ( editor ) {
  52. editor.show();
  53. // No point resizing the iframe in iOS
  54. if ( ! tinymce.Env.iOS && editorHeight ) {
  55. toolbarHeight = getToolbarHeight( editor );
  56. editorHeight = editorHeight - toolbarHeight + 14;
  57. // height cannot be under 50 or over 5000
  58. if ( editorHeight > 50 && editorHeight < 5000 ) {
  59. editor.theme.resizeTo( null, editorHeight );
  60. }
  61. }
  62. } else {
  63. tinymce.init( window.tinyMCEPreInit.mceInit[id] );
  64. }
  65. wrap.removeClass( 'html-active' ).addClass( 'tmce-active' );
  66. $textarea.attr( 'aria-hidden', true );
  67. window.setUserSetting( 'editor', 'tinymce' );
  68. } else if ( 'html' === mode ) {
  69. if ( editor && editor.isHidden() ) {
  70. return false;
  71. }
  72. if ( editor ) {
  73. if ( ! tinymce.Env.iOS ) {
  74. iframe = editor.iframeElement;
  75. editorHeight = iframe ? parseInt( iframe.style.height, 10 ) : 0;
  76. if ( editorHeight ) {
  77. toolbarHeight = getToolbarHeight( editor );
  78. editorHeight = editorHeight + toolbarHeight - 14;
  79. // height cannot be under 50 or over 5000
  80. if ( editorHeight > 50 && editorHeight < 5000 ) {
  81. textarea.style.height = editorHeight + 'px';
  82. }
  83. }
  84. }
  85. editor.hide();
  86. } else {
  87. // The TinyMCE instance doesn't exist, show the textarea
  88. $textarea.css({ 'display': '', 'visibility': '' });
  89. }
  90. wrap.removeClass( 'tmce-active' ).addClass( 'html-active' );
  91. $textarea.attr( 'aria-hidden', false );
  92. window.setUserSetting( 'editor', 'html' );
  93. }
  94. }
  95. // Replace paragraphs with double line breaks
  96. function removep( html ) {
  97. var blocklist = 'blockquote|ul|ol|li|dl|dt|dd|table|thead|tbody|tfoot|tr|th|td|h[1-6]|fieldset|figure',
  98. blocklist1 = blocklist + '|div|p',
  99. blocklist2 = blocklist + '|pre',
  100. preserve_linebreaks = false,
  101. preserve_br = false,
  102. preserve = [];
  103. if ( ! html ) {
  104. return '';
  105. }
  106. // Preserve script and style tags.
  107. if ( html.indexOf( '<script' ) !== -1 || html.indexOf( '<style' ) !== -1 ) {
  108. html = html.replace( /<(script|style)[^>]*>[\s\S]*?<\/\1>/g, function( match ) {
  109. preserve.push( match );
  110. return '<wp-preserve>';
  111. } );
  112. }
  113. // Protect pre tags.
  114. if ( html.indexOf( '<pre' ) !== -1 ) {
  115. preserve_linebreaks = true;
  116. html = html.replace( /<pre[^>]*>[\s\S]+?<\/pre>/g, function( a ) {
  117. a = a.replace( /<br ?\/?>(\r\n|\n)?/g, '<wp-line-break>' );
  118. a = a.replace( /<\/?p( [^>]*)?>(\r\n|\n)?/g, '<wp-line-break>' );
  119. return a.replace( /\r?\n/g, '<wp-line-break>' );
  120. });
  121. }
  122. // keep <br> tags inside captions and remove line breaks
  123. if ( html.indexOf( '[caption' ) !== -1 ) {
  124. preserve_br = true;
  125. html = html.replace( /\[caption[\s\S]+?\[\/caption\]/g, function( a ) {
  126. return a.replace( /<br([^>]*)>/g, '<wp-temp-br$1>' ).replace( /[\r\n\t]+/, '' );
  127. });
  128. }
  129. // Pretty it up for the source editor
  130. html = html.replace( new RegExp( '\\s*</(' + blocklist1 + ')>\\s*', 'g' ), '</$1>\n' );
  131. html = html.replace( new RegExp( '\\s*<((?:' + blocklist1 + ')(?: [^>]*)?)>', 'g' ), '\n<$1>' );
  132. // Mark </p> if it has any attributes.
  133. html = html.replace( /(<p [^>]+>.*?)<\/p>/g, '$1</p#>' );
  134. // Separate <div> containing <p>
  135. html = html.replace( /<div( [^>]*)?>\s*<p>/gi, '<div$1>\n\n' );
  136. // Remove <p> and <br />
  137. html = html.replace( /\s*<p>/gi, '' );
  138. html = html.replace( /\s*<\/p>\s*/gi, '\n\n' );
  139. html = html.replace( /\n[\s\u00a0]+\n/g, '\n\n' );
  140. html = html.replace( /\s*<br ?\/?>\s*/gi, '\n' );
  141. // Fix some block element newline issues
  142. html = html.replace( /\s*<div/g, '\n<div' );
  143. html = html.replace( /<\/div>\s*/g, '</div>\n' );
  144. html = html.replace( /\s*\[caption([^\[]+)\[\/caption\]\s*/gi, '\n\n[caption$1[/caption]\n\n' );
  145. html = html.replace( /caption\]\n\n+\[caption/g, 'caption]\n\n[caption' );
  146. html = html.replace( new RegExp('\\s*<((?:' + blocklist2 + ')(?: [^>]*)?)\\s*>', 'g' ), '\n<$1>' );
  147. html = html.replace( new RegExp('\\s*</(' + blocklist2 + ')>\\s*', 'g' ), '</$1>\n' );
  148. html = html.replace( /<((li|dt|dd)[^>]*)>/g, ' \t<$1>' );
  149. if ( html.indexOf( '<option' ) !== -1 ) {
  150. html = html.replace( /\s*<option/g, '\n<option' );
  151. html = html.replace( /\s*<\/select>/g, '\n</select>' );
  152. }
  153. if ( html.indexOf( '<hr' ) !== -1 ) {
  154. html = html.replace( /\s*<hr( [^>]*)?>\s*/g, '\n\n<hr$1>\n\n' );
  155. }
  156. if ( html.indexOf( '<object' ) !== -1 ) {
  157. html = html.replace( /<object[\s\S]+?<\/object>/g, function( a ) {
  158. return a.replace( /[\r\n]+/g, '' );
  159. });
  160. }
  161. // Unmark special paragraph closing tags
  162. html = html.replace( /<\/p#>/g, '</p>\n' );
  163. html = html.replace( /\s*(<p [^>]+>[\s\S]*?<\/p>)/g, '\n$1' );
  164. // Trim whitespace
  165. html = html.replace( /^\s+/, '' );
  166. html = html.replace( /[\s\u00a0]+$/, '' );
  167. // put back the line breaks in pre|script
  168. if ( preserve_linebreaks ) {
  169. html = html.replace( /<wp-line-break>/g, '\n' );
  170. }
  171. // and the <br> tags in captions
  172. if ( preserve_br ) {
  173. html = html.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
  174. }
  175. // Put back preserved tags.
  176. if ( preserve.length ) {
  177. html = html.replace( /<wp-preserve>/g, function() {
  178. return preserve.shift();
  179. } );
  180. }
  181. return html;
  182. }
  183. // Similar to `wpautop()` in formatting.php
  184. function autop( text ) {
  185. var preserve_linebreaks = false,
  186. preserve_br = false,
  187. blocklist = 'table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre' +
  188. '|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section' +
  189. '|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary';
  190. // Normalize line breaks
  191. text = text.replace( /\r\n|\r/g, '\n' );
  192. if ( text.indexOf( '\n' ) === -1 ) {
  193. return text;
  194. }
  195. if ( text.indexOf( '<object' ) !== -1 ) {
  196. text = text.replace( /<object[\s\S]+?<\/object>/g, function( a ) {
  197. return a.replace( /\n+/g, '' );
  198. });
  199. }
  200. text = text.replace( /<[^<>]+>/g, function( a ) {
  201. return a.replace( /[\n\t ]+/g, ' ' );
  202. });
  203. // Protect pre|script tags
  204. if ( text.indexOf( '<pre' ) !== -1 || text.indexOf( '<script' ) !== -1 ) {
  205. preserve_linebreaks = true;
  206. text = text.replace( /<(pre|script)[^>]*>[\s\S]*?<\/\1>/g, function( a ) {
  207. return a.replace( /\n/g, '<wp-line-break>' );
  208. });
  209. }
  210. if ( text.indexOf( '<figcaption' ) !== -1 ) {
  211. text = text.replace( /\s*(<figcaption[^>]*>)/g, '$1' );
  212. text = text.replace( /<\/figcaption>\s*/g, '</figcaption>' );
  213. }
  214. // keep <br> tags inside captions and convert line breaks
  215. if ( text.indexOf( '[caption' ) !== -1 ) {
  216. preserve_br = true;
  217. text = text.replace( /\[caption[\s\S]+?\[\/caption\]/g, function( a ) {
  218. // keep existing <br>
  219. a = a.replace( /<br([^>]*)>/g, '<wp-temp-br$1>' );
  220. // no line breaks inside HTML tags
  221. a = a.replace( /<[^<>]+>/g, function( b ) {
  222. return b.replace( /[\n\t ]+/, ' ' );
  223. });
  224. // convert remaining line breaks to <br>
  225. return a.replace( /\s*\n\s*/g, '<wp-temp-br />' );
  226. });
  227. }
  228. text = text + '\n\n';
  229. text = text.replace( /<br \/>\s*<br \/>/gi, '\n\n' );
  230. text = text.replace( new RegExp( '(<(?:' + blocklist + ')(?: [^>]*)?>)', 'gi' ), '\n\n$1' );
  231. text = text.replace( new RegExp( '(</(?:' + blocklist + ')>)', 'gi' ), '$1\n\n' );
  232. text = text.replace( /<hr( [^>]*)?>/gi, '<hr$1>\n\n' ); // hr is self closing block element
  233. text = text.replace( /\s*<option/gi, '<option' ); // No <p> or <br> around <option>
  234. text = text.replace( /<\/option>\s*/gi, '</option>' );
  235. text = text.replace( /\n\s*\n+/g, '\n\n' );
  236. text = text.replace( /([\s\S]+?)\n\n/g, '<p>$1</p>\n' );
  237. text = text.replace( /<p>\s*?<\/p>/gi, '');
  238. text = text.replace( new RegExp( '<p>\\s*(</?(?:' + blocklist + ')(?: [^>]*)?>)\\s*</p>', 'gi' ), '$1' );
  239. text = text.replace( /<p>(<li.+?)<\/p>/gi, '$1');
  240. text = text.replace( /<p>\s*<blockquote([^>]*)>/gi, '<blockquote$1><p>');
  241. text = text.replace( /<\/blockquote>\s*<\/p>/gi, '</p></blockquote>');
  242. text = text.replace( new RegExp( '<p>\\s*(</?(?:' + blocklist + ')(?: [^>]*)?>)', 'gi' ), '$1' );
  243. text = text.replace( new RegExp( '(</?(?:' + blocklist + ')(?: [^>]*)?>)\\s*</p>', 'gi' ), '$1' );
  244. // Remove redundant spaces and line breaks after existing <br /> tags
  245. text = text.replace( /(<br[^>]*>)\s*\n/gi, '$1' );
  246. // Create <br /> from the remaining line breaks
  247. text = text.replace( /\s*\n/g, '<br />\n');
  248. text = text.replace( new RegExp( '(</?(?:' + blocklist + ')[^>]*>)\\s*<br />', 'gi' ), '$1' );
  249. text = text.replace( /<br \/>(\s*<\/?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)>)/gi, '$1' );
  250. text = text.replace( /(?:<p>|<br ?\/?>)*\s*\[caption([^\[]+)\[\/caption\]\s*(?:<\/p>|<br ?\/?>)*/gi, '[caption$1[/caption]' );
  251. text = text.replace( /(<(?:div|th|td|form|fieldset|dd)[^>]*>)(.*?)<\/p>/g, function( a, b, c ) {
  252. if ( c.match( /<p( [^>]*)?>/ ) ) {
  253. return a;
  254. }
  255. return b + '<p>' + c + '</p>';
  256. });
  257. // put back the line breaks in pre|script
  258. if ( preserve_linebreaks ) {
  259. text = text.replace( /<wp-line-break>/g, '\n' );
  260. }
  261. if ( preserve_br ) {
  262. text = text.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
  263. }
  264. return text;
  265. }
  266. // Add old events
  267. function pre_wpautop( html ) {
  268. var obj = { o: exports, data: html, unfiltered: html };
  269. if ( $ ) {
  270. $( 'body' ).trigger( 'beforePreWpautop', [ obj ] );
  271. }
  272. obj.data = removep( obj.data );
  273. if ( $ ) {
  274. $( 'body' ).trigger( 'afterPreWpautop', [ obj ] );
  275. }
  276. return obj.data;
  277. }
  278. function wpautop( text ) {
  279. var obj = { o: exports, data: text, unfiltered: text };
  280. if ( $ ) {
  281. $( 'body' ).trigger( 'beforeWpautop', [ obj ] );
  282. }
  283. obj.data = autop( obj.data );
  284. if ( $ ) {
  285. $( 'body' ).trigger( 'afterWpautop', [ obj ] );
  286. }
  287. return obj.data;
  288. }
  289. if ( $ ) {
  290. $( document ).ready( init );
  291. } else if ( document.addEventListener ) {
  292. document.addEventListener( 'DOMContentLoaded', init, false );
  293. window.addEventListener( 'load', init, false );
  294. } else if ( window.attachEvent ) {
  295. window.attachEvent( 'onload', init );
  296. document.attachEvent( 'onreadystatechange', function() {
  297. if ( 'complete' === document.readyState ) {
  298. init();
  299. }
  300. } );
  301. }
  302. window.wp = window.wp || {};
  303. window.wp.editor = window.wp.editor || {};
  304. window.wp.editor.autop = wpautop;
  305. window.wp.editor.removep = pre_wpautop;
  306. exports = {
  307. go: switchEditor,
  308. wpautop: wpautop,
  309. pre_wpautop: pre_wpautop,
  310. _wp_Autop: autop,
  311. _wp_Nop: removep
  312. };
  313. return exports;
  314. }
  315. window.switchEditors = new SwitchEditors();
  316. }( window.jQuery ));