This repository has been archived on 2023-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
Omphaloskepsis/plugins/jetpack/modules/after-the-deadline/tinymce/plugin.js
2018-03-21 18:19:20 +00:00

428 lines
11 KiB
JavaScript

/* global tinymce */
/*
* TinyMCE Writing Improvement Tool Plugin
* Author: Raphael Mudge (raffi@automattic.com)
*
* Updated for TinyMCE 4.0
*
* http://www.afterthedeadline.com
*
* Distributed under the LGPL
*
* Derived from:
* $Id: editor_plugin_src.js 425 2007-11-21 15:17:39Z spocke $
*
* @author Moxiecode
* @copyright Copyright (C) 2004-2008, Moxiecode Systems AB, All rights reserved.
*
* Moxiecode Spell Checker plugin released under the LGPL with TinyMCE
*/
tinymce.PluginManager.add( 'AtD', function( editor ) {
var suggestionsMenu, started, atdCore, dom,
each = tinymce.each;
/* initializes the functions used by the AtD Core UI Module */
function initAtDCore() {
atdCore = new window.AtDCore();
atdCore.map = each;
atdCore._isTinyMCE = true;
atdCore.getAttrib = function( node, key ) {
return dom.getAttrib( node, key );
};
atdCore.findSpans = function( parent ) {
if ( parent === undefined ) {
return dom.select('span');
} else {
return dom.select( 'span', parent );
}
};
atdCore.hasClass = function( node, className ) {
return dom.hasClass( node, className );
};
atdCore.contents = function( node ) {
return node.childNodes;
};
atdCore.replaceWith = function( old_node, new_node ) {
return dom.replace( new_node, old_node );
};
atdCore.create = function( node_html ) {
return dom.create( 'span', { 'class': 'mceItemHidden', 'data-mce-bogus': 1 }, node_html );
};
atdCore.removeParent = function( node ) {
dom.remove( node, true );
return node;
};
atdCore.remove = function( node ) {
dom.remove( node );
};
atdCore.setIgnoreStrings( editor.getParam( 'atd_ignore_strings', [] ).join(',') );
atdCore.showTypes( editor.getParam( 'atd_show_types', '' ) );
}
function getLang( key, defaultStr ) {
return ( window.AtD_l10n_r0ar && window.AtD_l10n_r0ar[key] ) || defaultStr;
}
function isMarkedNode( node ) {
return ( node.className && /\bhidden(GrammarError|SpellError|Suggestion)\b/.test( node.className ) );
}
function markMyWords( errors ) {
return atdCore.markMyWords( atdCore.contents( editor.getBody() ), errors );
}
// If no more suggestions, finish.
function checkIfFinished() {
if ( ! editor.dom.select('span.hiddenSpellError, span.hiddenGrammarError, span.hiddenSuggestion').length ) {
if ( suggestionsMenu ) {
suggestionsMenu.hideMenu();
}
finish();
}
}
function ignoreWord( target, word, all ) {
var dom = editor.dom;
if ( all ) {
each( editor.dom.select( 'span.hiddenSpellError, span.hiddenGrammarError, span.hiddenSuggestion' ), function( node ) {
var text = node.innerText || node.textContent;
if ( text === word ) {
dom.remove( node, true );
}
});
} else {
dom.remove( target, true );
}
checkIfFinished();
}
// Called when the user clicks "Finish" or when no more suggestions left.
// Removes all remaining spans and fires custom event.
function finish() {
var node,
dom = editor.dom,
regex = new RegExp( 'mceItemHidden|hidden(((Grammar|Spell)Error)|Suggestion)' ),
nodes = dom.select('span'),
i = nodes.length;
while ( i-- ) { // reversed
node = nodes[i];
if ( node.className && regex.test( node.className ) ) {
dom.remove( node, true );
}
}
// Rebuild the DOM so AtD core can find the text nodes
editor.setContent( editor.getContent({ format: 'raw' }), { format: 'raw' } );
started = false;
editor.nodeChanged();
editor.fire('SpellcheckEnd');
}
function sendRequest( file, data, success ) {
var id = editor.getParam( 'atd_rpc_id', '12345678' ),
url = editor.getParam( 'atd_rpc_url', '{backend}' );
if ( url === '{backend}' || id === '12345678' ) {
window.alert( 'Please specify: atd_rpc_url and atd_rpc_id' );
return;
}
// create the nifty spinny thing that says "hizzo, I'm doing something fo realz"
editor.setProgressState( true );
tinymce.util.XHR.send({
url: url + '/' + file,
content_type: 'text/xml',
type: 'POST',
data: 'data=' + encodeURI( data ).replace( /&/g, '%26' ) + '&key=' + id,
success: success,
error: function( type, req, o ) {
editor.setProgressState();
window.alert( type + '\n' + req.status + '\nAt: ' + o.url );
}
});
}
function storeIgnoredStrings( /*text*/ ) {
// Store in sessionStorage?
}
function setAlwaysIgnore( text ) {
var url = editor.getParam( 'atd_ignore_rpc_url' );
if ( ! url || url === '{backend}' ) {
// Store ignored words for this session only
storeIgnoredStrings( text );
} else {
// Plugin is configured to send ignore preferences to server, do that
tinymce.util.XHR.send({
url: url + encodeURIComponent( text ) + '&key=' + editor.getParam( 'atd_rpc_id', '12345678' ),
content_type: 'text/xml',
type: 'GET',
error: function() {
storeIgnoredStrings( text );
}
});
}
// Update atd_ignore_strings with the new value
atdCore.setIgnoreStrings( text );
}
// Create the suggestions menu
function showSuggestions( target ) {
var pos, root, targetPos,
items = [],
text = target.innerText || target.textContent,
errorDescription = atdCore.findSuggestion( target );
if ( ! errorDescription ) {
items.push({
text: getLang( 'menu_title_no_suggestions', 'No suggestions' ),
classes: 'atd-menu-title',
disabled: true
});
} else {
items.push({
text: errorDescription.description,
classes: 'atd-menu-title',
disabled: true
});
if ( errorDescription.suggestions.length ) {
items.push({ text: '-' }); // separator
each( errorDescription.suggestions, function( suggestion ) {
items.push({
text: suggestion,
onclick: function() {
atdCore.applySuggestion( target, suggestion );
checkIfFinished();
}
});
});
}
}
if ( errorDescription && errorDescription.moreinfo ) {
items.push({ text: '-' }); // separator
items.push({
text: getLang( 'menu_option_explain', 'Explain...' ),
onclick : function() {
editor.windowManager.open({
title: getLang( 'menu_option_explain', 'Explain...' ),
url: errorDescription.moreinfo,
width: 480,
height: 380,
inline: true
});
}
});
}
items.push.apply( items, [
{ text: '-' }, // separator
{ text: getLang( 'menu_option_ignore_once', 'Ignore suggestion' ), onclick: function() {
ignoreWord( target, text );
}}
]);
if ( editor.getParam( 'atd_ignore_enable' ) ) {
items.push({
text: getLang( 'menu_option_ignore_always', 'Ignore always' ),
onclick: function() {
setAlwaysIgnore( text );
ignoreWord( target, text, true );
}
});
} else {
items.push({
text: getLang( 'menu_option_ignore_all', 'Ignore all' ),
onclick: function() {
ignoreWord( target, text, true );
}
});
}
// Render menu
suggestionsMenu = new tinymce.ui.Menu({
items: items,
context: 'contextmenu',
onautohide: function( event ) {
if ( isMarkedNode( event.target ) ) {
event.preventDefault();
}
},
onhide: function() {
suggestionsMenu.remove();
suggestionsMenu = null;
}
});
suggestionsMenu.renderTo( document.body );
// Position menu
pos = tinymce.DOM.getPos( editor.getContentAreaContainer() );
targetPos = editor.dom.getPos( target );
root = editor.dom.getRoot();
// Adjust targetPos for scrolling in the editor
if ( root.nodeName === 'BODY' ) {
targetPos.x -= root.ownerDocument.documentElement.scrollLeft || root.scrollLeft;
targetPos.y -= root.ownerDocument.documentElement.scrollTop || root.scrollTop;
} else {
targetPos.x -= root.scrollLeft;
targetPos.y -= root.scrollTop;
}
pos.x += targetPos.x;
pos.y += targetPos.y;
suggestionsMenu.moveTo( pos.x, pos.y + target.offsetHeight );
}
// Init everything
editor.on( 'init', function() {
if ( typeof window.AtDCore === 'undefined' ) {
return;
}
// Set dom and atdCore
dom = editor.dom;
initAtDCore();
// add a command to request a document check and process the results.
editor.addCommand( 'mceWritingImprovementTool', function( callback ) {
var results,
errorCount = 0;
if ( typeof callback !== 'function' ) {
callback = function(){};
}
// checks if a global var for click stats exists and increments it if it does...
if ( typeof window.AtD_proofread_click_count !== 'undefined' ) {
window.AtD_proofread_click_count++;
}
// remove the previous errors
if ( started ) {
finish();
return;
}
// send request to our service
sendRequest( 'checkDocument', editor.getContent({ format: 'raw' }), function( data, request ) {
// turn off the spinning thingie
editor.setProgressState();
// if the server is not accepting requests, let the user know
if ( request.status !== 200 || request.responseText.substr( 1, 4 ) === 'html' || ! request.responseXML ) {
editor.windowManager.alert(
getLang( 'message_server_error', 'There was a problem communicating with the Proofreading service. Try again in one minute.' ),
callback(0)
);
return;
}
// check to see if things are broken first and foremost
if ( request.responseXML.getElementsByTagName('message').item(0) !== null ) {
editor.windowManager.alert(
request.responseXML.getElementsByTagName('message').item(0).firstChild.data,
callback(0)
);
return;
}
results = atdCore.processXML( request.responseXML );
if ( results.count > 0 ) {
errorCount = markMyWords( results.errors );
}
if ( ! errorCount ) {
editor.windowManager.alert( getLang( 'message_no_errors_found', 'No writing errors were found.' ) );
} else {
started = true;
editor.fire('SpellcheckStart');
}
callback( errorCount );
});
});
if ( editor.settings.content_css !== false ) {
// CSS for underlining suggestions
dom.addStyle( '.hiddenSpellError{border-bottom:2px solid red;cursor:default;}' +
'.hiddenGrammarError{border-bottom:2px solid green;cursor:default;}' +
'.hiddenSuggestion{border-bottom:2px solid blue;cursor:default;}' );
}
// Menu z-index > DFW
tinymce.DOM.addStyle( 'div.mce-floatpanel{z-index:150100 !important;}' );
// Click on misspelled word
editor.on( 'click', function( event ) {
if ( isMarkedNode( event.target ) ) {
event.preventDefault();
editor.selection.select( event.target );
// Create the suggestions menu
showSuggestions( event.target );
}
});
});
editor.addMenuItem( 'spellchecker', {
text: getLang( 'button_proofread_tooltip', 'Proofread Writing' ),
context: 'tools',
cmd: 'mceWritingImprovementTool',
onPostRender: function() {
var self = this;
editor.on('SpellcheckStart SpellcheckEnd', function() {
self.active( started );
});
}
});
editor.addButton( 'spellchecker', {
tooltip: getLang( 'button_proofread_tooltip', 'Proofread Writing' ),
cmd: 'mceWritingImprovementTool',
onPostRender: function() {
var self = this;
editor.on( 'SpellcheckStart SpellcheckEnd', function() {
self.active( started );
});
}
});
editor.on( 'remove', function() {
if ( suggestionsMenu ) {
suggestionsMenu.remove();
suggestionsMenu = null;
}
});
});