Initial commit
This commit is contained in:
commit
28e6ddf404
1083 changed files with 191734 additions and 0 deletions
|
@ -0,0 +1,93 @@
|
|||
/* jshint devel: true, onevar: false */
|
||||
/* global tinyMCE, AtD_restore_if_proofreading, AtD_check, AtD_unbind_proofreader_listeners,
|
||||
AtD, AtD_bind_proofreader_listeners, AtD_check_when
|
||||
*/
|
||||
|
||||
/* the AtD/jQuery and AtD/TinyMCE plugins check if this variable exists and increment it when a proofread check happens */
|
||||
var AtD_proofread_click_count = 0;
|
||||
|
||||
/* This is function called when the publish/update button is pressed */
|
||||
function AtD_submit_check( e ) {
|
||||
/* User has already checked their document... no need to hold up their submit */
|
||||
if (AtD_proofread_click_count > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Let's not submit the form, shall we? */
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
|
||||
/* We'll call the AtD function based on which editor is currently active */
|
||||
if ( typeof(tinyMCE) !== 'undefined' && tinyMCE.activeEditor && !tinyMCE.activeEditor.isHidden() ) {
|
||||
/* Woo! We're running tinyMCE! */
|
||||
tinyMCE.activeEditor.execCommand('mceWritingImprovementTool', AtD_submit_check_callback);
|
||||
} else {
|
||||
/* Go Go HTML editor! */
|
||||
AtD_restore_if_proofreading();
|
||||
AtD_check( AtD_submit_check_callback );
|
||||
}
|
||||
}
|
||||
|
||||
/* This is the callback function that runs after the publish/update button is pressed */
|
||||
function AtD_submit_check_callback(count) {
|
||||
count = Number( count || 0 );
|
||||
AtD_unbind_proofreader_listeners();
|
||||
|
||||
if ( 0 === count || 1 < AtD_proofread_click_count ) {
|
||||
/* if no errors were found, submit form */
|
||||
AtD_update_post();
|
||||
} else if ( -1 === count ) {
|
||||
/* If there was an error, alert the user and submit form */
|
||||
alert( AtD.getLang('message_server_error', 'There was a problem communicating with the Proofreading service. Try again in one minute.') );
|
||||
AtD_update_post();
|
||||
} else {
|
||||
var original_post_status = jQuery('#original_post_status').val();
|
||||
|
||||
/* Okay, the user has tried to publish/update already but there are still errors. Ask them what to do */
|
||||
var message;
|
||||
if ( original_post_status === 'publish' ) {
|
||||
message = AtD.getLang('dialog_confirm_post_publish', 'The proofreader has suggestions for this post. Are you sure you want to publish it?\n\nPress OK to publish your post, or Cancel to view the suggestions and edit your post.');
|
||||
} else {
|
||||
message = AtD.getLang('dialog_confirm_post_update', 'The proofreader has suggestions for this post. Are you sure you want to update it?\n\nPress OK to update your post, or Cancel to view the suggestions and edit your post.');
|
||||
}
|
||||
|
||||
if ( confirm( message ) ) {
|
||||
AtD_update_post();
|
||||
} else {
|
||||
AtD_bind_proofreader_listeners();
|
||||
AtD_kill_autoproofread();
|
||||
}
|
||||
|
||||
/* Let's do some interface clean-up */
|
||||
jQuery('#publish').removeClass('button-primary-disabled');
|
||||
jQuery('#ajax-loading').hide();
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop the proofreader from doing its auto proofread thing (activated when the proofread button is clicked) */
|
||||
function AtD_kill_autoproofread() {
|
||||
jQuery('#publish').unbind('click.AtD_submit_check');
|
||||
}
|
||||
|
||||
/* a function to force the post to be submitted */
|
||||
function AtD_update_post() {
|
||||
|
||||
if ( typeof(tinyMCE) === 'undefined' || !tinyMCE.activeEditor || tinyMCE.activeEditor.isHidden() ) {
|
||||
AtD_restore_if_proofreading();
|
||||
}
|
||||
|
||||
jQuery('#publish').unbind('click.AtD_submit_check').click();
|
||||
}
|
||||
|
||||
/* init the autoproofread options */
|
||||
jQuery( document ).ready( function($){
|
||||
var orig_status = $('#original_post_status').val();
|
||||
|
||||
/* check if auto-check is enabled && if #content exists */
|
||||
if ( typeof AtD_check_when !== 'undefined' && $('#content').length &&
|
||||
( ( orig_status !== 'publish' && AtD_check_when.onpublish ) ||
|
||||
( ( orig_status === 'publish' || orig_status === 'schedule' ) && AtD_check_when.onupdate ) ) ) {
|
||||
$('#publish').bind( 'click.AtD_submit_check', AtD_submit_check );
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,267 @@
|
|||
/* jshint devel: true, onevar: false, smarttabs: true */
|
||||
/* global AtD, QTags, AtD_l10n_r0ar, edButtons, edButton, switchEditors, AtD_unbind_proofreader_listeners */
|
||||
/* exported AtD_unbind_proofreader_listeners */
|
||||
|
||||
var AtD_qtbutton, autosave;
|
||||
/* convienence method to restore the text area from the preview div */
|
||||
function AtD_restore_text_area() {
|
||||
var content;
|
||||
|
||||
/* swap the preview div for the textarea, notice how I have to restore the appropriate class/id/style attributes */
|
||||
if( jQuery('#atd-content').get(0) ) {
|
||||
AtD.remove('atd-content');
|
||||
content = jQuery('#atd-content').html();
|
||||
} else {
|
||||
AtD.remove('content');
|
||||
content = jQuery('#content').html();
|
||||
}
|
||||
|
||||
if ( navigator.appName === 'Microsoft Internet Explorer' ) {
|
||||
content = content.replace(/<BR.*?class.*?atd_remove_me.*?>/gi, '\n');
|
||||
}
|
||||
|
||||
// Remove emoji replacement images
|
||||
if ( typeof window.twemoji !== 'undefined' ) {
|
||||
content = content.replace( /<img [^>]+>/g, function( img ) {
|
||||
// The images should have class containing 'emoji'
|
||||
if ( img.indexOf( 'emoji' ) !== -1 ) {
|
||||
var alt = img.match( /alt="([^"]+)"/ );
|
||||
|
||||
if ( alt && alt[1] && window.twemoji.test( alt[1] ) ) {
|
||||
return alt[1];
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
});
|
||||
}
|
||||
|
||||
// jQuery('#content').replaceWith( AtD.content_canvas );
|
||||
jQuery('#content').val( content.replace(/\<\;/g, '<').replace(/\>\;/g, '>').replace(/\&/g, '&') )
|
||||
.height(AtD.height)
|
||||
.show();
|
||||
jQuery('#atd-content').remove();
|
||||
|
||||
if ( AtD_qtbutton ) {
|
||||
/* change the link text back to its original label */
|
||||
jQuery(AtD_qtbutton).val( AtD.getLang('button_proofread', 'proofread') );
|
||||
jQuery(AtD_qtbutton).css({ 'color' : '#464646' });
|
||||
|
||||
/* enable the toolbar buttons */
|
||||
jQuery( AtD_qtbutton ).siblings('input').andSelf().attr( 'disabled', false );
|
||||
}
|
||||
|
||||
/* restore autosave */
|
||||
if ( AtD.autosave !== undefined ) {
|
||||
if ( window.wp && window.wp.autosave && window.wp.autosave.server ) {
|
||||
window.wp.autosave.local.resume && window.wp.autosave.local.resume();
|
||||
window.wp.autosave.server.resume && window.wp.autosave.server.resume();
|
||||
} else {
|
||||
autosave = AtD.autosave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add the AtD button properly to quicktags
|
||||
if ( typeof(QTags) !== 'undefined' && QTags.addButton ) {
|
||||
jQuery(document).ready(function(){
|
||||
QTags.addButton( 'AtD', AtD_l10n_r0ar.button_proofread, AtD_check );
|
||||
});
|
||||
} else {
|
||||
edButtons[edButtons.length] = new edButton('ed_AtD', 'AtD', '', '', '');
|
||||
jQuery(document).ready(function($){
|
||||
$('#ed_AtD').replaceWith('<input type="button" id="ed_AtD" accesskey="" class="ed_button" onclick="AtD_check(this);" value="' + AtD_l10n_r0ar.button_proofread + '" />');
|
||||
});
|
||||
}
|
||||
|
||||
function AtD_restore_if_proofreading() {
|
||||
if ( AtD_qtbutton && jQuery(AtD_qtbutton).val() === AtD.getLang('button_edit_text', 'edit text') ) {
|
||||
AtD_restore_text_area();
|
||||
}
|
||||
}
|
||||
|
||||
function AtD_unbind_proofreader_listeners() {
|
||||
jQuery('#save-post, #post-preview, #publish, #edButtonPreview').unbind('focus', AtD_restore_if_proofreading );
|
||||
jQuery('#add_poll, #add_image, #add_video, #add_audio, #add_media').unbind('click', AtD_restore_if_proofreading );
|
||||
jQuery('#post').unbind('submit', AtD_restore_if_proofreading );
|
||||
}
|
||||
|
||||
function AtD_bind_proofreader_listeners() {
|
||||
jQuery('#save-post, #post-preview, #publish, #edButtonPreview').focus( AtD_restore_if_proofreading );
|
||||
jQuery('#add_poll, #add_image, #add_video, #add_audio, #add_media').click( AtD_restore_if_proofreading );
|
||||
jQuery('#post').submit( AtD_restore_if_proofreading );
|
||||
}
|
||||
|
||||
/* where the magic happens, checks the spelling or restores the form */
|
||||
function AtD_check(button) {
|
||||
var callback, divHeight;
|
||||
|
||||
if ( jQuery.isFunction( button ) ) {
|
||||
callback = button;
|
||||
|
||||
if ( !AtD_qtbutton ) {
|
||||
AtD_qtbutton = jQuery( '#qt_content_AtD, #ed_AtD' ).get( 0 );
|
||||
}
|
||||
} else {
|
||||
if ( !button.id ) {
|
||||
button = button[0];
|
||||
}
|
||||
|
||||
AtD_qtbutton = button;
|
||||
}
|
||||
|
||||
if ( !jQuery('#content').size() ) {
|
||||
if ( 'undefined' !== typeof callback ) {
|
||||
callback( 0 );
|
||||
}
|
||||
AtD_restore_if_proofreading();
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the text of the link says edit comment, then restore the textarea so the user can edit the text */
|
||||
|
||||
if ( jQuery(AtD_qtbutton).val() === AtD.getLang('button_edit_text', 'edit text') ) {
|
||||
AtD_restore_text_area();
|
||||
} else {
|
||||
// Disable editor expand/scroll
|
||||
if ( window.editorExpand && jQuery( '#postdivrich' ).hasClass( 'wp-editor-expand' ) ) {
|
||||
AtD.wpEditorExpand = true;
|
||||
// window.editorExpand.off && window.editorExpand.off();
|
||||
} else {
|
||||
AtD.wpEditorExpand = false;
|
||||
}
|
||||
|
||||
/* initialize some of the stuff related to this plugin */
|
||||
if ( ! AtD.height ) {
|
||||
|
||||
AtD.height = jQuery('#content').height();
|
||||
AtD_bind_proofreader_listeners();
|
||||
|
||||
/* make it so clicking the Visual button works when AtD is active */
|
||||
|
||||
jQuery('#edButtonPreview').attr( 'onclick', null ).click( function() {
|
||||
AtD_restore_if_proofreading();
|
||||
switchEditors.go( 'content', 'tinymce' );
|
||||
});
|
||||
|
||||
/* saved the textarea as we need to restore the original one for the toolbar to continue to function properly */
|
||||
AtD.content_canvas = jQuery('#content');
|
||||
|
||||
/* store the autosave, we're going to make it empty during spellcheck to prevent auto saved text from being
|
||||
over written with empty text */
|
||||
AtD.autosave = autosave;
|
||||
} else {
|
||||
// Update the height
|
||||
AtD.height = jQuery('#content').height();
|
||||
}
|
||||
|
||||
/* set the spell check link to a link that lets the user edit the text */
|
||||
/* disable the button to prevent a race condition where content is deleted if proofread is clicked with a check
|
||||
in progress. */
|
||||
jQuery(AtD_qtbutton).css({ 'color' : 'red' }).val( AtD.getLang('button_edit_text', 'edit text') ).attr('disabled', true);
|
||||
|
||||
/* replace the div */
|
||||
var $replacement,
|
||||
$textarea = jQuery('#content'),
|
||||
text = $textarea.val().replace( /\&/g, '&' ).replace( /</g, '<' ).replace( /\>/g, '>' ),
|
||||
fontFamily = $textarea.css('font-family'),
|
||||
fontSize = $textarea.css('font-size'),
|
||||
lineHeight = $textarea.css('line-height');
|
||||
|
||||
if ( navigator.appName === 'Microsoft Internet Explorer' ) {
|
||||
text = text.replace( /[\n\r\f]/gm, '<BR class="atd_remove_me">' );
|
||||
}
|
||||
|
||||
$replacement = jQuery( '<div class="input" id="atd-content">' + text + '</div>' );
|
||||
$textarea.after( $replacement ).hide();
|
||||
|
||||
divHeight = AtD.height;
|
||||
// AtD disables resizing of the Text editor, normalize the size of the replacement div.
|
||||
if ( divHeight < 200 ) {
|
||||
divHeight = 200;
|
||||
} else if ( divHeight > 1000 ) {
|
||||
divHeight = 1000;
|
||||
}
|
||||
var toolBarHeight = jQuery('#ed_toolbar').height();
|
||||
$replacement.css( {
|
||||
overflow: 'auto',
|
||||
'background-color': 'white',
|
||||
color: 'black',
|
||||
'white-space': 'pre-wrap',
|
||||
padding: '10px',
|
||||
'font-family': fontFamily || 'Consolas, Monaco, monospace',
|
||||
'font-size': fontSize || '13px',
|
||||
'line-height': lineHeight || '1.5',
|
||||
height: divHeight,
|
||||
'margin-top': toolBarHeight+7+'px'
|
||||
} );
|
||||
|
||||
/* kill autosave... :) */
|
||||
if ( window.wp && window.wp.autosave && window.wp.autosave.server ) {
|
||||
window.wp.autosave.local.suspend && window.wp.autosave.local.suspend();
|
||||
window.wp.autosave.server.suspend && window.wp.autosave.server.suspend();
|
||||
} else {
|
||||
autosave = function() { };
|
||||
}
|
||||
|
||||
/* disable the toolbar buttons */
|
||||
jQuery( AtD_qtbutton ).siblings('input').andSelf().attr( 'disabled', true ); // using .arrt instead of .prop so it's compat with older WP and jQuery
|
||||
|
||||
/* check the writing in the textarea */
|
||||
AtD.check('atd-content', {
|
||||
success: function(errorCount) {
|
||||
if ( Number( errorCount ) === 0 && typeof callback !== 'function' ) {
|
||||
alert( AtD.getLang('message_no_errors_found', 'No writing errors were found') );
|
||||
}
|
||||
AtD_restore_if_proofreading();
|
||||
},
|
||||
|
||||
ready: function(errorCount) {
|
||||
jQuery(AtD_qtbutton).attr('disabled', false);
|
||||
|
||||
if ( typeof callback === 'function' ) {
|
||||
callback( errorCount );
|
||||
}
|
||||
},
|
||||
|
||||
error: function() {
|
||||
jQuery(AtD_qtbutton).attr('disabled', false);
|
||||
|
||||
if ( typeof callback === 'function' ) {
|
||||
callback( -1 );
|
||||
} else {
|
||||
alert( AtD.getLang('message_server_error', 'There was a problem communicating with the Proofreading service. Try again in one minute.') );
|
||||
}
|
||||
|
||||
AtD_restore_if_proofreading();
|
||||
},
|
||||
|
||||
editSelection: function(element) {
|
||||
var text = prompt( AtD.getLang('dialog_replace_selection', 'Replace selection with:'), element.text() );
|
||||
|
||||
if ( text != null ) {
|
||||
element.replaceWith( text );
|
||||
}
|
||||
},
|
||||
|
||||
explain: function(url) {
|
||||
var left = (screen.width / 2) - (480 / 2);
|
||||
var top = (screen.height / 2) - (380 / 2);
|
||||
window.open( url, '', 'width=480,height=380,toolbar=0,status=0,resizable=0,location=0,menuBar=0,left=' + left + ',top=' + top).focus();
|
||||
},
|
||||
|
||||
ignore: function(word) {
|
||||
jQuery.ajax({
|
||||
type : 'GET',
|
||||
url : AtD.rpc_ignore + encodeURI( word ).replace( /&/g, '%26'),
|
||||
format : 'raw',
|
||||
error : function(XHR, status, error) {
|
||||
if ( AtD.callback_f !== undefined && AtD.callback_f.error !== undefined ) {
|
||||
AtD.callback_f.error(status + ': ' + error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
122
plugins/jetpack/modules/after-the-deadline/atd-rtl.css
Normal file
122
plugins/jetpack/modules/after-the-deadline/atd-rtl.css
Normal file
|
@ -0,0 +1,122 @@
|
|||
/* AtD error styles */
|
||||
|
||||
.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 styles derived from:
|
||||
* jquery.spellchecker.js - a simple jQuery Spell Checker
|
||||
* Copyright (c) 2008 Richard Willis
|
||||
* MIT license : http://www.opensource.org/licenses/mit-license.php
|
||||
* Project : http://jquery-spellchecker.googlecode.com
|
||||
*/
|
||||
|
||||
#suggestmenu
|
||||
{
|
||||
min-width: 122px;
|
||||
background: #ebeaeb;
|
||||
position: absolute;
|
||||
display: none;
|
||||
z-index: 9999;
|
||||
overflow: none;
|
||||
margin-top: 1px;
|
||||
text-align: right;
|
||||
font-size: 11px;
|
||||
font-family: Tahoma, Verdana, Arial, Helvetica;
|
||||
}
|
||||
|
||||
#suggestmenu strong
|
||||
{
|
||||
background: #cccccc;
|
||||
font-weight: bold;
|
||||
padding:3px 6px 3px 6px;
|
||||
display:block;
|
||||
border:1px solid #dddddd;
|
||||
border-bottom: 1px solid #aaaaaa;
|
||||
color: black;
|
||||
}
|
||||
|
||||
#suggestmenu em
|
||||
{
|
||||
text-align:center;
|
||||
padding:3px 6px 3px 6px;
|
||||
display:block;
|
||||
border-top:1px solid #ccc;
|
||||
border-right:1px solid #ccc;
|
||||
}
|
||||
|
||||
#suggestmenu a, #suggestmenu a:visited
|
||||
{
|
||||
background: #ebeaeb;
|
||||
border-right:1px solid #dddddd;
|
||||
border-left:1px solid #dddddd;
|
||||
padding:3px 6px 3px 6px;
|
||||
display:block;
|
||||
margin:0px;
|
||||
text-decoration:none;
|
||||
color: black;
|
||||
outline:none
|
||||
}
|
||||
|
||||
#suggestmenu a.first, #suggestmenu a.first:visited
|
||||
{
|
||||
border-top:1px solid #dddddd;
|
||||
}
|
||||
|
||||
.spell_sep_bottom
|
||||
{
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
|
||||
.spell_sep_top
|
||||
{
|
||||
border-top: 1px solid #aaaaaa;
|
||||
}
|
||||
|
||||
#suggestmenu a:hover
|
||||
{
|
||||
color:#000;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
#suggestmenu .foot
|
||||
{
|
||||
border-top:1px solid #aaaaaa;
|
||||
background:#fff
|
||||
}
|
||||
|
||||
#suggestmenu .foot a, #suggestmenu .foot a:visited
|
||||
{
|
||||
outline:none
|
||||
}
|
||||
|
||||
/* TinyMCE 4.0 */
|
||||
div.mce-atd-menu-title.mce-disabled {
|
||||
padding: 3px 12px 0;
|
||||
}
|
||||
|
||||
div.mce-atd-menu-title.mce-disabled:hover,
|
||||
div.mce-atd-menu-title.mce-disabled:hover span.mce-text {
|
||||
background: none;
|
||||
color: #888;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
div.mce-atd-menu-title.mce-disabled span.mce-text {
|
||||
font-weight: bold;
|
||||
color: #888;
|
||||
}
|
1
plugins/jetpack/modules/after-the-deadline/atd-rtl.min.css
vendored
Normal file
1
plugins/jetpack/modules/after-the-deadline/atd-rtl.min.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.hiddenSpellError{border-bottom:2px solid red;cursor:default}.hiddenGrammarError{border-bottom:2px solid green;cursor:default}.hiddenSuggestion{border-bottom:2px solid #00f;cursor:default}#suggestmenu{min-width:122px;background:#ebeaeb;position:absolute;display:none;z-index:9999;overflow:none;margin-top:1px;text-align:right;font-size:11px;font-family:Tahoma,Verdana,Arial,Helvetica}#suggestmenu strong{background:#ccc;font-weight:700;padding:3px 6px;display:block;border:1px solid #ddd;border-bottom:1px solid #aaa;color:#000}#suggestmenu em{text-align:center;padding:3px 6px;display:block;border-top:1px solid #ccc;border-right:1px solid #ccc}#suggestmenu a,#suggestmenu a:visited{background:#ebeaeb;border-right:1px solid #ddd;border-left:1px solid #ddd;padding:3px 6px;display:block;margin:0;text-decoration:none;color:#000;outline:0}#suggestmenu a.first,#suggestmenu a.first:visited{border-top:1px solid #ddd}.spell_sep_bottom{border-bottom:1px solid #ddd}.spell_sep_top{border-top:1px solid #aaa}#suggestmenu a:hover{color:#000;background:#f5f5f5}#suggestmenu .foot{border-top:1px solid #aaa;background:#fff}#suggestmenu .foot a,#suggestmenu .foot a:visited{outline:0}div.mce-atd-menu-title.mce-disabled{padding:3px 12px 0}div.mce-atd-menu-title.mce-disabled:hover,div.mce-atd-menu-title.mce-disabled:hover span.mce-text{background:0 0;color:#888;cursor:default}div.mce-atd-menu-title.mce-disabled span.mce-text{font-weight:700;color:#888}
|
640
plugins/jetpack/modules/after-the-deadline/atd.core.js
Normal file
640
plugins/jetpack/modules/after-the-deadline/atd.core.js
Normal file
|
@ -0,0 +1,640 @@
|
|||
/*
|
||||
* atd.core.js - A building block to create a front-end for AtD
|
||||
* Author : Raphael Mudge, Automattic
|
||||
* License : LGPL
|
||||
* Project : http://www.afterthedeadline.com/developers.slp
|
||||
* Contact : raffi@automattic.com
|
||||
*/
|
||||
|
||||
/* jshint sub: true, devel: true, onevar: false, smarttabs: true */
|
||||
/* exported EXPORTED_SYMBOLS, atd_sprintf */
|
||||
|
||||
/* EXPORTED_SYMBOLS is set so this file can be a JavaScript Module */
|
||||
var EXPORTED_SYMBOLS = ['AtDCore'];
|
||||
|
||||
function AtDCore() {
|
||||
/* these are the categories of errors AtD should ignore */
|
||||
this.ignore_types = ['Bias Language', 'Cliches', 'Complex Expression', 'Diacritical Marks', 'Double Negatives', 'Hidden Verbs', 'Jargon Language', 'Passive voice', 'Phrases to Avoid', 'Redundant Expression'];
|
||||
|
||||
/* these are the phrases AtD should ignore */
|
||||
this.ignore_strings = {};
|
||||
|
||||
/* Localized strings */
|
||||
// Back-compat, not used
|
||||
this.i18n = {};
|
||||
}
|
||||
|
||||
/*
|
||||
* Internationalization Functions
|
||||
*/
|
||||
|
||||
AtDCore.prototype.getLang = function( key, defaultk ) {
|
||||
return ( window.AtD_l10n_r0ar && window.AtD_l10n_r0ar[key] ) || defaultk;
|
||||
};
|
||||
|
||||
AtDCore.prototype.addI18n = function( obj ) {
|
||||
// Back-compat
|
||||
window.AtD_l10n_r0ar = obj;
|
||||
};
|
||||
|
||||
/*
|
||||
* Setters
|
||||
*/
|
||||
|
||||
AtDCore.prototype.setIgnoreStrings = function(string) {
|
||||
var parent = this;
|
||||
|
||||
this.map(string.split(/,\s*/g), function(string) {
|
||||
parent.ignore_strings[string] = 1;
|
||||
});
|
||||
};
|
||||
|
||||
AtDCore.prototype.showTypes = function(string) {
|
||||
var show_types = string.split(/,\s*/g);
|
||||
var types = {};
|
||||
|
||||
/* set some default types that we want to make optional */
|
||||
|
||||
/* grammar checker options */
|
||||
types['Double Negatives'] = 1;
|
||||
types['Hidden Verbs'] = 1;
|
||||
types['Passive voice'] = 1;
|
||||
types['Bias Language'] = 1;
|
||||
|
||||
/* style checker options */
|
||||
types['Cliches'] = 1;
|
||||
types['Complex Expression'] = 1;
|
||||
types['Diacritical Marks'] = 1;
|
||||
types['Jargon Language'] = 1;
|
||||
types['Phrases to Avoid'] = 1;
|
||||
types['Redundant Expression'] = 1;
|
||||
|
||||
var ignore_types = [];
|
||||
|
||||
this.map(show_types, function(string) {
|
||||
types[string] = undefined;
|
||||
});
|
||||
|
||||
this.map(this.ignore_types, function(string) {
|
||||
if (types[string] !== undefined) {
|
||||
ignore_types.push(string);
|
||||
}
|
||||
});
|
||||
|
||||
this.ignore_types = ignore_types;
|
||||
};
|
||||
|
||||
/*
|
||||
* Error Parsing Code
|
||||
*/
|
||||
|
||||
AtDCore.prototype.makeError = function(error_s, tokens, type, seps/*, pre*/) {
|
||||
var struct = {};
|
||||
struct.type = type;
|
||||
struct.string = error_s;
|
||||
struct.tokens = tokens;
|
||||
|
||||
if (new RegExp('\\b' + error_s + '\\b').test(error_s)) {
|
||||
struct.regexp = new RegExp('(?!'+error_s+'<)\\b' + error_s.replace(/\s+/g, seps) + '\\b');
|
||||
}
|
||||
else if (new RegExp(error_s + '\\b').test(error_s)) {
|
||||
struct.regexp = new RegExp('(?!'+error_s+'<)' + error_s.replace(/\s+/g, seps) + '\\b');
|
||||
}
|
||||
else if (new RegExp('\\b' + error_s).test(error_s)) {
|
||||
struct.regexp = new RegExp('(?!'+error_s+'<)\\b' + error_s.replace(/\s+/g, seps));
|
||||
}
|
||||
else {
|
||||
struct.regexp = new RegExp('(?!'+error_s+'<)' + error_s.replace(/\s+/g, seps));
|
||||
}
|
||||
|
||||
struct.used = false; /* flag whether we've used this rule or not */
|
||||
|
||||
return struct;
|
||||
};
|
||||
|
||||
AtDCore.prototype.addToErrorStructure = function(errors, list, type, seps) {
|
||||
var parent = this;
|
||||
|
||||
this.map(list, function(error) {
|
||||
var tokens = error['word'].split(/\s+/);
|
||||
var pre = error['pre'];
|
||||
var first = tokens[0];
|
||||
|
||||
if (errors['__' + first] === undefined) {
|
||||
errors['__' + first] = {};
|
||||
errors['__' + first].pretoks = {};
|
||||
errors['__' + first].defaults = [];
|
||||
}
|
||||
|
||||
if (pre === '') {
|
||||
errors['__' + first].defaults.push(parent.makeError(error['word'], tokens, type, seps, pre));
|
||||
} else {
|
||||
if (errors['__' + first].pretoks['__' + pre] === undefined) {
|
||||
errors['__' + first].pretoks['__' + pre] = [];
|
||||
}
|
||||
|
||||
errors['__' + first].pretoks['__' + pre].push(parent.makeError(error['word'], tokens, type, seps, pre));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
AtDCore.prototype.buildErrorStructure = function(spellingList, enrichmentList, grammarList) {
|
||||
var seps = this._getSeparators();
|
||||
var errors = {};
|
||||
|
||||
this.addToErrorStructure(errors, spellingList, 'hiddenSpellError', seps);
|
||||
this.addToErrorStructure(errors, grammarList, 'hiddenGrammarError', seps);
|
||||
this.addToErrorStructure(errors, enrichmentList, 'hiddenSuggestion', seps);
|
||||
return errors;
|
||||
};
|
||||
|
||||
AtDCore.prototype._getSeparators = function() {
|
||||
var re = '', i;
|
||||
var str = '"s!#$%&()*+,./:;<=>?@[\\]^_{|}';
|
||||
|
||||
// Build word separator regexp
|
||||
for (i=0; i<str.length; i++) {
|
||||
re += '\\' + str.charAt(i);
|
||||
}
|
||||
|
||||
return '(?:(?:[\xa0' + re + '])|(?:\\-\\-))+';
|
||||
};
|
||||
|
||||
AtDCore.prototype.processXML = function(responseXML) {
|
||||
|
||||
/* types of errors to ignore */
|
||||
var types = {};
|
||||
|
||||
this.map(this.ignore_types, function(type) {
|
||||
types[type] = 1;
|
||||
});
|
||||
|
||||
/* save suggestions in the editor object */
|
||||
this.suggestions = [];
|
||||
|
||||
/* process through the errors */
|
||||
var errors = responseXML.getElementsByTagName('error');
|
||||
|
||||
/* words to mark */
|
||||
var grammarErrors = [];
|
||||
var spellingErrors = [];
|
||||
var enrichment = [];
|
||||
|
||||
for (var i = 0; i < errors.length; i++) {
|
||||
if (errors[i].getElementsByTagName('string').item(0).firstChild !== null) {
|
||||
var errorString = errors[i].getElementsByTagName('string').item(0).firstChild.data;
|
||||
var errorType = errors[i].getElementsByTagName('type').item(0).firstChild.data;
|
||||
var errorDescription = errors[i].getElementsByTagName('description').item(0).firstChild.data;
|
||||
|
||||
var errorContext;
|
||||
|
||||
if (errors[i].getElementsByTagName('precontext').item(0).firstChild !== null) {
|
||||
errorContext = errors[i].getElementsByTagName('precontext').item(0).firstChild.data;
|
||||
} else {
|
||||
errorContext = '';
|
||||
}
|
||||
|
||||
/* create a hashtable with information about the error in the editor object, we will use this later
|
||||
to populate a popup menu with information and suggestions about the error */
|
||||
|
||||
if (this.ignore_strings[errorString] === undefined) {
|
||||
var suggestion = {};
|
||||
suggestion['description'] = errorDescription;
|
||||
suggestion['suggestions'] = [];
|
||||
|
||||
/* used to find suggestions when a highlighted error is clicked on */
|
||||
suggestion['matcher'] = new RegExp('^' + errorString.replace(/\s+/, this._getSeparators()) + '$');
|
||||
|
||||
suggestion['context'] = errorContext;
|
||||
suggestion['string'] = errorString;
|
||||
suggestion['type'] = errorType;
|
||||
|
||||
this.suggestions.push(suggestion);
|
||||
|
||||
if (errors[i].getElementsByTagName('suggestions').item(0) !== null) {
|
||||
var suggestions = errors[i].getElementsByTagName('suggestions').item(0).getElementsByTagName('option');
|
||||
for (var j = 0; j < suggestions.length; j++) {
|
||||
suggestion['suggestions'].push(suggestions[j].firstChild.data);
|
||||
}
|
||||
}
|
||||
|
||||
/* setup the more info url */
|
||||
if (errors[i].getElementsByTagName('url').item(0) !== null) {
|
||||
var errorUrl = errors[i].getElementsByTagName('url').item(0).firstChild.data;
|
||||
suggestion['moreinfo'] = errorUrl + '&theme=tinymce';
|
||||
}
|
||||
|
||||
if (types[errorDescription] === undefined) {
|
||||
if (errorType === 'suggestion') {
|
||||
enrichment.push({ word: errorString, pre: errorContext });
|
||||
}
|
||||
|
||||
if (errorType === 'grammar') {
|
||||
grammarErrors.push({ word: errorString, pre: errorContext });
|
||||
}
|
||||
}
|
||||
|
||||
if (errorType === 'spelling' || errorDescription === 'Homophone') {
|
||||
spellingErrors.push({ word: errorString, pre: errorContext });
|
||||
}
|
||||
|
||||
if (errorDescription === 'Cliches') {
|
||||
suggestion['description'] = 'Clichés'; /* done here for backwards compatability with current user settings */
|
||||
}
|
||||
|
||||
if (errorDescription === 'Spelling') {
|
||||
suggestion['description'] = this.getLang('menu_title_spelling', 'Spelling');
|
||||
}
|
||||
|
||||
if (errorDescription === 'Repeated Word') {
|
||||
suggestion['description'] = this.getLang('menu_title_repeated_word', 'Repeated Word');
|
||||
}
|
||||
|
||||
if (errorDescription === 'Did you mean...') {
|
||||
suggestion['description'] = this.getLang('menu_title_confused_word', 'Did you mean...');
|
||||
}
|
||||
} // end if ignore[errorString] == undefined
|
||||
} // end if
|
||||
} // end for loop
|
||||
|
||||
var errorStruct;
|
||||
var ecount = spellingErrors.length + grammarErrors.length + enrichment.length;
|
||||
|
||||
if (ecount > 0) {
|
||||
errorStruct = this.buildErrorStructure(spellingErrors, enrichment, grammarErrors);
|
||||
} else {
|
||||
errorStruct = undefined;
|
||||
}
|
||||
|
||||
/* save some state in this object, for retrieving suggestions later */
|
||||
return { errors: errorStruct, count: ecount, suggestions: this.suggestions };
|
||||
};
|
||||
|
||||
AtDCore.prototype.findSuggestion = function(element) {
|
||||
var text = element.innerHTML;
|
||||
var context = ( this.getAttrib(element, 'pre') + '' ).replace(/[\\,!\\?\\."\s]/g, '');
|
||||
if (this.getAttrib(element, 'pre') === undefined) {
|
||||
alert(element.innerHTML);
|
||||
}
|
||||
|
||||
var errorDescription;
|
||||
var len = this.suggestions.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
if ((context === '' || context === this.suggestions[i]['context']) && this.suggestions[i]['matcher'].test(text)) {
|
||||
errorDescription = this.suggestions[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return errorDescription;
|
||||
};
|
||||
|
||||
/*
|
||||
* TokenIterator class
|
||||
*/
|
||||
|
||||
function TokenIterator(tokens) {
|
||||
this.tokens = tokens;
|
||||
this.index = 0;
|
||||
this.count = 0;
|
||||
this.last = 0;
|
||||
}
|
||||
|
||||
TokenIterator.prototype.next = function() {
|
||||
var current = this.tokens[this.index];
|
||||
this.count = this.last;
|
||||
this.last += current.length + 1;
|
||||
this.index++;
|
||||
|
||||
/* strip single quotes from token, AtD does this when presenting errors */
|
||||
if (current !== '') {
|
||||
if (current[0] === '\'') {
|
||||
current = current.substring(1, current.length);
|
||||
}
|
||||
|
||||
if (current[current.length - 1] === '\'') {
|
||||
current = current.substring(0, current.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
};
|
||||
|
||||
TokenIterator.prototype.hasNext = function() {
|
||||
return this.index < this.tokens.length;
|
||||
};
|
||||
|
||||
TokenIterator.prototype.hasNextN = function(n) {
|
||||
return (this.index + n) < this.tokens.length;
|
||||
};
|
||||
|
||||
TokenIterator.prototype.skip = function(m, n) {
|
||||
this.index += m;
|
||||
this.last += n;
|
||||
|
||||
if (this.index < this.tokens.length) {
|
||||
this.count = this.last - this.tokens[this.index].length;
|
||||
}
|
||||
};
|
||||
|
||||
TokenIterator.prototype.getCount = function() {
|
||||
return this.count;
|
||||
};
|
||||
|
||||
TokenIterator.prototype.peek = function(n) {
|
||||
var peepers = [];
|
||||
var end = this.index + n;
|
||||
for (var x = this.index; x < end; x++) {
|
||||
peepers.push(this.tokens[x]);
|
||||
}
|
||||
return peepers;
|
||||
};
|
||||
|
||||
/*
|
||||
* code to manage highlighting of errors
|
||||
*/
|
||||
AtDCore.prototype.markMyWords = function(container_nodes, errors) {
|
||||
var seps = new RegExp(this._getSeparators()),
|
||||
nl = [],
|
||||
ecount = 0, /* track number of highlighted errors */
|
||||
parent = this,
|
||||
bogus = this._isTinyMCE ? ' data-mce-bogus="1"' : '',
|
||||
emptySpan = '<span class="mceItemHidden"' + bogus + '> </span>',
|
||||
textOnlyMode;
|
||||
|
||||
/**
|
||||
* Split a text node into an ordered list of siblings:
|
||||
* - text node to the left of the match
|
||||
* - the element replacing the match
|
||||
* - text node to the right of the match
|
||||
*
|
||||
* We have to leave the text to the left and right of the match alone
|
||||
* in order to prevent XSS
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function splitTextNode( textnode, regexp, replacement ) {
|
||||
var text = textnode.nodeValue,
|
||||
index = text.search( regexp ),
|
||||
match = text.match( regexp ),
|
||||
captured = [],
|
||||
cursor;
|
||||
|
||||
if ( index < 0 || ! match.length ) {
|
||||
return [ textnode ];
|
||||
}
|
||||
|
||||
if ( index > 0 ) {
|
||||
// capture left text node
|
||||
captured.push( document.createTextNode( text.substr( 0, index ) ) );
|
||||
}
|
||||
|
||||
// capture the replacement of the matched string
|
||||
captured.push( parent.create( match[0].replace( regexp, replacement ) ) );
|
||||
|
||||
cursor = index + match[0].length;
|
||||
|
||||
if ( cursor < text.length ) {
|
||||
// capture right text node
|
||||
captured.push( document.createTextNode( text.substr( cursor ) ) );
|
||||
}
|
||||
|
||||
return captured;
|
||||
}
|
||||
|
||||
function _isInPre( node ) {
|
||||
if ( node ) {
|
||||
while ( node.parentNode ) {
|
||||
if ( node.nodeName === 'PRE' ) {
|
||||
return true;
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Collect all text nodes */
|
||||
/* Our goal--ignore nodes that are already wrapped */
|
||||
|
||||
this._walk( container_nodes, function( n ) {
|
||||
if ( n.nodeType === 3 && ! parent.isMarkedNode( n ) && ! _isInPre( n ) ) {
|
||||
nl.push( n );
|
||||
}
|
||||
});
|
||||
|
||||
/* walk through the relevant nodes */
|
||||
|
||||
var iterator;
|
||||
|
||||
this.map( nl, function( n ) {
|
||||
var v;
|
||||
|
||||
if ( n.nodeType === 3 ) {
|
||||
v = n.nodeValue; /* we don't want to mangle the HTML so use the actual encoded string */
|
||||
var tokens = n.nodeValue.split( seps ); /* split on the unencoded string so we get access to quotes as " */
|
||||
var previous = '';
|
||||
|
||||
var doReplaces = [];
|
||||
|
||||
iterator = new TokenIterator(tokens);
|
||||
|
||||
while ( iterator.hasNext() ) {
|
||||
var token = iterator.next();
|
||||
var current = errors['__' + token];
|
||||
|
||||
var defaults;
|
||||
|
||||
if ( current !== undefined && current.pretoks !== undefined ) {
|
||||
defaults = current.defaults;
|
||||
current = current.pretoks['__' + previous];
|
||||
|
||||
var done = false;
|
||||
var prev, curr;
|
||||
|
||||
prev = v.substr(0, iterator.getCount());
|
||||
curr = v.substr(prev.length, v.length);
|
||||
|
||||
var checkErrors = function( error ) {
|
||||
if ( error !== undefined && ! error.used && foundStrings[ '__' + error.string ] === undefined && error.regexp.test( curr ) ) {
|
||||
foundStrings[ '__' + error.string ] = 1;
|
||||
doReplaces.push([ error.regexp, '<span class="'+error.type+'" pre="'+previous+'"' + bogus + '>$&</span>' ]);
|
||||
|
||||
error.used = true;
|
||||
done = true;
|
||||
}
|
||||
}; // jshint ignore:line
|
||||
|
||||
var foundStrings = {};
|
||||
|
||||
if (current !== undefined) {
|
||||
previous = previous + ' ';
|
||||
parent.map(current, checkErrors);
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
previous = '';
|
||||
parent.map(defaults, checkErrors);
|
||||
}
|
||||
}
|
||||
|
||||
previous = token;
|
||||
} // end while
|
||||
|
||||
/* do the actual replacements on this span */
|
||||
if ( doReplaces.length > 0 ) {
|
||||
var newNode = n;
|
||||
|
||||
for ( var x = 0; x < doReplaces.length; x++ ) {
|
||||
var regexp = doReplaces[x][0], result = doReplaces[x][1];
|
||||
|
||||
/* it's assumed that this function is only being called on text nodes (nodeType == 3), the iterating is necessary
|
||||
because eventually the whole thing gets wrapped in an mceItemHidden span and from there it's necessary to
|
||||
handle each node individually. */
|
||||
var bringTheHurt = function( node ) {
|
||||
var span, splitNodes;
|
||||
|
||||
if ( node.nodeType === 3 ) {
|
||||
ecount++;
|
||||
|
||||
/* sometimes IE likes to ignore the space between two spans, solution is to insert a placeholder span with
|
||||
a non-breaking space. The markup removal code substitutes this span for a space later */
|
||||
if ( parent.isIE() && node.nodeValue.length > 0 && node.nodeValue.substr(0, 1) === ' ' ) {
|
||||
return parent.create( emptySpan + node.nodeValue.substr( 1, node.nodeValue.length - 1 ).replace( regexp, result ), false );
|
||||
} else {
|
||||
if ( textOnlyMode ) {
|
||||
return parent.create( node.nodeValue.replace( regexp, result ), false );
|
||||
}
|
||||
|
||||
span = parent.create( '<span />' );
|
||||
if ( typeof textOnlyMode === 'undefined' ) {
|
||||
// cache this to avoid adding / removing nodes unnecessarily
|
||||
textOnlyMode = typeof span.appendChild !== 'function';
|
||||
if ( textOnlyMode ) {
|
||||
parent.remove( span );
|
||||
return parent.create( node.nodeValue.replace( regexp, result ), false );
|
||||
}
|
||||
}
|
||||
|
||||
// "Visual" mode
|
||||
splitNodes = splitTextNode( node, regexp, result );
|
||||
for ( var i = 0; i < splitNodes.length; i++ ) {
|
||||
span.appendChild( splitNodes[i] );
|
||||
}
|
||||
|
||||
node = span;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else {
|
||||
var contents = parent.contents(node);
|
||||
|
||||
for ( var y = 0; y < contents.length; y++ ) {
|
||||
if ( contents[y].nodeType === 3 && regexp.test( contents[y].nodeValue ) ) {
|
||||
var nnode;
|
||||
|
||||
if ( parent.isIE() && contents[y].nodeValue.length > 0 && contents[y].nodeValue.substr(0, 1) === ' ') {
|
||||
nnode = parent.create( emptySpan + contents[y].nodeValue.substr( 1, contents[y].nodeValue.length - 1 ).replace( regexp, result ), true );
|
||||
} else {
|
||||
nnode = parent.create( contents[y].nodeValue.replace( regexp, result ), true );
|
||||
}
|
||||
|
||||
parent.replaceWith( contents[y], nnode );
|
||||
parent.removeParent( nnode );
|
||||
|
||||
ecount++;
|
||||
|
||||
return node; /* we did a replacement so we can call it quits, errors only get used once */
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}; // jshint ignore:line
|
||||
|
||||
newNode = bringTheHurt(newNode);
|
||||
}
|
||||
|
||||
parent.replaceWith(n, newNode);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ecount;
|
||||
};
|
||||
|
||||
AtDCore.prototype._walk = function(elements, f) {
|
||||
var i;
|
||||
for (i = 0; i < elements.length; i++) {
|
||||
f.call(f, elements[i]);
|
||||
this._walk(this.contents(elements[i]), f);
|
||||
}
|
||||
};
|
||||
|
||||
AtDCore.prototype.removeWords = function(node, w) {
|
||||
var count = 0;
|
||||
var parent = this;
|
||||
|
||||
this.map(this.findSpans(node).reverse(), function(n) {
|
||||
if (n && (parent.isMarkedNode(n) || parent.hasClass(n, 'mceItemHidden') || parent.isEmptySpan(n)) ) {
|
||||
if (n.innerHTML === ' ') {
|
||||
var nnode = document.createTextNode(' '); /* hax0r */
|
||||
parent.replaceWith(n, nnode);
|
||||
} else if (!w || n.innerHTML === w) {
|
||||
parent.removeParent(n);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
AtDCore.prototype.isEmptySpan = function(node) {
|
||||
return (this.getAttrib(node, 'class') === '' && this.getAttrib(node, 'style') === '' && this.getAttrib(node, 'id') === '' && !this.hasClass(node, 'Apple-style-span') && this.getAttrib(node, 'mce_name') === '');
|
||||
};
|
||||
|
||||
AtDCore.prototype.isMarkedNode = function(node) {
|
||||
return (this.hasClass(node, 'hiddenGrammarError') || this.hasClass(node, 'hiddenSpellError') || this.hasClass(node, 'hiddenSuggestion'));
|
||||
};
|
||||
|
||||
/*
|
||||
* Context Menu Helpers
|
||||
*/
|
||||
AtDCore.prototype.applySuggestion = function(element, suggestion) {
|
||||
if (suggestion === '(omit)') {
|
||||
this.remove(element);
|
||||
}
|
||||
else {
|
||||
var node = this.create(suggestion);
|
||||
this.replaceWith(element, node);
|
||||
this.removeParent(node);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Check for an error
|
||||
*/
|
||||
AtDCore.prototype.hasErrorMessage = function(xmlr) {
|
||||
return (xmlr !== undefined && xmlr.getElementsByTagName('message').item(0) !== null);
|
||||
};
|
||||
|
||||
AtDCore.prototype.getErrorMessage = function(xmlr) {
|
||||
return xmlr.getElementsByTagName('message').item(0);
|
||||
};
|
||||
|
||||
/* this should always be an error, alas... not practical */
|
||||
AtDCore.prototype.isIE = function() {
|
||||
return navigator.appName === 'Microsoft Internet Explorer';
|
||||
};
|
||||
|
||||
// TODO: this doesn't seem used anywhere in AtD, moved here from install_atd_l10n.js for eventual back-compat
|
||||
/* a quick poor man's sprintf */
|
||||
function atd_sprintf(format, values) {
|
||||
var result = format;
|
||||
for (var x = 0; x < values.length; x++) {
|
||||
result = result.replace(new RegExp('%' + (x + 1) + '\\$', 'g'), values[x]);
|
||||
}
|
||||
return result;
|
||||
}
|
122
plugins/jetpack/modules/after-the-deadline/atd.css
Normal file
122
plugins/jetpack/modules/after-the-deadline/atd.css
Normal file
|
@ -0,0 +1,122 @@
|
|||
/* AtD error styles */
|
||||
|
||||
.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 styles derived from:
|
||||
* jquery.spellchecker.js - a simple jQuery Spell Checker
|
||||
* Copyright (c) 2008 Richard Willis
|
||||
* MIT license : http://www.opensource.org/licenses/mit-license.php
|
||||
* Project : http://jquery-spellchecker.googlecode.com
|
||||
*/
|
||||
|
||||
#suggestmenu
|
||||
{
|
||||
min-width: 122px;
|
||||
background: #ebeaeb;
|
||||
position: absolute;
|
||||
display: none;
|
||||
z-index: 9999;
|
||||
overflow: none;
|
||||
margin-top: 1px;
|
||||
text-align: left;
|
||||
font-size: 11px;
|
||||
font-family: Tahoma, Verdana, Arial, Helvetica;
|
||||
}
|
||||
|
||||
#suggestmenu strong
|
||||
{
|
||||
background: #cccccc;
|
||||
font-weight: bold;
|
||||
padding:3px 6px 3px 6px;
|
||||
display:block;
|
||||
border:1px solid #dddddd;
|
||||
border-bottom: 1px solid #aaaaaa;
|
||||
color: black;
|
||||
}
|
||||
|
||||
#suggestmenu em
|
||||
{
|
||||
text-align:center;
|
||||
padding:3px 6px 3px 6px;
|
||||
display:block;
|
||||
border-top:1px solid #ccc;
|
||||
border-left:1px solid #ccc;
|
||||
}
|
||||
|
||||
#suggestmenu a, #suggestmenu a:visited
|
||||
{
|
||||
background: #ebeaeb;
|
||||
border-left:1px solid #dddddd;
|
||||
border-right:1px solid #dddddd;
|
||||
padding:3px 6px 3px 6px;
|
||||
display:block;
|
||||
margin:0px;
|
||||
text-decoration:none;
|
||||
color: black;
|
||||
outline:none
|
||||
}
|
||||
|
||||
#suggestmenu a.first, #suggestmenu a.first:visited
|
||||
{
|
||||
border-top:1px solid #dddddd;
|
||||
}
|
||||
|
||||
.spell_sep_bottom
|
||||
{
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
|
||||
.spell_sep_top
|
||||
{
|
||||
border-top: 1px solid #aaaaaa;
|
||||
}
|
||||
|
||||
#suggestmenu a:hover
|
||||
{
|
||||
color:#000;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
#suggestmenu .foot
|
||||
{
|
||||
border-top:1px solid #aaaaaa;
|
||||
background:#fff
|
||||
}
|
||||
|
||||
#suggestmenu .foot a, #suggestmenu .foot a:visited
|
||||
{
|
||||
outline:none
|
||||
}
|
||||
|
||||
/* TinyMCE 4.0 */
|
||||
div.mce-atd-menu-title.mce-disabled {
|
||||
padding: 3px 12px 0;
|
||||
}
|
||||
|
||||
div.mce-atd-menu-title.mce-disabled:hover,
|
||||
div.mce-atd-menu-title.mce-disabled:hover span.mce-text {
|
||||
background: none;
|
||||
color: #888;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
div.mce-atd-menu-title.mce-disabled span.mce-text {
|
||||
font-weight: bold;
|
||||
color: #888;
|
||||
}
|
1
plugins/jetpack/modules/after-the-deadline/atd.min.css
vendored
Normal file
1
plugins/jetpack/modules/after-the-deadline/atd.min.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.hiddenSpellError{border-bottom:2px solid red;cursor:default}.hiddenGrammarError{border-bottom:2px solid green;cursor:default}.hiddenSuggestion{border-bottom:2px solid #00f;cursor:default}#suggestmenu{min-width:122px;background:#ebeaeb;position:absolute;display:none;z-index:9999;overflow:none;margin-top:1px;text-align:left;font-size:11px;font-family:Tahoma,Verdana,Arial,Helvetica}#suggestmenu strong{background:#ccc;font-weight:700;padding:3px 6px;display:block;border:1px solid #ddd;border-bottom:1px solid #aaa;color:#000}#suggestmenu em{text-align:center;padding:3px 6px;display:block;border-top:1px solid #ccc;border-left:1px solid #ccc}#suggestmenu a,#suggestmenu a:visited{background:#ebeaeb;border-left:1px solid #ddd;border-right:1px solid #ddd;padding:3px 6px;display:block;margin:0;text-decoration:none;color:#000;outline:0}#suggestmenu a.first,#suggestmenu a.first:visited{border-top:1px solid #ddd}.spell_sep_bottom{border-bottom:1px solid #ddd}.spell_sep_top{border-top:1px solid #aaa}#suggestmenu a:hover{color:#000;background:#f5f5f5}#suggestmenu .foot{border-top:1px solid #aaa;background:#fff}#suggestmenu .foot a,#suggestmenu .foot a:visited{outline:0}div.mce-atd-menu-title.mce-disabled{padding:3px 12px 0}div.mce-atd-menu-title.mce-disabled:hover,div.mce-atd-menu-title.mce-disabled:hover span.mce-text{background:0 0;color:#888;cursor:default}div.mce-atd-menu-title.mce-disabled span.mce-text{font-weight:700;color:#888}
|
BIN
plugins/jetpack/modules/after-the-deadline/button.gif
Normal file
BIN
plugins/jetpack/modules/after-the-deadline/button.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 515 B |
128
plugins/jetpack/modules/after-the-deadline/config-options.php
Normal file
128
plugins/jetpack/modules/after-the-deadline/config-options.php
Normal file
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
/*
|
||||
* Display the configuration options for AtD
|
||||
*/
|
||||
|
||||
/*
|
||||
* A convienence function to display the HTML for an AtD option
|
||||
*/
|
||||
function AtD_print_option( $name, $value, $options ) {
|
||||
// Attribute-safe version of $name
|
||||
$attr_name = sanitize_title($name); // Using sanitize_title since there's no comparable function for attributes
|
||||
?>
|
||||
<input type="checkbox" id="atd_<?php echo esc_attr( $attr_name ) ?>" name="<?php echo esc_attr( $options['name'] ); ?>[<?php echo esc_attr( $name ); ?>]" value="1" <?php checked( '1', isset( $options[$name] ) ? $options[$name] : false ); ?>> <label for="atd_<?php echo esc_attr( $attr_name ); ?>"><?php echo esc_html( $value ); ?></label>
|
||||
<?php
|
||||
}
|
||||
|
||||
/*
|
||||
* Save AtD options
|
||||
*/
|
||||
function AtD_process_options_update() {
|
||||
|
||||
$user = wp_get_current_user();
|
||||
|
||||
if ( ! $user || $user->ID == 0 )
|
||||
return;
|
||||
|
||||
AtD_update_options( $user->ID, 'AtD_options' );
|
||||
AtD_update_options( $user->ID, 'AtD_check_when' );
|
||||
AtD_update_options( $user->ID, 'AtD_guess_lang' );
|
||||
}
|
||||
|
||||
/*
|
||||
* Display the various AtD options
|
||||
*/
|
||||
function AtD_display_options_form() {
|
||||
|
||||
/* grab our user and validate their existence */
|
||||
$user = wp_get_current_user();
|
||||
if ( ! $user || $user->ID == 0 )
|
||||
return;
|
||||
|
||||
$options_show_types = AtD_get_options( $user->ID, 'AtD_options' );
|
||||
$options_check_when = AtD_get_options( $user->ID, 'AtD_check_when' );
|
||||
$options_guess_lang = AtD_get_options( $user->ID, 'AtD_guess_lang' );
|
||||
?>
|
||||
<table class="form-table">
|
||||
<tr valign="top">
|
||||
<th scope="row"> <a id="atd"></a> <?php _e( 'Proofreading', 'jetpack' ); ?></th>
|
||||
<td>
|
||||
<p><?php _e( 'Automatically proofread content when:', 'jetpack' ); ?>
|
||||
|
||||
<p><?php
|
||||
AtD_print_option( 'onpublish', __('a post or page is first published', 'jetpack'), $options_check_when );
|
||||
echo '<br />';
|
||||
AtD_print_option( 'onupdate', __('a post or page is updated', 'jetpack'), $options_check_when );
|
||||
?></p>
|
||||
|
||||
<p style="font-weight: bold"><?php _e('English Options', 'jetpack'); ?></p>
|
||||
|
||||
<p><?php _e('Enable proofreading for the following grammar and style rules when writing posts and pages:', 'jetpack'); ?></p>
|
||||
|
||||
<p><?php
|
||||
AtD_print_option( 'Bias Language', __('Bias Language', 'jetpack'), $options_show_types );
|
||||
echo '<br />';
|
||||
AtD_print_option( 'Cliches', __('Clichés', 'jetpack'), $options_show_types );
|
||||
echo '<br />';
|
||||
AtD_print_option( 'Complex Expression', __('Complex Phrases', 'jetpack'), $options_show_types );
|
||||
echo '<br />';
|
||||
AtD_print_option( 'Diacritical Marks', __('Diacritical Marks', 'jetpack'), $options_show_types );
|
||||
echo '<br />';
|
||||
AtD_print_option( 'Double Negative', __('Double Negatives', 'jetpack'), $options_show_types );
|
||||
echo '<br />';
|
||||
AtD_print_option( 'Hidden Verbs', __('Hidden Verbs', 'jetpack'), $options_show_types );
|
||||
echo '<br />';
|
||||
AtD_print_option( 'Jargon Language', __('Jargon', 'jetpack'), $options_show_types );
|
||||
echo '<br />';
|
||||
AtD_print_option( 'Passive voice', __('Passive Voice', 'jetpack'), $options_show_types );
|
||||
echo '<br />';
|
||||
AtD_print_option( 'Phrases to Avoid', __('Phrases to Avoid', 'jetpack'), $options_show_types );
|
||||
echo '<br />';
|
||||
AtD_print_option( 'Redundant Expression', __('Redundant Phrases', 'jetpack'), $options_show_types );
|
||||
?></p>
|
||||
<p><?php printf( __( '<a href="%s" target="_blank">Learn more</a> about these options.', 'jetpack' ), 'http://support.wordpress.com/proofreading/' );
|
||||
?></p>
|
||||
|
||||
<p style="font-weight: bold"><?php _e( 'Language', 'jetpack' ); ?></p>
|
||||
|
||||
<p><?php
|
||||
_e( 'The proofreader supports English, French, German, Portuguese, and Spanish. Your user interface language (see above) is the default proofreading language.', 'jetpack' );
|
||||
?></p>
|
||||
|
||||
<p><?php
|
||||
AtD_print_option( 'true', __('Use automatically detected language to proofread posts and pages', 'jetpack' ), $options_guess_lang );
|
||||
?></p>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an array of AtD user options specified by $name
|
||||
*/
|
||||
function AtD_get_options( $user_id, $name ) {
|
||||
$options_raw = AtD_get_setting( $user_id, $name, 'single' );
|
||||
|
||||
$options = array();
|
||||
$options['name'] = $name;
|
||||
|
||||
if ( $options_raw )
|
||||
foreach ( explode( ',', $options_raw ) as $option )
|
||||
$options[ $option ] = 1;
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/*
|
||||
* Saves set of user options specified by $name from POST data
|
||||
*/
|
||||
function AtD_update_options( $user_id, $name ) {
|
||||
/* We should probably run $_POST[name] through an esc_*() function... */
|
||||
if ( isset( $_POST[$name] ) && is_array( $_POST[$name] ) ) {
|
||||
$copy = array_map( 'strip_tags', array_keys( $_POST[$name] ) );
|
||||
AtD_update_setting( $user_id, AtD_sanitize( $name ), implode( ',', $copy ) );
|
||||
} else {
|
||||
AtD_update_setting( $user_id, AtD_sanitize( $name ), '');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
148
plugins/jetpack/modules/after-the-deadline/config-unignore.php
Normal file
148
plugins/jetpack/modules/after-the-deadline/config-unignore.php
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
/*
|
||||
* Called by the TinyMCE plugin when Ignore Always is clicked (setup as an action through admin-ajax.php)
|
||||
*/
|
||||
function AtD_ignore_call() {
|
||||
|
||||
if ( ! AtD_is_allowed() )
|
||||
return;
|
||||
|
||||
$user = wp_get_current_user();
|
||||
|
||||
if ( ! $user || $user->ID == 0 )
|
||||
return;
|
||||
|
||||
check_admin_referer( 'atd_ignore' );
|
||||
|
||||
$ignores = explode( ',', AtD_get_setting( $user->ID, 'AtD_ignored_phrases') );
|
||||
array_push( $ignores, $_GET['phrase'] );
|
||||
|
||||
$ignores = array_filter( array_map( 'strip_tags', $ignores ) );
|
||||
|
||||
AtD_update_setting( $user->ID, 'AtD_ignored_phrases', implode( ',', $ignores ) );
|
||||
|
||||
header( 'Content-Type: text/xml' );
|
||||
echo '<success></success>';
|
||||
die();
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a POST occurs, used to save AtD ignored phrases
|
||||
*/
|
||||
function AtD_process_unignore_update() {
|
||||
|
||||
if ( ! AtD_is_allowed() )
|
||||
return;
|
||||
|
||||
if ( ! isset( $_POST['AtD_ignored_phrases'] ) )
|
||||
return;
|
||||
|
||||
$user = wp_get_current_user();
|
||||
|
||||
if ( ! $user || $user->ID == 0 )
|
||||
return;
|
||||
|
||||
$ignores = array_filter( array_map( 'strip_tags', explode( ',', $_POST['AtD_ignored_phrases'] ) ) );
|
||||
AtD_update_setting( $user->ID, 'AtD_ignored_phrases', join( ',', $ignores ) );
|
||||
}
|
||||
|
||||
/*
|
||||
* Display the AtD unignore form on a page
|
||||
*/
|
||||
function AtD_display_unignore_form() {
|
||||
|
||||
if ( ! AtD_is_allowed() )
|
||||
return;
|
||||
|
||||
$user = wp_get_current_user();
|
||||
|
||||
if ( ! $user || $user->ID == 0 )
|
||||
return;
|
||||
|
||||
$ignores = AtD_get_setting( $user->ID, 'AtD_ignored_phrases' );
|
||||
?>
|
||||
<script>
|
||||
function atd_show_phrases( ignored )
|
||||
{
|
||||
var element = jQuery( '#atd_ignores' ),
|
||||
items = [],
|
||||
delLink;
|
||||
|
||||
ignored.sort();
|
||||
|
||||
element.empty();
|
||||
for ( var i = 0; i < ignored.length; i++ ) {
|
||||
if ( ignored[i].length > 0 ) {
|
||||
delLink = jQuery( '<span id="atd_' + i + '"> </span>' );
|
||||
delLink
|
||||
.text( delLink.text() + ignored[i] )
|
||||
.prepend( jQuery( '<a class="ntdelbutton">X</a>' ).data( 'ignored', ignored[i] ) );
|
||||
element.append( delLink ).append( '<br />' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function atd_unignore( phrase ) {
|
||||
/* get the ignored values and remove the unwanted phrase */
|
||||
var ignored = jQuery( '#AtD_ignored_phrases' ).val().split( /,/g );
|
||||
ignored = jQuery.map(ignored, function(value, index) { return value == phrase ? null : value; });
|
||||
jQuery( '#AtD_ignored_phrases' ).val( ignored.join(',') );
|
||||
|
||||
/* update the UI */
|
||||
atd_show_phrases( ignored );
|
||||
|
||||
/* show a nifty message to the user */
|
||||
jQuery( '#AtD_message' ).show();
|
||||
}
|
||||
|
||||
function atd_ignore () {
|
||||
/* get the ignored values and update the hidden field */
|
||||
var ignored = jQuery( '#AtD_ignored_phrases' ).val().split( /,/g );
|
||||
|
||||
jQuery.map(jQuery( '#AtD_add_ignore' ).val().split(/,\s*/g), function(value, index) { ignored.push(value); });
|
||||
|
||||
jQuery( '#AtD_ignored_phrases' ).val( ignored.join(',') );
|
||||
|
||||
/* update the UI */
|
||||
atd_show_phrases( ignored );
|
||||
jQuery( '#AtD_add_ignore' ).val('');
|
||||
|
||||
/* show that nifteroo messaroo to the useroo */
|
||||
jQuery( '#AtD_message' ).show();
|
||||
}
|
||||
|
||||
function atd_ignore_init() {
|
||||
jQuery( '#AtD_message' ).hide();
|
||||
jQuery( '#atd_ignores' ).on( 'click', 'a', function() {
|
||||
atd_unignore( jQuery(this).data( 'ignored' ) );
|
||||
return false;
|
||||
} );
|
||||
atd_show_phrases( jQuery( '#AtD_ignored_phrases' ).val().split( /,/g ) );
|
||||
}
|
||||
|
||||
/* document.ready() does not execute in IE6 unless it's at the bottom of the page. oi! */
|
||||
if (navigator.appName == 'Microsoft Internet Explorer')
|
||||
setTimeout( atd_ignore_init, 2500 );
|
||||
else
|
||||
jQuery( document ).ready( atd_ignore_init );
|
||||
</script>
|
||||
<input type="hidden" name="AtD_ignored_phrases" id="AtD_ignored_phrases" value="<?php echo esc_attr( $ignores ); ?>">
|
||||
|
||||
<p style="font-weight: bold"><?php _e( 'Ignored Phrases', 'jetpack' ); ?></p>
|
||||
|
||||
<p><?php _e( 'Identify words and phrases to ignore while proofreading your posts and pages:', 'jetpack' ); ?></p>
|
||||
|
||||
<p><input type="text" id="AtD_add_ignore" name="AtD_add_ignore"> <input type="button" value="<?php _e( 'Add', 'jetpack' ); ?>" onclick="javascript:atd_ignore()"></p>
|
||||
|
||||
<div class="tagchecklist" id="atd_ignores"></div>
|
||||
|
||||
<div class="plugin-update-tr" id="AtD_message" style="display: none">
|
||||
<div class="update-message"><strong><?php _e( 'Be sure to click "Update Profile" at the bottom of the screen to save your changes.', 'jetpack' ); ?></strong></div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<?php
|
||||
}
|
436
plugins/jetpack/modules/after-the-deadline/jquery.atd.js
Normal file
436
plugins/jetpack/modules/after-the-deadline/jquery.atd.js
Normal file
|
@ -0,0 +1,436 @@
|
|||
/*
|
||||
* jquery.atd.js - jQuery powered writing check with After the Deadline
|
||||
* Author : Raphael Mudge, Automattic Inc.
|
||||
* License : LGPL or MIT License (take your pick)
|
||||
* Project : http://www.afterthedeadline.com/development.slp
|
||||
* Contact : raffi@automattic.com
|
||||
*
|
||||
* Derived from:
|
||||
*
|
||||
* jquery.spellchecker.js - a simple jQuery Spell Checker
|
||||
* Copyright (c) 2008 Richard Willis
|
||||
* MIT license : http://www.opensource.org/licenses/mit-license.php
|
||||
* Project : http://jquery-spellchecker.googlecode.com
|
||||
* Contact : willis.rh@gmail.com
|
||||
*/
|
||||
|
||||
/* jshint onevar: false, sub: true, smarttabs: true */
|
||||
/* global AtDCore, AtD_proofread_click_count, CSSHttpRequest, ActiveXObject */
|
||||
|
||||
var AtD =
|
||||
{
|
||||
rpc : '', /* see the proxy.php that came with the AtD/TinyMCE plugin */
|
||||
rpc_css : 'http://www.polishmywriting.com/atd-jquery/server/proxycss.php?data=', /* you may use this, but be nice! */
|
||||
rpc_css_lang : 'en',
|
||||
api_key : '',
|
||||
i18n : {}, // Back-compat
|
||||
listener : {}
|
||||
};
|
||||
|
||||
AtD.getLang = function( key, defaultk ) {
|
||||
return ( window.AtD_l10n_r0ar && window.AtD_l10n_r0ar[key] ) || defaultk;
|
||||
};
|
||||
|
||||
AtD.addI18n = function( obj ) {
|
||||
// Back-compat
|
||||
window.AtD_l10n_r0ar = obj;
|
||||
};
|
||||
|
||||
AtD.setIgnoreStrings = function(string) {
|
||||
AtD.core.setIgnoreStrings(string);
|
||||
};
|
||||
|
||||
AtD.showTypes = function(string) {
|
||||
AtD.core.showTypes(string);
|
||||
};
|
||||
|
||||
AtD.checkCrossAJAX = function(container_id, callback_f) {
|
||||
/* checks if a global var for click stats exists and increments it if it does... */
|
||||
if (typeof AtD_proofread_click_count !== 'undefined') {
|
||||
AtD_proofread_click_count++;
|
||||
}
|
||||
|
||||
AtD.callback_f = callback_f; /* remember the callback for later */
|
||||
AtD.remove(container_id);
|
||||
var container = jQuery('#' + container_id);
|
||||
|
||||
var text = jQuery.trim(container.html());
|
||||
text = text.replace(/\</g, '<').replace(/\>/g, '>').replace(/\&/g, '&');
|
||||
text = encodeURIComponent( text.replace( /\%/g, '%25' ) ); /* % not being escaped here creates problems, I don't know why. */
|
||||
|
||||
/* do some sanity checks based on the browser */
|
||||
if ((text.length > 2000 && navigator.appName === 'Microsoft Internet Explorer') || text.length > 7800) {
|
||||
if (callback_f !== undefined && callback_f.error !== undefined) {
|
||||
callback_f.error('Maximum text length for this browser exceeded');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* do some cross-domain AJAX action with CSSHttpRequest */
|
||||
CSSHttpRequest.get(AtD.rpc_css + text + '&lang=' + AtD.rpc_css_lang + '&nocache=' + (new Date().getTime()), function(response) {
|
||||
/* do some magic to convert the response into an XML document */
|
||||
var xml;
|
||||
if (navigator.appName === 'Microsoft Internet Explorer') {
|
||||
xml = new ActiveXObject('Microsoft.XMLDOM');
|
||||
xml.async = false;
|
||||
xml.loadXML(response);
|
||||
} else {
|
||||
xml = (new DOMParser()).parseFromString(response, 'text/xml');
|
||||
}
|
||||
|
||||
/* check for and display error messages from the server */
|
||||
if (AtD.core.hasErrorMessage(xml)) {
|
||||
if (AtD.callback_f !== undefined && AtD.callback_f.error !== undefined) {
|
||||
AtD.callback_f.error(AtD.core.getErrorMessage(xml));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* highlight the errors */
|
||||
|
||||
AtD.container = container_id;
|
||||
var count = Number( AtD.processXML(container_id, xml) );
|
||||
|
||||
if (AtD.callback_f !== undefined && AtD.callback_f.ready !== undefined) {
|
||||
AtD.callback_f.ready(count);
|
||||
}
|
||||
|
||||
if (count === 0 && AtD.callback_f !== undefined && AtD.callback_f.success !== undefined) {
|
||||
AtD.callback_f.success(count);
|
||||
}
|
||||
|
||||
AtD.counter = count;
|
||||
AtD.count = count;
|
||||
});
|
||||
};
|
||||
|
||||
/* check a div for any incorrectly spelled words */
|
||||
AtD.check = function(container_id, callback_f) {
|
||||
/* checks if a global var for click stats exists and increments it if it does... */
|
||||
if (typeof AtD_proofread_click_count !== 'undefined') {
|
||||
AtD_proofread_click_count++;
|
||||
}
|
||||
|
||||
AtD.callback_f = callback_f; /* remember the callback for later */
|
||||
|
||||
AtD.remove(container_id);
|
||||
|
||||
var container = jQuery('#' + container_id);
|
||||
|
||||
var text = jQuery.trim(container.html());
|
||||
text = text.replace(/\</g, '<').replace(/\>/g, '>').replace(/\&/g, '&');
|
||||
text = encodeURIComponent( text ); /* re-escaping % is not necessary here. don't do it */
|
||||
|
||||
jQuery.ajax({
|
||||
type : 'POST',
|
||||
url : AtD.rpc + '/checkDocument',
|
||||
data : 'key=' + AtD.api_key + '&data=' + text,
|
||||
format : 'raw',
|
||||
dataType : (jQuery.browser.msie) ? 'text' : 'xml',
|
||||
|
||||
error : function(XHR, status, error) {
|
||||
if (AtD.callback_f !== undefined && AtD.callback_f.error !== undefined) {
|
||||
AtD.callback_f.error(status + ': ' + error);
|
||||
}
|
||||
},
|
||||
|
||||
success : function(data) {
|
||||
/* apparently IE likes to return XML as plain text-- work around from:
|
||||
http://docs.jquery.com/Specifying_the_Data_Type_for_AJAX_Requests */
|
||||
|
||||
var xml;
|
||||
if (typeof data === 'string') {
|
||||
xml = new ActiveXObject('Microsoft.XMLDOM');
|
||||
xml.async = false;
|
||||
xml.loadXML(data);
|
||||
}
|
||||
else {
|
||||
xml = data;
|
||||
}
|
||||
|
||||
if (AtD.core.hasErrorMessage(xml)) {
|
||||
if (AtD.callback_f !== undefined && AtD.callback_f.error !== undefined) {
|
||||
AtD.callback_f.error(AtD.core.getErrorMessage(xml));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* on with the task of processing and highlighting errors */
|
||||
|
||||
AtD.container = container_id;
|
||||
var count = Number( AtD.processXML(container_id, xml) );
|
||||
|
||||
if (AtD.callback_f !== undefined && AtD.callback_f.ready !== undefined) {
|
||||
AtD.callback_f.ready(count);
|
||||
}
|
||||
|
||||
if (count === 0 && AtD.callback_f !== undefined && AtD.callback_f.success !== undefined) {
|
||||
AtD.callback_f.success(count);
|
||||
}
|
||||
|
||||
AtD.counter = count;
|
||||
AtD.count = count;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
AtD.remove = function(container_id) {
|
||||
AtD._removeWords(container_id, null);
|
||||
};
|
||||
|
||||
AtD.clickListener = function(event) {
|
||||
if (AtD.core.isMarkedNode(event.target)) {
|
||||
AtD.suggest(event.target);
|
||||
}
|
||||
};
|
||||
|
||||
AtD.processXML = function(container_id, responseXML) {
|
||||
|
||||
var results = AtD.core.processXML(responseXML);
|
||||
|
||||
if (results.count > 0) {
|
||||
results.count = AtD.core.markMyWords(jQuery('#' + container_id).contents(), results.errors);
|
||||
}
|
||||
|
||||
jQuery('#' + container_id).unbind('click', AtD.clickListener);
|
||||
jQuery('#' + container_id).click(AtD.clickListener);
|
||||
|
||||
return results.count;
|
||||
};
|
||||
|
||||
AtD.useSuggestion = function(word) {
|
||||
this.core.applySuggestion(AtD.errorElement, word);
|
||||
|
||||
AtD.counter --;
|
||||
if (AtD.counter === 0 && AtD.callback_f !== undefined && AtD.callback_f.success !== undefined) {
|
||||
AtD.callback_f.success(AtD.count);
|
||||
}
|
||||
};
|
||||
|
||||
AtD.editSelection = function() {
|
||||
var parent = AtD.errorElement.parent();
|
||||
|
||||
if (AtD.callback_f !== undefined && AtD.callback_f.editSelection !== undefined) {
|
||||
AtD.callback_f.editSelection(AtD.errorElement);
|
||||
}
|
||||
|
||||
if (AtD.errorElement.parent() !== parent) {
|
||||
AtD.counter --;
|
||||
if (AtD.counter === 0 && AtD.callback_f !== undefined && AtD.callback_f.success !== undefined) {
|
||||
AtD.callback_f.success(AtD.count);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AtD.ignoreSuggestion = function() {
|
||||
AtD.core.removeParent(AtD.errorElement);
|
||||
|
||||
AtD.counter --;
|
||||
if (AtD.counter === 0 && AtD.callback_f !== undefined && AtD.callback_f.success !== undefined) {
|
||||
AtD.callback_f.success(AtD.count);
|
||||
}
|
||||
};
|
||||
|
||||
AtD.ignoreAll = function(container_id) {
|
||||
var target = AtD.errorElement.text();
|
||||
var removed = AtD._removeWords(container_id, target);
|
||||
|
||||
AtD.counter -= removed;
|
||||
|
||||
if (AtD.counter === 0 && AtD.callback_f !== undefined && AtD.callback_f.success !== undefined) {
|
||||
AtD.callback_f.success(AtD.count);
|
||||
}
|
||||
|
||||
if (AtD.callback_f !== undefined && AtD.callback_f.ignore !== undefined) {
|
||||
AtD.callback_f.ignore(target);
|
||||
AtD.core.setIgnoreStrings(target);
|
||||
}
|
||||
};
|
||||
|
||||
AtD.explainError = function() {
|
||||
if (AtD.callback_f !== undefined && AtD.callback_f.explain !== undefined) {
|
||||
AtD.callback_f.explain(AtD.explainURL);
|
||||
}
|
||||
};
|
||||
|
||||
AtD.suggest = function(element) {
|
||||
/* construct the menu if it doesn't already exist */
|
||||
var suggest;
|
||||
|
||||
if (jQuery('#suggestmenu').length === 0) {
|
||||
suggest = jQuery('<div id="suggestmenu"></div>');
|
||||
suggest.prependTo('body');
|
||||
} else {
|
||||
suggest = jQuery('#suggestmenu');
|
||||
suggest.hide();
|
||||
}
|
||||
|
||||
/* find the correct suggestions object */
|
||||
|
||||
var errorDescription = AtD.core.findSuggestion(element);
|
||||
|
||||
/* build up the menu y0 */
|
||||
|
||||
AtD.errorElement = jQuery(element);
|
||||
|
||||
suggest.empty();
|
||||
|
||||
if (errorDescription === undefined) {
|
||||
suggest.append('<strong>' + AtD.getLang('menu_title_no_suggestions', 'No suggestions') + '</strong>');
|
||||
} else if (errorDescription['suggestions'].length === 0) {
|
||||
suggest.append('<strong>' + errorDescription['description'] + '</strong>');
|
||||
} else {
|
||||
suggest.append('<strong>' + errorDescription['description'] + '</strong>');
|
||||
|
||||
for (var i = 0; i < errorDescription['suggestions'].length; i++) {
|
||||
(function(sugg) {
|
||||
suggest.append('<a href="javascript:AtD.useSuggestion(\'' + sugg.replace(/'/, '\\\'') + '\')">' + sugg + '</a>');
|
||||
})(errorDescription['suggestions'][i]); // jshint ignore:line
|
||||
}
|
||||
}
|
||||
|
||||
/* do the explain menu if configured */
|
||||
|
||||
if (AtD.callback_f !== undefined && AtD.callback_f.explain !== undefined && errorDescription['moreinfo'] !== undefined) {
|
||||
suggest.append('<a href="javascript:AtD.explainError()" class="spell_sep_top">' + AtD.getLang('menu_option_explain', 'Explain...') + '</a>');
|
||||
AtD.explainURL = errorDescription['moreinfo'];
|
||||
}
|
||||
|
||||
/* do the ignore option */
|
||||
|
||||
suggest.append('<a href="javascript:AtD.ignoreSuggestion()" class="spell_sep_top">' + AtD.getLang('menu_option_ignore_once', 'Ignore suggestion') + '</a>');
|
||||
|
||||
/* add the edit in place and ignore always option */
|
||||
|
||||
if (AtD.callback_f !== undefined && AtD.callback_f.editSelection !== undefined) {
|
||||
if (AtD.callback_f !== undefined && AtD.callback_f.ignore !== undefined) {
|
||||
suggest.append('<a href="javascript:AtD.ignoreAll(\'' + AtD.container + '\')">' + AtD.getLang('menu_option_ignore_always', 'Ignore always') + '</a>');
|
||||
} else {
|
||||
suggest.append('<a href="javascript:AtD.ignoreAll(\'' + AtD.container + '\')">' + AtD.getLang('menu_option_ignore_all', 'Ignore all') + '</a>');
|
||||
}
|
||||
|
||||
suggest.append('<a href="javascript:AtD.editSelection(\'' + AtD.container + '\')" class="spell_sep_bottom spell_sep_top">' + AtD.getLang('menu_option_edit_selection', 'Edit Selection...') + '</a>');
|
||||
}
|
||||
else {
|
||||
if (AtD.callback_f !== undefined && AtD.callback_f.ignore !== undefined) {
|
||||
suggest.append('<a href="javascript:AtD.ignoreAll(\'' + AtD.container + '\')" class="spell_sep_bottom">' + AtD.getLang('menu_option_ignore_always', 'Ignore always') + '</a>');
|
||||
} else {
|
||||
suggest.append('<a href="javascript:AtD.ignoreAll(\'' + AtD.container + '\')" class="spell_sep_bottom">' + AtD.getLang('menu_option_ignore_all', 'Ignore all') + '</a>');
|
||||
}
|
||||
}
|
||||
|
||||
/* show the menu */
|
||||
|
||||
var pos = jQuery(element).offset();
|
||||
var width = jQuery(element).width();
|
||||
|
||||
/* a sanity check for Internet Explorer--my favorite browser in every possible way */
|
||||
if (width > 100) {
|
||||
width = 50;
|
||||
}
|
||||
|
||||
jQuery(suggest).css({ left: (pos.left + width) + 'px', top: pos.top + 'px' });
|
||||
|
||||
jQuery(suggest).fadeIn(200);
|
||||
|
||||
/* bind events to make the menu disappear when the user clicks outside of it */
|
||||
|
||||
AtD.suggestShow = true;
|
||||
|
||||
setTimeout(function() {
|
||||
jQuery('body').bind('click', function() {
|
||||
if (!AtD.suggestShow) {
|
||||
jQuery('#suggestmenu').fadeOut(200);
|
||||
}
|
||||
});
|
||||
}, 1);
|
||||
|
||||
setTimeout(function() {
|
||||
AtD.suggestShow = false;
|
||||
}, 2);
|
||||
};
|
||||
|
||||
AtD._removeWords = function(container_id, w) {
|
||||
return this.core.removeWords(jQuery('#' + container_id), w);
|
||||
};
|
||||
|
||||
/*
|
||||
* Set prototypes used by AtD Core UI
|
||||
*/
|
||||
AtD.initCoreModule = function() {
|
||||
var core = new AtDCore();
|
||||
|
||||
core.hasClass = function(node, className) {
|
||||
return jQuery(node).hasClass(className);
|
||||
};
|
||||
|
||||
core.map = jQuery.map;
|
||||
|
||||
core.contents = function(node) {
|
||||
return jQuery(node).contents();
|
||||
};
|
||||
|
||||
core.replaceWith = function(old_node, new_node) {
|
||||
return jQuery(old_node).replaceWith(new_node);
|
||||
};
|
||||
|
||||
core.findSpans = function(parent) {
|
||||
return jQuery.makeArray(parent.find('span'));
|
||||
};
|
||||
|
||||
core.create = function(string/*, isTextNode*/) {
|
||||
// replace out all tags with &-equivalents so that we preserve tag text.
|
||||
string = string.replace(/\&/g, '&');
|
||||
string = string.replace(/</g, '<').replace(/\>/g, '>');
|
||||
|
||||
// find all instances of AtD-created spans
|
||||
var matches = string.match(/\<span class="hidden\w+?" pre="[^"]*"\>.*?\<\/span\>/g);
|
||||
var x;
|
||||
|
||||
// ... and fix the tags in those substrings.
|
||||
if (matches) {
|
||||
for (x = 0; x < matches.length; x++) {
|
||||
string = string.replace(matches[x], matches[x].replace(/\</gi, '<').replace(/\>/gi, '>'));
|
||||
}
|
||||
}
|
||||
|
||||
if (core.isIE()) {
|
||||
// and... one more round of corrections for our friends over at the Internet Explorer
|
||||
matches = string.match(/\<span class="mceItemHidden"\>\&nbsp;\<\/span>/g, string);
|
||||
//|<BR.*?class.*?atd_remove_me.*?\>/gi, string);
|
||||
if (matches) {
|
||||
for (x = 0; x < matches.length; x++) {
|
||||
string = string.replace(matches[x], matches[x].replace(/\</gi, '<').replace(/\>/gi, '>').replace(/\&/gi, '&'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var node = jQuery('<span class="mceItemHidden"></span>');
|
||||
node.html(string);
|
||||
return node;
|
||||
};
|
||||
|
||||
core.remove = function(node) {
|
||||
return jQuery(node).remove();
|
||||
};
|
||||
|
||||
core.removeParent = function(node) {
|
||||
/* unwrap exists in jQuery 1.4+ only. Thankfully because replaceWith as-used here won't work in 1.4 */
|
||||
if (jQuery(node).unwrap) {
|
||||
return jQuery(node).contents().unwrap();
|
||||
} else {
|
||||
return jQuery(node).replaceWith(jQuery(node).html());
|
||||
}
|
||||
};
|
||||
|
||||
core.getAttrib = function(node, name) {
|
||||
return jQuery(node).attr(name);
|
||||
};
|
||||
|
||||
return core;
|
||||
};
|
||||
|
||||
AtD.core = AtD.initCoreModule();
|
115
plugins/jetpack/modules/after-the-deadline/proxy.php
Normal file
115
plugins/jetpack/modules/after-the-deadline/proxy.php
Normal file
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
/*
|
||||
* This script redirects AtD AJAX requests to the AtD service
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns array with headers in $response[0] and body in $response[1]
|
||||
* Based on a function from Akismet
|
||||
*/
|
||||
function AtD_http_post( $request, $host, $path, $port = 80 ) {
|
||||
$http_args = array(
|
||||
'body' => $request,
|
||||
'headers' => array(
|
||||
'Content-Type' => 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ),
|
||||
'Host' => $host,
|
||||
'User-Agent' => 'AtD/0.1'
|
||||
),
|
||||
'httpversion' => '1.0',
|
||||
/**
|
||||
* Change the timeout time for AtD post.
|
||||
*
|
||||
* @module after-the-deadline
|
||||
*
|
||||
* @since 1.2.3
|
||||
*
|
||||
* @param int $var Timeout time in seconds, default 15.
|
||||
*/
|
||||
'timeout' => apply_filters( 'atd_http_post_timeout', 15 ),
|
||||
);
|
||||
|
||||
// Handle non-standard ports being passed in.
|
||||
if ( ( 80 !== $port ) && is_numeric( $port ) && ( intval( $port ) > 0 ) ) {
|
||||
$host .= ':' . intval( $port );
|
||||
}
|
||||
// Strip any / off the begining so we can add it back and protect against SSRF
|
||||
$path = ltrim( $path, '/' );
|
||||
$AtD_url = set_url_scheme( "http://{$host}/{$path}" );
|
||||
$response = wp_remote_post( $AtD_url, $http_args );
|
||||
$code = (int) wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
/**
|
||||
* Fires when there is a post error to AtD.
|
||||
*
|
||||
* @module after-the-deadline
|
||||
*
|
||||
* @since 1.2.3
|
||||
*
|
||||
* @param int|string http-error The error that AtD runs into.
|
||||
*/
|
||||
do_action( 'atd_http_post_error', 'http-error' );
|
||||
return array();
|
||||
} elseif ( 200 != $code ) {
|
||||
/** This action is documented in modules/after-the-deadline/proxy.php */
|
||||
do_action( 'atd_http_post_error', $code );
|
||||
}
|
||||
|
||||
return array(
|
||||
wp_remote_retrieve_headers( $response ),
|
||||
wp_remote_retrieve_body( $response ),
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called as an action handler to admin-ajax.php
|
||||
*/
|
||||
function AtD_redirect_call() {
|
||||
if ( $_SERVER['REQUEST_METHOD'] === 'POST' )
|
||||
$postText = trim( file_get_contents( 'php://input' ) );
|
||||
|
||||
check_admin_referer( 'proxy_atd' );
|
||||
|
||||
$url = $_GET['url'];
|
||||
/**
|
||||
* Change the AtD service domain.
|
||||
*
|
||||
* @module after-the-deadline
|
||||
*
|
||||
* @since 1.2.3
|
||||
*
|
||||
* @param string $var The URL for AtD service domain, default is service.afterthedeadline.com.
|
||||
*/
|
||||
$service = apply_filters( 'atd_service_domain', 'service.afterthedeadline.com' );
|
||||
|
||||
$user = wp_get_current_user();
|
||||
|
||||
$atd_lang = get_locale();
|
||||
|
||||
// If we're on WPCOM, this function should be available.
|
||||
if ( function_exists( 'get_user_lang_code' ) ) {
|
||||
$atd_lang = get_user_lang_code( $user->ID );
|
||||
}
|
||||
|
||||
if ( ! empty( $atd_lang ) ) {
|
||||
if ( strpos($atd_lang, 'pt') !== false )
|
||||
$service = 'pt.service.afterthedeadline.com';
|
||||
else if ( strpos($atd_lang, 'de') !== false )
|
||||
$service = 'de.service.afterthedeadline.com';
|
||||
else if ( strpos($atd_lang, 'es') !== false )
|
||||
$service = 'es.service.afterthedeadline.com';
|
||||
else if ( strpos($atd_lang, 'fr') !== false )
|
||||
$service = 'fr.service.afterthedeadline.com';
|
||||
}
|
||||
|
||||
$guess = strcmp( AtD_get_setting( $user->ID, 'AtD_guess_lang' ), "true" ) == 0 ? "true" : "false";
|
||||
|
||||
$data = AtD_http_post( $postText . "&guess=$guess", defined('ATD_HOST') ? ATD_HOST : $service, $url, defined('ATD_PORT') ? ATD_PORT : 80 );
|
||||
|
||||
header( 'Content-Type: text/xml' );
|
||||
|
||||
if ( ! empty( $data[1] ) )
|
||||
echo $data[1];
|
||||
|
||||
die();
|
||||
}
|
124
plugins/jetpack/modules/after-the-deadline/rtl/atd-rtl.css
Normal file
124
plugins/jetpack/modules/after-the-deadline/rtl/atd-rtl.css
Normal file
|
@ -0,0 +1,124 @@
|
|||
/* This file was automatically generated on Mar 20 2014 23:59:38 */
|
||||
|
||||
/* AtD error styles */
|
||||
|
||||
.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 styles derived from:
|
||||
* jquery.spellchecker.js - a simple jQuery Spell Checker
|
||||
* Copyright (c) 2008 Richard Willis
|
||||
* MIT license : http://www.opensource.org/licenses/mit-license.php
|
||||
* Project : http://jquery-spellchecker.googlecode.com
|
||||
*/
|
||||
|
||||
#suggestmenu
|
||||
{
|
||||
min-width: 122px;
|
||||
background: #ebeaeb;
|
||||
position: absolute;
|
||||
display: none;
|
||||
z-index: 9999;
|
||||
overflow: none;
|
||||
margin-top: 1px;
|
||||
text-align: right;
|
||||
font-size: 11px;
|
||||
font-family: Tahoma, Verdana, Arial, Helvetica;
|
||||
}
|
||||
|
||||
#suggestmenu strong
|
||||
{
|
||||
background: #cccccc;
|
||||
font-weight: bold;
|
||||
padding:3px 6px 3px 6px;
|
||||
display:block;
|
||||
border:1px solid #dddddd;
|
||||
border-bottom: 1px solid #aaaaaa;
|
||||
color: black;
|
||||
}
|
||||
|
||||
#suggestmenu em
|
||||
{
|
||||
text-align:center;
|
||||
padding:3px 6px 3px 6px;
|
||||
display:block;
|
||||
border-top:1px solid #ccc;
|
||||
border-right:1px solid #ccc;
|
||||
}
|
||||
|
||||
#suggestmenu a, #suggestmenu a:visited
|
||||
{
|
||||
background: #ebeaeb;
|
||||
border-right:1px solid #dddddd;
|
||||
border-left:1px solid #dddddd;
|
||||
padding:3px 6px 3px 6px;
|
||||
display:block;
|
||||
margin:0px;
|
||||
text-decoration:none;
|
||||
color: black;
|
||||
outline:none
|
||||
}
|
||||
|
||||
#suggestmenu a.first, #suggestmenu a.first:visited
|
||||
{
|
||||
border-top:1px solid #dddddd;
|
||||
}
|
||||
|
||||
.spell_sep_bottom
|
||||
{
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
|
||||
.spell_sep_top
|
||||
{
|
||||
border-top: 1px solid #aaaaaa;
|
||||
}
|
||||
|
||||
#suggestmenu a:hover
|
||||
{
|
||||
color:#000;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
#suggestmenu .foot
|
||||
{
|
||||
border-top:1px solid #aaaaaa;
|
||||
background:#fff
|
||||
}
|
||||
|
||||
#suggestmenu .foot a, #suggestmenu .foot a:visited
|
||||
{
|
||||
outline:none
|
||||
}
|
||||
|
||||
/* TinyMCE 4.0 */
|
||||
div.mce-atd-menu-title.mce-disabled {
|
||||
padding: 3px 12px 0;
|
||||
}
|
||||
|
||||
div.mce-atd-menu-title.mce-disabled:hover,
|
||||
div.mce-atd-menu-title.mce-disabled:hover span.mce-text {
|
||||
background: none;
|
||||
color: #888;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
div.mce-atd-menu-title.mce-disabled span.mce-text {
|
||||
font-weight: bold;
|
||||
color: #888;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 123 B |
|
@ -0,0 +1,18 @@
|
|||
.hiddenSpellError
|
||||
{
|
||||
border-bottom: 2px solid red;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.hiddenGrammarError
|
||||
{
|
||||
border-bottom: 2px solid green;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.hiddenSuggestion
|
||||
{
|
||||
border-bottom: 2px solid blue;
|
||||
cursor: default;
|
||||
}
|
||||
|
1
plugins/jetpack/modules/after-the-deadline/tinymce/css/content.min.css
vendored
Normal file
1
plugins/jetpack/modules/after-the-deadline/tinymce/css/content.min.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.hiddenSpellError{border-bottom:2px solid red;cursor:default}.hiddenGrammarError{border-bottom:2px solid green;cursor:default}.hiddenSuggestion{border-bottom:2px solid #00f;cursor:default}
|
|
@ -0,0 +1,479 @@
|
|||
/*
|
||||
* TinyMCE Writing Improvement Tool Plugin
|
||||
* Author: Raphael Mudge (raffi@automattic.com)
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* jshint onevar: false, sub: true, devel: true */
|
||||
/* global tinymce, AtDCore, AtD_proofread_click_count */
|
||||
|
||||
(function()
|
||||
{
|
||||
var each = tinymce.each, DOM = tinymce.DOM, core;
|
||||
|
||||
function getLang( key, defaultStr ) {
|
||||
return ( window.AtD_l10n_r0ar && window.AtD_l10n_r0ar[key] ) || defaultStr;
|
||||
}
|
||||
|
||||
tinymce.create('tinymce.plugins.AfterTheDeadlinePlugin',
|
||||
{
|
||||
getInfo : function()
|
||||
{
|
||||
return {
|
||||
longname : 'After The Deadline',
|
||||
author : 'Raphael Mudge',
|
||||
authorurl : 'http://blog.afterthedeadline.com',
|
||||
infourl : 'http://www.afterthedeadline.com',
|
||||
version : tinymce.majorVersion + '.' + tinymce.minorVersion
|
||||
};
|
||||
},
|
||||
|
||||
/* initializes the functions used by the AtD Core UI Module */
|
||||
initAtDCore : function(editor/*, plugin*/)
|
||||
{
|
||||
var core = new AtDCore();
|
||||
|
||||
core.map = each;
|
||||
|
||||
core.getAttrib = function(node, key)
|
||||
{
|
||||
return editor.dom.getAttrib(node, key);
|
||||
};
|
||||
|
||||
core.findSpans = function(parent)
|
||||
{
|
||||
if (!parent) {
|
||||
return editor.dom.select('span');
|
||||
} else {
|
||||
return editor.dom.select('span', parent);
|
||||
}
|
||||
};
|
||||
|
||||
core.hasClass = function(node, className)
|
||||
{
|
||||
return editor.dom.hasClass(node, className);
|
||||
};
|
||||
|
||||
core.contents = function(node)
|
||||
{
|
||||
return node.childNodes;
|
||||
};
|
||||
|
||||
core.replaceWith = function(old_node, new_node)
|
||||
{
|
||||
return editor.dom.replace(new_node, old_node);
|
||||
};
|
||||
|
||||
core.create = function(node_html)
|
||||
{
|
||||
return editor.dom.create('span', { 'class': 'mceItemHidden' }, node_html);
|
||||
};
|
||||
|
||||
core.removeParent = function(node)
|
||||
{
|
||||
editor.dom.remove(node, 1);
|
||||
return node;
|
||||
};
|
||||
|
||||
core.remove = function(node)
|
||||
{
|
||||
editor.dom.remove(node);
|
||||
};
|
||||
|
||||
core.setIgnoreStrings(editor.getParam('atd_ignore_strings', [] ).join(','));
|
||||
core.showTypes(editor.getParam('atd_show_types', ''));
|
||||
return core;
|
||||
},
|
||||
|
||||
/* called when the plugin is initialized */
|
||||
init : function(ed, url)
|
||||
{
|
||||
if ( typeof(AtDCore) === 'undefined' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var plugin = this;
|
||||
var editor = ed;
|
||||
this.url = url;
|
||||
this.editor = ed;
|
||||
|
||||
core = ed.core = this.initAtDCore(editor, plugin);
|
||||
|
||||
/* look at the atd_ignore variable and put that stuff into a hash */
|
||||
var ignore = tinymce.util.Cookie.getHash('atd_ignore');
|
||||
|
||||
if (!ignore) {
|
||||
ignore = {};
|
||||
}
|
||||
|
||||
/* add a command to request a document check and process the results. */
|
||||
editor.addCommand('mceWritingImprovementTool', function(callback)
|
||||
{
|
||||
/* checks if a global var for click stats exists and increments it if it does... */
|
||||
if (typeof AtD_proofread_click_count !== 'undefined') {
|
||||
AtD_proofread_click_count++;
|
||||
}
|
||||
|
||||
/* create the nifty spinny thing that says "hizzo, I'm doing something fo realz" */
|
||||
plugin.editor.setProgressState(1);
|
||||
|
||||
/* remove the previous errors */
|
||||
plugin._removeWords();
|
||||
|
||||
/* send request to our service */
|
||||
plugin.sendRequest('checkDocument', ed.getContent({ format: 'raw' }), function(data, request/*, someObject*/)
|
||||
{
|
||||
/* turn off the spinning thingie */
|
||||
plugin.editor.setProgressState(0);
|
||||
|
||||
/* if the server is not accepting requests, let the user know */
|
||||
if ( request.status !== 200 || request.responseText.substr(1, 4) === 'html' || !request.responseXML )
|
||||
{
|
||||
ed.windowManager.alert(
|
||||
getLang( 'message_server_error', 'There was a problem communicating with the Proofreading service. Try again in one minute.' ),
|
||||
callback ? function() { callback( 0 ); } : function() {}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
/* check to see if things are broken first and foremost */
|
||||
if (request.responseXML.getElementsByTagName('message').item(0) != null)
|
||||
{
|
||||
ed.windowManager.alert(
|
||||
request.responseXML.getElementsByTagName('message').item(0).firstChild.data,
|
||||
callback ? function() { callback( 0 ); } : function() {}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var results = core.processXML(request.responseXML);
|
||||
var ecount = 0;
|
||||
|
||||
if (results.count > 0)
|
||||
{
|
||||
ecount = plugin.markMyWords(results.errors);
|
||||
ed.suggestions = results.suggestions;
|
||||
}
|
||||
|
||||
if (ecount === 0 && (!callback || callback === undefined)) {
|
||||
ed.windowManager.alert( getLang('message_no_errors_found', 'No writing errors were found.') );
|
||||
} else if (callback) {
|
||||
callback(ecount);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* load cascading style sheet for this plugin */
|
||||
editor.onInit.add(function()
|
||||
{
|
||||
/* loading the content.css file, why? I have no clue */
|
||||
if (editor.settings.content_css !== false)
|
||||
{
|
||||
editor.dom.loadCSS(editor.getParam('atd_css_url', url + '/css/content.css'));
|
||||
}
|
||||
});
|
||||
|
||||
/* again showing a menu, I have no clue what */
|
||||
editor.onClick.add(plugin._showMenu, plugin);
|
||||
|
||||
/* we're showing some sort of menu, no idea what */
|
||||
editor.onContextMenu.add(plugin._showMenu, plugin);
|
||||
|
||||
/* strip out the markup before the contents is serialized (and do it on a copy of the markup so we don't affect the user experience) */
|
||||
editor.onPreProcess.add(function(sender, object)
|
||||
{
|
||||
var dom = sender.dom;
|
||||
|
||||
each(dom.select('span', object.node).reverse(), function(n)
|
||||
{
|
||||
if (n && (dom.hasClass(n, 'hiddenGrammarError') || dom.hasClass(n, 'hiddenSpellError') || dom.hasClass(n, 'hiddenSuggestion') || dom.hasClass(n, 'mceItemHidden') || (!dom.getAttrib(n, 'class') && !dom.getAttrib(n, 'style') && !dom.getAttrib(n, 'id') && !dom.hasClass(n, 'Apple-style-span') && !dom.getAttrib(n, 'mce_name'))))
|
||||
{
|
||||
dom.remove(n, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* cleanup the HTML before executing certain commands */
|
||||
editor.onBeforeExecCommand.add(function(editor, command)
|
||||
{
|
||||
if (command === 'mceCodeEditor')
|
||||
{
|
||||
plugin._removeWords();
|
||||
}
|
||||
else if (command === 'mceFullScreen')
|
||||
{
|
||||
plugin._done();
|
||||
}
|
||||
});
|
||||
|
||||
ed.addButton('AtD', {
|
||||
title: getLang( 'button_proofread_tooltip', 'Proofread Writing' ),
|
||||
image: ed.getParam('atd_button_url', url + '/atdbuttontr.gif'),
|
||||
cmd: 'mceWritingImprovementTool'
|
||||
});
|
||||
},
|
||||
|
||||
_removeWords : function(w)
|
||||
{
|
||||
var ed = this.editor, dom = ed.dom, se = ed.selection, b = se.getBookmark();
|
||||
|
||||
ed.core.removeWords(undefined, w);
|
||||
|
||||
/* force a rebuild of the DOM... even though the right elements are stripped, the DOM is still organized
|
||||
as if the span were there and this breaks my code */
|
||||
|
||||
dom.setHTML(dom.getRoot(), dom.getRoot().innerHTML);
|
||||
|
||||
se.moveToBookmark(b);
|
||||
},
|
||||
|
||||
markMyWords : function(errors)
|
||||
{
|
||||
var ed = this.editor;
|
||||
var se = ed.selection, b = se.getBookmark();
|
||||
|
||||
var ecount = ed.core.markMyWords(ed.core.contents(this.editor.getBody()), errors);
|
||||
|
||||
se.moveToBookmark(b);
|
||||
return ecount;
|
||||
},
|
||||
|
||||
_showMenu : function(ed, e)
|
||||
{
|
||||
var t = this;
|
||||
ed = t.editor; // not clear why this overwrites the function parameter
|
||||
var m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin());
|
||||
|
||||
if (!m)
|
||||
{
|
||||
p1 = DOM.getPos(ed.getContentAreaContainer());
|
||||
//p2 = DOM.getPos(ed.getContainer());
|
||||
|
||||
m = ed.controlManager.createDropMenu('spellcheckermenu',
|
||||
{
|
||||
offset_x : p1.x,
|
||||
offset_y : p1.y,
|
||||
'class' : 'mceNoIcons'
|
||||
});
|
||||
|
||||
t._menu = m;
|
||||
}
|
||||
|
||||
if (ed.core.isMarkedNode(e.target))
|
||||
{
|
||||
/* remove these other lame-o elements */
|
||||
m.removeAll();
|
||||
|
||||
/* find the correct suggestions object */
|
||||
var errorDescription = ed.core.findSuggestion(e.target);
|
||||
|
||||
if (!errorDescription)
|
||||
{
|
||||
m.add({ title: getLang( 'menu_title_no_suggestions', 'No suggestions' ), 'class': 'mceMenuItemTitle' }).setDisabled(1);
|
||||
}
|
||||
else if (errorDescription['suggestions'].length === 0)
|
||||
{
|
||||
m.add({ title: errorDescription['description'], 'class' : 'mceMenuItemTitle' }).setDisabled(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
m.add({ title : errorDescription['description'], 'class' : 'mceMenuItemTitle' }).setDisabled(1);
|
||||
|
||||
for (var i = 0; i < errorDescription['suggestions'].length; i++)
|
||||
{
|
||||
(function(sugg)
|
||||
{
|
||||
m.add({
|
||||
title : sugg,
|
||||
onclick : function()
|
||||
{
|
||||
ed.core.applySuggestion(e.target, sugg);
|
||||
t._checkDone();
|
||||
}
|
||||
});
|
||||
})(errorDescription['suggestions'][i]); // jshint ignore:line
|
||||
}
|
||||
|
||||
m.addSeparator();
|
||||
}
|
||||
|
||||
if (errorDescription && errorDescription['moreinfo'])
|
||||
{
|
||||
(function(url)
|
||||
{
|
||||
m.add({
|
||||
title : getLang( 'menu_option_explain', 'Explain...' ),
|
||||
onclick : function()
|
||||
{
|
||||
ed.windowManager.open({
|
||||
url : url,
|
||||
width : 480,
|
||||
height : 380,
|
||||
inline : true
|
||||
}, { theme_url : this.url });
|
||||
}
|
||||
});
|
||||
})(errorDescription['moreinfo']);
|
||||
|
||||
m.addSeparator();
|
||||
}
|
||||
|
||||
m.add({
|
||||
title : getLang( 'menu_option_ignore_once', 'Ignore suggestion' ),
|
||||
onclick : function()
|
||||
{
|
||||
dom.remove(e.target, 1);
|
||||
t._checkDone();
|
||||
}
|
||||
});
|
||||
|
||||
if (String(this.editor.getParam('atd_ignore_enable', 'false')) === 'true')
|
||||
{
|
||||
m.add({
|
||||
title : getLang( 'menu_option_ignore_always', 'Ignore always' ),
|
||||
onclick : function()
|
||||
{
|
||||
var url = t.editor.getParam('atd_ignore_rpc_url', '{backend}');
|
||||
|
||||
if (url === '{backend}')
|
||||
{
|
||||
/* Default scheme is to save ignore preferences in a cookie */
|
||||
|
||||
var ignore = tinymce.util.Cookie.getHash('atd_ignore');
|
||||
if (!ignore) { ignore = {}; }
|
||||
ignore[e.target.innerHTML] = 1;
|
||||
|
||||
tinymce.util.Cookie.setHash('atd_ignore', ignore, new Date( (new Date().getTime()) + 157680000000) );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Plugin is configured to send ignore preferences to server, do that */
|
||||
|
||||
var id = t.editor.getParam('atd_rpc_id', '12345678');
|
||||
|
||||
tinymce.util.XHR.send({
|
||||
url : url + encodeURI(e.target.innerHTML).replace(/&/g, '%26') + '&key=' + id,
|
||||
content_type : 'text/xml',
|
||||
async : true,
|
||||
type : 'GET',
|
||||
success : function(/* type, req, o */)
|
||||
{
|
||||
/* do nothing */
|
||||
},
|
||||
error : function( type, req, o )
|
||||
{
|
||||
alert( 'Ignore preference save failed\n' + type + '\n' + req.status + '\nAt: ' + o.url );
|
||||
}
|
||||
});
|
||||
|
||||
/* update atd_ignore_strings with the new value */
|
||||
t.editor.core.setIgnoreStrings(e.target.innerHTML); /* this does an update */
|
||||
}
|
||||
|
||||
t._removeWords(e.target.innerHTML);
|
||||
t._checkDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
m.add({
|
||||
title : getLang( 'menu_option_ignore_all', 'Ignore all' ),
|
||||
onclick : function()
|
||||
{
|
||||
t._removeWords(e.target.innerHTML);
|
||||
t._checkDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* show the menu please */
|
||||
ed.selection.select(e.target);
|
||||
p1 = dom.getPos(e.target);
|
||||
m.showMenu(p1.x, p1.y + e.target.offsetHeight - vp.y);
|
||||
|
||||
return tinymce.dom.Event.cancel(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
m.hideMenu();
|
||||
}
|
||||
},
|
||||
|
||||
/* loop through editor DOM, call _done if no mce tags exist. */
|
||||
_checkDone : function()
|
||||
{
|
||||
var t = this, ed = t.editor, dom = ed.dom, o;
|
||||
|
||||
each(dom.select('span'), function(n)
|
||||
{
|
||||
if (n && dom.hasClass(n, 'mceItemHidden'))
|
||||
{
|
||||
o = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (!o)
|
||||
{
|
||||
t._done();
|
||||
}
|
||||
},
|
||||
|
||||
/* remove all tags, hide the menu, and fire a dom change event */
|
||||
_done : function()
|
||||
{
|
||||
var plugin = this;
|
||||
plugin._removeWords();
|
||||
|
||||
if (plugin._menu)
|
||||
{
|
||||
plugin._menu.hideMenu();
|
||||
}
|
||||
|
||||
plugin.editor.nodeChanged();
|
||||
},
|
||||
|
||||
sendRequest : function(file, data, success)
|
||||
{
|
||||
var id = this.editor.getParam('atd_rpc_id', '12345678');
|
||||
var url = this.editor.getParam('atd_rpc_url', '{backend}');
|
||||
var plugin = this;
|
||||
|
||||
if (url === '{backend}' || id === '12345678')
|
||||
{
|
||||
this.editor.setProgressState(0);
|
||||
alert('Please specify: atd_rpc_url and atd_rpc_id');
|
||||
return;
|
||||
}
|
||||
|
||||
tinymce.util.XHR.send({
|
||||
url : url + '/' + file,
|
||||
content_type : 'text/xml',
|
||||
type : 'POST',
|
||||
data : 'data=' + encodeURI(data).replace(/&/g, '%26') + '&key=' + id,
|
||||
async : true,
|
||||
success : success,
|
||||
error : function( type, req, o )
|
||||
{
|
||||
plugin.editor.setProgressState(0);
|
||||
alert( type + '\n' + req.status + '\nAt: ' + o.url );
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Register plugin
|
||||
tinymce.PluginManager.add('AtD', tinymce.plugins.AfterTheDeadlinePlugin);
|
||||
})();
|
427
plugins/jetpack/modules/after-the-deadline/tinymce/plugin.js
Normal file
427
plugins/jetpack/modules/after-the-deadline/tinymce/plugin.js
Normal file
|
@ -0,0 +1,427 @@
|
|||
/* 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;
|
||||
}
|
||||
});
|
||||
});
|
Reference in a new issue