The changes in 948634fe31 meant that only the most-recently-changed placeholder is actually applied.
This commit refactors things so that we store all placeholder values in JS, and then apply them all in a single pass over the DOM. As well as fixing the bug, this should be a significant perf improvement for posts with lots of placeholders
Also introduces some simple system specs.
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
Previously, the replacement system would modify raw HTML, which is prone to issues and vulnerabilities. With this commit, we iterate over text nodes only, and do simple string replacements on their content. That means that the user input never gets passed into an HTML parser, and there is no chance of injection attacks.
The re-rendering system is also simplified to store the original value for re-use later, instead of mapping position/length of replacements.
This does mean the behavior is changed slightly. Replacements will no longer be applied to html attributes (e.g `a[href]`). If this affects your use-case, please let us know [on Meta](https://meta.discourse.org/t/113533).
This is a followup to the fix in a62f711d56
Setting cookies means that they're sent in the request headers for every HTTP request. This will have a (tiny) impact on performance, plus it can raise privacy concerns. Using localStorage is more appropriate for this use case.
This commit includes migration logic for any previously-saved values.
Previously the cookies were set to last for the 'session'. localStorage doesn't have an expiration mechanism, so this commit implements a 7-day expiration on the values.