/wp-includes/js/tinymce/plugins/wordpress/plugin.js

https://bitbucket.org/skyarch-iijima/wordpress · JavaScript · 1060 lines · 947 code · 87 blank · 26 comment · 103 complexity · 1bbdc179b04731a40281f164ae2da2b0 MD5 · raw file

  1. /* global getUserSetting, setUserSetting */
  2. ( function( tinymce ) {
  3. // Set the minimum value for the modals z-index higher than #wpadminbar (100000)
  4. if ( ! tinymce.ui.FloatPanel.zIndex || tinymce.ui.FloatPanel.zIndex < 100100 ) {
  5. tinymce.ui.FloatPanel.zIndex = 100100;
  6. }
  7. tinymce.PluginManager.add( 'wordpress', function( editor ) {
  8. var wpAdvButton, style,
  9. DOM = tinymce.DOM,
  10. each = tinymce.each,
  11. __ = editor.editorManager.i18n.translate,
  12. $ = window.jQuery,
  13. wp = window.wp,
  14. hasWpautop = ( wp && wp.editor && wp.editor.autop && editor.getParam( 'wpautop', true ) );
  15. if ( $ ) {
  16. $( document ).triggerHandler( 'tinymce-editor-setup', [ editor ] );
  17. }
  18. function toggleToolbars( state ) {
  19. var iframe, initial, toolbars,
  20. pixels = 0;
  21. initial = ( state === 'hide' );
  22. if ( editor.theme.panel ) {
  23. toolbars = editor.theme.panel.find('.toolbar:not(.menubar)');
  24. }
  25. if ( ! toolbars || toolbars.length < 2 || ( state === 'hide' && ! toolbars[1].visible() ) ) {
  26. return;
  27. }
  28. if ( ! state && toolbars[1].visible() ) {
  29. state = 'hide';
  30. }
  31. each( toolbars, function( toolbar, i ) {
  32. if ( i > 0 ) {
  33. if ( state === 'hide' ) {
  34. toolbar.hide();
  35. pixels += 30;
  36. } else {
  37. toolbar.show();
  38. pixels -= 30;
  39. }
  40. }
  41. });
  42. if ( pixels && ! initial ) {
  43. // Resize iframe, not needed in iOS
  44. if ( ! tinymce.Env.iOS ) {
  45. iframe = editor.getContentAreaContainer().firstChild;
  46. DOM.setStyle( iframe, 'height', iframe.clientHeight + pixels );
  47. }
  48. if ( state === 'hide' ) {
  49. setUserSetting('hidetb', '0');
  50. wpAdvButton && wpAdvButton.active( false );
  51. } else {
  52. setUserSetting('hidetb', '1');
  53. wpAdvButton && wpAdvButton.active( true );
  54. }
  55. }
  56. editor.fire( 'wp-toolbar-toggle' );
  57. }
  58. // Add the kitchen sink button :)
  59. editor.addButton( 'wp_adv', {
  60. tooltip: 'Toolbar Toggle',
  61. cmd: 'WP_Adv',
  62. onPostRender: function() {
  63. wpAdvButton = this;
  64. wpAdvButton.active( getUserSetting( 'hidetb' ) === '1' ? true : false );
  65. }
  66. });
  67. // Hide the toolbars after loading
  68. editor.on( 'PostRender', function() {
  69. if ( editor.getParam( 'wordpress_adv_hidden', true ) && getUserSetting( 'hidetb', '0' ) === '0' ) {
  70. toggleToolbars( 'hide' );
  71. }
  72. });
  73. editor.addCommand( 'WP_Adv', function() {
  74. toggleToolbars();
  75. });
  76. editor.on( 'focus', function() {
  77. window.wpActiveEditor = editor.id;
  78. });
  79. editor.on( 'BeforeSetContent', function( event ) {
  80. var title;
  81. if ( event.content ) {
  82. if ( event.content.indexOf( '<!--more' ) !== -1 ) {
  83. title = __( 'Read more...' );
  84. event.content = event.content.replace( /<!--more(.*?)-->/g, function( match, moretext ) {
  85. return '<img src="' + tinymce.Env.transparentSrc + '" data-wp-more="more" data-wp-more-text="' + moretext + '" ' +
  86. 'class="wp-more-tag mce-wp-more" alt="" title="' + title + '" data-mce-resize="false" data-mce-placeholder="1" />';
  87. });
  88. }
  89. if ( event.content.indexOf( '<!--nextpage-->' ) !== -1 ) {
  90. title = __( 'Page break' );
  91. event.content = event.content.replace( /<!--nextpage-->/g,
  92. '<img src="' + tinymce.Env.transparentSrc + '" data-wp-more="nextpage" class="wp-more-tag mce-wp-nextpage" ' +
  93. 'alt="" title="' + title + '" data-mce-resize="false" data-mce-placeholder="1" />' );
  94. }
  95. if ( event.load && event.format !== 'raw' && hasWpautop ) {
  96. event.content = wp.editor.autop( event.content );
  97. }
  98. if ( event.content.indexOf( '<script' ) !== -1 || event.content.indexOf( '<style' ) !== -1 ) {
  99. event.content = event.content.replace( /<(script|style)[^>]*>[\s\S]*?<\/\1>/g, function( match, tag ) {
  100. return '<img ' +
  101. 'src="' + tinymce.Env.transparentSrc + '" ' +
  102. 'data-wp-preserve="' + encodeURIComponent( match ) + '" ' +
  103. 'data-mce-resize="false" ' +
  104. 'data-mce-placeholder="1" '+
  105. 'class="mce-object" ' +
  106. 'width="20" height="20" '+
  107. 'alt="&lt;' + tag + '&gt;" ' +
  108. 'title="&lt;' + tag + '&gt;" ' +
  109. '/>';
  110. } );
  111. }
  112. }
  113. });
  114. editor.on( 'setcontent', function() {
  115. // Remove spaces from empty paragraphs.
  116. editor.$( 'p' ).each( function( i, node ) {
  117. if ( node.innerHTML && node.innerHTML.length < 10 ) {
  118. var html = tinymce.trim( node.innerHTML );
  119. if ( ! html || html === '&nbsp;' ) {
  120. node.innerHTML = ( tinymce.Env.ie && tinymce.Env.ie < 11 ) ? '' : '<br data-mce-bogus="1">';
  121. }
  122. }
  123. } );
  124. });
  125. editor.on( 'PostProcess', function( event ) {
  126. if ( event.get ) {
  127. event.content = event.content.replace(/<img[^>]+>/g, function( image ) {
  128. var match,
  129. string,
  130. moretext = '';
  131. if ( image.indexOf( 'data-wp-more="more"' ) !== -1 ) {
  132. if ( match = image.match( /data-wp-more-text="([^"]+)"/ ) ) {
  133. moretext = match[1];
  134. }
  135. string = '<!--more' + moretext + '-->';
  136. } else if ( image.indexOf( 'data-wp-more="nextpage"' ) !== -1 ) {
  137. string = '<!--nextpage-->';
  138. } else if ( image.indexOf( 'data-wp-preserve' ) !== -1 ) {
  139. if ( match = image.match( / data-wp-preserve="([^"]+)"/ ) ) {
  140. string = decodeURIComponent( match[1] );
  141. }
  142. }
  143. return string || image;
  144. });
  145. }
  146. });
  147. // Display the tag name instead of img in element path
  148. editor.on( 'ResolveName', function( event ) {
  149. var attr;
  150. if ( event.target.nodeName === 'IMG' && ( attr = editor.dom.getAttrib( event.target, 'data-wp-more' ) ) ) {
  151. event.name = attr;
  152. }
  153. });
  154. // Register commands
  155. editor.addCommand( 'WP_More', function( tag ) {
  156. var parent, html, title,
  157. classname = 'wp-more-tag',
  158. dom = editor.dom,
  159. node = editor.selection.getNode(),
  160. rootNode = editor.getBody();
  161. tag = tag || 'more';
  162. classname += ' mce-wp-' + tag;
  163. title = tag === 'more' ? 'Read more...' : 'Next page';
  164. title = __( title );
  165. html = '<img src="' + tinymce.Env.transparentSrc + '" alt="" title="' + title + '" class="' + classname + '" ' +
  166. 'data-wp-more="' + tag + '" data-mce-resize="false" data-mce-placeholder="1" />';
  167. // Most common case
  168. if ( node === rootNode || ( node.nodeName === 'P' && node.parentNode === rootNode ) ) {
  169. editor.insertContent( html );
  170. return;
  171. }
  172. // Get the top level parent node
  173. parent = dom.getParent( node, function( found ) {
  174. if ( found.parentNode && found.parentNode === rootNode ) {
  175. return true;
  176. }
  177. return false;
  178. }, editor.getBody() );
  179. if ( parent ) {
  180. if ( parent.nodeName === 'P' ) {
  181. parent.appendChild( dom.create( 'p', null, html ).firstChild );
  182. } else {
  183. dom.insertAfter( dom.create( 'p', null, html ), parent );
  184. }
  185. editor.nodeChanged();
  186. }
  187. });
  188. editor.addCommand( 'WP_Code', function() {
  189. editor.formatter.toggle('code');
  190. });
  191. editor.addCommand( 'WP_Page', function() {
  192. editor.execCommand( 'WP_More', 'nextpage' );
  193. });
  194. editor.addCommand( 'WP_Help', function() {
  195. var access = tinymce.Env.mac ? __( 'Ctrl + Alt + letter:' ) : __( 'Shift + Alt + letter:' ),
  196. meta = tinymce.Env.mac ? __( 'Cmd + letter:' ) : __( 'Ctrl + letter:' ),
  197. table1 = [],
  198. table2 = [],
  199. row1 = {},
  200. row2 = {},
  201. i1 = 0,
  202. i2 = 0,
  203. labels = editor.settings.wp_shortcut_labels,
  204. header, html, dialog, $wrap;
  205. if ( ! labels ) {
  206. return;
  207. }
  208. function tr( row, columns ) {
  209. var out = '<tr>';
  210. var i = 0;
  211. columns = columns || 1;
  212. each( row, function( text, key ) {
  213. out += '<td><kbd>' + key + '</kbd></td><td>' + __( text ) + '</td>';
  214. i++;
  215. });
  216. while ( i < columns ) {
  217. out += '<td></td><td></td>';
  218. i++;
  219. }
  220. return out + '</tr>';
  221. }
  222. each ( labels, function( label, name ) {
  223. var letter;
  224. if ( label.indexOf( 'meta' ) !== -1 ) {
  225. i1++;
  226. letter = label.replace( 'meta', '' ).toLowerCase();
  227. if ( letter ) {
  228. row1[ letter ] = name;
  229. if ( i1 % 2 === 0 ) {
  230. table1.push( tr( row1, 2 ) );
  231. row1 = {};
  232. }
  233. }
  234. } else if ( label.indexOf( 'access' ) !== -1 ) {
  235. i2++;
  236. letter = label.replace( 'access', '' ).toLowerCase();
  237. if ( letter ) {
  238. row2[ letter ] = name;
  239. if ( i2 % 2 === 0 ) {
  240. table2.push( tr( row2, 2 ) );
  241. row2 = {};
  242. }
  243. }
  244. }
  245. } );
  246. // Add remaining single entries.
  247. if ( i1 % 2 > 0 ) {
  248. table1.push( tr( row1, 2 ) );
  249. }
  250. if ( i2 % 2 > 0 ) {
  251. table2.push( tr( row2, 2 ) );
  252. }
  253. header = [ __( 'Letter' ), __( 'Action' ), __( 'Letter' ), __( 'Action' ) ];
  254. header = '<tr><th>' + header.join( '</th><th>' ) + '</th></tr>';
  255. html = '<div class="wp-editor-help">';
  256. // Main section, default and additional shortcuts
  257. html = html +
  258. '<h2>' + __( 'Default shortcuts,' ) + ' ' + meta + '</h2>' +
  259. '<table class="wp-help-th-center fixed">' +
  260. header +
  261. table1.join('') +
  262. '</table>' +
  263. '<h2>' + __( 'Additional shortcuts,' ) + ' ' + access + '</h2>' +
  264. '<table class="wp-help-th-center fixed">' +
  265. header +
  266. table2.join('') +
  267. '</table>';
  268. if ( editor.plugins.wptextpattern && ( ! tinymce.Env.ie || tinymce.Env.ie > 8 ) ) {
  269. // Text pattern section
  270. html = html +
  271. '<h2>' + __( 'When starting a new paragraph with one of these formatting shortcuts followed by a space, the formatting will be applied automatically. Press Backspace or Escape to undo.' ) + '</h2>' +
  272. '<table class="wp-help-th-center fixed">' +
  273. tr({ '*': 'Bullet list', '1.': 'Numbered list' }) +
  274. tr({ '-': 'Bullet list', '1)': 'Numbered list' }) +
  275. '</table>';
  276. html = html +
  277. '<h2>' + __( 'The following formatting shortcuts are replaced when pressing Enter. Press Escape or the Undo button to undo.' ) + '</h2>' +
  278. '<table class="wp-help-single">' +
  279. tr({ '>': 'Blockquote' }) +
  280. tr({ '##': 'Heading 2' }) +
  281. tr({ '###': 'Heading 3' }) +
  282. tr({ '####': 'Heading 4' }) +
  283. tr({ '#####': 'Heading 5' }) +
  284. tr({ '######': 'Heading 6' }) +
  285. tr({ '---': 'Horizontal line' }) +
  286. '</table>';
  287. }
  288. // Focus management section
  289. html = html +
  290. '<h2>' + __( 'Focus shortcuts:' ) + '</h2>' +
  291. '<table class="wp-help-single">' +
  292. tr({ 'Alt + F8': 'Inline toolbar (when an image, link or preview is selected)' }) +
  293. tr({ 'Alt + F9': 'Editor menu (when enabled)' }) +
  294. tr({ 'Alt + F10': 'Editor toolbar' }) +
  295. tr({ 'Alt + F11': 'Elements path' }) +
  296. '</table>' +
  297. '<p>' + __( 'To move focus to other buttons use Tab or the arrow keys. To return focus to the editor press Escape or use one of the buttons.' ) + '</p>';
  298. html += '</div>';
  299. dialog = editor.windowManager.open( {
  300. title: 'Keyboard Shortcuts',
  301. items: {
  302. type: 'container',
  303. classes: 'wp-help',
  304. html: html
  305. },
  306. buttons: {
  307. text: 'Close',
  308. onclick: 'close'
  309. }
  310. } );
  311. if ( dialog.$el ) {
  312. dialog.$el.find( 'div[role="application"]' ).attr( 'role', 'document' );
  313. $wrap = dialog.$el.find( '.mce-wp-help' );
  314. if ( $wrap[0] ) {
  315. $wrap.attr( 'tabindex', '0' );
  316. $wrap[0].focus();
  317. $wrap.on( 'keydown', function( event ) {
  318. // Prevent use of: page up, page down, end, home, left arrow, up arrow, right arrow, down arrow
  319. // in the dialog keydown handler.
  320. if ( event.keyCode >= 33 && event.keyCode <= 40 ) {
  321. event.stopPropagation();
  322. }
  323. });
  324. }
  325. }
  326. } );
  327. editor.addCommand( 'WP_Medialib', function() {
  328. if ( wp && wp.media && wp.media.editor ) {
  329. wp.media.editor.open( editor.id );
  330. }
  331. });
  332. // Register buttons
  333. editor.addButton( 'wp_more', {
  334. tooltip: 'Insert Read More tag',
  335. onclick: function() {
  336. editor.execCommand( 'WP_More', 'more' );
  337. }
  338. });
  339. editor.addButton( 'wp_page', {
  340. tooltip: 'Page break',
  341. onclick: function() {
  342. editor.execCommand( 'WP_More', 'nextpage' );
  343. }
  344. });
  345. editor.addButton( 'wp_help', {
  346. tooltip: 'Keyboard Shortcuts',
  347. cmd: 'WP_Help'
  348. });
  349. editor.addButton( 'wp_code', {
  350. tooltip: 'Code',
  351. cmd: 'WP_Code',
  352. stateSelector: 'code'
  353. });
  354. // Menubar
  355. // Insert->Add Media
  356. if ( wp && wp.media && wp.media.editor ) {
  357. editor.addMenuItem( 'add_media', {
  358. text: 'Add Media',
  359. icon: 'wp-media-library',
  360. context: 'insert',
  361. cmd: 'WP_Medialib'
  362. });
  363. }
  364. // Insert "Read More..."
  365. editor.addMenuItem( 'wp_more', {
  366. text: 'Insert Read More tag',
  367. icon: 'wp_more',
  368. context: 'insert',
  369. onclick: function() {
  370. editor.execCommand( 'WP_More', 'more' );
  371. }
  372. });
  373. // Insert "Next Page"
  374. editor.addMenuItem( 'wp_page', {
  375. text: 'Page break',
  376. icon: 'wp_page',
  377. context: 'insert',
  378. onclick: function() {
  379. editor.execCommand( 'WP_More', 'nextpage' );
  380. }
  381. });
  382. editor.on( 'BeforeExecCommand', function(e) {
  383. if ( tinymce.Env.webkit && ( e.command === 'InsertUnorderedList' || e.command === 'InsertOrderedList' ) ) {
  384. if ( ! style ) {
  385. style = editor.dom.create( 'style', {'type': 'text/css'},
  386. '#tinymce,#tinymce span,#tinymce li,#tinymce li>span,#tinymce p,#tinymce p>span{font:medium sans-serif;color:#000;line-height:normal;}');
  387. }
  388. editor.getDoc().head.appendChild( style );
  389. }
  390. });
  391. editor.on( 'ExecCommand', function( e ) {
  392. if ( tinymce.Env.webkit && style &&
  393. ( 'InsertUnorderedList' === e.command || 'InsertOrderedList' === e.command ) ) {
  394. editor.dom.remove( style );
  395. }
  396. });
  397. editor.on( 'init', function() {
  398. var env = tinymce.Env,
  399. bodyClass = ['mceContentBody'], // back-compat for themes that use this in editor-style.css...
  400. doc = editor.getDoc(),
  401. dom = editor.dom;
  402. if ( env.iOS ) {
  403. dom.addClass( doc.documentElement, 'ios' );
  404. }
  405. if ( editor.getParam( 'directionality' ) === 'rtl' ) {
  406. bodyClass.push('rtl');
  407. dom.setAttrib( doc.documentElement, 'dir', 'rtl' );
  408. }
  409. dom.setAttrib( doc.documentElement, 'lang', editor.getParam( 'wp_lang_attr' ) );
  410. if ( env.ie ) {
  411. if ( parseInt( env.ie, 10 ) === 9 ) {
  412. bodyClass.push('ie9');
  413. } else if ( parseInt( env.ie, 10 ) === 8 ) {
  414. bodyClass.push('ie8');
  415. } else if ( env.ie < 8 ) {
  416. bodyClass.push('ie7');
  417. }
  418. } else if ( env.webkit ) {
  419. bodyClass.push('webkit');
  420. }
  421. bodyClass.push('wp-editor');
  422. each( bodyClass, function( cls ) {
  423. if ( cls ) {
  424. dom.addClass( doc.body, cls );
  425. }
  426. });
  427. // Remove invalid parent paragraphs when inserting HTML
  428. editor.on( 'BeforeSetContent', function( event ) {
  429. if ( event.content ) {
  430. event.content = event.content.replace( /<p>\s*<(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre)( [^>]*)?>/gi, '<$1$2>' )
  431. .replace( /<\/(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre)>\s*<\/p>/gi, '</$1>' );
  432. }
  433. });
  434. if ( $ ) {
  435. $( document ).triggerHandler( 'tinymce-editor-init', [editor] );
  436. }
  437. if ( window.tinyMCEPreInit && window.tinyMCEPreInit.dragDropUpload ) {
  438. dom.bind( doc, 'dragstart dragend dragover drop', function( event ) {
  439. if ( $ ) {
  440. // Trigger the jQuery handlers.
  441. $( document ).trigger( new $.Event( event ) );
  442. }
  443. });
  444. }
  445. if ( editor.getParam( 'wp_paste_filters', true ) ) {
  446. editor.on( 'PastePreProcess', function( event ) {
  447. // Remove trailing <br> added by WebKit browsers to the clipboard
  448. event.content = event.content.replace( /<br class="?Apple-interchange-newline"?>/gi, '' );
  449. // In WebKit this is handled by removeWebKitStyles()
  450. if ( ! tinymce.Env.webkit ) {
  451. // Remove all inline styles
  452. event.content = event.content.replace( /(<[^>]+) style="[^"]*"([^>]*>)/gi, '$1$2' );
  453. // Put back the internal styles
  454. event.content = event.content.replace(/(<[^>]+) data-mce-style=([^>]+>)/gi, '$1 style=$2' );
  455. }
  456. });
  457. editor.on( 'PastePostProcess', function( event ) {
  458. // Remove empty paragraphs
  459. editor.$( 'p', event.node ).each( function( i, node ) {
  460. if ( dom.isEmpty( node ) ) {
  461. dom.remove( node );
  462. }
  463. });
  464. if ( tinymce.isIE ) {
  465. editor.$( 'a', event.node ).find( 'font, u' ).each( function( i, node ) {
  466. dom.remove( node, true );
  467. });
  468. }
  469. });
  470. }
  471. if ( editor.settings.wp_shortcut_labels && editor.theme.panel ) {
  472. var labels = {};
  473. var access = 'Shift+Alt+';
  474. var meta = 'Ctrl+';
  475. // For Mac: ctrl = \u2303, cmd = \u2318, alt = \u2325
  476. if ( tinymce.Env.mac ) {
  477. access = '\u2303\u2325';
  478. meta = '\u2318';
  479. }
  480. each( editor.settings.wp_shortcut_labels, function( value, name ) {
  481. labels[ name ] = value.replace( 'access', access ).replace( 'meta', meta );
  482. } );
  483. each( editor.theme.panel.find('button'), function( button ) {
  484. if ( button && button.settings.tooltip && labels.hasOwnProperty( button.settings.tooltip ) ) {
  485. // Need to translate now. We are changing the string so it won't match and cannot be translated later.
  486. button.settings.tooltip = editor.translate( button.settings.tooltip ) + ' (' + labels[ button.settings.tooltip ] + ')';
  487. }
  488. } );
  489. // listbox for the "blocks" drop-down
  490. each( editor.theme.panel.find('listbox'), function( listbox ) {
  491. if ( listbox && listbox.settings.text === 'Paragraph' ) {
  492. each( listbox.settings.values, function( item ) {
  493. if ( item.text && labels.hasOwnProperty( item.text ) ) {
  494. item.shortcut = '(' + labels[ item.text ] + ')';
  495. }
  496. } );
  497. }
  498. } );
  499. }
  500. });
  501. editor.on( 'SaveContent', function( event ) {
  502. // If editor is hidden, we just want the textarea's value to be saved
  503. if ( ! editor.inline && editor.isHidden() ) {
  504. event.content = event.element.value;
  505. return;
  506. }
  507. // Keep empty paragraphs :(
  508. event.content = event.content.replace( /<p>(?:<br ?\/?>|\u00a0|\uFEFF| )*<\/p>/g, '<p>&nbsp;</p>' );
  509. if ( hasWpautop ) {
  510. event.content = wp.editor.removep( event.content );
  511. }
  512. });
  513. editor.on( 'preInit', function() {
  514. var validElementsSetting = '@[id|accesskey|class|dir|lang|style|tabindex|' +
  515. 'title|contenteditable|draggable|dropzone|hidden|spellcheck|translate],' + // Global attributes.
  516. 'i,' + // Don't replace <i> with <em> and <b> with <strong> and don't remove them when empty.
  517. 'b,' +
  518. 'script[src|async|defer|type|charset|crossorigin|integrity]'; // Add support for <script>.
  519. editor.schema.addValidElements( validElementsSetting );
  520. if ( tinymce.Env.iOS ) {
  521. editor.settings.height = 300;
  522. }
  523. each( {
  524. c: 'JustifyCenter',
  525. r: 'JustifyRight',
  526. l: 'JustifyLeft',
  527. j: 'JustifyFull',
  528. q: 'mceBlockQuote',
  529. u: 'InsertUnorderedList',
  530. o: 'InsertOrderedList',
  531. m: 'WP_Medialib',
  532. z: 'WP_Adv',
  533. t: 'WP_More',
  534. d: 'Strikethrough',
  535. h: 'WP_Help',
  536. p: 'WP_Page',
  537. x: 'WP_Code'
  538. }, function( command, key ) {
  539. editor.shortcuts.add( 'access+' + key, '', command );
  540. } );
  541. editor.addShortcut( 'meta+s', '', function() {
  542. if ( wp && wp.autosave ) {
  543. wp.autosave.server.triggerSave();
  544. }
  545. } );
  546. if ( window.getUserSetting( 'editor_plain_text_paste_warning' ) > 1 ) {
  547. editor.settings.paste_plaintext_inform = false;
  548. }
  549. // Change the editor iframe title on MacOS, add the correct help shortcut.
  550. if ( tinymce.Env.mac ) {
  551. tinymce.$( editor.iframeElement ).attr( 'title', __( 'Rich Text Area. Press Control-Option-H for help.' ) );
  552. }
  553. } );
  554. editor.on( 'PastePlainTextToggle', function( event ) {
  555. // Warn twice, then stop.
  556. if ( event.state === true ) {
  557. var times = parseInt( window.getUserSetting( 'editor_plain_text_paste_warning' ), 10 ) || 0;
  558. if ( times < 2 ) {
  559. window.setUserSetting( 'editor_plain_text_paste_warning', ++times );
  560. }
  561. }
  562. });
  563. /**
  564. * Experimental: create a floating toolbar.
  565. * This functionality will change in the next releases. Not recommended for use by plugins.
  566. */
  567. editor.on( 'preinit', function() {
  568. var Factory = tinymce.ui.Factory,
  569. settings = editor.settings,
  570. activeToolbar,
  571. currentSelection,
  572. timeout,
  573. container = editor.getContainer(),
  574. wpAdminbar = document.getElementById( 'wpadminbar' ),
  575. mceIframe = document.getElementById( editor.id + '_ifr' ),
  576. mceToolbar,
  577. mceStatusbar,
  578. wpStatusbar,
  579. isChromeRtl = ( editor.rtl && /Chrome/.test( navigator.userAgent ) );
  580. if ( container ) {
  581. mceToolbar = tinymce.$( '.mce-toolbar-grp', container )[0];
  582. mceStatusbar = tinymce.$( '.mce-statusbar', container )[0];
  583. }
  584. if ( editor.id === 'content' ) {
  585. wpStatusbar = document.getElementById( 'post-status-info' );
  586. }
  587. function create( buttons, bottom ) {
  588. var toolbar,
  589. toolbarItems = [],
  590. buttonGroup;
  591. each( buttons, function( item ) {
  592. var itemName;
  593. function bindSelectorChanged() {
  594. var selection = editor.selection;
  595. if ( itemName === 'bullist' ) {
  596. selection.selectorChanged( 'ul > li', function( state, args ) {
  597. var i = args.parents.length,
  598. nodeName;
  599. while ( i-- ) {
  600. nodeName = args.parents[ i ].nodeName;
  601. if ( nodeName === 'OL' || nodeName == 'UL' ) {
  602. break;
  603. }
  604. }
  605. item.active( state && nodeName === 'UL' );
  606. } );
  607. }
  608. if ( itemName === 'numlist' ) {
  609. selection.selectorChanged( 'ol > li', function( state, args ) {
  610. var i = args.parents.length,
  611. nodeName;
  612. while ( i-- ) {
  613. nodeName = args.parents[ i ].nodeName;
  614. if ( nodeName === 'OL' || nodeName === 'UL' ) {
  615. break;
  616. }
  617. }
  618. item.active( state && nodeName === 'OL' );
  619. } );
  620. }
  621. if ( item.settings.stateSelector ) {
  622. selection.selectorChanged( item.settings.stateSelector, function( state ) {
  623. item.active( state );
  624. }, true );
  625. }
  626. if ( item.settings.disabledStateSelector ) {
  627. selection.selectorChanged( item.settings.disabledStateSelector, function( state ) {
  628. item.disabled( state );
  629. } );
  630. }
  631. }
  632. if ( item === '|' ) {
  633. buttonGroup = null;
  634. } else {
  635. if ( Factory.has( item ) ) {
  636. item = {
  637. type: item
  638. };
  639. if ( settings.toolbar_items_size ) {
  640. item.size = settings.toolbar_items_size;
  641. }
  642. toolbarItems.push( item );
  643. buttonGroup = null;
  644. } else {
  645. if ( ! buttonGroup ) {
  646. buttonGroup = {
  647. type: 'buttongroup',
  648. items: []
  649. };
  650. toolbarItems.push( buttonGroup );
  651. }
  652. if ( editor.buttons[ item ] ) {
  653. itemName = item;
  654. item = editor.buttons[ itemName ];
  655. if ( typeof item === 'function' ) {
  656. item = item();
  657. }
  658. item.type = item.type || 'button';
  659. if ( settings.toolbar_items_size ) {
  660. item.size = settings.toolbar_items_size;
  661. }
  662. item = Factory.create( item );
  663. buttonGroup.items.push( item );
  664. if ( editor.initialized ) {
  665. bindSelectorChanged();
  666. } else {
  667. editor.on( 'init', bindSelectorChanged );
  668. }
  669. }
  670. }
  671. }
  672. } );
  673. toolbar = Factory.create( {
  674. type: 'panel',
  675. layout: 'stack',
  676. classes: 'toolbar-grp inline-toolbar-grp',
  677. ariaRoot: true,
  678. ariaRemember: true,
  679. items: [ {
  680. type: 'toolbar',
  681. layout: 'flow',
  682. items: toolbarItems
  683. } ]
  684. } );
  685. toolbar.bottom = bottom;
  686. function reposition() {
  687. if ( ! currentSelection ) {
  688. return this;
  689. }
  690. var scrollX = window.pageXOffset || document.documentElement.scrollLeft,
  691. scrollY = window.pageYOffset || document.documentElement.scrollTop,
  692. windowWidth = window.innerWidth,
  693. windowHeight = window.innerHeight,
  694. iframeRect = mceIframe ? mceIframe.getBoundingClientRect() : {
  695. top: 0,
  696. right: windowWidth,
  697. bottom: windowHeight,
  698. left: 0,
  699. width: windowWidth,
  700. height: windowHeight
  701. },
  702. toolbar = this.getEl(),
  703. toolbarWidth = toolbar.offsetWidth,
  704. toolbarHeight = toolbar.clientHeight,
  705. selection = currentSelection.getBoundingClientRect(),
  706. selectionMiddle = ( selection.left + selection.right ) / 2,
  707. buffer = 5,
  708. spaceNeeded = toolbarHeight + buffer,
  709. wpAdminbarBottom = wpAdminbar ? wpAdminbar.getBoundingClientRect().bottom : 0,
  710. mceToolbarBottom = mceToolbar ? mceToolbar.getBoundingClientRect().bottom : 0,
  711. mceStatusbarTop = mceStatusbar ? windowHeight - mceStatusbar.getBoundingClientRect().top : 0,
  712. wpStatusbarTop = wpStatusbar ? windowHeight - wpStatusbar.getBoundingClientRect().top : 0,
  713. blockedTop = Math.max( 0, wpAdminbarBottom, mceToolbarBottom, iframeRect.top ),
  714. blockedBottom = Math.max( 0, mceStatusbarTop, wpStatusbarTop, windowHeight - iframeRect.bottom ),
  715. spaceTop = selection.top + iframeRect.top - blockedTop,
  716. spaceBottom = windowHeight - iframeRect.top - selection.bottom - blockedBottom,
  717. editorHeight = windowHeight - blockedTop - blockedBottom,
  718. className = '',
  719. iosOffsetTop = 0,
  720. iosOffsetBottom = 0,
  721. top, left;
  722. if ( spaceTop >= editorHeight || spaceBottom >= editorHeight ) {
  723. this.scrolling = true;
  724. this.hide();
  725. this.scrolling = false;
  726. return this;
  727. }
  728. // Add offset in iOS to move the menu over the image, out of the way of the default iOS menu.
  729. if ( tinymce.Env.iOS && currentSelection.nodeName === 'IMG' ) {
  730. iosOffsetTop = 54;
  731. iosOffsetBottom = 46;
  732. }
  733. if ( this.bottom ) {
  734. if ( spaceBottom >= spaceNeeded ) {
  735. className = ' mce-arrow-up';
  736. top = selection.bottom + iframeRect.top + scrollY - iosOffsetBottom;
  737. } else if ( spaceTop >= spaceNeeded ) {
  738. className = ' mce-arrow-down';
  739. top = selection.top + iframeRect.top + scrollY - toolbarHeight + iosOffsetTop;
  740. }
  741. } else {
  742. if ( spaceTop >= spaceNeeded ) {
  743. className = ' mce-arrow-down';
  744. top = selection.top + iframeRect.top + scrollY - toolbarHeight + iosOffsetTop;
  745. } else if ( spaceBottom >= spaceNeeded && editorHeight / 2 > selection.bottom + iframeRect.top - blockedTop ) {
  746. className = ' mce-arrow-up';
  747. top = selection.bottom + iframeRect.top + scrollY - iosOffsetBottom;
  748. }
  749. }
  750. if ( typeof top === 'undefined' ) {
  751. top = scrollY + blockedTop + buffer + iosOffsetBottom;
  752. }
  753. left = selectionMiddle - toolbarWidth / 2 + iframeRect.left + scrollX;
  754. if ( selection.left < 0 || selection.right > iframeRect.width ) {
  755. left = iframeRect.left + scrollX + ( iframeRect.width - toolbarWidth ) / 2;
  756. } else if ( toolbarWidth >= windowWidth ) {
  757. className += ' mce-arrow-full';
  758. left = 0;
  759. } else if ( ( left < 0 && selection.left + toolbarWidth > windowWidth ) || ( left + toolbarWidth > windowWidth && selection.right - toolbarWidth < 0 ) ) {
  760. left = ( windowWidth - toolbarWidth ) / 2;
  761. } else if ( left < iframeRect.left + scrollX ) {
  762. className += ' mce-arrow-left';
  763. left = selection.left + iframeRect.left + scrollX;
  764. } else if ( left + toolbarWidth > iframeRect.width + iframeRect.left + scrollX ) {
  765. className += ' mce-arrow-right';
  766. left = selection.right - toolbarWidth + iframeRect.left + scrollX;
  767. }
  768. // No up/down arrows on the menu over images in iOS.
  769. if ( tinymce.Env.iOS && currentSelection.nodeName === 'IMG' ) {
  770. className = className.replace( / ?mce-arrow-(up|down)/g, '' );
  771. }
  772. toolbar.className = toolbar.className.replace( / ?mce-arrow-[\w]+/g, '' ) + className;
  773. DOM.setStyles( toolbar, {
  774. 'left': left,
  775. 'top': top
  776. } );
  777. return this;
  778. }
  779. toolbar.on( 'show', function() {
  780. this.reposition();
  781. if ( isChromeRtl ) {
  782. tinymce.$( '.mce-widget.mce-tooltip' ).addClass( 'wp-hide-mce-tooltip' );
  783. }
  784. } );
  785. toolbar.on( 'hide', function() {
  786. if ( isChromeRtl ) {
  787. tinymce.$( '.mce-widget.mce-tooltip' ).removeClass( 'wp-hide-mce-tooltip' );
  788. }
  789. } );
  790. toolbar.on( 'keydown', function( event ) {
  791. if ( event.keyCode === 27 ) {
  792. this.hide();
  793. editor.focus();
  794. }
  795. } );
  796. editor.on( 'remove', function() {
  797. toolbar.remove();
  798. } );
  799. toolbar.reposition = reposition;
  800. toolbar.hide().renderTo( document.body );
  801. return toolbar;
  802. }
  803. editor.shortcuts.add( 'alt+119', '', function() {
  804. var node;
  805. if ( activeToolbar ) {
  806. node = activeToolbar.find( 'toolbar' )[0];
  807. node && node.focus( true );
  808. }
  809. } );
  810. editor.on( 'nodechange', function( event ) {
  811. var collapsed = editor.selection.isCollapsed();
  812. var args = {
  813. element: event.element,
  814. parents: event.parents,
  815. collapsed: collapsed
  816. };
  817. editor.fire( 'wptoolbar', args );
  818. currentSelection = args.selection || args.element;
  819. if ( activeToolbar && activeToolbar !== args.toolbar ) {
  820. activeToolbar.hide();
  821. }
  822. if ( args.toolbar ) {
  823. activeToolbar = args.toolbar;
  824. if ( activeToolbar.visible() ) {
  825. activeToolbar.reposition();
  826. } else {
  827. activeToolbar.show();
  828. }
  829. } else {
  830. activeToolbar = false;
  831. }
  832. } );
  833. editor.on( 'focus', function() {
  834. if ( activeToolbar ) {
  835. activeToolbar.show();
  836. }
  837. } );
  838. function hide( event ) {
  839. if ( activeToolbar ) {
  840. if ( activeToolbar.tempHide || event.type === 'hide' || event.type === 'blur' ) {
  841. activeToolbar.hide();
  842. activeToolbar = false;
  843. } else if ( (
  844. event.type === 'resizewindow' ||
  845. event.type === 'scrollwindow' ||
  846. event.type === 'resize' ||
  847. event.type === 'scroll'
  848. ) && ! activeToolbar.blockHide ) {
  849. clearTimeout( timeout );
  850. timeout = setTimeout( function() {
  851. if ( activeToolbar && typeof activeToolbar.show === 'function' ) {
  852. activeToolbar.scrolling = false;
  853. activeToolbar.show();
  854. }
  855. }, 250 );
  856. activeToolbar.scrolling = true;
  857. activeToolbar.hide();
  858. }
  859. }
  860. }
  861. // For full height editor.
  862. editor.on( 'resizewindow scrollwindow', hide );
  863. // For scrollable editor.
  864. editor.dom.bind( editor.getWin(), 'resize scroll', hide );
  865. editor.on( 'remove', function() {
  866. editor.off( 'resizewindow scrollwindow', hide );
  867. editor.dom.unbind( editor.getWin(), 'resize scroll', hide );
  868. } );
  869. editor.on( 'blur hide', hide );
  870. editor.wp = editor.wp || {};
  871. editor.wp._createToolbar = create;
  872. }, true );
  873. function noop() {}
  874. // Expose some functions (back-compat)
  875. return {
  876. _showButtons: noop,
  877. _hideButtons: noop,
  878. _setEmbed: noop,
  879. _getEmbed: noop
  880. };
  881. });
  882. }( window.tinymce ));