Skip to content

Commit 08f4f2e

Browse files
author
Oleksii Sinyaev
committed
Update to 3.0.0
1 parent 2c7d41a commit 08f4f2e

9 files changed

Lines changed: 1139 additions & 459 deletions

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# WProofreader Plugin for WordPress Changelog
22

3+
## 3.0.0 - 2025-10-31
4+
5+
* Added a few global activation toggles (disabled by default):
6+
* * Enable in admin area to run WProofreader across the WordPress admin.
7+
* * Enable on front end to run WProofreader on the public site.
8+
* Fixed issue where spelling autocorrect could modify URLs; now disabled by default in link-related fields.
9+
* Updated service path to remove the legacy component ssrv.cgi, which is no longer used.
10+
* Improved dedicated loading for the Block Editor (enqueue_block_assets), with safe exclusions for the Site Editor and Permalinks settings screen.
11+
* Improved asset versioning based on file modification time to reduce caching issues.
12+
* Improved unified script handles and localization objects for consistency.
13+
* Hardened AJAX endpoint for the language list with nonce verification, capability checks, strict sanitization, and structured JSON response.
14+
* Comprehensive refactoring and code cleanup: simplified initialization flow for admin, Block Editor, and front end; consolidated settings bootstrap and defaults.
15+
316
## 2.8.0 - 2025-06-25
417

518
* Fixed issue with user preferences not being remembered. A recent issue caused user-selected options (e.g., selected language, ignore settings, enabled check types) to reset on every page reload. Now, these preferences are stored.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Free WProofreader plugin supports only spell-check for 6 languages and comes wit
2525
* **Allowed number of websites:** 1 website.
2626
* **Languages available for spell checking:** (6 languages) American English, British English, French, German, Italian and Spanish.
2727
* **Languages available for grammar checking:** Not available.
28-
* **User-level custom dictionary:** No cloud backup. The dictionary is only available in one browser on a single device. The dictionary is not available for review and modification.
28+
* **User-level custom dictionary:** No cloud backup. The dictionary is only available in one browser on a single device.
2929
* **Organization-level custom dictionary:** Not available.
3030

3131
**Pro version: ($49)**

assets/environmentChecker.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
window.addEventListener("load", (event) => {
2+
var bundlePath = 'https://svc.webspellchecker.net/spellcheck31/wscbundle/wscbundle.js';
3+
4+
function loadScript(doc, src) {
5+
const script = doc.createElement('script');
6+
const appendTo = doc.head || doc.documentElement;
7+
8+
script.type = 'text/javascript';
9+
script.charset = 'UTF-8';
10+
script.async = false;
11+
script.src = src;
12+
13+
appendTo.appendChild(script);
14+
}
15+
16+
window.gutenbergIframe = document.querySelector('[name=editor-canvas]');
17+
window.WEBSPELLCHECKER_CONFIG.globalBadge= true;
18+
19+
if (window.gutenbergIframe) {
20+
window.WEBSPELLCHECKER_CONFIG.globalBadge = false;
21+
loadScript(window.gutenbergIframe.contentWindow.document, bundlePath);
22+
window.gutenbergIframe = null;
23+
}
24+
25+
loadScript(document, bundlePath);
26+
});

assets/gutenberg-environment.js

