diff --git a/plugins/avatars/avatars.min.js b/plugins/avatars/avatars.min.js new file mode 100644 index 000000000..e8a0ecab4 --- /dev/null +++ b/plugins/avatars/avatars.min.js @@ -0,0 +1 @@ +(e=>{const t=[],a=new Map,s=new Map,r="MailMessageView",i=e=>{let t=e.from[0];return`${"pass"==t.dkimStatus?1:0}/${t.email.toLowerCase()}`},o=e=>s.get(e.from[0].email.toLowerCase())||a.get(i(e)),n=(()=>{let s=t.shift();for(;s;){let r=o(s[0]);if(!r){let t=s[0].from[0];e.pluginRemoteRequest(((e,o)=>{!e&&o?.Result.type?(r=`data:${o.Result.type};base64,${o.Result.data}`,a.set(i(s[0]),r),s[1](r)):window.identiconSvg&&window.identiconSvg(t.email).then((e=>{r="data:image/svg+xml;base64,"+btoa(e),a.set(i(s[0]),r),s[1](r)})),n()}),"Avatar",{bimi:"pass"==t.dkimStatus?1:0,email:t.email});break}s[1](r),s=t.shift()}}).debounce(1e3);addEventListener("DOMContentLoaded",(()=>{if(parent.OC){const e=()=>parent.OC,t="DAV:",a="http://nextcloud.com/ns",r="urn:ietf:params:xml:ns:carddav",i=(e,t,a)=>e.getElementsByTagNameNS(t,a),o=(e,t,a)=>i(e,t,a)?.item(0)?.textContent,n=t=>e().webroot+"/remote.php"+t;e().requestToken&&fetch(n(`/dav/addressbooks/users/${e().currentUser}/contacts/`),{mode:"same-origin",cache:"no-cache",redirect:"error",credentials:"same-origin",method:"REPORT",headers:{requesttoken:e().requestToken,"Content-Type":"application/xml; charset=utf-8",Depth:1},body:''}).then((e=>e.status<400?e.text():Promise.reject(new Error({response:e})))).then((e=>{const n=new DOMParser,d=i(n.parseFromString(e,"application/xml").documentElement,t,"response");for(let e=0;e{s.set(e[1].toLowerCase(),o(i,t,"href")+"?photo")}))}}))}})),ko.bindingHandlers.fromPic={init:(e,a,s,r)=>{if(r){let a=o(r),s=t=>{e.src=t};if(a)s(a);else if(r.avatar){let t="pass"==r.from[0].dkimStatus?1:0;window.identiconSvg&&(e.onerror=()=>{window.identiconSvg(r.from[0].email).then((e=>s("data:image/svg+xml;base64,"+btoa(e))))}),s(`?Avatar/${t}/${r.avatar}`)}else t.push([r,s]),n()}}},addEventListener("rl-view-model.create",(e=>{if(r===e.detail.viewModelTemplateID){const a=document.getElementById(r).content.querySelector(".messageItemHeader");a&&a.prepend(Element.fromHTML(''));let s=e.detail;s.viewUserPic=ko.observable(""),s.viewUserPicVisible=ko.observable(!1),s.message.subscribe((e=>{if(s.viewUserPicVisible(!1),e){let a=o(e),r=e=>{s.viewUserPic(e),s.viewUserPicVisible(!0)};if(a)r(a);else if(e.avatar){r(`?Avatar/${"pass"==e.from[0].dkimStatus?1:0}/${e.avatar}`)}else t.push([e,r]),n()}}))}"MailMessageList"===e.detail.viewModelTemplateID&&document.getElementById("MailMessageList").content.querySelector(".messageCheckbox").append(Element.fromHTML(''))}))})(window.rl); \ No newline at end of file diff --git a/plugins/avatars/jdenticon.js b/plugins/avatars/jdenticon.js index 171f85414..536a75e07 100644 --- a/plugins/avatars/jdenticon.js +++ b/plugins/avatars/jdenticon.js @@ -48,7 +48,7 @@ class HSLColor { const h = this.h, s = this.s, l = this.l/*, a = this.a*/, hex = v => { - v = Math.max(0, Math.min(255, v * 255)).toString(16); + v = between(0, 255, v * 255).toString(16); return v.slice(0,2).padStart(2, "0"); }; let r=l, g=l, b=l; @@ -68,6 +68,8 @@ class HSLColor } const + between = (l, h, v) => Math.max(l, Math.min(h, v)), + parseHex = (hash, startPosition, octets) => parseInt(hash.substr(startPosition, octets), 16), /** @@ -86,99 +88,17 @@ const lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2; return new HSLColor(hue, saturation, lightness).toString(); - }, - - /** - * Gets the normalized current Jdenticon color configuration. Missing fields have default values. - * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A - * local configuration overrides the global configuration in it entirety. This parameter can for backward - * compatibility also contain a padding value. A padding value only overrides the global padding, not the - * entire global configuration. - * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor - * explicitly to the API method. - * @returns {ParsedConfiguration} - */ - getConfiguration = (paddingOrLocalConfig, defaultPadding) => { - const configObject = - typeof paddingOrLocalConfig == "object" && paddingOrLocalConfig || - { }, - - lightnessConfig = configObject["lightness"] || { }, - - // In versions < 2.1.0 there was no grayscale saturation - - // saturation was the color saturation. - saturation = configObject["saturation"] || { }, - colorSaturation = "color" in saturation ? saturation["color"] : saturation, - grayscaleSaturation = saturation["grayscale"], - - padding = configObject["padding"], - - /** - * Creates a lightness range. - */ - lightness = (configName, defaultRange) => { - var range = lightnessConfig[configName]; - - // Check if the lightness range is an array-like object. This way we ensure the - // array contain two values at the same time. - if (!(range && range.length > 1)) { - range = defaultRange; - } - - /** - * Gets a lightness relative the specified value in the specified lightness range. - */ - return function (value) { - value = range[0] + value * (range[1] - range[0]); - return value < 0 ? 0 : value > 1 ? 1 : value; - }; - }; - - return { - /** - * Gets a hue allowed by the configured hue restriction, - * provided the originally computed hue. - */ - X/*hue*/: originalHue => { - var hueConfig = configObject["hues"]; - var hue; - - // Check if 'hues' is an array-like object. This way we also ensure that - // the array is not empty, which would mean no hue restriction. - if (hueConfig && hueConfig.length > 0) { - // originalHue is in the range [0, 1] - // Multiply with 0.999 to change the range to [0, 1) and then truncate the index. - hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)]; - } - - return typeof hue == "number" ? - - // A hue was specified. We need to convert the hue from - // degrees on any turn - e.g. 746° is a perfectly valid hue - - // to turns in the range [0, 1). - ((((hue / 360) % 1) + 1) % 1) : - - // No hue configured => use original hue - originalHue; - }, - - p/*colorSaturation*/: typeof colorSaturation == "number" ? colorSaturation : 0.5, - H/*grayscaleSaturation*/: typeof grayscaleSaturation == "number" ? grayscaleSaturation : 0, - q/*colorLightness*/: lightness("color", [0.4, 0.8]), - I/*grayscaleLightness*/: lightness("grayscale", [0.3, 0.9]), - Y/*iconPadding*/: - typeof paddingOrLocalConfig == "number" ? paddingOrLocalConfig : - typeof padding == "number" ? padding : - defaultPadding - } }; /** * Represents a point. */ -function Point(x, y) { - this.x = x; - this.y = y; +class Point +{ + constructor(x, y) { + this.x = x; + this.y = y; + } } /** @@ -466,106 +386,6 @@ function outerShape(index, g, cell) { ); } -/** - * Gets a set of identicon color candidates for a specified hue and config. - * @param {number} hue - * @param {ParsedConfiguration} config - */ -function colorTheme(hue, config) { - hue = config.X/*hue*/(hue); - return [ - // Dark gray - correctedHsl(hue, config.H/*grayscaleSaturation*/, config.I/*grayscaleLightness*/(0)), - // Mid color - correctedHsl(hue, config.p/*colorSaturation*/, config.q/*colorLightness*/(0.5)), - // Light gray - correctedHsl(hue, config.H/*grayscaleSaturation*/, config.I/*grayscaleLightness*/(1)), - // Light color - correctedHsl(hue, config.p/*colorSaturation*/, config.q/*colorLightness*/(1)), - // Dark color - correctedHsl(hue, config.p/*colorSaturation*/, config.q/*colorLightness*/(0)) - ]; -} - -/** - * Draws an identicon to a specified renderer. - * @param {Renderer} renderer - * @param {string} hash - * @param {Object|number=} config - */ -function iconGenerator(renderer, hash, config) { - var parsedConfig = getConfiguration(config, 0.08); - - // Calculate padding and round to nearest integer - var size = renderer.k/*iconSize*/; - var padding = (0.5 + size * parsedConfig.Y/*iconPadding*/) | 0; - size -= padding * 2; - - var graphics = new Graphics(renderer); - - // Calculate cell size and ensure it is an integer - var cell = 0 | (size / 4); - - // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon - var x = 0 | (padding + size / 2 - cell * 2); - var y = 0 | (padding + size / 2 - cell * 2); - - function renderShape(colorIndex, shapes, index, rotationIndex, positions) { - var shapeIndex = parseHex(hash, index, 1); - var r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0; - - renderer.O/*beginShape*/(availableColors[selectedColorIndexes[colorIndex]]); - - for (var i = 0; i < positions.length; i++) { - graphics.A/*currentTransform*/ - = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4); - shapes(shapeIndex, graphics, cell, i); - } - - renderer.P/*endShape*/(); - } - - // AVAILABLE COLORS - var hue = parseHex(hash, -7) / 0xfffffff, - - // Available colors for this icon - availableColors = colorTheme(hue, parsedConfig), - - // The index of the selected colors - selectedColorIndexes = []; - - var index; - - function isDuplicate(values) { - if (values.indexOf(index) >= 0) { - for (var i = 0; i < values.length; i++) { - if (selectedColorIndexes.indexOf(values[i]) >= 0) { - return true; - } - } - } - } - - for (var i = 0; i < 3; i++) { - index = parseHex(hash, 8 + i, 1) % availableColors.length; - if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo - isDuplicate([2, 3])) { // Disallow light gray and light color combo - index = 1; - } - selectedColorIndexes.push(index); - } - - // ACTUAL RENDERING - // Sides - renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]); - // Corners - renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]); - // Center - renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]); - - renderer.finish(); -} - /** * Computes a SHA1 hash for any value and returns it as a hexadecimal string. * @@ -775,18 +595,99 @@ class SvgWriter /** * Draws an identicon as an SVG string. * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. - * @param {number} size - Icon size in pixels. - * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any - * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be - * specified in place of a configuration object. * @returns {string} SVG string */ -window.identiconSvg = async (hashOrValue, config) => { - var writer = new SvgWriter(50); - iconGenerator(new SvgRenderer(writer), - (/^[0-9a-f]{11,}$/i.test(hashOrValue) && hashOrValue) +window.identiconSvg = async (hashOrValue) => { + const writer = new SvgWriter(50), + renderer = new SvgRenderer(writer), + hash = (/^[0-9a-f]{11,}$/i.test(hashOrValue) && hashOrValue) || await computeHash(hashOrValue == null ? "" : "" + hashOrValue), - config); + config = { + p/*colorSaturation*/: 0.5, + H/*grayscaleSaturation*/: 0, + q/*colorLightness*/: value => between(0, 1, 0.4 + value * (0.8 - 0.4)), + I/*grayscaleLightness*/: value => between(0, 1, 0.3 + value * (0.9 - 0.3)) + }; + + var index; + + // Calculate padding and round to nearest integer + var size = renderer.k/*iconSize*/; + var padding = (0.5 + size * 0.08) | 0; + size -= padding * 2; + + const graphics = new Graphics(renderer), + + // Calculate cell size and ensure it is an integer + cell = 0 | (size / 4), + + // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon + s = 0 | (padding + size / 2 - cell * 2), + + // AVAILABLE COLORS + hue = parseHex(hash, -7) / 0xfffffff, + + // Available colors for this icon + availableColors = [ + // Dark gray + correctedHsl(hue, config.H/*grayscaleSaturation*/, config.I/*grayscaleLightness*/(0)), + // Mid color + correctedHsl(hue, config.p/*colorSaturation*/, config.q/*colorLightness*/(0.5)), + // Light gray + correctedHsl(hue, config.H/*grayscaleSaturation*/, config.I/*grayscaleLightness*/(1)), + // Light color + correctedHsl(hue, config.p/*colorSaturation*/, config.q/*colorLightness*/(1)), + // Dark color + correctedHsl(hue, config.p/*colorSaturation*/, config.q/*colorLightness*/(0)) + ], + + // The index of the selected colors + selectedColorIndexes = [], + + isDuplicate = values => { + if (values.indexOf(index) >= 0) { + for (var i = 0; i < values.length; i++) { + if (selectedColorIndexes.indexOf(values[i]) >= 0) { + return true; + } + } + } + }, + + renderShape = (colorIndex, shapes, index, rotationIndex, positions) => { + var shapeIndex = parseHex(hash, index, 1); + var r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0; + + renderer.O/*beginShape*/(availableColors[selectedColorIndexes[colorIndex]]); + + for (var i = 0; i < positions.length; i++) { + graphics.A/*currentTransform*/ + = new Transform(s + positions[i][0] * cell, s + positions[i][1] * cell, cell, r++ % 4); + shapes(shapeIndex, graphics, cell, i); + } + + renderer.P/*endShape*/(); + }; + + for (var i = 0; i < 3; i++) { + index = parseHex(hash, 8 + i, 1) % availableColors.length; + if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo + isDuplicate([2, 3])) { // Disallow light gray and light color combo + index = 1; + } + selectedColorIndexes.push(index); + } + + // ACTUAL RENDERING + // Sides + renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]); + // Corners + renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]); + // Center + renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]); + + renderer.finish(); + return writer.toString(); }; diff --git a/plugins/avatars/jdenticon.min.js b/plugins/avatars/jdenticon.min.js new file mode 100644 index 000000000..3a3d1cb02 --- /dev/null +++ b/plugins/avatars/jdenticon.min.js @@ -0,0 +1 @@ +(()=>{class t{constructor(t,i,s){this.h=t,this.s=i,this.l=s}toString(){const t=this.h,s=this.s,h=this.l,r=t=>(t=i(0,255,255*t).toString(16)).slice(0,2).padStart(2,"0");let n=h,e=h,a=h;if(0!=s){let i=h<.5?h*(s+1):h+s-h*s,r=2*h-i,o=t=>6*(t+=t<0?1:t>1?-1:0)<1?r+(i-r)*t*6:2*t<1?i:3*t<2?r+(i-r)*(2/3-t)*6:r;n=o(t+1/3),e=o(t),a=o(t-1/3)}return"#"+r(n)+r(e)+r(a)}}const i=(t,i,s)=>Math.max(t,Math.min(i,s)),s=(t,i,s)=>parseInt(t.substr(i,s),16),h=(i,s,h)=>{var r=[.55,.5,.5,.46,.6,.55,.55][6*i+.5|0];return new t(i,s,h=h<.5?h*r*2:r+(h-.5)*(1-r)*2).toString()};class r{constructor(t,i){this.x=t,this.y=i}}class n{constructor(t,i,s,h){this.u=t,this.v=i,this.K=s,this.Z=h}L(t,i,s,h){var n=this.u+this.K,e=this.v+this.K,a=this.Z;return 1===a?new r(n-i-(h||0),this.v+t):2===a?new r(n-t-(s||0),e-i-(h||0)):3===a?new r(this.u+i,e-t-(s||0)):new r(this.u+t,this.v+i)}}var e=new n(0,0,0,0);class a{constructor(t){this.M=t,this.A=e}g(t,i){for(var s=i?-2:2,h=[],r=i?t.length-2:0;r=0;r+=s)h.push(this.A.L(t[r],t[r+1]));this.M.g(h)}h(t,i,s,h){var r=this.A.L(t,i,s,s);this.M.h(r,s,h)}i(t,i,s,h,r){this.g([t,i,t+s,i,t+s,i+h,t,i+h],r)}j(t,i,s,h,r,n){var e=[t+s,i,t+s,i+h,t,i+h,t,i];e.splice((r||0)%4*2,2),this.g(e,n)}N(t,i,s,h,r){this.g([t+s/2,i,t+s,i+h/2,t+s/2,i+h,t,i+h/2],r)}}function o(t,i,s,h){var r,n,e,a,o,c;(t%=14)?1==t?(e=0|.5*s,a=0|.8*s,i.j(s-e,0,e,a,2)):2==t?(e=0|s/3,i.i(e,e,s-e,s-e)):3==t?(c=s<6?1:s<8?2:0|.25*s,o=(o=.1*s)>1?0|o:o>.5?1:o,i.i(c,c,s-o-c,s-o-c)):4==t?(n=0|.15*s,e=0|.5*s,i.h(s-e-n,s-e-n,e)):5==t?((c=4*(o=.1*s))>3&&(c|=0),i.i(0,0,s,s),i.g([c,c,s-o,c,c+(s-c-o)/2,s-o],!0)):6==t?i.g([0,0,s,0,s,.7*s,.4*s,.4*s,.7*s,s,0,s]):7==t?i.j(s/2,s/2,s/2,s/2,3):8==t?(i.i(0,0,s,s/2),i.i(0,s/2,s/2,s/2),i.j(s/2,s/2,s/2,s/2,1)):9==t?(o=.14*s,c=s<4?1:s<6?2:0|.35*s,o=s<8?o:0|o,i.i(0,0,s,s),i.i(c,c,s-c-o,s-c-o,!0)):10==t?(c=3*(o=.12*s),i.i(0,0,s,s),i.h(c,c,s-o-c,!0)):11==t?i.j(s/2,s/2,s/2,s/2,3):12==t?(n=.25*s,i.i(0,0,s,s),i.N(n,n,s-n,s-n,!0)):!h&&(n=.4*s,e=1.2*s,i.h(n,n,e)):(r=.42*s,i.g([0,0,s,0,s,s-2*r,s-r,s,0,s]))}function c(t,i,s){var h;(t%=4)?1==t?i.j(0,s/2,s,s/2,0):2==t?i.N(0,0,s,s):(h=s/6,i.h(h,h,s-2*h)):i.j(0,0,s,s,0)}function g(t){return(10*t+.5|0)/10}class l{constructor(){this.B=""}g(t){for(var i="",s=0;s'}m(t,i){i&&(this.F+='')}S(t,i){this.F+=''}toString(){return this.F+""}}window.identiconSvg=async t=>{const r=new v(50),e=new u(r),g=/^[0-9a-f]{11,}$/i.test(t)&&t||await async function(t){return Array.from(new Uint8Array(await crypto.subtle.digest("SHA-1",(new TextEncoder).encode(t)))).map((t=>t.toString(16).padStart(2,"0"))).join("")}(null==t?"":""+t),l=.5,w=0,f=t=>i(0,1,.4+.4*t),d=t=>i(0,1,.3+t*(.9-.3));var p,S=e.k,x=.5+.08*S|0;S-=2*x;const y=new a(e),j=0|S/4,m=0|x+S/2-2*j,A=s(g,-7)/268435455,M=[h(A,w,d(0)),h(A,l,f(.5)),h(A,w,d(1)),h(A,l,f(1)),h(A,l,f(0))],B=[],F=t=>{if(t.indexOf(p)>=0)for(var i=0;i=0)return!0},O=(t,i,h,r,a)=>{var o=s(g,h,1),c=r?s(g,r,1):0;e.O(M[B[t]]);for(var l=0;lGetThemes()) ? $sTheme : $this->Config()->Get('themes', 'default', 'Default'); } - public function compileCss(string $sTheme, bool $bAdmin) : string + public function compileCss(string $sTheme, bool $bAdmin, bool $bMinified = false) : string { $bCustomTheme = '@custom' === \substr($sTheme, -7); if ($bCustomTheme) { @@ -118,7 +118,7 @@ trait Themes } } - $mResult[] = $this->Plugins()->CompileCss($bAdmin, $bLess); + $mResult[] = $this->Plugins()->CompileCss($bAdmin, $bLess, $bMinified); $mResult = \preg_replace('@(url\(["\']?)(\\./)?([a-z]+[^:a-z])@', "\$1{$sBase}\$3", diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php index 37bcbe7c5..fec0fb938 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php @@ -179,11 +179,17 @@ class Manager return $this->bIsEnabled && \count($this->aJs[$bAdminScope ? 1 : 0]); } - public function CompileCss(bool $bAdminScope, bool &$bLess) : string + public function CompileCss(bool $bAdminScope, bool &$bLess, bool $bMinified) : string { $aResult = array(); if ($this->bIsEnabled) { foreach ($this->aCss[$bAdminScope ? 1 : 0] as $sFile) { + if ($bMinified) { + $sMinFile = \str_replace('.css', '.min.css', $sFile); + if (\is_readable($sMinFile)) { + $sFile = $sMinFile; + } + } if (\is_readable($sFile)) { $aResult[] = \file_get_contents($sFile); $bLess = $bLess || \str_ends_with($sFile, '.less'); @@ -193,15 +199,18 @@ class Manager return \implode("\n", $aResult); } - public function CompileJs(bool $bAdminScope = false) : string + public function CompileJs(bool $bAdminScope = false, bool $bMinified = false) : string { $aResult = array(); - if ($this->bIsEnabled) - { - foreach ($this->aJs[$bAdminScope ? 1 : 0] as $sFile) - { - if (\is_readable($sFile)) - { + if ($this->bIsEnabled) { + foreach ($this->aJs[$bAdminScope ? 1 : 0] as $sFile) { + if ($bMinified) { + $sMinFile = \str_replace('.js', '.min.js', $sFile); + if (\is_readable($sMinFile)) { + $sFile = $sMinFile; + } + } + if (\is_readable($sFile)) { $aResult[] = \file_get_contents($sFile); } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index 4717c29dc..a0894a2ff 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -506,31 +506,29 @@ class ServiceActions \header('Content-Type: application/javascript; charset=utf-8'); - $bCacheEnabled = $this->Config()->Get('labs', 'cache_system_data', true); - if ($bCacheEnabled) - { - $this->oActions->verifyCacheByKey($this->sQuery); + $bAppDebug = $this->Config()->Get('debug', 'enable', false); + $sMinify = ($bAppDebug || $this->Config()->Get('labs', 'use_app_debug_js', false)) ? '' : 'min'; + + $bCacheEnabled = !$bAppDebug && $this->Config()->Get('labs', 'cache_system_data', true); + if ($bCacheEnabled) { + $this->oActions->verifyCacheByKey($this->sQuery . $sMinify); } $sCacheFileName = ''; - if ($bCacheEnabled) - { - $sCacheFileName = KeyPathHelper::PluginsJsCache($this->oActions->Plugins()->Hash()); + if ($bCacheEnabled) { + $sCacheFileName = KeyPathHelper::PluginsJsCache($this->oActions->Plugins()->Hash()) . $sMinify; $sResult = $this->Cacher()->Get($sCacheFileName); } - if (!\strlen($sResult)) - { - $sResult = $this->Plugins()->CompileJs($bAdmin); - if ($bCacheEnabled && \strlen($sCacheFileName)) - { + if (!$sResult) { + $sResult = $this->Plugins()->CompileJs($bAdmin, !!$sMinify); + if ($sCacheFileName) { $this->Cacher()->Set($sCacheFileName, $sResult); } } - if ($bCacheEnabled) - { - $this->oActions->cacheByKey($this->sQuery); + if ($bCacheEnabled) { + $this->oActions->cacheByKey($this->sQuery . $sMinify); } return $sResult; @@ -539,45 +537,38 @@ class ServiceActions public function ServiceCss() : string { $sResult = ''; - $bAdmin = !empty($this->aPaths[2]) && 'Admin' === $this->aPaths[2]; $bJson = !empty($this->aPaths[9]) && 'Json' === $this->aPaths[9]; - if ($bJson) - { + if ($bJson) { \header('Content-Type: application/json; charset=utf-8'); - } - else - { + } else { \header('Content-Type: text/css; charset=utf-8'); } $sTheme = ''; - if (!empty($this->aPaths[4])) - { + if (!empty($this->aPaths[4])) { $sTheme = $this->oActions->ValidateTheme($this->aPaths[4]); - $bCacheEnabled = $this->Config()->Get('labs', 'cache_system_data', true); - if ($bCacheEnabled) - { - $this->oActions->verifyCacheByKey($this->sQuery); + $bAppDebug = $this->Config()->Get('debug', 'enable', false); + $sMinify = ($bAppDebug || $oConfig->Get('labs', 'use_app_debug_css', false)) ? '' : 'min'; + + $bCacheEnabled = !$bAppDebug && $this->Config()->Get('labs', 'cache_system_data', true); + if ($bCacheEnabled) { + $this->oActions->verifyCacheByKey($this->sQuery . $sMinify); } $sCacheFileName = ''; - if ($bCacheEnabled) - { - $sCacheFileName = KeyPathHelper::CssCache($sTheme, $this->oActions->Plugins()->Hash()); + if ($bCacheEnabled) { + $sCacheFileName = KeyPathHelper::CssCache($sTheme, $this->oActions->Plugins()->Hash()) . $sMinify; $sResult = $this->Cacher()->Get($sCacheFileName); } - if (!$sResult) - { + if (!$sResult) { try { $sResult = $this->oActions->compileCss($sTheme, $bAdmin); - - if ($bCacheEnabled && $sCacheFileName) - { + if ($sCacheFileName) { $this->Cacher()->Set($sCacheFileName, $sResult); } } @@ -587,9 +578,8 @@ class ServiceActions } } - if ($bCacheEnabled) - { - $this->oActions->cacheByKey($this->sQuery); + if ($bCacheEnabled) { + $this->oActions->cacheByKey($this->sQuery . $sMinify); } }