diff --git a/fonts/.DS_Store b/fonts/.DS_Store new file mode 100644 index 0000000..2d8c64c Binary files /dev/null and b/fonts/.DS_Store differ diff --git a/js/quote-convert.js b/js/quote-convert.js new file mode 100644 index 0000000..f7c6497 --- /dev/null +++ b/js/quote-convert.js @@ -0,0 +1,81 @@ +/** + * @file + * Cosmetic fix: converts straight quotes to typographic (curly) quotes + * in rendered field markup, on page load / AJAX render. + * + * IMPORTANT: This is a display-only patch. It does NOT change the stored + * field data in the database — it only rewrites text nodes in the DOM + * after render. Treat it as a stopgap until the migrated content itself + * is corrected; remove this once that's done. + * + * Adjust SELECTOR below to match whichever field wrappers actually need + * this (e.g. body fields, specific content types). Keep it as narrow as + * you reasonably can — this walks every text node inside each match. + */ +(function (Drupal, once) { + "use strict"; + + // CHANGE THIS to target only the fields that need it. + const SELECTOR = ".field--name-field-history-note"; + + // Tags whose text content should never be touched. + const SKIP_TAGS = new Set([ + "SCRIPT", + "STYLE", + "TEXTAREA", + "INPUT", + "CODE", + "PRE", + ]); + + /** + * Converts straight quotes/apostrophes in a string to curly equivalents. + * + * Heuristic (same logic SmartyPants-style tools use): + * - A straight double quote at the start of the string, or preceded by + * whitespace / an opening bracket / a dash, is an OPENING quote. + * Everything else is a CLOSING quote. + * - A straight single quote in the same "opening" positions becomes an + * opening single quote (‘); every other straight single quote — + * including apostrophes in contractions like don't — becomes the + * closing/apostrophe glyph (’), which is correct either way. + */ + function smartenQuotes(text) { + text = text.replace(/(^|[\s([{\-\u2014])"/g, "$1\u201C"); // opening “ + text = text.replace(/"/g, "\u201D"); // remaining → ” + text = text.replace(/(^|[\s([{\-\u2014])'/g, "$1\u2018"); // opening ‘ + text = text.replace(/'/g, "\u2019"); // remaining → ’ (also apostrophes) + return text; + } + + function fixTextNodesIn(root) { + const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { + acceptNode: function (node) { + if (SKIP_TAGS.has(node.parentNode.nodeName)) { + return NodeFilter.FILTER_REJECT; + } + if (!/["']/.test(node.nodeValue)) { + return NodeFilter.FILTER_SKIP; + } + return NodeFilter.FILTER_ACCEPT; + }, + }); + + const nodes = []; + let n; + while ((n = walker.nextNode())) { + nodes.push(n); + } + nodes.forEach(function (node) { + node.nodeValue = smartenQuotes(node.nodeValue); + }); + } + + Drupal.behaviors.smartQuotesCosmetic = { + attach: function (context) { + once("smart-quotes-cosmetic", SELECTOR, context).forEach(function (el) { + fixTextNodesIn(el); + }); + }, + }; +})(Drupal, once); diff --git a/olivesbooks.libraries.yml b/olivesbooks.libraries.yml index c1cd64c..851595c 100755 --- a/olivesbooks.libraries.yml +++ b/olivesbooks.libraries.yml @@ -57,6 +57,7 @@ global-styling: js: js/checkbox.js: {} js/custom.js: {} + js/quote-convert.js: {} dependencies: - core/drupal