PageRenderTime 41ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-content/plugins/jetpack/modules/after-the-deadline/tinymce/plugin.js

https://gitlab.com/juanito.abelo/nlmobile
JavaScript | 427 lines | 310 code | 71 blank | 46 comment | 46 complexity | e670dfa659516e3b35231ae5b501697e MD5 | raw file
  1. /* global tinymce */
  2. /*
  3. * TinyMCE Writing Improvement Tool Plugin
  4. * Author: Raphael Mudge (raffi@automattic.com)
  5. *
  6. * Updated for TinyMCE 4.0
  7. *
  8. * http://www.afterthedeadline.com
  9. *
  10. * Distributed under the LGPL
  11. *
  12. * Derived from:
  13. * $Id: editor_plugin_src.js 425 2007-11-21 15:17:39Z spocke $
  14. *
  15. * @author Moxiecode
  16. * @copyright Copyright (C) 2004-2008, Moxiecode Systems AB, All rights reserved.
  17. *
  18. * Moxiecode Spell Checker plugin released under the LGPL with TinyMCE
  19. */
  20. tinymce.PluginManager.add( 'AtD', function( editor ) {
  21. var suggestionsMenu, started, atdCore, dom,
  22. each = tinymce.each;
  23. /* initializes the functions used by the AtD Core UI Module */
  24. function initAtDCore() {
  25. atdCore = new window.AtDCore();
  26. atdCore.map = each;
  27. atdCore._isTinyMCE = true;
  28. atdCore.getAttrib = function( node, key ) {
  29. return dom.getAttrib( node, key );
  30. };
  31. atdCore.findSpans = function( parent ) {
  32. if ( parent === undefined ) {
  33. return dom.select('span');
  34. } else {
  35. return dom.select( 'span', parent );
  36. }
  37. };
  38. atdCore.hasClass = function( node, className ) {
  39. return dom.hasClass( node, className );
  40. };
  41. atdCore.contents = function( node ) {
  42. return node.childNodes;
  43. };
  44. atdCore.replaceWith = function( old_node, new_node ) {
  45. return dom.replace( new_node, old_node );
  46. };
  47. atdCore.create = function( node_html ) {
  48. return dom.create( 'span', { 'class': 'mceItemHidden', 'data-mce-bogus': 1 }, node_html );
  49. };
  50. atdCore.removeParent = function( node ) {
  51. dom.remove( node, true );
  52. return node;
  53. };
  54. atdCore.remove = function( node ) {
  55. dom.remove( node );
  56. };
  57. atdCore.setIgnoreStrings( editor.getParam( 'atd_ignore_strings', [] ).join(',') );
  58. atdCore.showTypes( editor.getParam( 'atd_show_types', '' ) );
  59. }
  60. function getLang( key, defaultStr ) {
  61. return ( window.AtD_l10n_r0ar && window.AtD_l10n_r0ar[key] ) || defaultStr;
  62. }
  63. function isMarkedNode( node ) {
  64. return ( node.className && /\bhidden(GrammarError|SpellError|Suggestion)\b/.test( node.className ) );
  65. }
  66. function markMyWords( errors ) {
  67. return atdCore.markMyWords( atdCore.contents( editor.getBody() ), errors );
  68. }
  69. // If no more suggestions, finish.
  70. function checkIfFinished() {
  71. if ( ! editor.dom.select('span.hiddenSpellError, span.hiddenGrammarError, span.hiddenSuggestion').length ) {
  72. if ( suggestionsMenu ) {
  73. suggestionsMenu.hideMenu();
  74. }
  75. finish();
  76. }
  77. }
  78. function ignoreWord( target, word, all ) {
  79. var dom = editor.dom;
  80. if ( all ) {
  81. each( editor.dom.select( 'span.hiddenSpellError, span.hiddenGrammarError, span.hiddenSuggestion' ), function( node ) {
  82. var text = node.innerText || node.textContent;
  83. if ( text === word ) {
  84. dom.remove( node, true );
  85. }
  86. });
  87. } else {
  88. dom.remove( target, true );
  89. }
  90. checkIfFinished();
  91. }
  92. // Called when the user clicks "Finish" or when no more suggestions left.
  93. // Removes all remaining spans and fires custom event.
  94. function finish() {
  95. var node,
  96. dom = editor.dom,
  97. regex = new RegExp( 'mceItemHidden|hidden(((Grammar|Spell)Error)|Suggestion)' ),
  98. nodes = dom.select('span'),
  99. i = nodes.length;
  100. while ( i-- ) { // reversed
  101. node = nodes[i];
  102. if ( node.className && regex.test( node.className ) ) {
  103. dom.remove( node, true );
  104. }
  105. }
  106. // Rebuild the DOM so AtD core can find the text nodes
  107. editor.setContent( editor.getContent({ format: 'raw' }), { format: 'raw' } );
  108. started = false;
  109. editor.nodeChanged();
  110. editor.fire('SpellcheckEnd');
  111. }
  112. function sendRequest( file, data, success ) {
  113. var id = editor.getParam( 'atd_rpc_id', '12345678' ),
  114. url = editor.getParam( 'atd_rpc_url', '{backend}' );
  115. if ( url === '{backend}' || id === '12345678' ) {
  116. window.alert( 'Please specify: atd_rpc_url and atd_rpc_id' );
  117. return;
  118. }
  119. // create the nifty spinny thing that says "hizzo, I'm doing something fo realz"
  120. editor.setProgressState( true );
  121. tinymce.util.XHR.send({
  122. url: url + '/' + file,
  123. content_type: 'text/xml',
  124. type: 'POST',
  125. data: 'data=' + encodeURI( data ).replace( /&/g, '%26' ) + '&key=' + id,
  126. success: success,
  127. error: function( type, req, o ) {
  128. editor.setProgressState();
  129. window.alert( type + '\n' + req.status + '\nAt: ' + o.url );
  130. }
  131. });
  132. }
  133. function storeIgnoredStrings( /*text*/ ) {
  134. // Store in sessionStorage?
  135. }
  136. function setAlwaysIgnore( text ) {
  137. var url = editor.getParam( 'atd_ignore_rpc_url' );
  138. if ( ! url || url === '{backend}' ) {
  139. // Store ignored words for this session only
  140. storeIgnoredStrings( text );
  141. } else {
  142. // Plugin is configured to send ignore preferences to server, do that
  143. tinymce.util.XHR.send({
  144. url: url + encodeURIComponent( text ) + '&key=' + editor.getParam( 'atd_rpc_id', '12345678' ),
  145. content_type: 'text/xml',
  146. type: 'GET',
  147. error: function() {
  148. storeIgnoredStrings( text );
  149. }
  150. });
  151. }
  152. // Update atd_ignore_strings with the new value
  153. atdCore.setIgnoreStrings( text );
  154. }
  155. // Create the suggestions menu
  156. function showSuggestions( target ) {
  157. var pos, root, targetPos,
  158. items = [],
  159. text = target.innerText || target.textContent,
  160. errorDescription = atdCore.findSuggestion( target );
  161. if ( ! errorDescription ) {
  162. items.push({
  163. text: getLang( 'menu_title_no_suggestions', 'No suggestions' ),
  164. classes: 'atd-menu-title',
  165. disabled: true
  166. });
  167. } else {
  168. items.push({
  169. text: errorDescription.description,
  170. classes: 'atd-menu-title',
  171. disabled: true
  172. });
  173. if ( errorDescription.suggestions.length ) {
  174. items.push({ text: '-' }); // separator
  175. each( errorDescription.suggestions, function( suggestion ) {
  176. items.push({
  177. text: suggestion,
  178. onclick: function() {
  179. atdCore.applySuggestion( target, suggestion );
  180. checkIfFinished();
  181. }
  182. });
  183. });
  184. }
  185. }
  186. if ( errorDescription && errorDescription.moreinfo ) {
  187. items.push({ text: '-' }); // separator
  188. items.push({
  189. text: getLang( 'menu_option_explain', 'Explain...' ),
  190. onclick : function() {
  191. editor.windowManager.open({
  192. title: getLang( 'menu_option_explain', 'Explain...' ),
  193. url: errorDescription.moreinfo,
  194. width: 480,
  195. height: 380,
  196. inline: true
  197. });
  198. }
  199. });
  200. }
  201. items.push.apply( items, [
  202. { text: '-' }, // separator
  203. { text: getLang( 'menu_option_ignore_once', 'Ignore suggestion' ), onclick: function() {
  204. ignoreWord( target, text );
  205. }}
  206. ]);
  207. if ( editor.getParam( 'atd_ignore_enable' ) ) {
  208. items.push({
  209. text: getLang( 'menu_option_ignore_always', 'Ignore always' ),
  210. onclick: function() {
  211. setAlwaysIgnore( text );
  212. ignoreWord( target, text, true );
  213. }
  214. });
  215. } else {
  216. items.push({
  217. text: getLang( 'menu_option_ignore_all', 'Ignore all' ),
  218. onclick: function() {
  219. ignoreWord( target, text, true );
  220. }
  221. });
  222. }
  223. // Render menu
  224. suggestionsMenu = new tinymce.ui.Menu({
  225. items: items,
  226. context: 'contextmenu',
  227. onautohide: function( event ) {
  228. if ( isMarkedNode( event.target ) ) {
  229. event.preventDefault();
  230. }
  231. },
  232. onhide: function() {
  233. suggestionsMenu.remove();
  234. suggestionsMenu = null;
  235. }
  236. });
  237. suggestionsMenu.renderTo( document.body );
  238. // Position menu
  239. pos = tinymce.DOM.getPos( editor.getContentAreaContainer() );
  240. targetPos = editor.dom.getPos( target );
  241. root = editor.dom.getRoot();
  242. // Adjust targetPos for scrolling in the editor
  243. if ( root.nodeName === 'BODY' ) {
  244. targetPos.x -= root.ownerDocument.documentElement.scrollLeft || root.scrollLeft;
  245. targetPos.y -= root.ownerDocument.documentElement.scrollTop || root.scrollTop;
  246. } else {
  247. targetPos.x -= root.scrollLeft;
  248. targetPos.y -= root.scrollTop;
  249. }
  250. pos.x += targetPos.x;
  251. pos.y += targetPos.y;
  252. suggestionsMenu.moveTo( pos.x, pos.y + target.offsetHeight );
  253. }
  254. // Init everything
  255. editor.on( 'init', function() {
  256. if ( typeof window.AtDCore === 'undefined' ) {
  257. return;
  258. }
  259. // Set dom and atdCore
  260. dom = editor.dom;
  261. initAtDCore();
  262. // add a command to request a document check and process the results.
  263. editor.addCommand( 'mceWritingImprovementTool', function( callback ) {
  264. var results,
  265. errorCount = 0;
  266. if ( typeof callback !== 'function' ) {
  267. callback = function(){};
  268. }
  269. // checks if a global var for click stats exists and increments it if it does...
  270. if ( typeof window.AtD_proofread_click_count !== 'undefined' ) {
  271. window.AtD_proofread_click_count++;
  272. }
  273. // remove the previous errors
  274. if ( started ) {
  275. finish();
  276. return;
  277. }
  278. // send request to our service
  279. sendRequest( 'checkDocument', editor.getContent({ format: 'raw' }), function( data, request ) {
  280. // turn off the spinning thingie
  281. editor.setProgressState();
  282. // if the server is not accepting requests, let the user know
  283. if ( request.status !== 200 || request.responseText.substr( 1, 4 ) === 'html' || ! request.responseXML ) {
  284. editor.windowManager.alert(
  285. getLang( 'message_server_error', 'There was a problem communicating with the Proofreading service. Try again in one minute.' ),
  286. callback(0)
  287. );
  288. return;
  289. }
  290. // check to see if things are broken first and foremost
  291. if ( request.responseXML.getElementsByTagName('message').item(0) !== null ) {
  292. editor.windowManager.alert(
  293. request.responseXML.getElementsByTagName('message').item(0).firstChild.data,
  294. callback(0)
  295. );
  296. return;
  297. }
  298. results = atdCore.processXML( request.responseXML );
  299. if ( results.count > 0 ) {
  300. errorCount = markMyWords( results.errors );
  301. }
  302. if ( ! errorCount ) {
  303. editor.windowManager.alert( getLang( 'message_no_errors_found', 'No writing errors were found.' ) );
  304. } else {
  305. started = true;
  306. editor.fire('SpellcheckStart');
  307. }
  308. callback( errorCount );
  309. });
  310. });
  311. if ( editor.settings.content_css !== false ) {
  312. // CSS for underlining suggestions
  313. dom.addStyle( '.hiddenSpellError{border-bottom:2px solid red;cursor:default;}' +
  314. '.hiddenGrammarError{border-bottom:2px solid green;cursor:default;}' +
  315. '.hiddenSuggestion{border-bottom:2px solid blue;cursor:default;}' );
  316. }
  317. // Menu z-index > DFW
  318. tinymce.DOM.addStyle( 'div.mce-floatpanel{z-index:150100 !important;}' );
  319. // Click on misspelled word
  320. editor.on( 'click', function( event ) {
  321. if ( isMarkedNode( event.target ) ) {
  322. event.preventDefault();
  323. editor.selection.select( event.target );
  324. // Create the suggestions menu
  325. showSuggestions( event.target );
  326. }
  327. });
  328. });
  329. editor.addMenuItem( 'spellchecker', {
  330. text: getLang( 'button_proofread_tooltip', 'Proofread Writing' ),
  331. context: 'tools',
  332. cmd: 'mceWritingImprovementTool',
  333. onPostRender: function() {
  334. var self = this;
  335. editor.on('SpellcheckStart SpellcheckEnd', function() {
  336. self.active( started );
  337. });
  338. }
  339. });
  340. editor.addButton( 'spellchecker', {
  341. tooltip: getLang( 'button_proofread_tooltip', 'Proofread Writing' ),
  342. cmd: 'mceWritingImprovementTool',
  343. onPostRender: function() {
  344. var self = this;
  345. editor.on( 'SpellcheckStart SpellcheckEnd', function() {
  346. self.active( started );
  347. });
  348. }
  349. });
  350. editor.on( 'remove', function() {
  351. if ( suggestionsMenu ) {
  352. suggestionsMenu.remove();
  353. suggestionsMenu = null;
  354. }
  355. });
  356. });