3 changed files with 82 additions and 0 deletions
Binary file not shown.
@ -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); |
||||
Loading…
Reference in new issue