Lines changed: 49 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,78 @@
1-
jQuery(document).ready(function ($){
2-
3-
function isGutenbergActive() {
4-
return document.body.classList.contains('block-editor-page');
5-
}
6-
7-
function isContentEditable(element){
1+
(function () {
2+
'use strict';
3+
const SELECTORS = {
4+
RICH_TEXT: '.rich-text',
5+
MCE_CONTENT_BODY: '.mce-content-body',
6+
GUTENBERG_PAGE: 'block-editor-page',
7+
GUTENBERG_IFRAME: 'block-editor-iframe__body',
8+
TABLE_CELL: 'wp-block-table__cell-content'
9+
};
10+
11+
const INSTANCE_ATTRIBUTE = 'data-wsc-instance';
12+
const INIT_DELAY = 100;
13+
14+
const isGutenbergActive = () => {
15+
return document.body.classList.contains(SELECTORS.GUTENBERG_PAGE) ||
16+
document.body.classList.contains(SELECTORS.GUTENBERG_IFRAME);
17+
};
18+
19+
function isContentEditable(element) {
820
return element.isContentEditable;
921
}
10-
11-
function isInstanceCreated(element){
12-
return element.hasAttribute('data-wsc-instance');
22+
function isInstanceCreated(element) {
23+
return element.hasAttribute(INSTANCE_ATTRIBUTE);
1324
}
1425

15-
function isGutenbergTableCell(element){
16-
return element.classList.contains('wp-block-table__cell-content');
26+
function isGutenbergTableCell(element) {
27+
return element.classList.contains(SELECTORS.TABLE_CELL);
1728
}
1829

19-
function ignoreElement(element) {
20-
if (!isContentEditable(element)){
30+
const shouldIgnoreElement = (element) => {
31+
if (!isContentEditable(element)) {
2132
return true;
2233
}
2334

24-
if (isInstanceCreated(element)){
35+
if (isInstanceCreated(element)) {
2536
return true;
2637
}
2738

28-
if (isGutenbergTableCell(element)){
39+
if (isGutenbergTableCell(element)) {
2940
return true;
3041
}
3142

3243
return false;
33-
}
44+
};
3445

35-
function createInstance(element){
46+
const createInstance = (element) => {
3647
WEBSPELLCHECKER.init({
3748
container: element,
3849
});
39-
}
50+
};
4051

41-
if (!isGutenbergActive()){
42-
return;
43-
}
44-
45-
function handleGutenbergReady(){
46-
47-
$(".rich-text").each(function (index) {
48-
const element = jQuery(this).get(0);
49-
if (ignoreElement(element)) {
52+
const initializeElements = (selector) => {
53+
document.querySelectorAll(selector).forEach((element) => {
54+
if (shouldIgnoreElement(element)) {
5055
return;
5156
}
57+
5258
createInstance(element);
5359
});
60+
};
5461

55-
wp.data.subscribe(function () {
56-
var isSidebarOpened = wp.data.select('core/edit-post').isEditorSidebarOpened();
57-
if (isSidebarOpened) {
58-
jQuery('.wsc-badge__wrapper').css('right', '300px');
59-
} else {
60-
jQuery('.wsc-badge__wrapper').css('right', '30px');
61-
}
62-
});
63-
}
64-
function handleTinyMceInit(editor) {
65-
const element = editor.getBody();
66-
if (ignoreElement(element)) {
67-
return;
68-
}
69-
createInstance(element);
70-
}
71-
function handleGutenbergReadyWithDelay(){
72-
setTimeout(handleGutenbergReady, 100);
73-
}
62+
const handleGutenbergReady = () => {
63+
initializeElements(SELECTORS.RICH_TEXT);
64+
initializeElements(SELECTORS.MCE_CONTENT_BODY);
65+
};
7466

75-
window._wpLoadBlockEditor.then(handleGutenbergReadyWithDelay);
67+
const handleGutenbergReadyWithDelay = () => {
68+
setTimeout(handleGutenbergReady, INIT_DELAY);
69+
};
7670

77-
if (window.tinymce) {
78-
tinymce.on('addeditor', function (event) {
79-
const editor = event.editor;
80-
editor.on('init', function () {
81-
handleTinyMceInit(editor);
82-
});
83-
});
84-
}
71+
window.webspellcheckerAlreadyLoaded = () => {
72+
if (!isGutenbergActive() || !window.WEBSPELLCHECKER_CONFIG?.globalBadge) {
73+
return;
74+
}
8575

86-
});
76+
handleGutenbergReadyWithDelay();
77+
};
78+
})();

assets/instance.js

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,94 @@
1-
jQuery(document).ready(function ($) {
2-
var CheckEnableGrammar = (ProofreaderInstance.enableGrammar === 'true');
3-
var AppInstance;
4-
AppInstance = WEBSPELLCHECKER.initWebApi({
1+
/**
2+
* WProofreader Settings Page
3+
* Initializes the spell checker instance and fetches languages.
4+
*/
5+
jQuery(function ($) {
6+
'use strict';
7+
8+
// Guards
9+
if (typeof WEBSPELLCHECKER === 'undefined' || typeof ProofreaderInstance === 'undefined') return;
10+
11+
const $root = $('#wsc_proofreader');
12+
let $select = $root.find('select[name="wsc_proofreader[slang]"]');
13+
if (!$select.length) return;
14+
15+
const enableGrammar = (ProofreaderInstance.enableGrammar === 'true');
16+
17+
// Init WebSpellChecker
18+
const app = WEBSPELLCHECKER.initWebApi({
519
autoSearch: true,
620
serviceProtocol: 'https',
721
serviceHost: 'svc.webspellchecker.net',
8-
servicePath: 'spellcheck31/script/ssrv.cgi',
22+
servicePath: 'spellcheck31/api',
923
servicePort: '443',
10-
enableGrammar: CheckEnableGrammar,
24+
enableGrammar: enableGrammar,
1125
serviceId: ProofreaderInstance.key_for_proofreader,
1226
lang: ProofreaderInstance.slang,
13-
appType:'wp_plugin',
14-
function(instance) {
15-
AppInstance = instance;
16-
},
17-
27+
appType: 'wp_plugin'
1828
});
19-
AppInstance.getInfo({
20-
success: function (result) {
21-
jQuery.ajax({
22-
type: "POST",
23-
url: ajaxurl,
29+
30+
// Get info → send to WP → render languages
31+
app.getInfo({
32+
success(result) {
33+
$.ajax({
34+
type: 'POST',
35+
url: window.ajaxurl,
2436
data: {
25-
action: "get_proofreader_info_callback",
37+
action: 'get_proofreader_info_callback',
2638
security: ProofreaderInstance.ajax_nonce,
2739
getInfoResult: result
28-
},
29-
success: function (data) {
30-
$('#wsc_proofreader select ').html(data);
31-
},
32-
error: function (errorThrown) {
33-
/// console.log(errorThrown);
3440
}
35-
});
41+
})
42+
.done((res) => {
43+
// Support both: raw HTML string OR {success:true, data:"..."} OR {success:true, data:{html:"..."}}
44+
const html =
45+
(typeof res === 'string') ? res :
46+
(res && res.success && typeof res.data?.html === 'string') ? res.data.html :
47+
(res && res.success && typeof res.data === 'string') ? res.data : '';
48+
49+
if (!html) {
50+
displayError('Failed to load language list. Invalid response format.');
51+
return;
52+
}
53+
54+
// If a full <select> returned — replace; otherwise swap <option>s
55+
if (/<\s*select[^>]*>/i.test(html)) {
56+
$select.replaceWith(html);
57+
$select = $root.find('select[name="wsc_proofreader[slang]"]');
58+
} else {
59+
$select.html(html);
60+
}
61+
})
62+
.fail((jqXHR, textStatus) => {
63+
let msg = 'Failed to load language list.';
64+
if (jqXHR?.responseJSON?.data?.message) {
65+
msg = jqXHR.responseJSON.data.message;
66+
} else if (textStatus) {
67+
msg += ` (${textStatus})`;
68+
}
69+
displayError(msg);
70+
});
3671
},
37-
error: function (e) {
38-
if(e.message){
39-
var message = '<strong>WProofreader:</strong> '+e.message;
40-
$('#wsc_proofreader').prepend('<div class="error"><p>'+message+'.</p><p><a target="_blank" href="https://webspellchecker.com/contact-us/">Contact us</a> for technical assistance.</p></div>');
41-
}
72+
error(err) {
73+
displayError(err?.message || 'Unexpected initialization error.');
4274
}
4375
});
76+
77+
function displayError(message) {
78+
const html =
79+
'<div class="error">' +
80+
`<p><strong>WProofreader:</strong> ${escapeHtml(message)}</p>` +
81+
'<p><a target="_blank" href="https://webspellchecker.com/contact-us/">Contact us</a> for technical assistance.</p>' +
82+
'</div>';
83+
$root.prepend(html);
84+
}
85+
86+
function escapeHtml(str) {
87+
return String(str)
88+
.replace(/&/g, '&amp;')
89+
.replace(/</g, '&lt;')
90+
.replace(/>/g, '&gt;')
91+
.replace(/"/g, '&quot;')
92+
.replace(/'/g, '&#039;');
93+
}
4494
});

0 commit comments

Comments
 (0)