From 1c524efbf7da91cb99bb4516feb514071e938495 Mon Sep 17 00:00:00 2001 From: Evgeny Zinoviev Date: Sun, 10 Jul 2022 01:30:05 +0300 Subject: dark theme support --- build_static.php | 96 +++++ dark-theme-diff.js | 14 + deploy.sh | 7 +- engine/RequestDispatcher.php | 7 +- engine/Skin.php | 5 + engine/SkinContext.php | 8 +- htdocs/img/enter.svg | 1 - htdocs/js/common.js | 293 ++++++++++++++- htdocs/sass.php | 9 +- htdocs/scss/app/blog.scss | 385 ++++++++++++++++++++ htdocs/scss/app/common.scss | 289 +++++++++++++++ htdocs/scss/app/form.scss | 59 ++++ htdocs/scss/app/head.scss | 157 +++++++++ htdocs/scss/app/hljs.scss | 1 + htdocs/scss/app/mobile.scss | 41 +++ htdocs/scss/app/pages.scss | 14 + htdocs/scss/blog.scss | 383 -------------------- htdocs/scss/bundle_admin.scss | 3 + htdocs/scss/bundle_common.scss | 10 + htdocs/scss/colors/dark.scss | 46 +++ htdocs/scss/colors/light.scss | 46 +++ htdocs/scss/common-bundle.scss | 9 - htdocs/scss/common.scss | 415 ---------------------- htdocs/scss/entries/admin/dark.scss | 2 + htdocs/scss/entries/admin/light.scss | 2 + htdocs/scss/entries/common/dark.scss | 2 + htdocs/scss/entries/common/light.scss | 2 + htdocs/scss/form.scss | 59 ---- htdocs/scss/hljs.scss | 1 - htdocs/scss/hljs/github.scss | 36 +- htdocs/scss/mobile.scss | 41 --- htdocs/scss/pages.scss | 14 - htdocs/scss/vars.scss | 39 +- lang/en.php | 5 + model/Upload.php | 2 +- package-lock.json | 647 ++++++++++++++++++++++++++++++++++ package.json | 7 + prepare_static.php | 48 --- skin/base.skin.php | 120 +++++-- 39 files changed, 2255 insertions(+), 1070 deletions(-) create mode 100755 build_static.php create mode 100755 dark-theme-diff.js delete mode 100644 htdocs/img/enter.svg create mode 100644 htdocs/scss/app/blog.scss create mode 100644 htdocs/scss/app/common.scss create mode 100644 htdocs/scss/app/form.scss create mode 100644 htdocs/scss/app/head.scss create mode 100644 htdocs/scss/app/hljs.scss create mode 100644 htdocs/scss/app/mobile.scss create mode 100644 htdocs/scss/app/pages.scss delete mode 100644 htdocs/scss/blog.scss create mode 100644 htdocs/scss/bundle_admin.scss create mode 100644 htdocs/scss/bundle_common.scss create mode 100644 htdocs/scss/colors/dark.scss create mode 100644 htdocs/scss/colors/light.scss delete mode 100644 htdocs/scss/common-bundle.scss delete mode 100644 htdocs/scss/common.scss create mode 100644 htdocs/scss/entries/admin/dark.scss create mode 100644 htdocs/scss/entries/admin/light.scss create mode 100644 htdocs/scss/entries/common/dark.scss create mode 100644 htdocs/scss/entries/common/light.scss delete mode 100644 htdocs/scss/form.scss delete mode 100644 htdocs/scss/hljs.scss delete mode 100644 htdocs/scss/mobile.scss delete mode 100644 htdocs/scss/pages.scss create mode 100644 package-lock.json create mode 100644 package.json delete mode 100755 prepare_static.php diff --git a/build_static.php b/build_static.php new file mode 100755 index 0000000..a9ec027 --- /dev/null +++ b/build_static.php @@ -0,0 +1,96 @@ +#!/usr/bin/env php8.1 + '.escapeshellarg($output); + system($cmd); + + if (file_exists($output)) { + unlink($file); + rename($output, $file); + } else { + fwrite(STDERR, "error: could not cleancss $file\n"); + } +} + +function dark_diff(string $light_file, string $dark_file): void { + $temp_output = $dark_file.'.diff'; + $cmd = ROOT.'/dark-theme-diff.js '.escapeshellarg($light_file).' '.$dark_file.' > '.$temp_output; + exec($cmd, $output, $code); + if ($code != 0) { + fwrite(STDERR, "dark_diff failed with code $code\n"); + return; + } + + unlink($dark_file); + rename($temp_output, $dark_file); +} + +require __DIR__.'/init.php'; + +function build_static(): void { + $css_dir = ROOT.'/htdocs/css'; + $hashes = []; + + if (!file_exists($css_dir)) + mkdir($css_dir); + + // 1. scss -> css + $themes = ['light', 'dark']; + $entries = ['common', 'admin']; + foreach ($themes as $theme) { + foreach ($entries as $entry) { + $input = ROOT.'/htdocs/scss/entries/'.$entry.'/'.$theme.'.scss'; + $output = $css_dir.'/'.$entry.($theme == 'dark' ? '_dark' : '').'.css'; + if (sassc($input, $output) != 0) { + fwrite(STDERR, "error: could not compile entries/$entry/$theme.scss\n"); + continue; + } + + // 1.1. apply clean-css optimizations and transformations + clean_css($output); + } + } + + // 2. generate dark theme diff + foreach ($entries as $entry) { + $light_file = $css_dir.'/'.$entry.'.css'; + $dark_file = str_replace('.css', '_dark.css', $light_file); + dark_diff($light_file, $dark_file); + } + + // 3. calculate hashes + foreach (['css', 'js'] as $type) { + $reldir = ROOT.'/htdocs/'; + $entries = glob_recursive($reldir.$type.'/*.'.$type); + if (empty($entries)) { + continue; + } + foreach ($entries as $file) { + $name = preg_replace('/^'.preg_quote($reldir, '/').'/', '', $file); + $hashes[$name] = gethash($file); + } + } + + // 4. write config-static.php + $scfg = "/dev/null diff --git a/engine/RequestDispatcher.php b/engine/RequestDispatcher.php index 38b965d..3c3f684 100644 --- a/engine/RequestDispatcher.php +++ b/engine/RequestDispatcher.php @@ -39,11 +39,14 @@ class RequestDispatcher { } $skin = new Skin(); - $skin->static[] = '/css/common-bundle.css'; + $skin->static[] = '/css/common.css'; $skin->static[] = '/js/common.js'; + $lang = LangData::getInstance(); + $skin->addLangKeys($lang->search('/^theme_/')); + /** @var RequestHandler $handler */ - $handler = new $handler_class($skin, LangData::getInstance(), $router_input); + $handler = new $handler_class($skin, $lang, $router_input); $resp = $handler->beforeDispatch(); if ($resp instanceof Response) { $resp->send(); diff --git a/engine/Skin.php b/engine/Skin.php index 57f8b90..917eef7 100644 --- a/engine/Skin.php +++ b/engine/Skin.php @@ -23,11 +23,16 @@ class Skin { else $js = null; + $theme = ($_COOKIE['theme'] ?? 'auto'); + if (!in_array($theme, ['auto', 'dark', 'light'])) + $theme = 'auto'; + $layout_ctx = new SkinContext('\\skin\\base'); $lang = $this->getLang(); $lang = !empty($lang) ? json_encode($lang, JSON_UNESCAPED_UNICODE) : ''; return new Response(200, $layout_ctx->layout( static: $this->static, + theme: $theme, title: $this->title, opts: $this->options, js: $js, diff --git a/engine/SkinContext.php b/engine/SkinContext.php index cfb5068..c395453 100644 --- a/engine/SkinContext.php +++ b/engine/SkinContext.php @@ -53,10 +53,12 @@ class SkinContext extends SkinBase { return call_user_func_array($fn, $arguments); } - public function __get(string $name) { + public function &__get(string $name) { $fn = $this->ns.'\\'.$name; - if (function_exists($fn)) - return [$this, $name]; + if (function_exists($fn)) { + $f = [$this, $name]; + return $f; + } if (array_key_exists($name, $this->data)) return $this->data[$name]; diff --git a/htdocs/img/enter.svg b/htdocs/img/enter.svg deleted file mode 100644 index 6fe49ed..0000000 --- a/htdocs/img/enter.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/htdocs/js/common.js b/htdocs/js/common.js index 4e4199c..959b672 100644 --- a/htdocs/js/common.js +++ b/htdocs/js/common.js @@ -14,6 +14,39 @@ if (!String.prototype.endsWith) { }; } +if (!Object.assign) { + Object.defineProperty(Object, 'assign', { + enumerable: false, + configurable: true, + writable: true, + value: function(target, firstSource) { + 'use strict'; + if (target === undefined || target === null) { + throw new TypeError('Cannot convert first argument to object'); + } + + var to = Object(target); + for (var i = 1; i < arguments.length; i++) { + var nextSource = arguments[i]; + if (nextSource === undefined || nextSource === null) { + continue; + } + + var keysArray = Object.keys(Object(nextSource)); + for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { + var nextKey = keysArray[nextIndex]; + var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); + if (desc !== undefined && desc.enumerable) { + to[nextKey] = nextSource[nextKey]; + } + } + } + return to; + } + }); +} + + // // AJAX // @@ -87,7 +120,7 @@ if (!String.prototype.endsWith) { break; } - opts = extend({}, defaultOpts, opts); + opts = Object.assign({}, defaultOpts, opts); var xhr = createXMLHttpRequest(); xhr.open(method, url); @@ -244,11 +277,11 @@ function setCookie(name, value, days) { date.setTime(date.getTime() + (days*24*60*60*1000)); expires = "; expires=" + date.toUTCString(); } - document.cookie = name + "=" + (value || "") + expires + "; path=/"; + document.cookie = name + "=" + (value || "") + expires + "; domain=" + window.appConfig.cookieHost + "; path=/"; } function unsetCookie(name) { - document.cookie = name+'=; Max-Age=-99999999;'; + document.cookie = name + '=; Max-Age=-99999999; domain=' + window.appConfig.cookieHost + "; path=/"; } function getCookie(name) { @@ -312,6 +345,34 @@ function escape(str) { return pre.innerHTML; } +function parseUrl(uri) { + var parser = document.createElement('a'); + parser.href = uri; + + return { + protocol: parser.protocol, // => "http:" + host: parser.host, // => "example.com:3000" + hostname: parser.hostname, // => "example.com" + port: parser.port, // => "3000" + pathname: parser.pathname, // => "/pathname/" + hash: parser.hash, // => "#hash" + search: parser.search, // => "?search=test" + origin: parser.origin, // => "http://example.com:3000" + path: (parser.pathname || '') + (parser.search || '') + } +} + +function once(fn, context) { + var result; + return function() { + if (fn) { + result = fn.apply(context || this, arguments); + fn = null; + } + return result; + }; +} + // // @@ -390,3 +451,229 @@ window.__lang = {}; unsetCookie('is_retina'); } })(); + +var StaticManager = { + loadedStyles: [], + versions: {}, + + setStyles: function(list, versions) { + this.loadedStyles = list; + this.versions = versions; + }, + + loadStyle: function(name, theme, callback) { + var url; + if (!window.appConfig.devMode) { + var filename = name + (theme === 'dark' ? '_dark' : '') + '.css'; + url = '/css/'+filename+'?'+this.versions[filename]; + } else { + url = '/sass.php?name='+name+'&theme='+theme; + } + + var el = document.createElement('link'); + el.onerror = callback + el.onload = callback + el.setAttribute('rel', 'stylesheet'); + el.setAttribute('type', 'text/css'); + el.setAttribute('id', 'style_'+name+'_dark'); + el.setAttribute('href', url); + + document.getElementsByTagName('head')[0].appendChild(el); + } +}; + +var ThemeSwitcher = (function() { + /** + * @type {string[]} + */ + var modes = ['auto', 'dark', 'light']; + + /** + * @type {number} + */ + var currentModeIndex = -1; + + /** + * @type {boolean|null} + */ + var systemState = null; + + /** + * @returns {boolean} + */ + function isSystemModeSupported() { + try { + // crashes on: + // Mozilla/5.0 (Windows NT 6.2; ARM; Trident/7.0; Touch; rv:11.0; WPDesktop; Lumia 630 Dual SIM) like Gecko + // Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Mobile/15E148 Safari/604.1 + // Mozilla/5.0 (iPad; CPU OS 12_5_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Mobile/15E148 Safari/604.1 + // + // error examples: + // - window.matchMedia("(prefers-color-scheme: dark)").addEventListener is not a function. (In 'window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",this.onSystemSettingChange.bind(this))', 'window.matchMedia("(prefers-color-scheme: dark)").addEventListener' is undefined) + // - Object [object MediaQueryList] has no method 'addEventListener' + return !!window['matchMedia'] + && typeof window.matchMedia("(prefers-color-scheme: dark)").addEventListener === 'function'; + } catch (e) { + return false + } + } + + /** + * @returns {boolean} + */ + function isDarkModeApplied() { + var st = StaticManager.loadedStyles; + for (var i = 0; i < st.length; i++) { + var name = st[i]; + if (ge('style_'+name+'_dark')) + return true; + } + return false; + } + + /** + * @returns {string} + */ + function getSavedMode() { + var val = getCookie('theme'); + if (!val) + return modes[0]; + if (modes.indexOf(val) === -1) { + console.error('[ThemeSwitcher getSavedMode] invalid cookie value') + unsetCookie('theme') + return modes[0] + } + return val + } + + /** + * @param {boolean} dark + */ + function changeTheme(dark) { + addClass(document.body, 'theme-changing'); + + var onDone = function() { + window.requestAnimationFrame(function() { + removeClass(document.body, 'theme-changing'); + }) + }; + + window.requestAnimationFrame(function() { + if (dark) + enableDark(onDone); + else + disableDark(onDone); + }) + } + + /** + * @param {function} callback + */ + function enableDark(callback) { + var names = []; + StaticManager.loadedStyles.forEach(function(name) { + var el = ge('style_'+name+'_dark'); + if (el) + return; + names.push(name); + }); + + var left = names.length; + names.forEach(function(name) { + StaticManager.loadStyle(name, 'dark', once(function(e) { + left--; + if (left === 0) + callback(); + })); + }) + } + + /** + * @param {function} callback + */ + function disableDark(callback) { + StaticManager.loadedStyles.forEach(function(name) { + var el = ge('style_'+name+'_dark'); + if (el) + el.remove(); + }) + callback(); + } + + /** + * @param {string} mode + */ + function setLabel(mode) { + var labelEl = ge('theme-switcher-label'); + labelEl.innerHTML = escape(lang('theme_'+mode)); + } + + return { + init: function() { + var cur = getSavedMode(); + currentModeIndex = modes.indexOf(cur); + + var systemSupported = isSystemModeSupported(); + if (!systemSupported) { + if (currentModeIndex === 0) { + modes.shift(); // remove 'auto' from the list + currentModeIndex = 1; // set to 'light' + if (isDarkModeApplied()) + disableDark(); + } + } else { + /** + * @param {boolean} dark + */ + var onSystemChange = function(dark) { + var prevSystemState = systemState; + systemState = dark; + + if (modes[currentModeIndex] !== 'auto') + return; + + if (systemState !== prevSystemState) + changeTheme(systemState); + }; + + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) { + onSystemChange(e.matches === true) + }); + + onSystemChange(window.matchMedia('(prefers-color-scheme: dark)').matches === true); + } + + setLabel(modes[currentModeIndex]); + }, + + next: function(e) { + if (hasClass(document.body, 'theme-changing')) { + console.log('next: theme changing is in progress, ignoring...') + return; + } + + currentModeIndex = (currentModeIndex + 1) % modes.length; + switch (modes[currentModeIndex]) { + case 'auto': + if (systemState !== null) + changeTheme(systemState); + break; + + case 'light': + if (isDarkModeApplied()) + changeTheme(false); + break; + + case 'dark': + if (!isDarkModeApplied()) + changeTheme(true); + break; + } + + setLabel(modes[currentModeIndex]); + setCookie('theme', modes[currentModeIndex]); + + return cancelEvent(e); + } + }; +})(); \ No newline at end of file diff --git a/htdocs/sass.php b/htdocs/sass.php index eb24962..186b706 100644 --- a/htdocs/sass.php +++ b/htdocs/sass.php @@ -4,7 +4,14 @@ require __DIR__.'/../init.php'; global $config; $name = $_REQUEST['name'] ?? ''; -if (!$config['is_dev'] || !$name || !file_exists($path = ROOT.'/htdocs/scss/'.$name.'.scss')) { +$theme = $_REQUEST['theme'] ?? ''; + +if ($theme != 'light' && $theme != 'dark') { + http_response_code(403); + exit; +} + +if (!$config['is_dev'] || !$name || !file_exists($path = ROOT.'/htdocs/scss/entries/'.$name.'/'.$theme.'.scss')) { // logError(__FILE__.': access denied'); http_response_code(403); exit; diff --git a/htdocs/scss/app/blog.scss b/htdocs/scss/app/blog.scss new file mode 100644 index 0000000..53ec7e4 --- /dev/null +++ b/htdocs/scss/app/blog.scss @@ -0,0 +1,385 @@ +@import '../vars'; + +.blog-write-link-wrap { + margin-bottom: $base-padding; +} +.blog-write-table { + table-layout: fixed; + border-collapse: collapse; + border: 0; + width: 100%; + + > tbody > tr > td { + text-align: left; + vertical-align: top; + } + > tbody > tr > td:first-child { + padding-right: 8px; + width: 45%; + } + > tbody > tr > td:last-child { + padding-left: 8px; + } +} + +.blog-write-form { + .form-field-input { + width: 100%; + } + textarea.form-field-input { + height: 400px; + font-family: $ffMono; + font-size: 12px; + } + textarea.form-field-input.nowrap { + white-space: pre; + overflow-wrap: normal; + } +} +.blog-write-options-table { + width: 100%; + border-collapse: collapse; + table-layout: fixed; + + td { + padding-top: 12px; + } + td:nth-child(1) { + width: 70%; + } + td:nth-child(2) { + width: 30%; + padding-left: 10px; + } + tr:first-child td { + padding-top: 0px; + } + button[type="submit"] { + margin-left: 3px; + } +} + +.blog-write-form-toggle-link { + margin-top: 3px; + display: inline-block; +} + +.blog-upload-form { + padding-bottom: $base-padding; +} + +.blog-upload-list {} +.blog-upload-item { + border-top: 1px $border-color solid; + padding: 10px 0; +} +.blog-upload-item-actions { + float: right; +} +.blog-upload-item-name { + font-weight: bold; + margin-bottom: 2px; +} +.blog-upload-item-info { + color: $grey; + font-size: $fs - 2px; +} +.blog-upload-item-note { + padding: 0 0 4px; +} +.blog-upload-item-md { + margin-top: 3px; +} + +.blog-post-title { + margin: 0 0 16px; +} +.blog-post-title h1 { + font-size: 22px; + font-weight: bold; + padding: 0; + margin: 0; +} + +.blog-post-date { + color: $grey; + margin-top: 5px; + font-size: $fs - 1px; + > a { + margin-left: 5px; + } +} + +.blog-post-tags { + margin-top: 16px; + margin-bottom: -1px; +} +.blog-post-tags > a { + display: block; + float: left; + font-size: $fs - 1px; + margin-right: 8px; + cursor: pointer; +} +.blog-post-tags > a:last-child { + margin-right: 0; +} +.blog-post-tags > a > span { + opacity: 0.5; +} + +.blog-post-text {} +.blog-post-text { + li { + margin: 13px 0; + } + + p { + margin-top: 13px; + margin-bottom: 13px; + } + p:first-child { + margin-top: 0; + } + p:last-child { + margin-bottom: 0; + } + + pre { + background-color: $code-block-bg; + font-family: $ffMono; + //font-size: $fsMono; + overflow: auto; + @include radius(3px); + } + + code { + background-color: $inline-code-block-bg; + font-family: $ffMono; + font-size: $fsMono; + padding: 3px 5px; + @include radius(3px); + } + + pre code { + display: block; + padding: 12px; + line-height: 145%; + background-color: $code-block-bg; + + span.term-prompt { + color: $light-grey; + @include user-select(none); + } + } + + blockquote { + border-left: 3px #e0e0e0 solid; + margin-left: 0; + padding: 5px 0 5px 12px; + color: $grey; + } + + table.table-100 { + border-collapse: collapse; + border: 0; + margin: 0; + width: 100%; + table-layout: fixed; + } + table.table-100 td { + padding: 0; + border: 0; + vertical-align: top; + text-align: left; + padding: 0 4px; + } + table.table-100 td:first-child { + padding-left: 0; + } + table.table-100 td:last-child { + padding-right: 0; + } + td > pre:first-child { + margin-top: 0; + } + td > pre:last-child { + margin-bottom: 0; + } + + h1 { + margin: 40px 0 16px; + font-weight: 600; + font-size: 30px; + border-bottom: 1px $border-color solid; + padding-bottom: 8px; + } + + h2 { + margin: 35px 0 16px; + font-weight: 500; + font-size: 25px; + border-bottom: 1px $border-color solid; + padding-bottom: 6px; + } + + h3 { + margin: 27px 0 16px; + font-size: 24px; + font-weight: 500; + } + + h4 { + font-size: 18px; + margin: 24px 0 16px; + } + + h5 { + font-size: 15px; + margin: 24px 0 16px; + } + + h6 { + font-size: 13px; + margin: 24px 0 16px; + color: #666; + } + + h3:first-child, + h4:first-child, + h5:first-child, + h6:first-child { + margin-top: 0; + } + h1:first-child, + h2:first-child { + margin-top: 5px; + } + + hr { + height: 1px; + border: 0; + background: $border-color; + margin: 17px 0; + } +} +.blog-post-comments { + margin-top: $base-padding; + padding: 12px 15px; + border: 1px $border-color solid; + @include radius(3px); +} +.blog-post-comments img { + vertical-align: middle; + position: relative; + top: -1px; + margin-left: 2px; +} + +$blog-tags-width: 175px; + +.index-blog-block { + margin-top: 23px; +} + +.blog-list {} +.blog-list.withtags { + margin-right: $blog-tags-width + $base-padding*2; +} +.blog-list-title { + font-size: 22px; + margin-bottom: 15px; + > span { + margin-left: 2px; + > a { + font-size: 16px; + margin-left: 2px; + } + } +} +.blog-list-table-wrap { + padding: 5px 0; +} +.blog-list-table { + border-collapse: collapse; +} +.blog-list-table td { + vertical-align: top; + padding: 0 0 13px; +} +.blog-list-table tr:last-child td { + padding-bottom: 0; +} +td.blog-item-date-cell { + width: 1px; + white-space: nowrap; + text-align: right; + padding-right: 10px; +} +.blog-item-date { + color: $grey; + //text-transform: lowercase; +} +td.blog-item-title-cell { + text-align: left; +} +.blog-item-title { + //font-weight: bold; +} +.blog-item-row { + font-size: $fs; + line-height: 140%; +} +.blog-item-row.ishidden a.blog-item-title { + color: $fg; +} +.blog-item-row-year { + td { + padding-top: 10px; + text-align: right; + font-size: 20px; + letter-spacing: -0.5px; + } + &:first-child td { + padding-top: 0; + } +} + +/* +a.blog-item-view-all-link { + display: inline-block; + padding: 4px 17px; + @include radius(5px); + background-color: #f4f4f4; + color: #555; + margin-top: 2px; +} +a.blog-item-view-all-link:hover { + text-decoration: none; + background-color: #ededed; +} +*/ + +.blog-tags { + float: right; + width: $blog-tags-width; + padding-left: $base-padding - 10px; + border-left: 1px $border-color solid; +} +.blog-tags-title { + margin-bottom: 15px; + font-size: 22px; + padding: 0 7px; +} +.blog-tag-item { + padding: 6px 10px; + font-size: $fs - 1px; +} +.blog-tag-item > a { + color: $fg; +} +.blog-tag-item-count { + color: #aaa; + margin-left: 6px; + text-decoration: none !important; +} diff --git a/htdocs/scss/app/common.scss b/htdocs/scss/app/common.scss new file mode 100644 index 0000000..b9c5959 --- /dev/null +++ b/htdocs/scss/app/common.scss @@ -0,0 +1,289 @@ +@import "../vars"; + +.clearfix:after { + content: "."; + display: block; + clear: both; + visibility: hidden; + line-height: 0; + height: 0; +} + +html, body { + padding: 0; + margin: 0; + border: 0; + background-color: $bg; + color: $fg; + height: 100%; + min-height: 100%; +} +body { + font-family: $ff; + font-size: $fs; +} + +.base-width { + max-width: $base-width; + margin: 0 auto; + position: relative; +} + +body.full-width .base-width { + max-width: 100%; + margin-left: auto; + margin-right: auto; +} + +input[type="text"], +input[type="password"], +textarea { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + box-sizing: border-box; + border: 1px $input-border solid; + border-radius: 0; + background-color: $input-bg; + color: $fg; + font-family: $ff; + font-size: $fs; + padding: 6px; + outline: none; + @include radius(3px); + + &:focus { + border-color: $input-border-focused; + } +} + +textarea { + resize: vertical; +} + +//input[type="checkbox"] { +// margin-left: 0; +//} + +//button { +// border-radius: 2px; +// background-color: $light-bg; +// color: $fg; +// padding: 7px 12px; +// border: none; +// /*box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);*/ +// font-family: $ff; +// font-size: $fs - 1px; +// outline: none; +// cursor: pointer; +// position: relative; +//} +//button:hover { +// box-shadow: 0 1px 9px rgba(0, 0, 0, 0.2); +//} +//button:active { +// top: 1px; +//} + +a { + text-decoration: none; + color: $link-color; + outline: none; +} +a:hover { + text-decoration: underline; +} + +p, p code { + line-height: 150%; +} + +.unicode { font-family: sans-serif; } + +.ff_ms { font-family: $ffMono } +.fl_r { float: right } +.fl_l { float: left } +.pos_rel { position: relative } +.pos_abs { position: absolute } +.pos_fxd { position: fixed } + +.page-content { + padding: 0 $side-padding; +} +.page-content-inner { + padding: $base-padding 0; +} + + +table.contacts { + border: 0; + border-collapse: collapse; + margin: 8px auto 0; + //width: 100%; + //table-layout: fixed; +} +table.contacts td { + white-space: nowrap; + padding-bottom: 15px; + vertical-align: top; +} +table.contacts td.label { + text-align: right; + width: 30%; + color: $dark-grey; +} +table.contacts td.value { + text-align: left; + padding-left: 8px; +} +table.contacts td.value span { + background: $inline-code-block-bg; + padding: 3px 7px 4px; + border-radius: 3px; + color: $fg; + font-family: $ffMono; + font-size: $fs - 1px; +} +table.contacts td b { + font-weight: 600; +} +table.contacts td pre { + padding: 0; + margin: 0; + font-size: 12px; +} + +table.contacts div.note { + font-size: $fs - 3px; + padding-top: 2px; + color: $dark-grey; + > a { + color: $dark-grey; + border-bottom: 1px $border-color solid; + &:hover { + text-decoration: none; + color: $link-color; + border-bottom-color: $link-color; + } + } +} + +.pt { + margin: 5px 0 20px; + color: $dark-fg; + padding-bottom: 7px; + border-bottom: 2px rgba(255, 255, 255, 0.12) solid; +} +.pt h3 { + margin: 0; + display: inline-block; + font-weight: bold; + font-size: $fs; + color: $fg; +} +.pt h3:not(:first-child) { + margin-left: 5px; +} +.pt a { + margin-right: 5px; +} +.pt a:not(:first-child) { + margin-left: 5px; +} +.pt a, .pt h3 { + position: relative; + top: 1px; +} +.pt_r { margin-top: 5px } + +.empty { + text-align: center; + padding: 40px 20px; + color: $dark-fg; + @include radius(3px); + background-color: #f7f7f7; +} + +.contact-img { + display: inline-block; + width: 77px; + height: 12px; + background: transparent url(/img/contact.gif?1) no-repeat; + background-size: 77px 12px; +} +@media (-o-min-device-pixel-ratio:3/2), (-webkit-min-device-pixel-ratio:1.5), (min--moz-device-pixel-ratio:1.5), (min-resolution:1.5dppx) { +.contact-img { + background-image: url(/img/contact@2x.gif?1); +} +} + +.md-file-attach { + padding: 3px 0; +} +.md-file-attach-icon { + width: 14px; + height: 14px; + background: transparent url(/img/attachment.svg) no-repeat center center; + background-size: 14px 14px; + display: inline-block; + margin-right: 5px; + position: relative; + top: 1px; +} +.md-file-attach > a { + //font-weight: bold; +} +.md-file-attach-size { + color: $grey; + margin-left: 2px; +} +.md-file-attach-note { + color: $fg; + margin-left: 2px; +} + +.md-image { + padding: 3px 0; + line-height: 0; + max-width: 100%; +} +.md-images { + margin-bottom: -8px; + padding: 3px 0; + max-width: 100%; +} +.md-images .md-image { + padding-top: 0; + padding-bottom: 0; +} +.md-images > span { + display: block; + float: left; + margin: 0 8px 8px 0; + max-width: 100%; +} +.md-image.align-center { text-align: center; } +.md-image.align-left { text-align: left; } +.md-image.align-right { text-align: right; } +.md-image-wrap { + display: inline-block; + max-width: 100%; + overflow: hidden; +} +.md-image-wrap > a { + display: block; + max-width: 100%; +} +.md-image-note { + line-height: 150%; + color: $dark-grey; + padding: 2px 0 4px; +} + +.md-video video { + max-width: 100%; +} + +.language-ascii { + line-height: 125% !important; +} diff --git a/htdocs/scss/app/form.scss b/htdocs/scss/app/form.scss new file mode 100644 index 0000000..197732c --- /dev/null +++ b/htdocs/scss/app/form.scss @@ -0,0 +1,59 @@ +@import '../vars'; + +$form-field-label-width: 120px; + +form { display: block; margin: 0; } + +.form-layout-h { + .form-field-wrap { + padding: 8px 0; + } + .form-field-label { + float: left; + width: $form-field-label-width; + text-align: right; + padding: 7px 0 0; + } + .form-field { + margin-left: $form-field-label-width + 10px; + } +} + +.form-layout-v { + .form-field-wrap { + padding: 6px 0; + } + .form-field-wrap:first-child { + padding-top: 0; + } + .form-field-wrap:last-child { + padding-bottom: 0; + } + .form-field-label { + padding: 0 0 4px 4px; + font-weight: bold; + font-size: 12px; + letter-spacing: 0.5px; + text-transform: uppercase; + color: $grey; + } + .form-field { + //margin-left: $form-field-label-width + 10px; + } +} + +.form-error { + padding: 10px 13px; + margin-bottom: $base-padding; + background-color: $error-block-bg; + color: $error-block-fg; + @include radius(3px); +} + +.form-success { + padding: 10px 13px; + margin-bottom: $base-padding; + background-color: $success-block-bg; + color: $success-block-fg; + @include radius(3px); +} diff --git a/htdocs/scss/app/head.scss b/htdocs/scss/app/head.scss new file mode 100644 index 0000000..fed30d1 --- /dev/null +++ b/htdocs/scss/app/head.scss @@ -0,0 +1,157 @@ +.head { + padding: 0 $side-padding; +} +.head-inner { + //padding: 13px 0; + position: relative; + border-bottom: 2px $border-color solid; +} +.head-logo { + padding: 4px 0; + font-family: $ffMono; + font-size: 15px; + display: inline-block; + position: absolute; + left: 0; + background-color: transparent; +} +body:not(.theme-changing) .head-logo { + @include transition(background-color, 0.03s); +} +.head-logo { + padding: 16px 0; + background-color: $bg; +} +.head-logo:after { + content: ''; + display: block; + width: 40px; + position: absolute; + right: -40px; + top: 0; + bottom: 0; + border-left: 8px $bg solid; + box-sizing: border-box; + background: linear-gradient(to left, rgba($bg, 0) 0%, rgba($bg, 1) 100%); /* W3C */ +} +.head-logo > a { + color: $fg; + font-size: 14px; +} +.head-logo > a:hover { + text-decoration: none; +} +.head-logo-enter { + background: $code-block-bg; + color: $hljs_fg; + display: inline; + opacity: 0; + font-size: 11px; + position: relative; + padding: 2px 5px; + font-weight: normal; + vertical-align: middle; + top: -1px; +} +body:not(.theme-changing) .head-logo-enter { + @include transition(opacity, 0.03s); +} +.head-logo-enter-icon { + width: 12px; + height: 7px; + display: inline-block; + margin-right: 5px; + position: relative; + top: 1px; + + > svg { + path { + fill: $hljs_fg; + } + } +} +.head-logo > a:hover .head-logo-enter { + opacity: 1; +} +.head-logo-path { + color: $fg; + font-weight: bold; + -webkit-font-smoothing: antialiased; +} +body:not(.theme-changing) .head-logo-path { + @include transition(color, 0.03s); +} +.head-logo > a:hover .head-logo-path:not(.alwayshover) { + color: $light_grey; +} +.head-logo-path:not(.neverhover):hover { + color: $fg !important; +} +.head-logo-dolsign { + color: $head_green_color; + font-weight: normal; + &.is_root { + color: $head_red_color; + } +} +.head-logo-cd { + display: none; +} +.head-logo > a:hover .head-logo-cd { + display: inline; +} +.head-logo-path-mapped { + padding: 3px 5px; + background: #f1f1f1; + pointer-events: none; + @include radius(3px); + margin: 0 2px; +} + +.head-items { + float: right; + color: #777; // color of separators + //padding: 8px 0; +} +a.head-item { + color: $fg; + font-size: $fs - 1px; + display: block; + float: left; + padding: 16px 0; + + > span { + position: relative; + padding: 0 12px; + border-right: 1px $head-items-separator solid; + + > span { + padding: 2px 0; + + > span.moon-icon { + position: absolute; + left: 0; + + > svg path { + fill: $fg; + } + } + } + } + + &.is-theme-switcher > span { + padding-left: 20px; + } + + &:last-child > span { + border-right: 0; + padding-right: 1px; + } +} +a.head-item:hover { + //color: $link-color; + text-decoration: none; +} +a.head-item:hover > span > span { + border-bottom: 1px $head-items-separator solid; +} \ No newline at end of file diff --git a/htdocs/scss/app/hljs.scss b/htdocs/scss/app/hljs.scss new file mode 100644 index 0000000..913c45e --- /dev/null +++ b/htdocs/scss/app/hljs.scss @@ -0,0 +1 @@ +@import "../hljs/github.css"; diff --git a/htdocs/scss/app/mobile.scss b/htdocs/scss/app/mobile.scss new file mode 100644 index 0000000..a765e25 --- /dev/null +++ b/htdocs/scss/app/mobile.scss @@ -0,0 +1,41 @@ +@import '../vars'; + +textarea { + -webkit-overflow-scrolling: touch; +} + +// header +.head-logo { + position: static; + display: block; + //padding-bottom: 6px; + // not very good fix: + overflow: hidden; + white-space: nowrap; + padding-bottom: 0; +} +.head-logo::after { + display: none; +} +.head-items { + float: none; +} +a.head-item:hover, +a.head-item:active { + -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; +} +a.head-item:last-child > span { + border-right: 0; + padding-right: 12px; +} +a.head-item:first-child > span { + padding-left: 1px; +} + +// blog +.blog-tags { + display: none; +} +.blog-list.withtags { + margin-right: 0; +} diff --git a/htdocs/scss/app/pages.scss b/htdocs/scss/app/pages.scss new file mode 100644 index 0000000..873a6ae --- /dev/null +++ b/htdocs/scss/app/pages.scss @@ -0,0 +1,14 @@ +.page { + +} +.page-edit-links { + display: none; + float: right; + font-size: 15px; + > a { + margin-left: 5px; + } +} +.page-content-inner:hover .page-edit-links { + display: block; +} diff --git a/htdocs/scss/blog.scss b/htdocs/scss/blog.scss deleted file mode 100644 index 7641683..0000000 --- a/htdocs/scss/blog.scss +++ /dev/null @@ -1,383 +0,0 @@ -@import 'vars'; - -.blog-write-link-wrap { - margin-bottom: $base-padding; -} -.blog-write-table { - table-layout: fixed; - border-collapse: collapse; - border: 0; - width: 100%; - - > tbody > tr > td { - text-align: left; - vertical-align: top; - } - > tbody > tr > td:first-child { - padding-right: 8px; - width: 45%; - } - > tbody > tr > td:last-child { - padding-left: 8px; - } -} - -.blog-write-form { - .form-field-input { - width: 100%; - } - textarea.form-field-input { - height: 400px; - font-family: $ffMono; - font-size: 12px; - } - textarea.form-field-input.nowrap { - white-space: pre; - overflow-wrap: normal; - } -} -.blog-write-options-table { - width: 100%; - border-collapse: collapse; - table-layout: fixed; - - td { - padding-top: 12px; - } - td:nth-child(1) { - width: 70%; - } - td:nth-child(2) { - width: 30%; - padding-left: 10px; - } - tr:first-child td { - padding-top: 0px; - } - button[type="submit"] { - margin-left: 3px; - } -} - -.blog-write-form-toggle-link { - margin-top: 3px; - display: inline-block; -} - -.blog-upload-form { - padding-bottom: $base-padding; -} - -.blog-upload-list {} -.blog-upload-item { - border-top: 1px $border-color solid; - padding: 10px 0; -} -.blog-upload-item-actions { - float: right; -} -.blog-upload-item-name { - font-weight: bold; - margin-bottom: 2px; -} -.blog-upload-item-info { - color: #888; - font-size: $fs - 2px; -} -.blog-upload-item-note { - padding: 0 0 4px; -} -.blog-upload-item-md { - margin-top: 3px; -} - -.blog-post-title { - margin: 0 0 16px; -} -.blog-post-title h1 { - font-size: 22px; - font-weight: bold; - padding: 0; - margin: 0; -} - -.blog-post-date { - color: #888; - margin-top: 5px; - font-size: $fs - 1px; - > a { - margin-left: 5px; - } -} - -.blog-post-tags { - margin-top: 16px; - margin-bottom: -1px; -} -.blog-post-tags > a { - display: block; - float: left; - font-size: $fs - 1px; - margin-right: 8px; - cursor: pointer; -} -.blog-post-tags > a:last-child { - margin-right: 0; -} -.blog-post-tags > a > span { - opacity: 0.5; -} - -.blog-post-text {} -.blog-post-text { - li { - margin: 13px 0; - } - - p { - margin-top: 13px; - margin-bottom: 13px; - } - p:first-child { - margin-top: 0; - } - p:last-child { - margin-bottom: 0; - } - - pre { - background-color: $code-block-bg; - font-family: $ffMono; - //font-size: $fsMono; - overflow: auto; - @include radius(3px); - } - - code { - background-color: $inline-code-block-bg; - font-family: $ffMono; - font-size: $fsMono; - padding: 3px 5px; - @include radius(3px); - } - - pre code { - display: block; - padding: 12px; - line-height: 145%; - background-color: $code-block-bg; - - span.term-prompt { - color: #999; - @include user-select(none); - } - } - - blockquote { - border-left: 3px #e0e0e0 solid; - margin-left: 0; - padding: 5px 0 5px 12px; - color: #888; - } - - table.table-100 { - border-collapse: collapse; - border: 0; - margin: 0; - width: 100%; - table-layout: fixed; - } - table.table-100 td { - padding: 0; - border: 0; - vertical-align: top; - text-align: left; - padding: 0 4px; - } - table.table-100 td:first-child { - padding-left: 0; - } - table.table-100 td:last-child { - padding-right: 0; - } - td > pre:first-child { - margin-top: 0; - } - td > pre:last-child { - margin-bottom: 0; - } - - h1 { - margin: 40px 0 16px; - font-weight: 600; - font-size: 30px; - border-bottom: 1px $border-color solid; - padding-bottom: 8px; - } - - h2 { - margin: 35px 0 16px; - font-weight: 500; - font-size: 25px; - border-bottom: 1px $border-color solid; - padding-bottom: 6px; - } - - h3 { - margin: 27px 0 16px; - font-size: 24px; - font-weight: 500; - } - - h4 { - font-size: 18px; - margin: 24px 0 16px; - } - - h5 { - font-size: 15px; - margin: 24px 0 16px; - } - - h6 { - font-size: 13px; - margin: 24px 0 16px; - color: #666; - } - - h3:first-child, - h4:first-child, - h5:first-child, - h6:first-child { - margin-top: 0; - } - h1:first-child, - h2:first-child { - margin-top: 5px; - } - - hr { - height: 1px; - border: 0; - background: $border-color; - margin: 17px 0; - } -} -.blog-post-comments { - margin-top: $base-padding; - padding: 12px 15px; - border: 1px #e0e0e0 solid; - @include radius(3px); -} -.blog-post-comments img { - vertical-align: middle; - position: relative; - top: -1px; - margin-left: 2px; -} - -$blog-tags-width: 175px; - -.index-blog-block { - margin-top: 23px; -} - -.blog-list {} -.blog-list.withtags { - margin-right: $blog-tags-width + $base-padding*2; -} -.blog-list-title { - font-size: 22px; - margin-bottom: 15px; - > span { - margin-left: 2px; - > a { - font-size: 16px; - margin-left: 2px; - } - } -} -.blog-list-table-wrap { - padding: 5px 0; -} -.blog-list-table { - border-collapse: collapse; -} -.blog-list-table td { - vertical-align: top; - padding: 0 0 13px; -} -.blog-list-table tr:last-child td { - padding-bottom: 0; -} -td.blog-item-date-cell { - width: 1px; - white-space: nowrap; - text-align: right; - padding-right: 10px; -} -.blog-item-date { - color: #777; - //text-transform: lowercase; -} -td.blog-item-title-cell { - text-align: left; -} -.blog-item-title { - //font-weight: bold; -} -.blog-item-row { - font-size: $fs; - line-height: 140%; -} -.blog-item-row.ishidden a.blog-item-title { - color: $fg; -} -.blog-item-row-year { - td { - padding-top: 10px; - text-align: right; - font-size: 20px; - letter-spacing: -0.5px; - } - &:first-child td { - padding-top: 0; - } -} -a.blog-item-view-all-link { - display: inline-block; - padding: 4px 17px; - @include radius(5px); - background-color: #f4f4f4; - color: #555; - margin-top: 2px; -} -a.blog-item-view-all-link:hover { - text-decoration: none; - background-color: #ededed; -} - - -.blog-tags { - float: right; - width: $blog-tags-width; - padding-left: $base-padding - 10px; - border-left: 1px $border-color solid; -} -.blog-tags-title { - margin-bottom: 15px; - font-size: 22px; - padding: 0 7px; -} -.blog-tag-item { - padding: 6px 10px; - font-size: $fs - 1px; -} -.blog-tag-item > a { - color: #222; -} -.blog-tag-item-count { - color: #aaa; - margin-left: 6px; - text-decoration: none !important; -} diff --git a/htdocs/scss/bundle_admin.scss b/htdocs/scss/bundle_admin.scss new file mode 100644 index 0000000..06808d0 --- /dev/null +++ b/htdocs/scss/bundle_admin.scss @@ -0,0 +1,3 @@ +.admin-page { + line-height: 155%; +} diff --git a/htdocs/scss/bundle_common.scss b/htdocs/scss/bundle_common.scss new file mode 100644 index 0000000..dd0bd6a --- /dev/null +++ b/htdocs/scss/bundle_common.scss @@ -0,0 +1,10 @@ +@import "./app/common"; +@import "./app/head"; +@import "./app/blog"; +@import "./app/form"; +@import "./app/pages"; +@import "./hljs/github.scss"; + +@media screen and (max-width: 600px) { + @import "./app/mobile"; +} diff --git a/htdocs/scss/colors/dark.scss b/htdocs/scss/colors/dark.scss new file mode 100644 index 0000000..c7c25cf --- /dev/null +++ b/htdocs/scss/colors/dark.scss @@ -0,0 +1,46 @@ +$head_green_color: #0bad19; +$head_red_color: #e23636; +$link-color: #71abe5; + +$grey: #798086; +$dark-grey: $grey; +$light-grey: $grey; +$fg: #eee; +$bg: #222; + +$code-block-bg: #394146; +$inline-code-block-bg: #394146; + +$light-bg: #464c4e; +$dark-bg: #272C2D; +$dark-fg: #999; + +$input-border: #48535a; +$input-border-focused: #48535a; +$input-bg: #30373b; +$border-color: #48535a; + +$error-block-bg: #f9eeee; +$error-block-fg: #d13d3d; + +$success-block-bg: #eff5f0; +$success-block-fg: #2a6f34; + +$head-items-separator: #5e6264; + +// colors from https://github.com/Kelbster/highlightjs-material-dark-theme/blob/master/css/materialdark.css +$hljs_fg: #CDD3D8; +$hljs_bg: #2B2B2D; +$hljs_quote: #6272a4; +$hljs_string: #f1fa8c; +$hljs_literal: #bd93f9; +$hljs_title: #75A5FF; +$hljs_keyword: #C792EA; +$hljs_type: #da4939; +$hljs_tag: #abb2bf; +$hljs_regexp: #F77669; +$hljs_symbol: #C792EA; +$hljs_builtin: #C792EA; +$hljs_meta: #75A5FF; +$hljs_deletion: #e6e1dc; +$hljs_addition: #144212; \ No newline at end of file diff --git a/htdocs/scss/colors/light.scss b/htdocs/scss/colors/light.scss new file mode 100644 index 0000000..e6c14ba --- /dev/null +++ b/htdocs/scss/colors/light.scss @@ -0,0 +1,46 @@ +$head_green_color: #0bad19; +$head_red_color: #ce1a1a; +$link-color: #116fd4; + +$grey: #888; +$dark-grey: #777; +$light-grey: #999; +$fg: #222; +$bg: #fff; + +$code-block-bg: #f3f3f3; +$inline-code-block-bg: #f1f1f1; + +$light-bg: #464c4e; +$dark-bg: #272C2D; +$dark-fg: #999; + +$input-border: #e0e0e0; +$input-border-focused: #e0e0e0; +$input-bg: #f7f7f7; +$border-color: #e0e0e0; + +$error-block-bg: #f9eeee; +$error-block-fg: #d13d3d; + +$success-block-bg: #eff5f0; +$success-block-fg: #2a6f34; + +$head-items-separator: #d0d0d0; + +// github.com style (c) Vasily Polovnyov +$hljs_fg: #333; +$hljs_bg: #f8f8f8; +$hljs_quote: #998; +$hljs_string: #d14; +$hljs_literal: #008080; +$hljs_title: #900; +$hljs_keyword: $hljs_fg; +$hljs_type: #458; +$hljs_tag: #000080; +$hljs_regexp: #009926; +$hljs_symbol: #990073; +$hljs_builtin: #0086b3; +$hljs_meta: #999; +$hljs_deletion: #fdd; +$hljs_addition: #dfd; \ No newline at end of file diff --git a/htdocs/scss/common-bundle.scss b/htdocs/scss/common-bundle.scss deleted file mode 100644 index 397f0c3..0000000 --- a/htdocs/scss/common-bundle.scss +++ /dev/null @@ -1,9 +0,0 @@ -@import "./common.scss"; -@import "./blog.scss"; -@import "./form.scss"; -@import "./hljs/github.scss"; -@import "./pages.scss"; - -@media screen and (max-width: 600px) { - @import "./mobile.scss"; -} diff --git a/htdocs/scss/common.scss b/htdocs/scss/common.scss deleted file mode 100644 index 644a080..0000000 --- a/htdocs/scss/common.scss +++ /dev/null @@ -1,415 +0,0 @@ -@import "vars"; - -.clearfix:after { - content: "."; - display: block; - clear: both; - visibility: hidden; - line-height: 0; - height: 0; -} - -html, body { - padding: 0; - margin: 0; - border: 0; - //background-color: $bg; - color: $fg; - height: 100%; - min-height: 100%; -} -body { - font-family: $ff; - font-size: $fs; -} - -.base-width { - max-width: $base-width; - margin: 0 auto; - position: relative; -} - -body.full-width .base-width { - max-width: 100%; - margin-left: auto; - margin-right: auto; -} - -input[type="text"], textarea { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - box-sizing: border-box; - border: 1px $input-border solid; - border-radius: 0px; - background-color: $input-bg; - color: $fg; - font-family: $ff; - font-size: $fs; - padding: 6px; - outline: none; - @include radius(3px); -} -textarea { - resize: vertical; -} -input[type="text"]:focus, -textarea:focus { - border-color: $input-border-focused; -} -//input[type="checkbox"] { -// margin-left: 0; -//} - -//button { -// border-radius: 2px; -// background-color: $light-bg; -// color: $fg; -// padding: 7px 12px; -// border: none; -// /*box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);*/ -// font-family: $ff; -// font-size: $fs - 1px; -// outline: none; -// cursor: pointer; -// position: relative; -//} -//button:hover { -// box-shadow: 0 1px 9px rgba(0, 0, 0, 0.2); -//} -//button:active { -// top: 1px; -//} - -a { - text-decoration: none; - color: $link-color; - outline: none; -} -a:hover { - text-decoration: underline; -} - -p, p code { - line-height: 150%; -} - -.unicode { font-family: sans-serif; } - -.ff_ms { font-family: $ffMono } -.fl_r { float: right } -.fl_l { float: left } -.pos_rel { position: relative } -.pos_abs { position: absolute } -.pos_fxd { position: fixed } - -.page-content { - padding: 0 $side-padding; -} -.page-content-inner { - padding: $base-padding 0; -} - -.head { - padding: 0 $side-padding; -} -.head-inner { - //padding: 13px 0; - position: relative; - border-bottom: 2px $border-color solid; -} -.head-logo { - padding: 4px 0; - font-family: $ffMono; - font-size: 15px; - display: inline-block; - position: absolute; - left: 0; - background-color: transparent; - @include transition(background-color, 0.03s); -} -.head-logo { - padding: 16px 0; - background-color: #fff; -} -.head-logo:after { - content: ''; - display: block; - width: 40px; - position: absolute; - right: -40px; - top: 0; - bottom: 0; - border-left: 8px #fff solid; - box-sizing: border-box; - background: linear-gradient(to left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); /* W3C */ -} -.head-logo > a { - color: $fg; - font-size: 14px; -} -.head-logo > a:hover { - text-decoration: none; -} -.head-logo-enter { - display: inline; - opacity: 0; - font-size: 11px; - position: relative; - background: #eee; - padding: 2px 5px; - color: #333; - font-weight: normal; - vertical-align: middle; - top: -1px; - @include transition(opacity, 0.03s); -} -.head-logo-enter-icon { - width: 12px; - height: 7px; - display: inline-block; - background: url(/img/enter.svg) 0 0 no-repeat; - background-size: 12px 7px; - margin-right: 5px; -} -.head-logo > a:hover .head-logo-enter { - opacity: 1; -} -.head-logo-path { - color: $fg; - font-weight: bold; - -webkit-font-smoothing: antialiased; - @include transition(color, 0.03s); -} -.head-logo > a:hover .head-logo-path:not(.alwayshover) { - color: #aaa; -} -.head-logo-path:not(.neverhover):hover { - color: #000 !important; -} -.head-logo-dolsign { - color: $head-green-color; - font-weight: normal; - &.is_root { - color: $head-red-color; - } -} -.head-logo-cd { - display: none; -} -.head-logo > a:hover .head-logo-cd { - display: inline; -} -.head-logo-path-mapped { - padding: 3px 5px; - background: #f1f1f1; - pointer-events: none; - @include radius(3px); - margin: 0 2px; -} - -.head-items { - float: right; - color: #777; // color of separators - //padding: 8px 0; -} -a.head-item { - color: $fg; - font-size: $fs - 1px; - display: block; - float: left; - padding: 16px 0; -} -a.head-item > span { - padding: 0 12px; - border-right: 1px #d0d0d0 solid; -} -a.head-item > span > span { - padding: 2px 0; -} -a.head-item:last-child > span { - border-right: 0; - padding-right: 1px; -} -/*a.head-item:first-child > span { - padding-left: 2px; -}*/ -a.head-item:hover { - //color: $link-color; - text-decoration: none; -} -a.head-item:hover > span > span { - border-bottom: 1px #d0d0d0 solid; -} - -table.contacts { - border: 0; - border-collapse: collapse; - margin: 8px auto 0; - //width: 100%; - //table-layout: fixed; -} -table.contacts td { - white-space: nowrap; - padding-bottom: 15px; - vertical-align: top; -} -table.contacts td.label { - text-align: right; - width: 30%; - color: #777; -} -table.contacts td.value { - text-align: left; - padding-left: 8px; -} -table.contacts td.value span { - background: #eee; - padding: 3px 7px 4px; - border-radius: 3px; - color: #333; - font-family: $ffMono; - font-size: $fs - 1px; -} -table.contacts td b { - font-weight: 600; -} -table.contacts td pre { - padding: 0; - margin: 0; - font-size: 12px; -} - -table.contacts div.note { - font-size: $fs - 3px; - padding-top: 2px; - color: #777; - > a { - color: #777; - border-bottom: 1px #ccc solid; - &:hover { - text-decoration: none; - border-bottom-color: #999; - } - } -} - -.pt { - margin: 5px 0 20px; - color: $dark-fg; - padding-bottom: 7px; - border-bottom: 2px rgba(255, 255, 255, 0.12) solid; -} -.pt h3 { - margin: 0; - display: inline-block; - font-weight: bold; - font-size: $fs; - color: $fg; -} -.pt h3:not(:first-child) { - margin-left: 5px; -} -.pt a { - margin-right: 5px; -} -.pt a:not(:first-child) { - margin-left: 5px; -} -.pt a, .pt h3 { - position: relative; - top: 1px; -} -.pt_r { margin-top: 5px } - -.empty { - text-align: center; - padding: 40px 20px; - color: $dark-fg; - @include radius(3px); - background-color: #f7f7f7; -} - -.contact-img { - display: inline-block; - width: 77px; - height: 12px; - background: transparent url(/img/contact.gif?1) no-repeat; - background-size: 77px 12px; -} -@media (-o-min-device-pixel-ratio:3/2), (-webkit-min-device-pixel-ratio:1.5), (min--moz-device-pixel-ratio:1.5), (min-resolution:1.5dppx) { -.contact-img { - background-image: url(/img/contact@2x.gif?1); -} -} - -.md-file-attach { - padding: 3px 0; -} -.md-file-attach-icon { - width: 14px; - height: 14px; - background: transparent url(/img/attachment.svg) no-repeat center center; - background-size: 14px 14px; - display: inline-block; - margin-right: 5px; - position: relative; - top: 1px; -} -.md-file-attach > a { - //font-weight: bold; -} -.md-file-attach-size { - color: #888; - margin-left: 2px; -} -.md-file-attach-note { - color: #000; - margin-left: 2px; -} - -.md-image { - padding: 3px 0; - line-height: 0; - max-width: 100%; -} -.md-images { - margin-bottom: -8px; - padding: 3px 0; - max-width: 100%; -} -.md-images .md-image { - padding-top: 0; - padding-bottom: 0; -} -.md-images > span { - display: block; - float: left; - margin: 0 8px 8px 0; - max-width: 100%; -} -.md-image.align-center { text-align: center; } -.md-image.align-left { text-align: left; } -.md-image.align-right { text-align: right; } -.md-image-wrap { - display: inline-block; - max-width: 100%; - overflow: hidden; -} -.md-image-wrap > a { - display: block; - max-width: 100%; -} -.md-image-note { - line-height: 150%; - color: #777; - padding: 2px 0 4px; -} - -.md-video video { - max-width: 100%; -} - -.language-ascii { - line-height: 125% !important; -} diff --git a/htdocs/scss/entries/admin/dark.scss b/htdocs/scss/entries/admin/dark.scss new file mode 100644 index 0000000..d704436 --- /dev/null +++ b/htdocs/scss/entries/admin/dark.scss @@ -0,0 +1,2 @@ +@import '../../colors/dark'; +@import '../../bundle_admin'; \ No newline at end of file diff --git a/htdocs/scss/entries/admin/light.scss b/htdocs/scss/entries/admin/light.scss new file mode 100644 index 0000000..4ac8bf0 --- /dev/null +++ b/htdocs/scss/entries/admin/light.scss @@ -0,0 +1,2 @@ +@import '../../colors/light'; +@import '../../bundle_admin'; \ No newline at end of file diff --git a/htdocs/scss/entries/common/dark.scss b/htdocs/scss/entries/common/dark.scss new file mode 100644 index 0000000..f983214 --- /dev/null +++ b/htdocs/scss/entries/common/dark.scss @@ -0,0 +1,2 @@ +@import '../../colors/dark'; +@import '../../bundle_common'; \ No newline at end of file diff --git a/htdocs/scss/entries/common/light.scss b/htdocs/scss/entries/common/light.scss new file mode 100644 index 0000000..a3044bd --- /dev/null +++ b/htdocs/scss/entries/common/light.scss @@ -0,0 +1,2 @@ +@import '../../colors/light'; +@import '../../bundle_common'; \ No newline at end of file diff --git a/htdocs/scss/form.scss b/htdocs/scss/form.scss deleted file mode 100644 index 4faa4d1..0000000 --- a/htdocs/scss/form.scss +++ /dev/null @@ -1,59 +0,0 @@ -@import 'vars'; - -$form-field-label-width: 120px; - -form { display: block; margin: 0; } - -.form-layout-h { - .form-field-wrap { - padding: 8px 0; - } - .form-field-label { - float: left; - width: $form-field-label-width; - text-align: right; - padding: 7px 0 0; - } - .form-field { - margin-left: $form-field-label-width + 10px; - } -} - -.form-layout-v { - .form-field-wrap { - padding: 6px 0; - } - .form-field-wrap:first-child { - padding-top: 0; - } - .form-field-wrap:last-child { - padding-bottom: 0; - } - .form-field-label { - padding: 0 0 4px 4px; - font-weight: bold; - font-size: 12px; - letter-spacing: 0.5px; - text-transform: uppercase; - color: #888; - } - .form-field { - //margin-left: $form-field-label-width + 10px; - } -} - -.form-error { - padding: 10px 13px; - margin-bottom: $base-padding; - background-color: $error-block-bg; - color: $error-block-fg; - @include radius(3px); -} - -.form-success { - padding: 10px 13px; - margin-bottom: $base-padding; - background-color: $success-block-bg; - color: $success-block-fg; - @include radius(3px); -} diff --git a/htdocs/scss/hljs.scss b/htdocs/scss/hljs.scss deleted file mode 100644 index 36f52de..0000000 --- a/htdocs/scss/hljs.scss +++ /dev/null @@ -1 +0,0 @@ -@import "./hljs/github.css"; diff --git a/htdocs/scss/hljs/github.scss b/htdocs/scss/hljs/github.scss index 791932b..ddbfc0a 100644 --- a/htdocs/scss/hljs/github.scss +++ b/htdocs/scss/hljs/github.scss @@ -1,27 +1,21 @@ -/* - -github.com style (c) Vasily Polovnyov - -*/ - .hljs { display: block; overflow-x: auto; padding: 0.5em; - color: #333; - background: #f8f8f8; + color: $hljs_fg; + background: $hljs_bg; } .hljs-comment, .hljs-quote { - color: #998; + color: $hljs_quote; font-style: italic; } .hljs-keyword, .hljs-selector-tag, .hljs-subst { - color: #333; + color: $hljs_fg; font-weight: bold; } @@ -30,18 +24,18 @@ github.com style (c) Vasily Polovnyov .hljs-variable, .hljs-template-variable, .hljs-tag .hljs-attr { - color: #008080; + color: $hljs_literal; } .hljs-string, .hljs-doctag { - color: #d14; + color: $hljs_string; } .hljs-title, .hljs-section, .hljs-selector-id { - color: #900; + color: $hljs_title; font-weight: bold; } @@ -51,43 +45,43 @@ github.com style (c) Vasily Polovnyov .hljs-type, .hljs-class .hljs-title { - color: #458; + color: $hljs_type; font-weight: bold; } .hljs-tag, .hljs-name, .hljs-attribute { - color: #000080; + color: $hljs_tag; font-weight: normal; } .hljs-regexp, .hljs-link { - color: #009926; + color: $hljs_regexp; } .hljs-symbol, .hljs-bullet { - color: #990073; + color: $hljs_symbol; } .hljs-built_in, .hljs-builtin-name { - color: #0086b3; + color: $hljs_builtin; } .hljs-meta { - color: #999; + color: $hljs_meta; font-weight: bold; } .hljs-deletion { - background: #fdd; + background: $hljs_deletion; } .hljs-addition { - background: #dfd; + background: $hljs_addition; } .hljs-emphasis { diff --git a/htdocs/scss/mobile.scss b/htdocs/scss/mobile.scss deleted file mode 100644 index d4d0d25..0000000 --- a/htdocs/scss/mobile.scss +++ /dev/null @@ -1,41 +0,0 @@ -@import 'vars'; - -textarea { - -webkit-overflow-scrolling: touch; -} - -// header -.head-logo { - position: static; - display: block; - //padding-bottom: 6px; - // not very good fix: - overflow: hidden; - white-space: nowrap; - padding-bottom: 0; -} -.head-logo::after { - display: none; -} -.head-items { - float: none; -} -a.head-item:hover, -a.head-item:active { - -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; -} -a.head-item:last-child > span { - border-right: 0; - padding-right: 12px; -} -a.head-item:first-child > span { - padding-left: 1px; -} - -// blog -.blog-tags { - display: none; -} -.blog-list.withtags { - margin-right: 0; -} diff --git a/htdocs/scss/pages.scss b/htdocs/scss/pages.scss deleted file mode 100644 index 873a6ae..0000000 --- a/htdocs/scss/pages.scss +++ /dev/null @@ -1,14 +0,0 @@ -.page { - -} -.page-edit-links { - display: none; - float: right; - font-size: 15px; - > a { - margin-left: 5px; - } -} -.page-content-inner:hover .page-edit-links { - display: block; -} diff --git a/htdocs/scss/vars.scss b/htdocs/scss/vars.scss index e056740..cc67f04 100644 --- a/htdocs/scss/vars.scss +++ b/htdocs/scss/vars.scss @@ -4,42 +4,9 @@ $ff: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif; $ffMono: SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, monospace; $base-width: 900px; -//$sb-width: 120px; $side-padding: 25px; $base-padding: 18px; - $footer-height: 64px; -$head-green-color: #0bad19; -$head-red-color: #ce1a1a; -$link-color: #116fd4; - -$bg: #f7f7f7; -$content-bg: #fff; -$code-block-bg: #f3f3f3; -$inline-code-block-bg: #f1f1f1; - -$fg: #222; -$blue1: #729fcf; -$blue2: #3465a4; -$blue3: #204a87; -$orange1: #fcaf3e; -$orange2: #f57900; -$orange3: #ce5c00; - -$light-bg: #464c4e; -$dark-bg: #272C2D; -$dark-fg: #999; - -$input-border: #e0e0e0; -$input-border-focused: #e0e0e0; -$input-bg: #f7f7f7; -$border-color: #e0e0e0; - -$error-block-bg: #f9eeee; -$error-block-fg: #d13d3d; - -$success-block-bg: #eff5f0; -$success-block-fg: #2a6f34; @mixin radius($radius) { -o-border-radius: $radius; @@ -50,9 +17,9 @@ $success-block-fg: #2a6f34; } @mixin transition($property, $duration, $easing: linear) { - transition: $property $duration $easing; - -webkit-transition: $property $duration $easing; - -moz-transition: $property $duration $easing; + transition: $property $duration $easing; + -webkit-transition: $property $duration $easing; + -moz-transition: $property $duration $easing; } @mixin linearGradient($top, $bottom){ diff --git a/lang/en.php b/lang/en.php index cf2af6c..5736104 100644 --- a/lang/en.php +++ b/lang/en.php @@ -20,6 +20,11 @@ return [ 'delete' => 'delete', 'info_saved' => 'Information saved.', + // theme switcher + 'theme_auto' => 'auto', + 'theme_dark' => 'dark', + 'theme_light' => 'light', + // contacts 'contacts_email' => 'email', 'contacts_pgp' => 'OpenPGP public key', diff --git a/model/Upload.php b/model/Upload.php index 586be24..441a14d 100644 --- a/model/Upload.php +++ b/model/Upload.php @@ -142,7 +142,7 @@ class Upload extends Model { if (is_file($dir.'/'.$f)) unlink($dir.'/'.$f); else - logError('deleteAllImagePreviews: '.$dir.'/'.$f.' is not a file!'); + logError(__METHOD__.': '.$dir.'/'.$f.' is not a file!'); $deleted++; } } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..578f958 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,647 @@ +{ + "name": "www", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "clean-css": "^5.3.0", + "clean-css-cli": "^5.6.0", + "css-patch": "^1.2.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/clean-css": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz", + "integrity": "sha512-YYuuxv4H/iNb1Z/5IbMRoxgrzjWGhOEFfd+groZ5dMCVkpENiMZmwspdrzBo9286JjM1gZJPAyL7ZIdzuvu2AQ==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-css-cli": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-5.6.0.tgz", + "integrity": "sha512-68vorNEG808D1QzeerO9AlwQVTuaR8YSK4aqwIsjJq0wDSyPH11ApHY0O+EQrdEGUZcN+d72v+Nn/gpxjAFewQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "clean-css": "^5.3.0", + "commander": "7.x", + "glob": "^7.1.6" + }, + "bin": { + "cleancss": "bin/cleancss" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/css-patch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-patch/-/css-patch-1.2.0.tgz", + "integrity": "sha512-wCIyPGugTmf10KO39QLD3N+qIum0ljrj/8pJdULjjuXQ6oEeYd5+quMF7jIdnEL5Ftp0wmbvO8qPvAmzrw0EaA==", + "dev": true, + "dependencies": { + "diff": "^5.0.0", + "stylis": "^4.0.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylis": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.1.tgz", + "integrity": "sha512-lVrM/bNdhVX2OgBFNa2YJ9Lxj7kPzylieHd3TNjuGE0Re9JB7joL5VUKOVH1kdNNJTgGPpT8hmwIAPLaSyEVFQ==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + } + }, + "dependencies": { + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "clean-css": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz", + "integrity": "sha512-YYuuxv4H/iNb1Z/5IbMRoxgrzjWGhOEFfd+groZ5dMCVkpENiMZmwspdrzBo9286JjM1gZJPAyL7ZIdzuvu2AQ==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } + }, + "clean-css-cli": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-5.6.0.tgz", + "integrity": "sha512-68vorNEG808D1QzeerO9AlwQVTuaR8YSK4aqwIsjJq0wDSyPH11ApHY0O+EQrdEGUZcN+d72v+Nn/gpxjAFewQ==", + "dev": true, + "requires": { + "chokidar": "^3.5.2", + "clean-css": "^5.3.0", + "commander": "7.x", + "glob": "^7.1.6" + } + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "css-patch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-patch/-/css-patch-1.2.0.tgz", + "integrity": "sha512-wCIyPGugTmf10KO39QLD3N+qIum0ljrj/8pJdULjjuXQ6oEeYd5+quMF7jIdnEL5Ftp0wmbvO8qPvAmzrw0EaA==", + "dev": true, + "requires": { + "diff": "^5.0.0", + "stylis": "^4.0.13" + } + }, + "diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "stylis": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.1.tgz", + "integrity": "sha512-lVrM/bNdhVX2OgBFNa2YJ9Lxj7kPzylieHd3TNjuGE0Re9JB7joL5VUKOVH1kdNNJTgGPpT8hmwIAPLaSyEVFQ==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..3abc792 --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "devDependencies": { + "clean-css": "^5.3.0", + "clean-css-cli": "^5.6.0", + "css-patch": "^1.2.0" + } +} diff --git a/prepare_static.php b/prepare_static.php deleted file mode 100755 index e3663f8..0000000 --- a/prepare_static.php +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env php8.1 - $config['domain'], + 'devMode' => $config['is_dev'], + 'cookieHost' => $config['cookie_host'], +]); + return << @@ -12,16 +22,16 @@ return << {$title} + {$ctx->renderMeta($meta)} - {$ctx->renderStatic($static)} + {$ctx->renderStatic($static, $theme)} if_true($opts['full_width'], ' class="full-width"')}> - {$ctx->renderHeader(renderLogo($ctx, $opts['logo_path_map'], $opts['logo_link_map']))} + {$ctx->renderHeader($theme, renderLogo($ctx, $opts['logo_path_map'], $opts['logo_link_map']))}
{$unsafe_body}
- {$ctx->if_true($js != '' || !empty($lang) || $opts['dynlogo_enabled'], - $ctx->renderScript, $js, $unsafe_lang, $opts['dynlogo_enabled'])} + {$ctx->renderScript($js, $unsafe_lang, $opts['dynlogo_enabled'])}