Add info block for find regex flags #3647
| @@ -16,6 +16,13 @@ | ||
| 16 | 16 | </small> |
| 17 | 17 | <hr /> |
| 18 | 18 | |
| 19 | + <div id="regex_info_block" class="info-block"> | |
| 20 | + <span class="info-block-text"></span> | |
| 21 | + <a class="info-block-flags-hint" href="http://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#advanced_searching_with_flags" target="_blank" rel="noopener noreferrer"> | |
| 22 | + <i class="fa-solid fa-circle-info" title="Click here to learn more about regex flags."></i> | |
| 23 | + </a> | |
| 24 | + </div> | |
| 25 | + | |
| 19 | 26 | <div id="regex_test_mode" class="flex1 flex-container displayNone"> |
| 20 | 27 | <div class="flex1"> |
| 21 | 28 | <label class="title_restorable" for="regex_test_input"> |
| @@ -7,7 +7,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from ' | ||
| 7 | 7 | import { enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 8 | 8 | import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; |
| 9 | 9 | import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; |
| 10 | 10 | import { download, getFileText, getSortableDelay, regexFromString, uuidv4 } from '../../utils.js'; |
| 11 | 11 | import { regex_placement, runRegexScript, substitute_find_regex } from './engine.js'; |
| 12 | 12 | import { t } from '../../i18n.js'; |
| 13 | 13 | import { accountStorage } from '../../util/AccountStorage.js'; |
| @@ -258,6 +258,8 @@ async function onRegexEditorOpenClick(existingId, isScoped) { | ||
| 258 | 258 | }); |
| 259 | 259 | |
| 260 | 260 | function updateTestResult() { |
| 261 | + updateInfoBlock(editorHtml); | |
| 262 | + | |
| 261 | 263 | if (!editorHtml.find('#regex_test_mode').is(':visible')) { |
| 262 | 264 | return; |
| 263 | 265 | } |
| @@ -276,6 +278,7 @@ async function onRegexEditorOpenClick(existingId, isScoped) { | ||
| 276 | 278 | } |
| 277 | 279 | |
| 278 | 280 | editorHtml.find('input, textarea, select').on('input', updateTestResult); |
| 281 | + updateInfoBlock(editorHtml); | |
| 279 | 282 | |
| 280 | 283 | const popupResult = await callPopup(editorHtml, 'confirm', undefined, { okButton: t`Save` }); |
| 281 | 284 | if (popupResult) { |
| @@ -305,6 +308,48 @@ async function onRegexEditorOpenClick(existingId, isScoped) { | ||
| 305 | 308 | } |
| 306 | 309 | } |
| 307 | 310 | |
| 311 | +/** | |
| 312 | + * Updates the info block in the regex editor with hints regarding the find regex. | |
| 313 | + * @param {JQuery<HTMLElement>} editorHtml The editor HTML | |
| 314 | + */ | |
| 315 | +function updateInfoBlock(editorHtml) { | |
| 316 | + const infoBlock = editorHtml.find('.info-block'); | |
| 317 | + const infoBlockTextSpan = infoBlock.find('.info-block-text'); | |
| 318 | + const infoBlockFlagsHint = infoBlock.find('.info-block-flags-hint'); | |
| 319 | + const findRegex = String(editorHtml.find('.find_regex').val()); | |
| 320 | + | |
| 321 | + infoBlock.removeClass('error hint info warning'); | |
| 322 | + infoBlockFlagsHint.hide(); | |
| 323 | + | |
| 324 | + // Clear the info block if the find regex is empty | |
| 325 | + if (!findRegex) { | |
| 326 | + infoBlock.addClass('info'); | |
| 327 | + infoBlockTextSpan.text(t`Find Regex is empty`); | |
| 328 | + return; | |
| 329 | + } | |
| 330 | + | |
| 331 | + try { | |
| 332 | + const regex = regexFromString(findRegex); | |
| 333 | + if (!regex) { | |
| 334 | + throw new Error(t`Invalid Find Regex`); | |
| 335 | + } | |
| 336 | + | |
| 337 | + const flagInfo = []; | |
| 338 | + // Check flags | |
| 339 | + | |
| 340 | + flagInfo.push(regex.flags.includes('g') ? t`Applies to all matches` : t`Applies to the first match`); | |
| 341 | + flagInfo.push(regex.flags.includes('i') ? t`Case insensitive` : t`Case sensitive`); | |
| 342 | + | |
| 343 | + infoBlock.addClass('hint'); | |
| 344 | + infoBlockTextSpan.text(flagInfo.join('. ')); | |
| 345 | + infoBlockFlagsHint.show(); | |
| 346 | + } catch (error) { | |
| 347 | + infoBlock.addClass('error'); | |
| 348 | + infoBlockTextSpan.text(error.message); | |
| 349 | + return; | |
| 350 | + } | |
| 351 | +} | |
| 352 | + | |
| 308 | 353 | // Common settings migration function. Some parts will eventually be removed |
| 309 | 354 | // TODO: Maybe migrate placement to strings? |
| 310 | 355 | function migrateSettings() { |
| @@ -105,3 +105,21 @@ input.enable_scoped { | ||
| 105 | 105 | .disable_regex:not(:checked) ~ .regex-toggle-on { |
| 106 | 106 | display: block; |
| 107 | 107 | } |
| 108 | + | |
| 109 | +#regex_info_block { | |
| 110 | + position: relative; | |
| 111 | + margin: 10px 0; | |
| 112 | + padding: 5px 20px; | |
| 113 | + font-size: 0.9em; | |
| 114 | +} | |
| 115 | + | |
| 116 | +#regex_info_block:empty { | |
| 117 | + display: none; | |
| 118 | +} | |
| 119 | + | |
| 120 | +#regex_info_block .info-block-flags-hint { | |
| 121 | + position: absolute; | |
| 122 | + top: 50%; | |
| 123 | + right: 10px; | |
| 124 | + transform: translateY(-50%); | |
| 125 | +} | |