diff --git a/.jshintignore b/.jshintignore index 3e6f7d5ed1..cbf6511d81 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1,7 +1,7 @@ node_modules **/jquery* **/*.min.js -themes/default/scripts/qrcode.js -themes/default/scripts/sha256.js +themes/default/scripts/ext/qrcode.js +themes/default/scripts/ext/sha256.js themes/default/scripts/bbcode.js vendor/**/*.js diff --git a/SSI.php b/SSI.php index af87fa65b4..3c751ed764 100644 --- a/SSI.php +++ b/SSI.php @@ -1137,7 +1137,7 @@ function ssi_login($redirect_to = '', $output_method = 'echo') $context['default_username'] = isset($_POST['user']) ? preg_replace('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($_POST['user'], ENT_COMPAT, 'UTF-8')) : ''; echo ' - '; + '; echo '
diff --git a/sources/ElkArte/AdminController/ManageFeatures.php b/sources/ElkArte/AdminController/ManageFeatures.php index 097f114885..736878116e 100644 --- a/sources/ElkArte/AdminController/ManageFeatures.php +++ b/sources/ElkArte/AdminController/ManageFeatures.php @@ -640,7 +640,7 @@ private function _notificationsSettings() global $txt, $modSettings; Txt::load('Profile+UserNotifications'); - loadJavascriptFile('jquery.multiselect.min.js'); + loadJavascriptFile('ext/jquery.multiselect.min.js'); theme()->addInlineJavascript(' $(\'.select_multiple\').multiselect({\'language_strings\': {\'Select all\': ' . JavascriptEscape($txt['notify_select_all']) . '}});', true); loadCSSFile('multiselect.css'); diff --git a/sources/ElkArte/AdminController/ManageRegistration.php b/sources/ElkArte/AdminController/ManageRegistration.php index 53542fa910..3b47759179 100644 --- a/sources/ElkArte/AdminController/ManageRegistration.php +++ b/sources/ElkArte/AdminController/ManageRegistration.php @@ -228,7 +228,7 @@ public function action_register() } // Basic stuff. - loadJavascriptFile('mailcheck.min.js'); + loadJavascriptFile('ext/mailcheck.min.js'); theme()->addInlineJavascript('disableAutoComplete(); $("input[type=email]").on("blur", function(event) { $(this).mailcheck({ diff --git a/sources/ElkArte/Controller/Auth.php b/sources/ElkArte/Controller/Auth.php index 03e1423678..10ba769c77 100644 --- a/sources/ElkArte/Controller/Auth.php +++ b/sources/ElkArte/Controller/Auth.php @@ -86,7 +86,7 @@ public function action_login() } theme()->getTemplates()->load('Login'); - loadJavascriptFile('sha256.js', array('defer' => true)); + loadJavascriptFile('ext/sha256.js', array('defer' => true)); $context['sub_template'] = 'login'; // Get the template ready.... not really much else to do. @@ -170,7 +170,7 @@ public function action_login2() // Load the template stuff theme()->getTemplates()->load('Login'); - loadJavascriptFile('sha256.js', array('defer' => true)); + loadJavascriptFile('ext/sha256.js', array('defer' => true)); $context['sub_template'] = 'login'; // Set up the default/fallback stuff. @@ -645,7 +645,7 @@ public function action_kickguest() Txt::load('Login'); theme()->getTemplates()->load('Login'); - loadJavascriptFile('sha256.js', array('defer' => true)); + loadJavascriptFile('ext/sha256.js', array('defer' => true)); createToken('login'); // Never redirect to an attachment @@ -673,7 +673,7 @@ public function action_maintenance_mode() Txt::load('Login'); theme()->getTemplates()->load('Login'); - loadJavascriptFile('sha256.js', array('defer' => true)); + loadJavascriptFile('ext/sha256.js', array('defer' => true)); createToken('login'); // Send a 503 header, so search engines don't bother indexing while we're in maintenance mode. diff --git a/sources/ElkArte/Controller/Display.php b/sources/ElkArte/Controller/Display.php index 14173e58f5..2f6ed7e783 100644 --- a/sources/ElkArte/Controller/Display.php +++ b/sources/ElkArte/Controller/Display.php @@ -309,7 +309,7 @@ public function action_display() // Quick reply & modify enabled? if ($context['can_reply'] && !empty($options['display_quick_reply'])) { - loadJavascriptFile(['mentioning.js', 'quickQuote.js'], ['defer' => true]); + loadJavascriptFile(['editor/mentioning.js', 'quickQuote.js'], ['defer' => true]); $this->_template_layers->addBefore('quickreply', 'moderation_buttons'); theme()->addInlineJavascript(" document.addEventListener('DOMContentLoaded', () => new Elk_QuickQuote(), false);", true diff --git a/sources/ElkArte/Controller/Register.php b/sources/ElkArte/Controller/Register.php index d54ba461de..c95e21e9ff 100644 --- a/sources/ElkArte/Controller/Register.php +++ b/sources/ElkArte/Controller/Register.php @@ -194,7 +194,7 @@ public function action_register() // Show the user the right form. $context['sub_template'] = $current_step === 1 ? 'registration_agreement' : 'registration_form'; $context['page_title'] = $current_step === 1 ? $txt['registration_agreement'] : $txt['registration_form']; - loadJavascriptFile(array('register.js', 'mailcheck.min.js')); + loadJavascriptFile(array('register.js', 'ext/mailcheck.min.js')); // Add the register chain to the link tree. $context['linktree'][] = array( @@ -848,7 +848,7 @@ public function action_activate() Txt::load('Login'); theme()->getTemplates()->load('Login'); - loadJavascriptFile('sha256.js', array('defer' => true)); + loadJavascriptFile('ext/sha256.js', array('defer' => true)); // Need a user id to activate if (empty($this->_req->query->u) && empty($this->_req->post->user)) @@ -1232,7 +1232,7 @@ public function action_contact() } else { - loadJavascriptFile('mailcheck.min.js'); + loadJavascriptFile('ext/mailcheck.min.js'); $context['sub_template'] = 'contact_form'; $context['page_title'] = $txt['admin_contact_form']; diff --git a/sources/ElkArte/Controller/Reminder.php b/sources/ElkArte/Controller/Reminder.php index 11b0e96517..a905d6acc5 100644 --- a/sources/ElkArte/Controller/Reminder.php +++ b/sources/ElkArte/Controller/Reminder.php @@ -279,7 +279,7 @@ public function action_setpassword2() call_integration_hook('integrate_reset_pass', array($member['member_name'], $member['member_name'], $this->_req->post->passwrd1)); theme()->getTemplates()->load('Login'); - loadJavascriptFile('sha256.js', array('defer' => true)); + loadJavascriptFile('ext/sha256.js', array('defer' => true)); $context += array( 'page_title' => $txt['reminder_password_set'], 'sub_template' => 'login', @@ -357,7 +357,7 @@ public function action_secret2() // Tell them it went fine. theme()->getTemplates()->load('Login'); - loadJavascriptFile('sha256.js', array('defer' => true)); + loadJavascriptFile('ext/sha256.js', array('defer' => true)); $context += array( 'page_title' => $txt['reminder_password_set'], 'sub_template' => 'login', diff --git a/sources/ElkArte/Controller/Stats.php b/sources/ElkArte/Controller/Stats.php index ce61918681..885f1fffdc 100644 --- a/sources/ElkArte/Controller/Stats.php +++ b/sources/ElkArte/Controller/Stats.php @@ -101,7 +101,7 @@ public function action_stats() // Stats it is Txt::load('Stats'); theme()->getTemplates()->load('Stats'); - loadJavascriptFile(['stats.js', 'chart.min.js', 'elk_chart.js']); + loadJavascriptFile(['stats.js', 'ext/chart.min.js', 'elk_chart.js']); // Build the link tree...... $context['linktree'][] = [ diff --git a/sources/ElkArte/EmojiIntegrate.php b/sources/ElkArte/EmojiIntegrate.php index c1c4fd56ce..fe62e47090 100644 --- a/sources/ElkArte/EmojiIntegrate.php +++ b/sources/ElkArte/EmojiIntegrate.php @@ -81,11 +81,11 @@ public static function integrate_editor_plugins($editor_id) if (empty($context['mentions_enabled'])) { loadCSSFile('jquery.atwho.css'); - loadJavascriptFile(['jquery.atwho.min.js', 'jquery.caret.min.js', 'emoji.plugin.js'], ['defer' => true]); + loadJavascriptFile(['editor/jquery.atwho.min.js', 'editor/jquery.caret.min.js', 'editor/emoji.plugin.js'], ['defer' => true]); } else { - loadJavascriptFile(['emoji.plugin.js'], ['defer' => true]); + loadJavascriptFile(['editor/emoji.plugin.js'], ['defer' => true]); } // Add the emoji plugin to the editor diff --git a/sources/ElkArte/Helper/HttpReq.php b/sources/ElkArte/Helper/HttpReq.php index 6087beecd5..d142721ce7 100644 --- a/sources/ElkArte/Helper/HttpReq.php +++ b/sources/ElkArte/Helper/HttpReq.php @@ -111,42 +111,11 @@ private function _loadParsed() $cleaned = array_intersect_key($_REQUEST, $this->_derived_post); $this->_derived_post = array_merge($this->_derived_post, $cleaned); - // Load any json into _derived_post - $this->_loadJson(); - // Make the $_GET $_POST super globals available as R/W properties $this->post = new \ArrayObject($this->_derived_post, \ArrayObject::ARRAY_AS_PROPS); $this->query = new \ArrayObject($derived_get, \ArrayObject::ARRAY_AS_PROPS); } - /** - * Looks for the post value "jsonString" and expands values to POST - * - * What it does: - * - * - Looks for jsonString passed in post - * - json decodes the string and loads its values in to POST - * - Does *not* overwrite any existing keys - */ - private function _loadJson() - { - // Was the magic json value posted? - if (!empty($this->_derived_post['jsonString'])) - { - $json = json_decode($this->_derived_post['jsonString'], true); - - // Valid decode - if (!empty($json)) - { - // Maintain the original keys only add new ones - $json = array_diff_key($json, $this->_derived_post); - $this->_derived_post = array_merge($this->_derived_post, $json); - } - - unset($this->_derived_post['jsonString']); - } - } - /** * Generic fetch access for values contained in the super globals * diff --git a/sources/ElkArte/Modules/Drafts/Display.php b/sources/ElkArte/Modules/Drafts/Display.php index 8dce9357a8..a2deeedb94 100644 --- a/sources/ElkArte/Modules/Drafts/Display.php +++ b/sources/ElkArte/Modules/Drafts/Display.php @@ -122,7 +122,7 @@ public function prepare_context($use_quick_reply, &$editorOptions, $board) 'value' => empty($context['id_draft']) ? 0 : $context['id_draft'], ]; - loadJavascriptFile('drafts.plugin.js', ['defer' => true]); + loadJavascriptFile('editor/drafts.plugin.js', ['defer' => true]); } } } diff --git a/sources/ElkArte/Modules/Drafts/PersonalMessage.php b/sources/ElkArte/Modules/Drafts/PersonalMessage.php index 0596f6e32d..a4644e3994 100644 --- a/sources/ElkArte/Modules/Drafts/PersonalMessage.php +++ b/sources/ElkArte/Modules/Drafts/PersonalMessage.php @@ -262,7 +262,7 @@ public function prepare_send_context(&$editorOptions) bPM: true }'; - loadJavascriptFile('drafts.plugin.js', ['defer' => true]); + loadJavascriptFile('editor/drafts.plugin.js', ['defer' => true]); Txt::load('Post'); // Our not so concise shortcut line diff --git a/sources/ElkArte/Modules/Drafts/Post.php b/sources/ElkArte/Modules/Drafts/Post.php index 460951a355..ca17f87e06 100644 --- a/sources/ElkArte/Modules/Drafts/Post.php +++ b/sources/ElkArte/Modules/Drafts/Post.php @@ -158,7 +158,7 @@ public function finalize_post_form(&$editorOptions, $board, $topic, $template_la id_draft: ' . (empty($context['id_draft']) ? 0 : $context['id_draft']) . ' }'; - loadJavascriptFile('drafts.plugin.js', ['defer' => true]); + loadJavascriptFile('editor/drafts.plugin.js', ['defer' => true]); } $context['shortcuts_text'] = $context['shortcuts_text'] ?? $txt['shortcuts_drafts']; diff --git a/sources/ElkArte/Notifications/UserNotification.php b/sources/ElkArte/Notifications/UserNotification.php index ff3ea00237..857ac842ab 100644 --- a/sources/ElkArte/Notifications/UserNotification.php +++ b/sources/ElkArte/Notifications/UserNotification.php @@ -80,7 +80,7 @@ protected function _addFaviconNumbers($number) { call_integration_hook('integrate_adjust_favicon_number', [&$number]); - loadJavascriptFile('favico.js', ['defer' => true]); + loadJavascriptFile('ext/favico.js', ['defer' => true]); $notif_opt = []; $rules = [ @@ -126,7 +126,7 @@ protected function settingExists($key) */ protected function _addDesktopNotifications() { - loadJavascriptFile(['push.min.js', 'desktop-notify.js'], ['defer' => true]); + loadJavascriptFile(['ext/push.min.js', 'desktop-notify.js'], ['defer' => true]); theme()->addInlineJavascript(' $(function() { Push.config({serviceWorker: "./elkServiceWorker.min.js"}); diff --git a/sources/ElkArte/Profile/ProfileInfo.php b/sources/ElkArte/Profile/ProfileInfo.php index 18298c2d93..8c3de523d5 100644 --- a/sources/ElkArte/Profile/ProfileInfo.php +++ b/sources/ElkArte/Profile/ProfileInfo.php @@ -980,7 +980,7 @@ public function action_statPanel() global $txt, $context, $modSettings; require_once(SUBSDIR . '/Stats.subs.php'); - loadJavascriptFile(['chart.min.js', 'elk_chart.js']); + loadJavascriptFile(['ext/chart.min.js', 'elk_chart.js']); $context['page_title'] = $txt['statPanel_showStats'] . ' ' . $this->_profile['real_name']; diff --git a/sources/ElkArte/Profile/ProfileOptions.php b/sources/ElkArte/Profile/ProfileOptions.php index 309dfd883b..4786d8e364 100644 --- a/sources/ElkArte/Profile/ProfileOptions.php +++ b/sources/ElkArte/Profile/ProfileOptions.php @@ -330,7 +330,7 @@ public function action_account() $fields = self::getFields('account_otp'); setupProfileContext($fields['fields'], $fields['hook']); - loadJavascriptFile('qrcode.js'); + loadJavascriptFile('ext/qrcode.js'); $context['load_google_authenticator'] = true; } else diff --git a/sources/ElkArte/Themes/Javascript.php b/sources/ElkArte/Themes/Javascript.php index f4b0d74534..d8a9fe9b8b 100644 --- a/sources/ElkArte/Themes/Javascript.php +++ b/sources/ElkArte/Themes/Javascript.php @@ -124,9 +124,9 @@ public function templateJquery() // Just use the local file case 'local': echo ' - ', + ', (empty($modSettings['jquery_include_ui']) ? '' : ' - '); + '); break; // CDN with local fallback case 'auto': @@ -136,9 +136,9 @@ public function templateJquery() '); echo ' '; break; } diff --git a/sources/ElkArte/Themes/Theme.php b/sources/ElkArte/Themes/Theme.php index 76b9b34f4c..282f0f8477 100644 --- a/sources/ElkArte/Themes/Theme.php +++ b/sources/ElkArte/Themes/Theme.php @@ -477,7 +477,7 @@ public function addCodePrettify() if (!empty($modSettings['enableCodePrettify'])) { $this->loadVariant('prettify'); - loadJavascriptFile('prettify.min.js', ['defer' => true]); + loadJavascriptFile('ext/prettify.min.js', ['defer' => true]); $this->addInlineJavascript(' document.addEventListener("DOMContentLoaded", () => { diff --git a/sources/ElkArte/Themes/ThemeLoader.php b/sources/ElkArte/Themes/ThemeLoader.php index 504dbbd472..560fb107b0 100644 --- a/sources/ElkArte/Themes/ThemeLoader.php +++ b/sources/ElkArte/Themes/ThemeLoader.php @@ -702,7 +702,7 @@ private function showLoginBar() $context['show_login_bar'] = true; $context['theme_header_callbacks'][] = 'login_bar'; - loadJavascriptFile('sha256.js', ['defer' => true]); + loadJavascriptFile('ext/sha256.js', ['defer' => true]); } /** diff --git a/sources/Security.php b/sources/Security.php index 1c1717dc4b..d6aa6a4385 100644 --- a/sources/Security.php +++ b/sources/Security.php @@ -236,7 +236,7 @@ function is_not_guest($message = '', $is_fatal = true) else { theme()->getTemplates()->load('Login'); - loadJavascriptFile('sha256.js', ['defer' => true]); + loadJavascriptFile('ext/sha256.js', ['defer' => true]); createToken('login'); $context['sub_template'] = 'kick_guest'; $context['robot_no_index'] = true; diff --git a/sources/subs/Auth.subs.php b/sources/subs/Auth.subs.php index 2473a37b1e..f366fbc097 100644 --- a/sources/subs/Auth.subs.php +++ b/sources/subs/Auth.subs.php @@ -202,7 +202,7 @@ function adminLogin($type = 'admin') Txt::load('Admin'); theme()->getTemplates()->load('Login'); - loadJavascriptFile('sha256.js', array('defer' => true)); + loadJavascriptFile('ext/sha256.js', array('defer' => true)); // Validate what type of session check this is. $types = array(); diff --git a/sources/subs/Editor.subs.php b/sources/subs/Editor.subs.php index 70c3f11c16..9e16c73594 100644 --- a/sources/subs/Editor.subs.php +++ b/sources/subs/Editor.subs.php @@ -76,10 +76,10 @@ function create_control_richedit($editorOptions) // JS makes the editor go round loadJavascriptFile([ - 'jquery.sceditor.bbcode.min.js', - 'jquery.sceditor.elkarte.js', + 'editor/jquery.sceditor.bbcode.min.js', + 'editor/jquery.sceditor.elkarte.js', 'post.js', - 'dropAttachments.js' + 'editor/dropAttachments.js' ]); theme()->addJavascriptVar([ @@ -184,30 +184,30 @@ function loadEditorPlugins($editor_context) $neededCSS = []; $plugins[] = 'initialLoad'; - $neededJS[] = 'initialLoad.plugin.js'; + $neededJS[] = 'editor/initialLoad.plugin.js'; if (!empty($modSettings['enableSplitTag'])) { $plugins[] = 'splittag'; - $neededJS[] = 'splittag.plugin.js'; + $neededJS[] = 'editor/splittag.plugin.js'; } if (!empty($modSettings['enableUndoRedo'])) { $plugins[] = 'undo'; - $neededJS[] = 'undo.plugin.min.js'; + $neededJS[] = 'editor/undo.plugin.min.js'; } if (!empty($modSettings['mentions_enabled'])) { $plugins[] = 'mention'; - $neededJS = array_merge($neededJS, ['jquery.atwho.min.js', 'jquery.caret.min.js', 'mentioning.plugin.js']); + $neededJS = array_merge($neededJS, ['editor/jquery.atwho.min.js', 'editor/jquery.caret.min.js', 'editor/mentioning.plugin.js']); } if (!empty($modSettings['enableGiphy'])) { $plugins[] = 'giphy'; - $neededJS[] = 'giphy.plugin.js'; + $neededJS[] = 'editor/giphy.plugin.js'; $neededCSS[] = 'sceditor.giphy.css'; } diff --git a/themes/default/ProfileOptions.template.php b/themes/default/ProfileOptions.template.php index 91139cda05..8ae06d311f 100644 --- a/themes/default/ProfileOptions.template.php +++ b/themes/default/ProfileOptions.template.php @@ -23,7 +23,7 @@ function template_ProfileOptions_init() if (!empty($context['menu_item_selected']) && $context['menu_item_selected'] === 'notification') { - loadJavascriptFile('jquery.multiselect.min.js'); + loadJavascriptFile('ext/jquery.multiselect.min.js'); theme()->addInlineJavascript(' $(\'.select_multiple\').multiselect({\'language_strings\': {\'Select all\': ' . JavascriptEscape($txt['notify_select_all']) . '}});' , true); diff --git a/themes/default/scripts/drafts.plugin.js b/themes/default/scripts/editor/drafts.plugin.js similarity index 64% rename from themes/default/scripts/drafts.plugin.js rename to themes/default/scripts/editor/drafts.plugin.js index d11136974e..0b14ffd619 100644 --- a/themes/default/scripts/drafts.plugin.js +++ b/themes/default/scripts/editor/drafts.plugin.js @@ -6,6 +6,8 @@ * @version 2.0 dev */ +/** global: elk_session_var, elk_session_id, elk_scripturl, sceditor */ + /** * This file contains javascript associated with the drafts auto function as it * relates to an sceditor invocation @@ -15,12 +17,12 @@ 'use strict'; // Editor instance - var editor; + let editor; function Elk_Drafts(options) { // All the passed options and defaults are loaded to the opts object - this.opts = $.extend({}, this.defaults, options); + this.opts = Object.assign({}, this.defaults, options || {}); // Attach mousedown events to the forms submit buttons this.formCheck(); @@ -48,7 +50,7 @@ } // Get the editor text, either from sceditor or from the quicktext textarea - var sPostdata = editor.val(); + const sPostdata = editor.val(); // Nothing to save? if (isEmptyText(sPostdata)) @@ -61,17 +63,28 @@ this.opts._bInDraftMode = true; // Create a clone form to populate - var $aForm = $('#postmodify').clone(); + let oForm = document.querySelector('#postmodify').cloneNode(true); + + let textarea = oForm.querySelector("textarea[name='message']"); + textarea.value = sPostdata.replace(/&#/g, "&#"); + + let inputSaveDraft = document.createElement('input'); + inputSaveDraft.setAttribute('name', 'save_draft'); + inputSaveDraft.value = 1; + oForm.appendChild(inputSaveDraft); - $aForm.find("textarea[name='message']").val(sPostdata.replace(/&#/g, "&#")); - $aForm.append($('').attr('name', 'save_draft').val(true)); - $aForm.append($('').attr('name', 'autosave').val(true)); + let inputAutosave = document.createElement('input'); + inputAutosave.setAttribute('name', 'autosave'); + inputAutosave.value = 1; + oForm.appendChild(inputAutosave); - // Keep track of source or wysiwyg when using the full editor - $aForm.append($('').attr('name', 'message_mode').val(editor.inSourceMode() ? '1' : '0')); + let inputMessageMode = document.createElement('input'); + inputMessageMode.setAttribute('name', 'message_mode'); + inputMessageMode.value = editor.inSourceMode() ? '1' : '0'; + oForm.appendChild(inputMessageMode); // Send in the request to save the data - this.draftAjax($aForm.serialize(), "?action=post2;board=" + this.opts.iBoard + ";api=xml"); + this.draftAjax(serialize(oForm), "?action=post2;board=" + this.opts.iBoard + ";api=xml"); }; /** @@ -96,7 +109,7 @@ } // Get the editor data - var sPostdata = editor.val(); + let sPostdata = editor.val(); // Nothing to save if (isEmptyText(sPostdata)) @@ -109,33 +122,57 @@ this.opts._bInDraftMode = true; // Get the to and bcc values - var aTo = this.draftGetRecipient('recipient_to[]'), + let aTo = this.draftGetRecipient('recipient_to[]'), aBcc = this.draftGetRecipient('recipient_bcc[]'); // Clone the form, we will use this to send - var $aForm = $('#pmFolder').clone(); + let oForm = document.querySelector('#pmFolder').cloneNode(true); + + let messageTextarea = oForm.querySelector("textarea[name='message']"); + messageTextarea.value = sPostdata.replace(/&#/g, "&#"); + + let subjectInput = oForm.querySelector("input[name='subject']"); + subjectInput.value = subjectInput.value.replace(/&#/g, "&#"); - $aForm.find("textarea[name='message']").val(sPostdata.replace(/&#/g, "&#")); - $aForm.find("input[name='subject']").val($aForm.find("input[name='subject']").val().replace(/&#/g, "&#")); - $aForm.find("input[name='replied_to']").val(parseInt($aForm.find("input[name='replied_to']").val())); - if ($aForm.find("input[name='id_pm_draft']").length == 1) + let repliedToInput = oForm.querySelector("input[name='replied_to']"); + repliedToInput.value = parseInt(repliedToInput.value).toString(); + + let idPmDraftInput = oForm.querySelector("input[name='id_pm_draft']"); + if (idPmDraftInput) { - $aForm.find("input[name='id_pm_draft']").val(parseInt($aForm.find("input[name='id_pm_draft']").val())); + idPmDraftInput.value = parseInt(idPmDraftInput.value).toString(); } else { - $aForm.append($('').attr('name', 'id_pm_draft').val(0)); + let newInput = document.createElement('input'); + newInput.name = 'id_pm_draft'; + newInput.value = '0'; + oForm.appendChild(newInput); } - $aForm.append($('').attr('name', 'recipient_to').val(aTo)); - $aForm.append($('').attr('name', 'recipient_bcc').val(aBcc)); - $aForm.append($('').attr('name', 'save_draft').val(true)); - $aForm.append($('').attr('name', 'autosave').val(true)); - // Keep track of source or wysiwyg when using the full editor - $aForm.append($('').attr('name', 'message_mode').val(editor.inSourceMode() ? '1' : '0')); + ['recipient_to', 'recipient_bcc', 'save_draft', 'autosave', 'message_mode'].forEach(name => { + let input = document.createElement('input'); + input.name = name; + switch (name) + { + case 'message_mode': + input.value = editor.inSourceMode() ? '1' : '0'; + break; + case 'save_draft': + case 'autosave': + input.value = '1'; + break; + case 'recipient_to': + input.value = aTo.join(', '); + break; + case 'recipient_bcc': + input.value = aBcc.join(', '); + break; } + oForm.appendChild(input); + }); // Send in (post) the document for saving - this.draftAjax($aForm.serialize(), "?action=pm;sa=send2;api=xml"); + this.draftAjax(serialize(oForm), "?action=pm;sa=send2;api=xml"); }; /** @@ -153,21 +190,28 @@ Elk_Drafts.prototype.draftAjax = function (post, action) { // Send in the request to save the data - $.ajax({ - type: "POST", - dataType: 'xml', - url: elk_scripturl + action, - data: post, - context: this + fetch(elk_scripturl + action, { + method: 'POST', + body: post, + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'Content-Type': 'application/x-www-form-urlencoded' + } }) - .done(function (data, textStatus, jqXHR) - { - // If it is not valid then move on - if (textStatus === 'success' && $(data).find("draft").length !== 0) + .then(response => { + if (!response.ok) + { + throw new Error("HTTP error " + response.status); + } + return response.text(); + }) + .then(str => new window.DOMParser().parseFromString(str, "text/xml")) + .then(data => { + if (data.getElementsByTagName("draft").length !== 0) { // Grab the returned draft id and saved time from the response - this.opts._sCurDraftId = $(data).find("draft").attr('id'); - this.opts._sLastSaved = $(data).find("draft").text(); + this.opts._sCurDraftId = data.getElementsByTagName("draft")[0].getAttribute('id'); + this.opts._sLastSaved = data.getElementsByTagName("draft")[0].childNodes[0].nodeValue; // Update the form to show we finished, if the id is not set, then set it document.getElementById(this.opts.sLastID).value = this.opts._sCurDraftId; @@ -181,8 +225,13 @@ } } }) - .always(function () - { + .catch(error => { + if ('console' in window && console.error) + { + console.error('Error : ', error); + } + }) + .finally(() => { // No matter what, we clear the saving indicator this.opts._bInDraftMode = false; document.getElementById('throbber').style.display = 'none'; @@ -201,21 +250,23 @@ */ Elk_Drafts.prototype.draftGetRecipient = function (sField) { - var oRecipient = document.forms.pmFolder.elements[sField], + let oRecipient = document.forms.pmFolder.elements[sField], aRecipient = []; if (typeof (oRecipient) !== 'undefined') { // Just one recipient - if ('value' in oRecipient) + if (oRecipient.nodeName === 'INPUT') { aRecipient.push(parseInt(oRecipient.value)); } + // Many recipients else { - // Or many ! - for (var i = 0, n = oRecipient.length; i < n; i++) + for (let i = 0, n = oRecipient.length; i < n; i++) + { aRecipient.push(parseInt(oRecipient[i].value)); + } } } @@ -236,7 +287,7 @@ */ Elk_Drafts.prototype.startSaver = function () { - var oInstance = this; + const oInstance = this; if (this.opts.bPM) { this.opts._interval_id = setInterval(function () @@ -260,18 +311,19 @@ */ Elk_Drafts.prototype.formCheck = function () { - var oInstance = this, - formID = $('#' + this.opts.sTextareaID).closest("form").attr('id'); + const oInstance = this; - // Prevent autosave on post/save selection by mouse or keyboard - var $_form_submitt = $('#' + formID + ' [name="post"]'); - $_form_submitt.on('mousedown', oInstance, function() { - oInstance.opts._bInDraftMode = true; - }); + let formID = document.getElementById(this.opts.sTextareaID).closest("form").id; + let form_submitt = document.querySelectorAll('#' + formID + ' [name="post"]'); - $_form_submitt.on('keydown', oInstance, function () - { - oInstance.opts._bInDraftMode = true; + // Prevent autosave on post/save selection by mouse or keyboard + form_submitt.forEach(function(form) { + form.addEventListener('mousedown', function() { + oInstance.opts._bInDraftMode = true; + }); + form.addEventListener('keydown', function() { + oInstance.opts._bInDraftMode = true; + }); }); }; @@ -279,34 +331,19 @@ * Private draft vars to keep track of what/where/why */ Elk_Drafts.prototype.defaults = { - /** - * If we are currently in draft saving mode - * @type {Boolean} - */ + /** @type {Boolean} If we are currently in draft saving mode */ _bInDraftMode: false, - /** - * The id of the draft so it save/appends - * @type {String} - */ + /** @type {String} The id of the draft so it save/appends */ _sCurDraftId: null, - /** - * How often we are going to save a draft in the background - * @type {Number} - */ + /** @type {Number} How often we are going to save a draft in the background */ _interval_id: null, - /** - * The text to place in the last saved Div, comes from the xml response - * @type {String} - */ + /** @type {String} The text to place in the last saved Div, comes from the xml response */ _sLastSaved: null, - /** - * If the user has pressed a key since the last save - * @type {Boolean} - */ + /** @type {Boolean} If the user has pressed a key since the last save */ _bCheckDraft: false }; @@ -323,7 +360,7 @@ */ sceditor.plugins.draft = function () { - var base = this, + let base = this, oDrafts; /** diff --git a/themes/default/scripts/dropAttachments.js b/themes/default/scripts/editor/dropAttachments.js similarity index 99% rename from themes/default/scripts/dropAttachments.js rename to themes/default/scripts/editor/dropAttachments.js index f2196e1b47..2fe65b8809 100644 --- a/themes/default/scripts/dropAttachments.js +++ b/themes/default/scripts/editor/dropAttachments.js @@ -345,13 +345,13 @@ $('#' + dataToSend.attachid).off().remove(); updateStatusText(); } - else if ('console' in window) + else if ('console' in window && console.info) { window.console.info(resp.data); } }).fail(function (jqXHR, textStatus, errorThrown) { - if ('console' in window) + if ('console' in window && console.info) { window.console.info('Error:', textStatus, errorThrown.name); window.console.info(jqXHR.responseText); diff --git a/themes/default/scripts/emoji.plugin.js b/themes/default/scripts/editor/emoji.plugin.js similarity index 93% rename from themes/default/scripts/emoji.plugin.js rename to themes/default/scripts/editor/emoji.plugin.js index f275a3f543..6e3acbdd4a 100644 --- a/themes/default/scripts/emoji.plugin.js +++ b/themes/default/scripts/editor/emoji.plugin.js @@ -7,7 +7,7 @@ * */ -/** global: elk_smileys_url, elk_emoji_url, emojis, custom */ +/** global: elk_smileys_url, elk_emoji_url, emojis, custom, sceditor */ /** * This file contains javascript associated with the :emoji: function as it @@ -34,7 +34,7 @@ var disableDrafts = false; function Elk_Emoji(options) { // All the passed options and defaults are loaded to the opts object - this.opts = $.extend({}, this.defaults, options); + this.opts = Object.assign({}, this.defaults, options || {}); } /** @@ -117,7 +117,7 @@ var disableDrafts = false; return ":" + tpl[0] + ":"; } - return ":" + tpl[1] + ":"; + return ":" + tpl[1] + ":" + tpl[2] + ""; }, tplEval: function (tpl, map) { @@ -144,7 +144,7 @@ var disableDrafts = false; } catch (_error) { - if ('console' in window) + if ('console' in window && console.info) { window.console.info(_error); } @@ -199,10 +199,9 @@ var disableDrafts = false; { let opts = this.opts; - $(".sceditor-button-source").on("click", function (event, offset) - { + document.querySelector('.sceditor-button-source').addEventListener('click', function () { // If the button has the active class, we clicked and entered wizzy mode - if (!$(this).hasClass("active")) + if (!this.classList.contains('active')) { Elk_Emoji.prototype.processEmoji(opts); } @@ -301,14 +300,10 @@ var disableDrafts = false; emojis = Object.values(emojisMap); }; - /** - * Private emoji vars - */ + /** @type {{}} Represents the defaults configuration for Elk_Emoji objects. */ Elk_Emoji.prototype.defaults = {_names: []}; - /** - * Holds all current emoji (defaults + passed options) - */ + /** @type {{}} Holds all current emoji (defaults + passed options) */ Elk_Emoji.prototype.opts = {}; /** @@ -354,7 +349,7 @@ var disableDrafts = false; let inlineScript = document.createElement('script'); inlineScript.innerHTML = 'let custom = [];'; document.head.append(inlineScript); - if ('console' in window) + if ('console' in window && console.info) { window.console.info('custom_tags.js file missing or in error'); } @@ -385,9 +380,8 @@ var disableDrafts = false; oEmoji.attachAtWho($(oIframeBody), oIframeWindow); } }, - error => - { - if ('console' in window) + error => { + if ('console' in window && console.info) { window.console.info(`Error: ${error.message}`); } diff --git a/themes/default/scripts/giphy.plugin.js b/themes/default/scripts/editor/giphy.plugin.js similarity index 100% rename from themes/default/scripts/giphy.plugin.js rename to themes/default/scripts/editor/giphy.plugin.js diff --git a/themes/default/scripts/initialLoad.plugin.js b/themes/default/scripts/editor/initialLoad.plugin.js similarity index 98% rename from themes/default/scripts/initialLoad.plugin.js rename to themes/default/scripts/editor/initialLoad.plugin.js index c81271f74c..8d634b6a4f 100644 --- a/themes/default/scripts/initialLoad.plugin.js +++ b/themes/default/scripts/editor/initialLoad.plugin.js @@ -12,7 +12,7 @@ // Core Editor startup options, css, smiley box, validate wizzy, move into view when needed sceditor.plugins.initialLoad = function() { let base = this; - const MAX_RETRIES = 120; + const MAX_RETRIES = 300; const isEditorLoaded = async selector => { let retries = 0; while (document.querySelector(selector) === null && retries < MAX_RETRIES) diff --git a/themes/default/scripts/jquery.atwho.min.js b/themes/default/scripts/editor/jquery.atwho.min.js similarity index 100% rename from themes/default/scripts/jquery.atwho.min.js rename to themes/default/scripts/editor/jquery.atwho.min.js diff --git a/themes/default/scripts/jquery.caret.min.js b/themes/default/scripts/editor/jquery.caret.min.js similarity index 100% rename from themes/default/scripts/jquery.caret.min.js rename to themes/default/scripts/editor/jquery.caret.min.js diff --git a/themes/default/scripts/jquery.sceditor.bbcode.min.js b/themes/default/scripts/editor/jquery.sceditor.bbcode.min.js similarity index 100% rename from themes/default/scripts/jquery.sceditor.bbcode.min.js rename to themes/default/scripts/editor/jquery.sceditor.bbcode.min.js diff --git a/themes/default/scripts/jquery.sceditor.elkarte.js b/themes/default/scripts/editor/jquery.sceditor.elkarte.js similarity index 98% rename from themes/default/scripts/jquery.sceditor.elkarte.js rename to themes/default/scripts/editor/jquery.sceditor.elkarte.js index 518bbc47ad..eda4422651 100644 --- a/themes/default/scripts/jquery.sceditor.elkarte.js +++ b/themes/default/scripts/editor/jquery.sceditor.elkarte.js @@ -495,9 +495,8 @@ sceditor.command { let content = ''; - $.each(selected.split(/\r?\n/), function () - { - content += (content ? '\n' : '') + '[li]' + this + '[/li]'; + selected.split(/\r?\n/).forEach(function(item) { + content += (content ? '\n' : '') + '[li]' + item + '[/li]'; }); return this.insertText('[list]\n' + content + '\n[/list]'); @@ -513,9 +512,8 @@ sceditor.command { let content = ''; - $.each(selected.split(/\r?\n/), function () - { - content += (content ? '\n' : '') + '[li]' + this + '[/li]'; + selected.split(/\r?\n/).forEach(function(item) { + content += (content ? '\n' : '') + '[li]' + item + '[/li]'; }); return this.insertText('[list type=decimal]\n' + content + '\n[/list]'); @@ -547,7 +545,6 @@ sceditor.formats.bbcode }) .set('pre', { tags: { - pre: null, pre: {'class': ['bbc_pre']} }, isInline: false, @@ -799,7 +796,7 @@ sceditor.formats.bbcode { // Convert it to an author tag attr_link = sceditor.escapeEntities(attrs.defaultattr); - sLink = (attr_link.substr(0, 7) === 'http://' || attr_link.substr(0, 8) === 'https://') + sLink = (attr_link.substring(0, 7) === 'http://' || attr_link.substring(0, 8) === 'https://') ? sceditor.escapeUriScheme(attr_link) : elk_scripturl + '?' + attr_link; sAuthor = '' + bbc_quote_from + ': ' + sLink + ''; @@ -812,7 +809,7 @@ sceditor.formats.bbcode { attr_link = key.length > 4 ? key.substr(5) + '=' + attrs[key] : attrs[key]; attr_link = $.sceditor.escapeEntities(attr_link); - sLink = (attr_link.substr(0, 7) === 'http://' || attr_link.substr(0, 8) === 'https://') + sLink = (attr_link.substring(0, 7) === 'http://' || attr_link.substring(0, 8) === 'https://') ? attr_link : elk_scripturl + '?' + attr_link; sAuthor = sAuthor === '' diff --git a/themes/default/scripts/mentioning.js b/themes/default/scripts/editor/mentioning.js similarity index 83% rename from themes/default/scripts/mentioning.js rename to themes/default/scripts/editor/mentioning.js index 3fa59149ed..391ea934e9 100644 --- a/themes/default/scripts/mentioning.js +++ b/themes/default/scripts/editor/mentioning.js @@ -151,34 +151,38 @@ elk_mentions.prototype.attachAtWho = function () { function suggest(obj, callback) { - let postString = "jsonString=" + JSON.stringify(obj) + "&" + elk_session_var + "=" + elk_session_id; - _self.names = []; - - // And how to ask for it - $.ajax({ - url: elk_scripturl + "?action=suggest;api=xml", - data: postString, - type: "post", - async: true + let postString = serialize(obj) + "&" + elk_session_var + "=" + elk_session_id; + + fetch(elk_scripturl + "?action=suggest;api=xml", { + method: 'POST', + body: postString, + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'Content-Type': 'application/x-www-form-urlencoded' + } }) - .done(function (request) { - $(request).find('item').each(function (idx, item) { - // Add each one to the dropdown list for view/selection - _self.names[idx] = { - "id": $(item).attr('id'), - "name": $(item).text() + .then(response => { + if (!response.ok) + { + throw new Error("HTTP error " + response.status); + } + return response.text(); + }) + .then(str => new window.DOMParser().parseFromString(str, "text/xml")) + .then(data => { + _self.names = Array.from(data.getElementsByTagName('item')).map((item, idx) => { + return { + "id": item.getAttribute('id'), + "name": item.textContent }; }); - callback(); }) - .fail(function (jqXHR, textStatus, errorThrown) { - if ('console' in window) + .catch((error) => { + if ('console' in window && console.info) { - window.console.info('Error:', textStatus, errorThrown.name); - window.console.info(jqXHR.responseText); + window.console.info('Error:', error); } - callback(); }); } diff --git a/themes/default/scripts/mentioning.plugin.js b/themes/default/scripts/editor/mentioning.plugin.js similarity index 87% rename from themes/default/scripts/mentioning.plugin.js rename to themes/default/scripts/editor/mentioning.plugin.js index 4b7401fa44..24f6c4a87b 100644 --- a/themes/default/scripts/mentioning.plugin.js +++ b/themes/default/scripts/editor/mentioning.plugin.js @@ -6,6 +6,8 @@ * @version 2.0 dev */ +/** global: elk_session_var, elk_session_id, elk_scripturl, sceditor */ + /** * This file contains javascript associated with the @mentions function as it * relates to an sceditor invocation @@ -195,44 +197,48 @@ var disableDrafts = false; } /** - * Makes the ajax call for data, returns to callback function when done. + * Makes the fetch call for data, returns to callback function when done. * * @param obj values to pass to action suggest * @param callback function to call when we have completed our call */ function suggest(obj, callback) { - let postString = "jsonString=" + JSON.stringify(obj) + "&" + elk_session_var + "=" + elk_session_id; + let postString = serialize(obj) + "&" + elk_session_var + "=" + elk_session_id; oMentions.opts._names = []; - - $.ajax({ - url: elk_scripturl + "?action=suggest;api=xml", - type: "post", - async: true, - data: postString, - dataType: "xml" + fetch(elk_scripturl + "?action=suggest;api=xml", { + method: "POST", + body: postString, + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'Content-Type': 'application/x-www-form-urlencoded' + } }) - .done(function (data) - { - $(data).find('item').each(function (idx, item) + .then(response => { + if (!response.ok) { + throw new Error("HTTP error " + response.status); + } + return response.text(); + }) + .then(str => new window.DOMParser().parseFromString(str, "text/xml")) + .then(data => { + let items = data.querySelectorAll('item'); + items.forEach((item, idx) => { oMentions.opts._names[idx] = { - "id": $(item).attr('id'), - "name": $(item).text() + "id": item.getAttribute('id'), + "name": item.textContent }; }); - + console.log(callback); callback(); }) - .fail(function (jqXHR, textStatus, errorThrown) - { - if ('console' in window) + .catch(function (error) { + if ('console' in window && console.info) { - window.console.info('Error:', textStatus, errorThrown.name); - window.console.info(jqXHR.responseText); + window.console.info('Error: ', error.message); } - callback(); }); } diff --git a/themes/default/scripts/splittag.plugin.js b/themes/default/scripts/editor/splittag.plugin.js similarity index 95% rename from themes/default/scripts/splittag.plugin.js rename to themes/default/scripts/editor/splittag.plugin.js index a4caf5b3ad..4e916007f9 100644 --- a/themes/default/scripts/splittag.plugin.js +++ b/themes/default/scripts/editor/splittag.plugin.js @@ -37,7 +37,7 @@ */ base.init = function () { - // The this variable will be set to the instance of the editor calling it + // "this" will be set to the instance of the editor calling it editor = this; // Add handler for a ctrl+enter key press, this is our keystroke cue to split the tag(s) @@ -168,7 +168,6 @@ contentAfterRangeStart, quote, range, - blank, attributes; // Save the current state in case this goes bad @@ -197,9 +196,8 @@ contentAfterRangeStart = range.extractContents(); // Apply the existing quote attributes to the new quote - $.each(attributes, function () - { - $(contentAfterRangeStart).attr(this.name, this.value); + Object.keys(attributes).forEach(function (key) { + contentAfterRangeStart.setAttribute(key, attributes[key]); }); // Collapse the block quote range, we want to insert after this. @@ -210,8 +208,8 @@ // range.collapse(false); // Create an area to place between the two quotes for text entry - blank = quote.ownerDocument.createElement('p'); - $(blank).html(' '); + let blank = quote.ownerDocument.createElement('p'); + blank.innerHTML = ' '; // Insert the new elements range.insertNode(contentAfterRangeStart); diff --git a/themes/default/scripts/undo.plugin.min.js b/themes/default/scripts/editor/undo.plugin.min.js similarity index 100% rename from themes/default/scripts/undo.plugin.min.js rename to themes/default/scripts/editor/undo.plugin.min.js diff --git a/themes/default/scripts/chart.min.js b/themes/default/scripts/ext/chart.min.js similarity index 100% rename from themes/default/scripts/chart.min.js rename to themes/default/scripts/ext/chart.min.js diff --git a/themes/default/scripts/favico.js b/themes/default/scripts/ext/favico.js similarity index 100% rename from themes/default/scripts/favico.js rename to themes/default/scripts/ext/favico.js diff --git a/themes/default/scripts/jquery-3.7.1.min.js b/themes/default/scripts/ext/jquery-3.7.1.min.js similarity index 100% rename from themes/default/scripts/jquery-3.7.1.min.js rename to themes/default/scripts/ext/jquery-3.7.1.min.js diff --git a/themes/default/scripts/jquery-ui-1.13.2.min.js b/themes/default/scripts/ext/jquery-ui-1.13.2.min.js similarity index 100% rename from themes/default/scripts/jquery-ui-1.13.2.min.js rename to themes/default/scripts/ext/jquery-ui-1.13.2.min.js diff --git a/themes/default/scripts/jquery.multiselect.min.js b/themes/default/scripts/ext/jquery.multiselect.min.js similarity index 100% rename from themes/default/scripts/jquery.multiselect.min.js rename to themes/default/scripts/ext/jquery.multiselect.min.js diff --git a/themes/default/scripts/mailcheck.min.js b/themes/default/scripts/ext/mailcheck.min.js similarity index 100% rename from themes/default/scripts/mailcheck.min.js rename to themes/default/scripts/ext/mailcheck.min.js diff --git a/themes/default/scripts/prettify.min.js b/themes/default/scripts/ext/prettify.min.js similarity index 100% rename from themes/default/scripts/prettify.min.js rename to themes/default/scripts/ext/prettify.min.js diff --git a/themes/default/scripts/push.min.js b/themes/default/scripts/ext/push.min.js similarity index 100% rename from themes/default/scripts/push.min.js rename to themes/default/scripts/ext/push.min.js diff --git a/themes/default/scripts/qrcode.js b/themes/default/scripts/ext/qrcode.js similarity index 100% rename from themes/default/scripts/qrcode.js rename to themes/default/scripts/ext/qrcode.js diff --git a/themes/default/scripts/sha256.js b/themes/default/scripts/ext/sha256.js similarity index 100% rename from themes/default/scripts/sha256.js rename to themes/default/scripts/ext/sha256.js diff --git a/themes/default/scripts/like_posts.js b/themes/default/scripts/like_posts.js index 8453bde2fb..9642ba7a2b 100644 --- a/themes/default/scripts/like_posts.js +++ b/themes/default/scripts/like_posts.js @@ -343,7 +343,7 @@ // the output from php error or access denied fatal errors etc // err.data = oTxt.error_occurred + ' : ' + errorThrown; // handleError(err); - if ('console' in window) + if ('console' in window && console.info) { window.console.info('fail:', textStatus, errorThrown.name); window.console.info(err.responseText); diff --git a/themes/default/scripts/post.js b/themes/default/scripts/post.js index 0a4adc3820..72f028b795 100644 --- a/themes/default/scripts/post.js +++ b/themes/default/scripts/post.js @@ -622,9 +622,9 @@ function onDraftsReturned(oXMLDoc) } /** - * Checks for empty subject or body on post submit. These are also checked server side - * but this provides a nice current page reminder. + * Checks for empty subject or body on post submit. * + * - These are also checked server side but this provides a nice current page reminder. * - If empty fields are found will use errorbox_handler to populate error(s) * - If empty adds listener to fields to clear errors as they are fixed * diff --git a/themes/default/scripts/profile.js b/themes/default/scripts/profile.js index 41a3768e9c..c99a96f206 100644 --- a/themes/default/scripts/profile.js +++ b/themes/default/scripts/profile.js @@ -55,7 +55,7 @@ function start_tabs() ui.jqXHR.fail(function (jqXHR, textStatus, errorThrown) { ui.panel.html('
'); - if ('console' in window) + if ('console' in window && console.info) { window.console.info(textStatus); window.console.info(errorThrown); diff --git a/themes/default/scripts/script.js b/themes/default/scripts/script.js index 6de78b6eb1..9e3135c3cb 100644 --- a/themes/default/scripts/script.js +++ b/themes/default/scripts/script.js @@ -94,7 +94,7 @@ function fetchDocument(sUrl, funcCallback, sType = null, bHeader = true) return data; }) .catch(error => { - if ('console' in window) + if ('console' in window && console.info) { window.console.info(error); } diff --git a/themes/default/scripts/script_elk.js b/themes/default/scripts/script_elk.js index 474f11ce9c..5e08f2c240 100644 --- a/themes/default/scripts/script_elk.js +++ b/themes/default/scripts/script_elk.js @@ -841,7 +841,7 @@ function revalidateMentions(sForm, sInput) }) .fail(function (jqXHR, textStatus, errorThrown) { - if ('console' in window) + if ('console' in window && console.info) { window.console.info(errorThrown); } @@ -2388,3 +2388,32 @@ function debounce(func, wait, immediate) }, wait); }; } + +/** + * Serializes a form or object into a URL-encoded string. + * + * @param {HTMLFormElement|object} form - The form element, or object to serialize. + * @returns {string} - The serialized data as a URL-encoded string. + */ +function serialize(form) +{ + // Passed a form + if (form instanceof HTMLFormElement) + { + const formData = new FormData(form); + + return new URLSearchParams(formData).toString(); + } + + // Or an object of key->pairs + let str = []; + for (let key in form) + { + if (form.hasOwnProperty(key)) + { + str.push(encodeURIComponent(key) + "=" + encodeURIComponent(form[key])); + } + } + + return str.join("&"); +} \ No newline at end of file diff --git a/themes/default/scripts/suggest.js b/themes/default/scripts/suggest.js index 829addf87e..65d367495b 100644 --- a/themes/default/scripts/suggest.js +++ b/themes/default/scripts/suggest.js @@ -9,6 +9,8 @@ * @version 2.0 dev */ +/** global: elk_session_var, elk_session_id, elk_scripturl */ + /** * This file contains javascript associated with an auto suggest control. */ @@ -763,7 +765,7 @@ smc_AutoSuggest.prototype.autoSuggestUpdate = function () postString; // Post values plus session - postString = "jsonString=" + JSON.stringify(obj) + "&" + this.opt.sSessionVar + "=" + this.opt.sSessionId; + postString = serialize(obj) + "&" + this.opt.sSessionVar + "=" + this.opt.sSessionId; sendXMLDocument.call(this, this.sRetrieveURL.replace(/%scripturl%/g, elk_prepareScriptUrl(elk_scripturl)), postString, this.onSuggestionReceived); diff --git a/themes/default/scripts/topic.js b/themes/default/scripts/topic.js index bd15169b51..18b3d1762d 100644 --- a/themes/default/scripts/topic.js +++ b/themes/default/scripts/topic.js @@ -463,7 +463,7 @@ QuickModify.prototype.onMessageReceived = function (XMLDoc) // If this is not valid then simply give up. if (!document.getElementById(this.sCurMessageId)) { - if ('console' in window) + if ('console' in window && console.info) { window.console.info('no id'); }