diff --git a/.github/workflows/minified-assets.yml b/.github/workflows/minified-assets.yml new file mode 100644 index 0000000..99366fd --- /dev/null +++ b/.github/workflows/minified-assets.yml @@ -0,0 +1,23 @@ +name: Minified assets +on: [push] +permissions: + contents: read +jobs: + build: + runs-on: ubuntu-latest + + steps: + # git checkout + - uses: actions/checkout@v2 + + # node setup (esbuild is fetched on the fly via npx) + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + # regenerate redturtle_chefcookie(.tech).min.js and styles.min.css + # and fail if that produces a diff: it means a source file was + # edited without regenerating its minified counterpart. + - name: check minified assets are up to date + run: make check-minified diff --git a/CHANGES.rst b/CHANGES.rst index dd53a18..bb176b8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,10 @@ Changelog 3.0.1 (unreleased) ------------------ -- Nothing changed yet. +- Serve minified versions of ``redturtle_chefcookie.js``, ``redturtle_chefcookie_tech.js`` and ``styles.css``. + [mamico] +- Add ``defer`` attribute to injected script tags so they no longer block HTML parsing. + [mamico] 3.0.0 (2026-03-26) diff --git a/DEVELOP.rst b/DEVELOP.rst index cd8979a..9ab99fc 100644 --- a/DEVELOP.rst +++ b/DEVELOP.rst @@ -40,3 +40,19 @@ run a specific tox env: $ tox -e py37-Plone52 + +Minified JS/CSS assets +----------------------- + +``redturtle_chefcookie.js``, ``redturtle_chefcookie_tech.js`` and ``styles.css`` +(under ``src/redturtle/chefcookie/browser/static/``) are shipped both as +readable sources and as minified files that are actually served to the +browser. If you edit one of those sources, regenerate the minified files +with (requires Node/npx, esbuild is fetched on the fly):: + + $ make minify + +CI (``.github/workflows/minified-assets.yml``) runs ``make check-minified``, +which regenerates the minified files and fails if that produces a diff, to +catch a source edited without regenerating its ``.min`` counterpart. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..55bb5fb --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +STATIC_DIR = src/redturtle/chefcookie/browser/static +ESBUILD = npx --yes esbuild@0.28.1 + +JS_SOURCES = \ + $(STATIC_DIR)/redturtle_chefcookie.js \ + $(STATIC_DIR)/redturtle_chefcookie_tech.js +JS_MINIFIED = $(JS_SOURCES:.js=.min.js) + +CSS_SOURCES = $(STATIC_DIR)/styles.css +CSS_MINIFIED = $(CSS_SOURCES:.css=.min.css) + +.PHONY: minify minify-js minify-css check-minified + +minify: minify-js minify-css + +minify-js: + @for src in $(JS_SOURCES); do \ + out="$${src%.js}.min.js"; \ + echo "minifying $$src -> $$out"; \ + $(ESBUILD) $$src --minify --outfile=$$out; \ + done + +minify-css: + @for src in $(CSS_SOURCES); do \ + out="$${src%.css}.min.css"; \ + echo "minifying $$src -> $$out"; \ + $(ESBUILD) $$src --minify --outfile=$$out; \ + done + +# Regenerates the minified assets and fails if that changes anything that +# is not already committed: catches the case where someone edited a source +# file (redturtle_chefcookie.js, redturtle_chefcookie_tech.js, styles.css) +# without regenerating its .min counterpart. +# NOTE: this compares file *content* (git diff), not modification times: +# git does not preserve mtimes on checkout, so an mtime-based check would +# always "pass" right after a fresh clone/checkout in CI regardless of +# which file was actually edited last. +check-minified: minify + git diff --exit-code -- $(JS_MINIFIED) $(CSS_MINIFIED) diff --git a/src/redturtle/chefcookie/browser/static/redturtle_chefcookie.min.js b/src/redturtle/chefcookie/browser/static/redturtle_chefcookie.min.js new file mode 100644 index 0000000..762afe5 --- /dev/null +++ b/src/redturtle/chefcookie/browser/static/redturtle_chefcookie.min.js @@ -0,0 +1,69 @@ +class redturtlechefcookie extends chefcookie{showSettings(){this.logTracking("settings_open");let e=document.querySelector(".chefcookie__settings-container");e.classList.add("chefcookie__settings-container--visible"),e.style.minHeight=e.scrollHeight+"px",setTimeout(()=>{e.classList.contains("chefcookie__settings-container--visible")&&(e.style.height="auto"),e.focus()},this.animationSpeed),this.fixMaxHeight()}acceptAllScripts(){let e=[];this.config.settings.forEach(i=>{i.scripts!==void 0&&Object.entries(i.scripts).forEach(([c,t])=>{this.accept(c,!1)})})}switchSettingsLabelsOpen(){document.querySelector(".chefcookie__button--accept").style.display="block",document.querySelector(".chefcookie__button--settings").style.display="none"}buildDom(){document.body.insertAdjacentHTML("afterbegin",` +
+ `)}} diff --git a/src/redturtle/chefcookie/browser/static/redturtle_chefcookie_tech.min.js b/src/redturtle/chefcookie/browser/static/redturtle_chefcookie_tech.min.js new file mode 100644 index 0000000..f28cde6 --- /dev/null +++ b/src/redturtle/chefcookie/browser/static/redturtle_chefcookie_tech.min.js @@ -0,0 +1,48 @@ +class redturtlechefcookie extends chefcookie{showSettings(){this.logTracking("settings_open");let e=document.querySelector(".chefcookie__settings-container");e.classList.add("chefcookie__settings-container--visible"),e.style.minHeight=e.scrollHeight+"px",setTimeout(()=>{e.classList.contains("chefcookie__settings-container--visible")&&(e.style.height="auto")},this.animationSpeed),this.fixMaxHeight()}acceptAllScripts(){let e=[];this.config.settings.forEach(i=>{i.scripts!==void 0&&Object.entries(i.scripts).forEach(([c,t])=>{this.accept(c,!1)})})}switchSettingsLabelsOpen(){document.querySelector(".chefcookie__button--accept").style.display="block",document.querySelector(".chefcookie__button--settings").style.display="none"}buildDom(){document.body.insertAdjacentHTML("afterbegin",` + + `)}} diff --git a/src/redturtle/chefcookie/browser/static/styles.min.css b/src/redturtle/chefcookie/browser/static/styles.min.css new file mode 100644 index 0000000..4d74784 --- /dev/null +++ b/src/redturtle/chefcookie/browser/static/styles.min.css @@ -0,0 +1 @@ +html.chefcookie--fade body:after,html.chefcookie--blur body:after{z-index:0;width:0px;height:0px}#cc-banner .close{position:absolute;right:32px;top:32px;width:32px;height:32px;opacity:.3}#cc-banner .close:hover{opacity:1}#cc-banner .close:before,#cc-banner .close:after{position:absolute;left:15px;content:" ";height:33px;width:2px;background-color:#333}#cc-banner .close:before{transform:rotate(45deg)}#cc-banner .close:after{transform:rotate(-45deg)}#cc-banner .chefcookie--bottom .chefcookie__box{background-color:#505050}#cc-banner .chefcookie--noscroll body{position:unset;width:auto;overflow:unset}#cc-banner .chefcookie--blur body:after{backdrop-filter:unset;background-color:unset}#cc-banner .chefcookie__button--accept{border-color:unset;color:inherit!important}#cc-banner .chefcookie__group .chefcookie__group_wrapper{display:flex;position:relative}#cc-banner .chefcookie__group .chefcookie__group_wrapper .chefcookie__group-label{width:auto;flex:1 1 50%;position:relative}#cc-banner .chefcookie__group .chefcookie__group_wrapper .chefcookie__group-label .chefcookie__group-label-text{font-size:1.8rem;display:inline-block}#cc-banner .chefcookie__group .chefcookie__group_wrapper .chefcookie__group-description{display:none;position:absolute;left:20px;width:300px;background-color:#fff;border:1px solid grey;padding:1.5rem;z-index:1}#cc-banner .show-info{position:relative;background:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+PHN2ZyBmaWxsPSIjMDAwMDAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiPiAgICA8cGF0aCBkPSJNIDEyIDIgQyA2LjQ4ODk5NzEgMiAyIDYuNDg4OTk3MSAyIDEyIEMgMiAxNy41MTEwMDMgNi40ODg5OTcxIDIyIDEyIDIyIEMgMTcuNTExMDAzIDIyIDIyIDE3LjUxMTAwMyAyMiAxMiBDIDIyIDYuNDg4OTk3MSAxNy41MTEwMDMgMiAxMiAyIHogTSAxMiA0IEMgMTYuNDMwMTIzIDQgMjAgNy41Njk4Nzc0IDIwIDEyIEMgMjAgMTYuNDMwMTIzIDE2LjQzMDEyMyAyMCAxMiAyMCBDIDcuNTY5ODc3NCAyMCA0IDE2LjQzMDEyMyA0IDEyIEMgNCA3LjU2OTg3NzQgNy41Njk4Nzc0IDQgMTIgNCB6IE0gMTEgNyBMIDExIDkgTCAxMyA5IEwgMTMgNyBMIDExIDcgeiBNIDExIDExIEwgMTEgMTcgTCAxMyAxNyBMIDEzIDExIEwgMTEgMTEgeiIvPjwvc3ZnPg==);width:16px;height:16px;background-size:16px;cursor:pointer;display:inline-block}#cc-banner .show-info:hover .chefcookie__group-description,#cc-banner .show-info:focus .chefcookie__group-description{display:block}#cc-banner ul.chefcookie__scripts{display:flex;margin-top:1rem;margin-left:-.5rem;margin-right:-.5rem;flex-wrap:wrap}#cc-banner ul.chefcookie__scripts li.chefcookie__script{flex:0 1 33.333%}#cc-banner ul.chefcookie__scripts li.chefcookie__script:first-child{margin-top:0}#cc-banner ul.chefcookie__scripts li.chefcookie__script .chefcookie__script_wrapper{background-color:#ccc;padding:1rem;margin:.5rem}#cc-banner .chefcookie__script::marker{content:none}#cc-banner .chefcookie__group-checkbox:focus~.chefcookie__group-checkbox-icon{box-shadow:0 0 6px 3px #6a6a6a}#cc-banner .chefcookie__script-checkbox:focus~.chefcookie__script-checkbox-icon{box-shadow:0 0 6px 3px #fff}#cc-banner .chefcookie__group.chefcookie__group--disabled .chefcookie__group-checkbox[data-status="2"]~.chefcookie__group-checkbox-icon{background-color:#02c102}#cc-banner .chefcookie__group.chefcookie__group--disabled .chefcookie__group-checkbox[data-status="2"]~.chefcookie__group-checkbox-icon:after{background-color:#ccc}#cc-banner .chefcookie__group.chefcookie__group--disabled ul.chefcookie__scripts .chefcookie__script-checkbox-icon{background-color:#02c102}#cc-banner .chefcookie__group .chefcookie__group-checkbox[data-status="0"]~.chefcookie__group-checkbox-icon{background-color:red}#cc-banner .chefcookie__group .chefcookie__group-checkbox[data-status="1"]~.chefcookie__group-checkbox-icon{background-color:#d3d3d3}#cc-banner .chefcookie__group .chefcookie__group-checkbox[data-status="2"]~.chefcookie__group-checkbox-icon{background-color:#02c102}#cc-banner .chefcookie__group .chefcookie__group-checkbox[data-status="0"]~.chefcookie__group-checkbox-icon:after{background-color:#eee}#cc-banner .chefcookie__group .chefcookie__group-checkbox[data-status="1"]~.chefcookie__group-checkbox-icon:after{background-color:#eee}#cc-banner .chefcookie__group .chefcookie__group-checkbox[data-status="2"]~.chefcookie__group-checkbox-icon:after{background-color:#eee}#cc-banner .chefcookie__group.chefcookie__group ul.chefcookie__scripts .chefcookie__script-label .chefcookie__script-checkbox-icon:after{background-color:#eee}#cc-banner .chefcookie__group.chefcookie__group--disabled ul.chefcookie__scripts .chefcookie__script-label .chefcookie__script-checkbox-icon:after{background-color:#ccc}#cc-banner .chefcookie__group.chefcookie__group ul.chefcookie__scripts .chefcookie__script-checkbox-icon{background-color:red}#cc-banner .chefcookie__group.chefcookie__group ul.chefcookie__scripts input:checked~.chefcookie__script-checkbox-icon{background-color:#02c102}#cc-banner .chefcookie__box .chefcookie__message a:focus{color:#000;font-weight:700}#cc-banner .chefcookie__group label input.chefcookie__group-checkbox[disabled][type=checkbox],#cc-banner .chefcookie__group label input.chefcookie__script-checkbox[disabled][type=checkbox]{display:none}#cc-banner .chefcookie__buttons a.context{color:#fff}#cc-banner .chefcookie__buttons a.chefcookie__button:hover,.chefcookie__buttons a.chefcookie__button:focus{opacity:1}#cc-banner .chefcookie__buttons a.chefcookie__button:not(.context):hover,.chefcookie__buttons a.chefcookie__button:not(.context):focus{background-color:#ccc}#cc-banner .chefcookie__settings-container{overflow:visible}#cc-banner .chefcookie__box .chefcookie__settings-container{display:none;height:auto}#cc-banner .chefcookie__box .chefcookie__settings-container.chefcookie__settings-container--visible{display:block}#cc-banner .chefcookie__groups .chefcookie__script-checkbox~.chefcookie__script-checkbox-icon:before,#cc-banner .chefcookie__groups .chefcookie__group-checkbox~.chefcookie__group-checkbox-icon:before{width:0;content:""}a#cookie-settings-open{position:fixed;bottom:calc(50% - 17.5px);right:0;background:#fff;border:1px solid #ccc;border-top-left-radius:5px;border-bottom-left-radius:5px;width:35px;height:35px;text-decoration:none;transition:all .3s linear;z-index:1000;display:flex;align-items:center;justify-content:center}a#cookie-settings-open svg{color:#000;margin:0;font-size:10px;transition:all .3s ease;width:20px;height:20px}@media screen and (max-width:840px){.close{right:10px;top:10px}#cc-banner ul.chefcookie__scripts li.chefcookie__script{flex:0 1 50%}} diff --git a/src/redturtle/chefcookie/browser/templates/get_chefcookie_js.pt b/src/redturtle/chefcookie/browser/templates/get_chefcookie_js.pt index 148ba68..a40e9d8 100644 --- a/src/redturtle/chefcookie/browser/templates/get_chefcookie_js.pt +++ b/src/redturtle/chefcookie/browser/templates/get_chefcookie_js.pt @@ -1,6 +1,6 @@