Last active
May 6, 2024 10:50
-
-
Save gaabora/77de511c9bfe84bbfa13354fc21a5d97 to your computer and use it in GitHub Desktop.
batch-style-replace-magic.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Test report tpl vars</title> | |
<style> | |
* { font-size: 10px; } | |
html, body { margin: 0; padding: 0; } | |
body { padding: 0 5px; } | |
.container { height: 100%; display: flex; flex-direction: row; } | |
.fill-height { height: 100vh; } | |
.horiz { flex: 1; display: flex; flex-direction: column; } | |
input,textarea { border: none; background: #eee; padding: 5px; margin: 2px; color: #7b7b7b } | |
textarea:read-only { background: #fafafa; color: #a7a7a7 } | |
textarea { height: 100%; resize: none; } | |
.container pre { padding: 0 5px; } | |
button { margin: 0 2px; font-weight: bold; } | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<div class="horiz fill-height"> | |
<pre>1). paste <b>exported styles JSON</b> here:</pre> | |
<textarea id="source" title="Paste here exported styles JSON"></textarea> | |
</div> | |
<div class="horiz fill-height"> | |
<pre>2). set strings or regex to replace: <input type="checkbox" id="use-regex" placeholder="use regex" /> use regex</pre> | |
<input id="replace-from" placeholder="from" /> | |
<input id="replace-to" placeholder="to" /> | |
<pre>3). paste here classname selectors (from 'sel') to process (1 per line)</pre> | |
<pre>it wil automatically use regex if line contains some regex-related symbols like *?+^$()|{}</pre> | |
<div class="container"> | |
<div class="horiz"> | |
<pre>a) process only this</pre> | |
<textarea id="only-classes"></textarea> | |
</div> | |
<div class="horiz"> | |
<pre>b) or ignore any of this</pre> | |
<textarea id="ignore-classes"></textarea> | |
</div> | |
</div> | |
<div class="container"> | |
<div class="horiz"> | |
<pre>affected classes</pre> | |
<textarea id="affected-classes" readonly></textarea> | |
</div> | |
<div class="horiz"> | |
<pre>skipped classes</pre> | |
<textarea id="skipped-classes" readonly></textarea> | |
</div> | |
</div> | |
<pre>[debug]: affected pages</pre> | |
<textarea id="affected-pages" readonly></textarea> | |
</div> | |
<div class="horiz fill-height"> | |
<pre>4). run</pre> | |
<button onclick="asyncReplaceString()">replace</button> | |
<button onclick="downloadResult()">download result</button> | |
<pre>copy result and compare somewhere like in vscode</pre> | |
<textarea id="result"></textarea> | |
</div> | |
</div> | |
<script> | |
class Loader { | |
constructor(iconSrc='/favicon.ico') { | |
this.loadingEl = document.getElementById('WebflowMagicLoading'); | |
if (!this.loadingEl) { | |
this.loadingEl = document.createElement('div'); | |
this.loadingEl.id = 'WebflowMagicLoading'; | |
const faviconImg = document.createElement('img'); | |
faviconImg.src = iconSrc; | |
this.loadingEl.appendChild(faviconImg); | |
document.body.appendChild(this.loadingEl); | |
var style = document.createElement('style'); | |
style.innerHTML = ` | |
#WebflowMagicLoading { | |
display: none; | |
width: 32px; | |
height: 32px; | |
position: fixed; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
animation: 1s linear 0s infinite normal none running WebflowMagicLoadingSpin; | |
z-index: 99999; | |
} | |
#WebflowMagicLoading img { width: 100%; height: 100%; } | |
@keyframes WebflowMagicLoadingSpin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
`; | |
document.head.appendChild(style); | |
} | |
} | |
setLoadingState(state) { | |
this.loadingEl.style.display = state ? 'block' : 'none'; | |
} | |
} | |
const loadingSpinner = new Loader('https://webflow.com/favicon.ico'); | |
function asyncReplaceString() { | |
loadingSpinner.setLoadingState(true); | |
const promise = new Promise(resolve => { | |
setTimeout(() => { | |
resolve(replaceString()); | |
}, 0); // Use a short timeout to allow the UI to update (simulating an asynchronous operation) | |
}); | |
promise.then((response) => { | |
loadingSpinner.setLoadingState(false); | |
notify(response); | |
}); | |
} | |
async function downloadResult() { | |
const resultString = document.getElementById("result").value; | |
if (!resultString.trim()) { | |
notify('result is empty, nothing to download'); | |
return; | |
} | |
try { | |
const JSONContent = JSON.parse(resultString); | |
downloadJSON(JSONContent); | |
} catch (e) { | |
notify(`result JSON is invalid: ${e}`); | |
} | |
} | |
function downloadJSON(JSONContent) { | |
var link = document.createElement("a"); | |
const date = new Date().toISOString().replace(/\D/g, '').substring(0, 12); | |
link.setAttribute('href', 'data:application/JSON;charset=utf-8,' + encodeURIComponent(JSON.stringify(JSONContent, null, 2))); | |
link.setAttribute('download', `changed_styles_${date}.JSON`); | |
document.body.appendChild(link); | |
link.click(); | |
link.remove(); | |
} | |
function replaceString() { | |
const useRegex = document.getElementById("use-regex").checked; | |
const replaceFrom = document.getElementById("replace-from").value; | |
const REGEX_FROM = useRegex ? new RegExp(replaceFrom, 'g') : null; | |
const replaceTo = document.getElementById("replace-to").value; | |
const REGEX_TO = useRegex ? new RegExp(replaceTo, 'g') : null; | |
const onlyClasses = document.getElementById("only-classes").value.split("\n").filter(el => el.trim() !== ''); | |
const ignoreClasses = document.getElementById("ignore-classes").value.split("\n").filter(el => el.trim() !== ''); | |
let sourceJson; | |
try { | |
sourceJson = JSON.parse(document.getElementById("source").value); | |
} catch (error) { | |
return `Unable to parse JSON, check imported file syntax and integrity. ${error}`; | |
} | |
let replacementsCount = 0; | |
const skippedElements = new Set(); | |
const replacedElements = new Set(); | |
sourceJson.forEach(el => { | |
const selectionFn = (selectorString) => { | |
const hasRegexSymbols = /[\[\]*?+^$()|{}\\]/.test(selectorString); | |
if (hasRegexSymbols) { | |
const regex = new RegExp(selectorString); | |
return regex.test(el.sel); | |
} else { | |
return el.sel === selectorString; | |
} | |
}; | |
const shouldProcess = onlyClasses.length === 0 || onlyClasses.some(selectionFn); | |
const shouldIgnore = ignoreClasses.some(selectionFn); | |
if (shouldIgnore) { | |
skippedElements.add(el); | |
} else if (shouldProcess) { | |
if (el.styleLess) { | |
const replacedStyleLess = useRegex ? el.styleLess.replace(REGEX_FROM, replaceTo) : el.styleLess.replace(replaceFrom, replaceTo); | |
if (replacedStyleLess !== el.styleLess) { | |
el.styleLess = replacedStyleLess; | |
replacedElements.add(el); | |
replacementsCount++; | |
} | |
} | |
if (el.variants) { | |
Object.keys(el.variants).forEach(variant => { | |
const variantEl = el.variants[variant]; | |
if (variantEl.styleLess) { | |
const replacedVariantStyleLess = useRegex ? variantEl.styleLess.replace(REGEX_FROM, replaceTo) : variantEl.styleLess.replace(replaceFrom, replaceTo); | |
if (replacedVariantStyleLess !== variantEl.styleLess) { | |
variantEl.styleLess = replacedVariantStyleLess; | |
replacedElements.add(el); | |
replacementsCount++; | |
} | |
} | |
}); | |
} | |
} | |
}); | |
const replacedElementsArr = [...replacedElements]; | |
document.getElementById("result").value = JSON.stringify(sourceJson, null, 2); | |
document.getElementById("skipped-classes").value = [...skippedElements].map(el => el.sel).sort().join("\n"); | |
document.getElementById("affected-classes").value = replacedElementsArr.map(el => el.sel).sort().join("\n"); | |
document.getElementById("affected-pages").value = [...new Set(replacedElementsArr.map(el => el.__onPages).join(', ').split(', '))].sort().join("\n"); | |
return `Replacements made: ${replacementsCount} in ${replacedElementsArr.length} styles`; | |
} | |
function notify(message) { | |
alert(message); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment