From 1072ee525dad533b3be798f70f2f831f47df46e1 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Tue, 10 Jan 2017 17:31:34 +0100 Subject: [PATCH 01/20] first go at a dispatcher to dynamically recolor SVGs The script accepts the following parameters: svg - the SVG to load. Either an image in the img directory next to the script or a media file id. ACLs are checked f - wanted fill color s - wanted stroke color fh - wanted fill color on hover sh - wanted stroke color on hover Colors are to be given in hex in the following formats: RGB RRGGBB RRGGBBAA What's missing: * being able to define what is styled, currently hardcoded to 'path' elements only * caching - no need to do all the processing every time * background setting - that would require wrapping an additional or element around all content and style. I'm not sure how to do that best. * unit tests --- svg.php | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 svg.php diff --git a/svg.php b/svg.php new file mode 100644 index 0000000..10da30f --- /dev/null +++ b/svg.php @@ -0,0 +1,165 @@ +insertBefore( + $dom->ownerDocument->createElement($name, $value), + $dom->firstChild + ); + + return simplexml_import_dom($new, get_class($this)); + } +} + +/** + * Manage SVG recoloring + */ +class SVG { + + const IMGDIR = __DIR__ . '/img/'; + + /** @var SvgNode */ + protected $xml; + + /** + * SVG constructor + */ + public function __construct() { + global $INPUT; + + $svg = cleanID($INPUT->str('svg')); + if(blank($svg)) $this->abort(404); + + // try local file first + $file = self::IMGDIR . $svg; + if(!file_exists($file)) { + // media files are ACL protected + if(auth_quickaclcheck($svg)) $this->abort(403); + $file = mediaFN($svg); + } + // check if media exists + if(!file_exists($file)) $this->abort(404); + + $this->xml = simplexml_load_file($file, SvgNode::class); + } + + /** + * Generate and output + */ + public function out() { + $this->setStyle(); + header('image/svg+xml'); + echo $this->xml->asXML(); + } + + /** + * Generate a style setting from the input variables + * + * @return string + */ + protected function makeStyle() { + global $INPUT; + + $element = 'path'; // FIXME configurable? + + $colors = array( + 's' => $this->fixColor($INPUT->str('s')), + 'f' => $this->fixColor($INPUT->str('f')), + 'sh' => $this->fixColor($INPUT->str('sh')), + 'fh' => $this->fixColor($INPUT->str('fh')), + ); + + $style = ''; + if($colors['s'] || $colors['f']) { + $style .= $element . '{'; + if($colors['s']) $style .= 'stroke:' . $colors['s'] . ';'; + if($colors['f']) $style .= 'fill:' . $colors['f'] . ';'; + $style .= '}'; + } + + if($colors['sh'] || $colors['fh']) { + $style .= $element . ':hover{'; + if($colors['sh']) $style .= 'stroke:' . $colors['sh'] . ';'; + if($colors['fh']) $style .= 'fill:' . $colors['fh'] . ';'; + $style .= '}'; + } + + return $style; + } + + /** + * Takes a hexadecimal color string in the following forms: + * + * RGB + * RRGGBB + * RRGGBBAA + * + * Converts it to rgba() form + * + * @param string $color + * @return string + */ + protected function fixColor($color) { + if(preg_match('/^([0-9a-f])([0-9a-f])([0-9a-f])$/i', $color, $m)) { + $r = hexdec($m[1] . $m[1]); + $g = hexdec($m[2] . $m[2]); + $b = hexdec($m[3] . $m[3]); + $a = hexdec('ff'); + } elseif(preg_match('/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i', $color, $m)) { + $r = hexdec($m[1]); + $g = hexdec($m[2]); + $b = hexdec($m[3]); + if(isset($m[4])) { + $a = hexdec($m[4]); + } else { + $a = hexdec('ff'); + } + } else { + return ''; + } + + return "rgba($r,$g,$b,$a)"; + } + + /** + * Apply the style to the SVG + */ + protected function setStyle() { + $defs = $this->xml->defs; + if(!$defs) { + $defs = $this->xml->prependChild('defs'); + } + $defs->addChild('style', $this->makeStyle()); + } + + /** + * Abort processing with given status code + * + * @param int $status + */ + protected function abort($status) { + http_status($status); + exit; + } + +} + +// main +$svg = new SVG(); +$svg->out(); + From 80d784e1de81a9bd4f2967a7d4fc39110dcb54d3 Mon Sep 17 00:00:00 2001 From: Michael Grosse Date: Wed, 11 Jan 2017 14:42:51 +0100 Subject: [PATCH 02/20] feat: add background-colors to SVG-dispatcher This extends the query with to parameters for the background: b - wanted background color bh - wanted background color on hover --- svg.php | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/svg.php b/svg.php index 10da30f..9e2d12c 100644 --- a/svg.php +++ b/svg.php @@ -24,6 +24,28 @@ class SvgNode extends \SimpleXMLElement { return simplexml_import_dom($new, get_class($this)); } + + /** + * @param \SimpleXMLElement $node the node to be added + * @return \SimpleXMLElement + */ + public function appendNode(\SimpleXMLElement $node) { + $dom = dom_import_simplexml($this); + $domNode = dom_import_simplexml($node); + + $newNode = $dom->appendChild($domNode); + return simplexml_import_dom($newNode, get_class($this)); + } + + /** + * @param \SimpleXMLElement $node the child to remove + * @return \SimpleXMLElement + */ + public function removeChild(\SimpleXMLElement $node) { + $dom = dom_import_simplexml($node); + $dom->parentNode->removeChild($dom); + return $node; + } } /** @@ -32,6 +54,7 @@ class SvgNode extends \SimpleXMLElement { class SVG { const IMGDIR = __DIR__ . '/img/'; + const BACKGROUNDCLASS = 'sprintdoc-background'; /** @var SvgNode */ protected $xml; @@ -62,6 +85,8 @@ class SVG { * Generate and output */ public function out() { + $g = $this->wrapChildren(); + $this->setBackground($g); $this->setStyle(); header('image/svg+xml'); echo $this->xml->asXML(); @@ -80,20 +105,31 @@ class SVG { $colors = array( 's' => $this->fixColor($INPUT->str('s')), 'f' => $this->fixColor($INPUT->str('f')), + 'b' => $this->fixColor($INPUT->str('b')), 'sh' => $this->fixColor($INPUT->str('sh')), 'fh' => $this->fixColor($INPUT->str('fh')), + 'bh' => $this->fixColor($INPUT->str('bh')), ); - $style = ''; + if (empty($colors['b'])) { + $colors['b'] = $this->fixColor('00000000'); + } + + $style = 'g rect.' . self::BACKGROUNDCLASS . '{fill:' . $colors['b'] . ';}'; + + if($colors['bh']) { + $style .= 'g:hover rect.' . self::BACKGROUNDCLASS . '{fill:' . $colors['bh'] . ';}'; + } + if($colors['s'] || $colors['f']) { - $style .= $element . '{'; + $style .= 'g ' . $element . '{'; if($colors['s']) $style .= 'stroke:' . $colors['s'] . ';'; if($colors['f']) $style .= 'fill:' . $colors['f'] . ';'; $style .= '}'; } if($colors['sh'] || $colors['fh']) { - $style .= $element . ':hover{'; + $style .= 'g:hover ' . $element . '{'; if($colors['sh']) $style .= 'stroke:' . $colors['sh'] . ';'; if($colors['fh']) $style .= 'fill:' . $colors['fh'] . ';'; $style .= '}'; @@ -136,6 +172,41 @@ class SVG { return "rgba($r,$g,$b,$a)"; } + /** + * sets a rectangular background of the size of the svg/this itself + * + * @param SvgNode $g + * @return SvgNode + */ + protected function setBackground(SvgNode $g) { + $attributes = $this->xml->attributes(); + $rect = $g->prependChild('rect'); + $rect->addAttribute('class', self::BACKGROUNDCLASS); + + $rect->addAttribute('x', '0'); + $rect->addAttribute('y', '0'); + $rect->addAttribute('height', $attributes['height']); + $rect->addAttribute('width', $attributes['width']); + return $rect; + } + + /** + * Wraps all elements of $this in a `` tag + * + * @return SvgNode + */ + protected function wrapChildren() { + $svgChildren = array(); + foreach ($this->xml->children() as $child) { + $svgChildren[] = $this->xml->removeChild($child); + } + $g = $this->xml->prependChild('g'); + foreach ($svgChildren as $child) { + $g->appendNode($child); + } + return $g; + } + /** * Apply the style to the SVG */ From 3ec07d58b7e4f7d33cfe896e8d5911829f58bcbe Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Thu, 12 Jan 2017 09:25:13 +0100 Subject: [PATCH 03/20] fixed auth check --- svg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/svg.php b/svg.php index 10da30f..684b811 100644 --- a/svg.php +++ b/svg.php @@ -49,7 +49,7 @@ class SVG { $file = self::IMGDIR . $svg; if(!file_exists($file)) { // media files are ACL protected - if(auth_quickaclcheck($svg)) $this->abort(403); + if(auth_quickaclcheck($svg) < AUTH_READ) $this->abort(403); $file = mediaFN($svg); } // check if media exists From 4fd6492bc9b3ec7a7e4f4e8bb15ac2ba50328b48 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Thu, 12 Jan 2017 09:25:28 +0100 Subject: [PATCH 04/20] fixed content type header --- svg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/svg.php b/svg.php index 684b811..f81b3c2 100644 --- a/svg.php +++ b/svg.php @@ -63,7 +63,7 @@ class SVG { */ public function out() { $this->setStyle(); - header('image/svg+xml'); + header('Content-Type: image/svg+xml'); echo $this->xml->asXML(); } From 24ab1f725c47400c809211b7ebf1197468d8252d Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Thu, 19 Jan 2017 18:30:37 +0100 Subject: [PATCH 05/20] add caching and fix wrapping --- svg.php | 145 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 89 insertions(+), 56 deletions(-) diff --git a/svg.php b/svg.php index f79382e..f1bc30f 100644 --- a/svg.php +++ b/svg.php @@ -46,6 +46,37 @@ class SvgNode extends \SimpleXMLElement { $dom->parentNode->removeChild($dom); return $node; } + + /** + * Wraps all elements of $this in a `` tag + * + * @return SvgNode + */ + public function groupChildren() { + $dom = dom_import_simplexml($this); + + $g = $dom->ownerDocument->createElement('g'); + while ($dom->childNodes->length > 0) { + $child = $dom->childNodes->item(0); + $dom->removeChild($child); + $g->appendChild($child); + } + $g = $dom->appendChild($g); + + return simplexml_import_dom($g, get_class($this)); + } + + /** + * Add new style definitions to this element + * @param string $style + */ + public function addStyle($style) { + $defs = $this->defs; + if(!$defs) { + $defs = $this->prependChild('defs'); + } + $defs->addChild('style', $style); + } } /** @@ -56,8 +87,7 @@ class SVG { const IMGDIR = __DIR__ . '/img/'; const BACKGROUNDCLASS = 'sprintdoc-background'; - /** @var SvgNode */ - protected $xml; + protected $file; /** * SVG constructor @@ -78,31 +108,51 @@ class SVG { // check if media exists if(!file_exists($file)) $this->abort(404); - $this->xml = simplexml_load_file($file, SvgNode::class); + $this->file = $file; } /** * Generate and output */ public function out() { - $g = $this->wrapChildren(); - $this->setBackground($g); - $this->setStyle(); + $file = $this->file; + $params = $this->getParameters(); + header('Content-Type: image/svg+xml'); - echo $this->xml->asXML(); + $cachekey = md5($file . serialize($params)); + $cache = new \cache($cachekey, '.svg'); + $cache->_event = 'SVG_CACHE'; + + http_cached($cache->cache, $cache->useCache(array('files' => array($file, __FILE__)))); + http_cached_finish($cache->cache, $this->generateSVG($file, $params)); } /** - * Generate a style setting from the input variables + * Generate a new SVG based on the input file and the parameters * - * @return string + * @param string $file the SVG file to load + * @param array $params the parameters as returned by getParameters() + * @return string the new XML contents */ - protected function makeStyle() { + protected function generateSVG($file, $params) { + /** @var SvgNode $xml */ + $xml = simplexml_load_file($file, SvgNode::class); + $xml->addStyle($this->makeStyle($params)); + $this->createBackground($xml); + $xml->groupChildren(); + + return $xml->asXML(); + } + + /** + * Get the supported parameters from request + * + * @return array + */ + protected function getParameters() { global $INPUT; - $element = 'path'; // FIXME configurable? - - $colors = array( + $params = array( 's' => $this->fixColor($INPUT->str('s')), 'f' => $this->fixColor($INPUT->str('f')), 'b' => $this->fixColor($INPUT->str('b')), @@ -111,27 +161,39 @@ class SVG { 'bh' => $this->fixColor($INPUT->str('bh')), ); - if (empty($colors['b'])) { - $colors['b'] = $this->fixColor('00000000'); + return $params; + } + + /** + * Generate a style setting from the input variables + * + * @param array $params associative array with the given parameters + * @return string + */ + protected function makeStyle($params) { + $element = 'path'; // FIXME configurable? + + if(empty($params['b'])) { + $params['b'] = $this->fixColor('00000000'); } - $style = 'g rect.' . self::BACKGROUNDCLASS . '{fill:' . $colors['b'] . ';}'; + $style = 'g rect.' . self::BACKGROUNDCLASS . '{fill:' . $params['b'] . ';}'; - if($colors['bh']) { - $style .= 'g:hover rect.' . self::BACKGROUNDCLASS . '{fill:' . $colors['bh'] . ';}'; + if($params['bh']) { + $style .= 'g:hover rect.' . self::BACKGROUNDCLASS . '{fill:' . $params['bh'] . ';}'; } - if($colors['s'] || $colors['f']) { + if($params['s'] || $params['f']) { $style .= 'g ' . $element . '{'; - if($colors['s']) $style .= 'stroke:' . $colors['s'] . ';'; - if($colors['f']) $style .= 'fill:' . $colors['f'] . ';'; + if($params['s']) $style .= 'stroke:' . $params['s'] . ';'; + if($params['f']) $style .= 'fill:' . $params['f'] . ';'; $style .= '}'; } - if($colors['sh'] || $colors['fh']) { + if($params['sh'] || $params['fh']) { $style .= 'g:hover ' . $element . '{'; - if($colors['sh']) $style .= 'stroke:' . $colors['sh'] . ';'; - if($colors['fh']) $style .= 'fill:' . $colors['fh'] . ';'; + if($params['sh']) $style .= 'stroke:' . $params['sh'] . ';'; + if($params['fh']) $style .= 'fill:' . $params['fh'] . ';'; $style .= '}'; } @@ -178,46 +240,17 @@ class SVG { * @param SvgNode $g * @return SvgNode */ - protected function setBackground(SvgNode $g) { - $attributes = $this->xml->attributes(); + protected function createBackground(SvgNode $g) { $rect = $g->prependChild('rect'); $rect->addAttribute('class', self::BACKGROUNDCLASS); $rect->addAttribute('x', '0'); $rect->addAttribute('y', '0'); - $rect->addAttribute('height', $attributes['height']); - $rect->addAttribute('width', $attributes['width']); + $rect->addAttribute('height', '100%'); + $rect->addAttribute('width', '100%'); return $rect; } - /** - * Wraps all elements of $this in a `` tag - * - * @return SvgNode - */ - protected function wrapChildren() { - $svgChildren = array(); - foreach ($this->xml->children() as $child) { - $svgChildren[] = $this->xml->removeChild($child); - } - $g = $this->xml->prependChild('g'); - foreach ($svgChildren as $child) { - $g->appendNode($child); - } - return $g; - } - - /** - * Apply the style to the SVG - */ - protected function setStyle() { - $defs = $this->xml->defs; - if(!$defs) { - $defs = $this->xml->prependChild('defs'); - } - $defs->addChild('style', $this->makeStyle()); - } - /** * Abort processing with given status code * From c24a2e1e525f015b644fce07c8c5eba21a6c1621 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Mon, 13 Feb 2017 14:02:16 +0100 Subject: [PATCH 06/20] SVG Dispatch: allow for referencing material design icons A SVG not found in the template or in the local media storage will now be looked up in the Material Design Icon library (via a cached HTTP request to the rawgit CDN). --- svg.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/svg.php b/svg.php index f1bc30f..a40f39c 100644 --- a/svg.php +++ b/svg.php @@ -56,7 +56,7 @@ class SvgNode extends \SimpleXMLElement { $dom = dom_import_simplexml($this); $g = $dom->ownerDocument->createElement('g'); - while ($dom->childNodes->length > 0) { + while($dom->childNodes->length > 0) { $child = $dom->childNodes->item(0); $dom->removeChild($child); $g->appendChild($child); @@ -86,6 +86,7 @@ class SVG { const IMGDIR = __DIR__ . '/img/'; const BACKGROUNDCLASS = 'sprintdoc-background'; + const CDNBASE = 'https://cdn.rawgit.com/Templarian/MaterialDesign/master/icons/svg/'; protected $file; @@ -101,9 +102,17 @@ class SVG { // try local file first $file = self::IMGDIR . $svg; if(!file_exists($file)) { - // media files are ACL protected - if(auth_quickaclcheck($svg) < AUTH_READ) $this->abort(403); + // try media file $file = mediaFN($svg); + if(file_exists($file)) { + // media files are ACL protected + if(auth_quickaclcheck($svg) < AUTH_READ) $this->abort(403); + } else { + // get it from material design icons + $file = getCacheName($svg, '.svg'); + io_download(self::CDNBASE . $svg, $file); + } + } // check if media exists if(!file_exists($file)) $this->abort(404); From 94def893f074ab26c70bf8a3cef75cd050a11756 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Mon, 13 Feb 2017 14:28:04 +0100 Subject: [PATCH 07/20] allow style.ini replacements in SVG dispatcher --- svg.php | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/svg.php b/svg.php index a40f39c..e2f8db9 100644 --- a/svg.php +++ b/svg.php @@ -89,6 +89,7 @@ class SVG { const CDNBASE = 'https://cdn.rawgit.com/Templarian/MaterialDesign/master/icons/svg/'; protected $file; + protected $replacements; /** * SVG constructor @@ -216,12 +217,15 @@ class SVG { * RRGGBB * RRGGBBAA * - * Converts it to rgba() form + * Converts it to rgba() form. + * + * Alternatively takes a replacement name from the current template's style.ini * * @param string $color * @return string */ - protected function fixColor($color) { + protected function fixColor($color, $ini = true) { + if(preg_match('/^([0-9a-f])([0-9a-f])([0-9a-f])$/i', $color, $m)) { $r = hexdec($m[1] . $m[1]); $g = hexdec($m[2] . $m[2]); @@ -237,6 +241,12 @@ class SVG { $a = hexdec('ff'); } } else { + if($ini) { + if(!$this->replacements) $this->initReplacements(); + if(isset($this->replacements[$color])) { + return $this->replacements[$color]; + } + } return ''; } @@ -270,6 +280,19 @@ class SVG { exit; } + /** + * Initialize the available replacement patterns + * + * Loads the style.ini from the template (and various local locations) + * via a core function only available through some hack. + */ + protected function initReplacements() { + global $conf; + define('SIMPLE_TEST', 1); // hacky shit + include DOKU_INC . 'lib/exe/css.php'; + $ini = css_styleini($conf['tpl']); + $this->replacements = $ini['replacements']; + } } // main From 9d8dba14895bab5a7f4470498af7099e73bfb9f5 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Mon, 13 Feb 2017 15:17:18 +0100 Subject: [PATCH 08/20] New sidebar JavaScript logic This implements the JS logic as discussed in our meeting. A sidebar can now contain virtually any wiki syntax. (currently hardcoded) H2 elements will toggle all their following content which is wrapped in a wrapper div. This is currently implemented on top of the old JavaScript which should be removed. The toggle element should be made configurable. We probably want to reuse some of the toggle logic for the user tools. --- js/sidebar.js | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ script.js | 3 +++ 2 files changed, 60 insertions(+) create mode 100644 js/sidebar.js diff --git a/js/sidebar.js b/js/sidebar.js new file mode 100644 index 0000000..16b9232 --- /dev/null +++ b/js/sidebar.js @@ -0,0 +1,57 @@ +/** + * Initialize the sidebar to have a toggleable menu system with icon support + */ +jQuery(function () { + const $nav = jQuery('#dokuwiki__aside').find('nav.nav-main'); + if (!$nav.length) return; + + const ELEMENT = 'h2'; // FIXME move to config + const $elements = $nav.find(ELEMENT); + $elements.each(function () { + const $me = jQuery(this); + + // prepare text and the optional icon + const data = $me.text().split('@', 2); + const text = data[0].trim(); + const $icon = jQuery('') + .text(text.substr(0, 1).toUpperCase() + text.substr(1, 1).toLowerCase()); + if (data[1]) { + const src = data[1].trim(); + const $svg = jQuery(''); + $svg.attr('src', DOKU_BASE + 'lib/tpl/sprintdoc/svg.php?svg='+src+'&f=__link__'); + $svg.on('load', function () { + $icon.html($svg); + }); + } + + // make the new toggler + const $toggler = jQuery('
') + .addClass('navi-toggle') + .text(text) + .prepend($icon) + ; + + // wrap all following sibling til the next element in a wrapper + const $wrap = jQuery('
').addClass('navi-pane'); + const $sibs = $me.nextAll(); + for (let i = 0; i < $sibs.length; i++) { + if ($sibs[i].tagName.toLowerCase() === ELEMENT) break; + const $sib = jQuery($sibs[i]); + $sib.detach().appendTo($wrap); + } + $wrap.hide(); + $wrap.insertAfter($me); + + // replace element with toggler + $me.replaceWith($toggler); + + // add toggling the wrapper + $toggler.click(function () { + $wrap.dw_toggle(undefined, function () { + $me.toggleClass('open'); + }); + }); + + }); + +}); diff --git a/script.js b/script.js index 73e1f93..c9d2ca4 100755 --- a/script.js +++ b/script.js @@ -33,3 +33,6 @@ var $_LANG = {}; /* DOKUWIKI:include js/sidebar-menu.js */ /* DOKUWIKI:include js/meta-box.js */ + + +/* DOKUWIKI:include js/sidebar.js */ From 96fae1c8bc5af3260c74eb66c9578c5fafb2176f Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Mon, 13 Feb 2017 15:24:19 +0100 Subject: [PATCH 09/20] very simple styling of the new sidebar behaviour. I had a really hard time to understand the existing CSS which seemed much too complicated for what it does. Variable use was suboptimal. I couldn't figure out which variable defines the proper size for the icon and the resulting margin. --- css/area_main-sidebar.less | 250 ++++++------------------------------- 1 file changed, 39 insertions(+), 211 deletions(-) diff --git a/css/area_main-sidebar.less b/css/area_main-sidebar.less index d801f34..ba9f779 100755 --- a/css/area_main-sidebar.less +++ b/css/area_main-sidebar.less @@ -4,233 +4,62 @@ * @author Jana Deutschlaender */ - #dokuwiki__aside.main-sidebar { - counter-reset: nav-counter; - - -/* + + + + + nav main + + + + + */ .nav-main { - > ul { - @icon-size: @margin-default; + @icon-size: @font-size-big; + @menu-margin: @icon-size + @margin-small*2; // FIXME this is still wrong - margin: 0 0 @margin-default; - padding: 0; + margin-left: @menu-margin; // moves *all* sidebar content to the right - ul { - margin-left: (@icon-size + .5); - } + .navi-toggle { + cursor: pointer; - li { - list-style-type: none; + height: @line-height-big; + line-height: @line-height-big; + font-weight: normal; + margin-left: (@menu-margin * -1); // moves the toggles back to the left + + color: @color-nav; + border: 1px solid transparent; + border-radius: @fix_border-radius; + // margin-bottom: -.3rem; FIXME + // padding: .7em 0 .7em (@margin-big + 1); FIXME WTF? + transition: @transition color, @transition background-color, @transition border-color; + + span { + display: inline-block; + width: @menu-margin; + height: @line-height-big; + border-right: 1px solid @color-border; + text-align: center; + margin-right: @margin-small; color: @color-nav; - &:first-of-type { - padding-top: .3em; + img { + width: @icon-size; + height: @icon-size; + filter: grayscale(100%); //fixme maybe we could find a better way } } - .li { - a { - display: list-item; - list-style-type: square; - color: @color-nav; - font-weight: normal; - padding-top: .1em; - padding-bottom: .1em; - transition: @transition color; + &:hover, + &:focus, + &:active { + background-color: @button_color; + border-color: @button_background; + color: @button_background; + text-decoration: none; - &:hover, - &:focus, - &:active { - color: @button_background; - } - } - } - - -/* + + + first level + + + */ - > li { - counter-increment: nav-counter; - position: relative; - list-style-type: none; - margin: 0; - padding: 0 0 .3em; - - &:not(:last-of-type)::after { - content: ''; - position: absolute; - left: 0; - bottom: 0; - display: block; - width: 100%; - height: 1px; - background-color: @color-border; - } - - > .li { - position: relative; - z-index: 1; - margin-top: -1px; - padding: .5em 0 .5em (@margin-big + 1.1); - - &[class="li"] { - border-bottom: solid 1px @color-border; - } - - &::before { - .display-flex(); - .flex-direction(); - .justify-content(); - - content: counter(nav-counter); - position: absolute; - top: 0; - left: 0; - height: 100%; - width: @margin-big; - overflow: hidden; - color: @color-nav; - text-align: center; - margin-top: auto; - margin-bottom: auto; - } - - &::after { - content: ''; - position: absolute; - top: 15%; - bottom: 15%; - width: 1px; - left: (@margin-big + .5); - background-color: @color-border; - } - - &.opened, - &.closed { - padding: 0; - - &::before, - &::after { - display: none; - } - - a { - position: relative; - display: block; - border: 1px solid transparent; - border-radius: @fix_border-radius; - margin-bottom: -.3rem; - padding: .7em 0 .7em (@margin-big + 1); - transition: @transition color, @transition background-color, @transition border-color; - - &::before { - .display-flex(); - .flex-direction(); - .justify-content(); - - content: counter(nav-counter); - position: absolute; - top: 1px; - bottom: 1px; - left: 0; - height: 100%; - width: @margin-big; - overflow: hidden; - color: @color-nav; - text-align: center; - margin-top: auto; - margin-bottom: auto; - transition: @transition color, @transition background-color; - } - - &::after { - content: ''; - position: absolute; - top: 15%; - bottom: 15%; - width: 1px; - left: @margin-big; - background-color: @color-border; - transition: @transition background-color; - } - - &:hover, - &:focus, - &:active { - background-color: @button_color; - border-color: @button_background; - color: @button_background; - text-decoration: none; - - &::before { - color: @button_background; - } - - &::after { - background-color: @button_background; - } - } - } - } - - &.opened { - a { - background-color: @button_background; - color: @button_color; - - &::before { - background-color: @color-site-bg; - border-radius: @fix_border-radius 0 0 @fix_border-radius; - } - - &::after { - display: none; - } - - &:hover, - &:focus, - &:active { - background-color: @button_color; - border-color: @button_background; - color: @button_background; - text-decoration: none; - - &::before { - background-color: @button_color; - color: @button_background; - } - - &::after { - display: block; - background-color: @button_background; - } - } - } - - + ul { - margin-top: .5em; - margin-bottom: .25em; - } - } - - + ul { - height: auto; - overflow: hidden; - } - - &.closed + ul { - height: 0; - li a{ - display: none; - } - } + span img { + filter: none; } } } } } +// FIXME check if the stuff below is still relevant + /* + + + + + + + + + + + + + + + + + + + + + + + + + + */ /* min-width: 1440px */ @@ -259,7 +88,6 @@ } } - /* + + + + + + + + + + + + + + + + + + + + + + + + + + */ /* max-width: 1199px */ From 9fd3d99b8fe1b2b890f96712f48e363323b08e7b Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Mon, 13 Feb 2017 16:19:43 +0100 Subject: [PATCH 10/20] directly embed SVGs for better styling Now the SVGs are loaded via AJAX and are directly embedded into the HTML. This allows for styling them via CSS and we can apply proper hover styles. The svg dispatcher was extended with an embed option (boolean parameter 'e') which will return an absolute minimum svg with absolutely no styles. --- css/area_main-sidebar.less | 10 ++++++---- js/sidebar.js | 6 +----- svg.php | 31 +++++++++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/css/area_main-sidebar.less b/css/area_main-sidebar.less index ba9f779..52a03b0 100755 --- a/css/area_main-sidebar.less +++ b/css/area_main-sidebar.less @@ -35,10 +35,12 @@ margin-right: @margin-small; color: @color-nav; - img { + svg { width: @icon-size; height: @icon-size; - filter: grayscale(100%); //fixme maybe we could find a better way + path { + fill: @color-nav; + } } } @@ -50,8 +52,8 @@ color: @button_background; text-decoration: none; - span img { - filter: none; + span svg path { + fill: @button_background; } } } diff --git a/js/sidebar.js b/js/sidebar.js index 16b9232..9279c1e 100644 --- a/js/sidebar.js +++ b/js/sidebar.js @@ -17,11 +17,7 @@ jQuery(function () { .text(text.substr(0, 1).toUpperCase() + text.substr(1, 1).toLowerCase()); if (data[1]) { const src = data[1].trim(); - const $svg = jQuery(''); - $svg.attr('src', DOKU_BASE + 'lib/tpl/sprintdoc/svg.php?svg='+src+'&f=__link__'); - $svg.on('load', function () { - $icon.html($svg); - }); + $icon.load(DOKU_BASE + 'lib/tpl/sprintdoc/svg.php?svg=' + src + '&e=1'); // directly embed } // make the new toggler diff --git a/svg.php b/svg.php index e2f8db9..f1c5d0a 100644 --- a/svg.php +++ b/svg.php @@ -129,12 +129,17 @@ class SVG { $params = $this->getParameters(); header('Content-Type: image/svg+xml'); - $cachekey = md5($file . serialize($params)); + $cachekey = md5($file . serialize($params) . filemtime(__FILE__)); $cache = new \cache($cachekey, '.svg'); $cache->_event = 'SVG_CACHE'; http_cached($cache->cache, $cache->useCache(array('files' => array($file, __FILE__)))); - http_cached_finish($cache->cache, $this->generateSVG($file, $params)); + if($params['e']) { + $content = $this->embedSVG($file); + } else { + $content = $this->generateSVG($file, $params); + } + http_cached_finish($cache->cache, $content); } /** @@ -154,6 +159,26 @@ class SVG { return $xml->asXML(); } + /** + * Return the absolute minimum path definition for direct embedding + * + * No styles will be applied. They have to be done in CSS + * + * @param string $file the SVG file to load + * @return string the new XML contents + */ + protected function embedSVG($file) { + /** @var SvgNode $xml */ + $xml = simplexml_load_file($file, SvgNode::class); + + $def = hsc((string) $xml->path['d']); + $w = hsc($xml['width']); + $h = hsc($xml['height']); + $v = hsc($xml['viewBox']); + + return ""; + } + /** * Get the supported parameters from request * @@ -163,6 +188,7 @@ class SVG { global $INPUT; $params = array( + 'e' => $INPUT->bool('e', false), 's' => $this->fixColor($INPUT->str('s')), 'f' => $this->fixColor($INPUT->str('f')), 'b' => $this->fixColor($INPUT->str('b')), @@ -299,3 +325,4 @@ class SVG { $svg = new SVG(); $svg->out(); + From f293a16b9c1ea8b393c72a061ef3b893b8dae4dd Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Tue, 14 Feb 2017 11:59:08 +0100 Subject: [PATCH 11/20] use jQuery slector to define the elements This makes it possible to use multiple. For now I just use all headlines --- js/sidebar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/sidebar.js b/js/sidebar.js index 9279c1e..15e14f6 100644 --- a/js/sidebar.js +++ b/js/sidebar.js @@ -5,7 +5,7 @@ jQuery(function () { const $nav = jQuery('#dokuwiki__aside').find('nav.nav-main'); if (!$nav.length) return; - const ELEMENT = 'h2'; // FIXME move to config + const ELEMENT = 'h1,h2,h3,h4,h5'; // FIXME move to config const $elements = $nav.find(ELEMENT); $elements.each(function () { const $me = jQuery(this); @@ -31,8 +31,8 @@ jQuery(function () { const $wrap = jQuery('
').addClass('navi-pane'); const $sibs = $me.nextAll(); for (let i = 0; i < $sibs.length; i++) { - if ($sibs[i].tagName.toLowerCase() === ELEMENT) break; const $sib = jQuery($sibs[i]); + if($sib.is(ELEMENT)) break; $sib.detach().appendTo($wrap); } $wrap.hide(); From 285c4f96a7bf542c86a909eec254bc890763ec6d Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Wed, 15 Feb 2017 14:55:47 +0100 Subject: [PATCH 12/20] style the fake icon When no icon is provided, we use the first two letters as icon. A fancy border makes it look more like an icon. --- css/area_main-sidebar.less | 23 +++++++++++++++++++++++ js/sidebar.js | 3 ++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/css/area_main-sidebar.less b/css/area_main-sidebar.less index 52a03b0..1b65553 100755 --- a/css/area_main-sidebar.less +++ b/css/area_main-sidebar.less @@ -35,6 +35,24 @@ margin-right: @margin-small; color: @color-nav; + // simple fake icon + strong { + display: inline-block; + font-size: @icon-size * 0.5; + width: @icon-size * 0.9; + height: @icon-size * 0.9; + line-height: @icon-size * 0.9; + margin: @icon-size * 0.05; + vertical-align: baseline; + text-align: center; + color: @color-nav; + border: 1px solid @color-nav; + border-top-left-radius: 50%; + border-bottom-right-radius: 50%; + + } + + // real icon svg { width: @icon-size; height: @icon-size; @@ -52,6 +70,11 @@ color: @button_background; text-decoration: none; + span strong { + color: @button_background; + border-color: @button_background; + } + span svg path { fill: @button_background; } diff --git a/js/sidebar.js b/js/sidebar.js index 15e14f6..52b7521 100644 --- a/js/sidebar.js +++ b/js/sidebar.js @@ -14,7 +14,8 @@ jQuery(function () { const data = $me.text().split('@', 2); const text = data[0].trim(); const $icon = jQuery('') - .text(text.substr(0, 1).toUpperCase() + text.substr(1, 1).toLowerCase()); + .text(text.substr(0, 1).toUpperCase() + text.substr(1, 1).toLowerCase()) + .wrapInner(''); if (data[1]) { const src = data[1].trim(); $icon.load(DOKU_BASE + 'lib/tpl/sprintdoc/svg.php?svg=' + src + '&e=1'); // directly embed From 6c61749bf2a1268695f9fe689d24c6c675880110 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Thu, 16 Feb 2017 14:40:25 +0100 Subject: [PATCH 13/20] complete refactor of the sidebar behaviour javascript RES-754 Now all toggling of the sidebar and the related menus is done through the same click handler. HTML has been unified. All sidebar related content is now in a single include file instead of several. Icons for the site tools user tools and trace have been added. The CSS for the whole sidebar still has to be refactored. --- css/area_main-sidebar.less | 127 +++++++++++++-------------- img/account-settings.svg | 1 + img/apple-safari.svg | 1 + img/sitemap.svg | 1 + js/sidebar-menu.js | 153 --------------------------------- js/sidebar.js | 171 ++++++++++++++++++++++++++++--------- main.php | 53 +----------- script.js | 3 - tpl/main-sidebar-nav.php | 71 +++++++++++++++ tpl/nav-main.php | 12 --- tpl/nav-sitetools.php | 14 --- tpl/nav-trace.php | 11 --- tpl/nav-usermenu.php | 23 ----- 13 files changed, 273 insertions(+), 368 deletions(-) create mode 100644 img/account-settings.svg create mode 100644 img/apple-safari.svg create mode 100644 img/sitemap.svg delete mode 100755 js/sidebar-menu.js create mode 100644 tpl/main-sidebar-nav.php delete mode 100755 tpl/nav-main.php delete mode 100755 tpl/nav-sitetools.php delete mode 100755 tpl/nav-trace.php delete mode 100755 tpl/nav-usermenu.php diff --git a/css/area_main-sidebar.less b/css/area_main-sidebar.less index 1b65553..107f76a 100755 --- a/css/area_main-sidebar.less +++ b/css/area_main-sidebar.less @@ -4,88 +4,91 @@ * @author Jana Deutschlaender */ -#dokuwiki__aside.main-sidebar { - .nav-main { - @icon-size: @font-size-big; - @menu-margin: @icon-size + @margin-small*2; // FIXME this is still wrong +#dokuwiki__aside { + @icon-size: @font-size-big; + @menu-margin: @icon-size + @margin-small*2; // FIXME this is still wrong - margin-left: @menu-margin; // moves *all* sidebar content to the right + margin-left: @menu-margin; // moves *all* sidebar content to the right - .navi-toggle { - cursor: pointer; + // the toggle element + h6 { + cursor: pointer; + height: @line-height-big; + line-height: @line-height-big; + font-weight: normal; + margin-left: (@menu-margin * -1); // moves the toggles back to the left + + color: @color-nav; + border: 1px solid transparent; + border-radius: @fix_border-radius; + // margin-bottom: -.3rem; FIXME + // padding: .7em 0 .7em (@margin-big + 1); FIXME WTF? + transition: @transition color, @transition background-color, @transition border-color; + + span { + display: inline-block; + width: @menu-margin; height: @line-height-big; - line-height: @line-height-big; - font-weight: normal; - margin-left: (@menu-margin * -1); // moves the toggles back to the left - + border-right: 1px solid @color-border; + text-align: center; + margin-right: @margin-small; color: @color-nav; - border: 1px solid transparent; - border-radius: @fix_border-radius; - // margin-bottom: -.3rem; FIXME - // padding: .7em 0 .7em (@margin-big + 1); FIXME WTF? - transition: @transition color, @transition background-color, @transition border-color; - span { + // simple fake icon + strong { display: inline-block; - width: @menu-margin; - height: @line-height-big; - border-right: 1px solid @color-border; + font-size: @icon-size * 0.5; + width: @icon-size * 0.9; + height: @icon-size * 0.9; + line-height: @icon-size * 0.9; + margin: @icon-size * 0.05; + vertical-align: baseline; text-align: center; - margin-right: @margin-small; color: @color-nav; + border: 2px solid @color-nav; + border-top-right-radius: 50%; + border-bottom-left-radius: 50%; - // simple fake icon - strong { - display: inline-block; - font-size: @icon-size * 0.5; - width: @icon-size * 0.9; - height: @icon-size * 0.9; - line-height: @icon-size * 0.9; - margin: @icon-size * 0.05; - vertical-align: baseline; - text-align: center; - color: @color-nav; - border: 1px solid @color-nav; - border-top-left-radius: 50%; - border-bottom-right-radius: 50%; - - } - - // real icon - svg { - width: @icon-size; - height: @icon-size; - path { - fill: @color-nav; - } - } } - &:hover, - &:focus, - &:active { - background-color: @button_color; - border-color: @button_background; - color: @button_background; - text-decoration: none; - - span strong { - color: @button_background; - border-color: @button_background; - } - - span svg path { - fill: @button_background; + // real icon + svg { + width: @icon-size; + height: @icon-size; + path { + fill: @color-nav; } } } + + &:hover, + &:focus, + &:active { + background-color: @button_color; + border-color: @button_background; + color: @button_background; + text-decoration: none; + + span strong { + color: @button_background; + border-color: @button_background; + } + + span svg path { + fill: @button_background; + } + } + } + + // the panel (hidden by default) + div.nav-panel { + display: none; } } // FIXME check if the stuff below is still relevant - /* + + + + + + + + + + + + + + + + + + + + + + + + + + */ /* min-width: 1440px */ diff --git a/img/account-settings.svg b/img/account-settings.svg new file mode 100644 index 0000000..a7223e9 --- /dev/null +++ b/img/account-settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/apple-safari.svg b/img/apple-safari.svg new file mode 100644 index 0000000..5edc4f4 --- /dev/null +++ b/img/apple-safari.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/sitemap.svg b/img/sitemap.svg new file mode 100644 index 0000000..1afc9f7 --- /dev/null +++ b/img/sitemap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/js/sidebar-menu.js b/js/sidebar-menu.js deleted file mode 100755 index 471bc47..0000000 --- a/js/sidebar-menu.js +++ /dev/null @@ -1,153 +0,0 @@ -( function( $, spc ) { - - var addToggleLink = function($elem){ - $elem.wrapInner(''); - }, - - setContentMinHeight = function(){ - var $sidebar = $('.page-wrapper').find('> .tools').find('.col-xs-12'); - - if($sidebar.length == 1){ - var h = $sidebar.height(), - num = parseFloat(h); - if(!isNaN(num)){ - $('#dokuwiki__content').css('minHeight',num + 100); - } - - } - - }, - setWideContent = function(){ - var $openTogglers = $('.main-sidebar').find('.opened').find('.toggler'); - $openTogglers.trigger( "click" ); - $('body').addClass('wide-content'); - }, - setDefaultContent= function(){ - $('body').removeClass('wide-content'); - - }, - toggleState = function($toggler){ - $toggler.toggleClass('closed'); - $toggler.toggleClass('opened'); - }, - focusFirstSubLink = function($elem, is2nd){ - - var $foc = (is2nd) ? $elem.find('a')[1] : $elem.find('a')[0]; - - if($foc){ - $foc.focus(); - } - return $foc; - }, - focusLastSubLink = function($elem){ - - var $foc = $elem.find('a:last-child'), - height = $elem.find('p').scrollHeight; - - if($foc){ - $foc.focus(); - } - $elem.scrollTop(height); - return $foc; - }, - - mainMenu = function(){ - var $menu = $('.nav-main').find('> ul'); - - try{ - if($menu.length > 0){ - var $toggler = $menu.find('> li.level1 > .li'), - $submenu = $menu.find('> li.level1 > ul'); - - if($toggler.length > 0 && $submenu.length > 0){ - - $toggler.addClass('closed'); - addToggleLink($toggler); - $toggler.each(function( index ) { - $(this).on( "click", function(e) { - e.preventDefault(); - var $this = $(this); - toggleState($this); - if($this.hasClass('opened')){ - var $foc = focusFirstSubLink($this.closest('li.level1'), true); - } - if($('body').hasClass('wide-content')){ - setDefaultContent(); - } - - }); - }); - - - //FIXME: store current nav state with local storage - } - - - } - - }catch(err){ - - } - }, - toggleMainContent = function(){ - var $toggler = $('.togglelink.page_main-content').find('a'); - $toggler.on("click", function (e) { - e.preventDefault(); - var $link = $(this); - - if($('body').hasClass('wide-content')){ - setDefaultContent(); - }else{ - setWideContent(); - } - - }); - }, - sideMenu = function(){ - var $menus = $('.tools').find('.toggle-menu'); - - - try{ - $menus.each(function( ) { - var $menu = $(this); - if($menu.length > 0){ - var $toggler = $menu.find('h6'), - $submenu = $menu.find('nav > ul, nav > div'); - if($toggler.length > 0 && $submenu.length > 0) { - - $toggler.addClass('closed'); - addToggleLink($toggler); - $toggler.each(function (index) { - $(this).on("click", function (e) { - e.preventDefault(); - var $this = $(this); - toggleState($this); - if ($this.hasClass('opened')) { - var $elem = ($submenu.is('div')) ? focusLastSubLink($submenu): focusFirstSubLink($submenu,false); - } - if($('body').hasClass('wide-content')){ - setDefaultContent(); - } - }); - }); - - //FIXME: store current nav state with local storage - } - } - }); - - - }catch(err){ - //console.log('err'); - } - }; - - $(function(){ - mainMenu(); - sideMenu(); - toggleMainContent(); - setContentMinHeight(); - }); - -} )( jQuery, spc ); - diff --git a/js/sidebar.js b/js/sidebar.js index 52b7521..fd13efb 100644 --- a/js/sidebar.js +++ b/js/sidebar.js @@ -1,54 +1,143 @@ /** - * Initialize the sidebar to have a toggleable menu system with icon support + * Sets up the sidebar behaviour */ jQuery(function () { - const $nav = jQuery('#dokuwiki__aside').find('nav.nav-main'); + const $nav = jQuery('#dokuwiki__aside'); if (!$nav.length) return; - const ELEMENT = 'h1,h2,h3,h4,h5'; // FIXME move to config - const $elements = $nav.find(ELEMENT); - $elements.each(function () { - const $me = jQuery(this); + /** + * closes sidebar + */ + const setWideContent = function () { + $nav.find('div.nav-panel').hide(); // close all panels + jQuery('body').addClass('wide-content'); + }; - // prepare text and the optional icon - const data = $me.text().split('@', 2); - const text = data[0].trim(); - const $icon = jQuery('') - .text(text.substr(0, 1).toUpperCase() + text.substr(1, 1).toLowerCase()) - .wrapInner(''); - if (data[1]) { - const src = data[1].trim(); - $icon.load(DOKU_BASE + 'lib/tpl/sprintdoc/svg.php?svg=' + src + '&e=1'); // directly embed - } + /** + * opens the sidebar + */ + const setDefaultContent = function () { + jQuery('body').removeClass('wide-content'); - // make the new toggler - const $toggler = jQuery('
') - .addClass('navi-toggle') - .text(text) - .prepend($icon) - ; + }; - // wrap all following sibling til the next element in a wrapper - const $wrap = jQuery('
').addClass('navi-pane'); - const $sibs = $me.nextAll(); - for (let i = 0; i < $sibs.length; i++) { - const $sib = jQuery($sibs[i]); - if($sib.is(ELEMENT)) break; - $sib.detach().appendTo($wrap); - } - $wrap.hide(); - $wrap.insertAfter($me); + /** + * Accessibility helper, focuses the first link witih the given element + * + * @param {jQuery} $elem + */ + const focusFirstSubLink = function ($elem) { + $elem.find('a').first().focus(); + }; - // replace element with toggler - $me.replaceWith($toggler); - - // add toggling the wrapper - $toggler.click(function () { - $wrap.dw_toggle(undefined, function () { - $me.toggleClass('open'); - }); + /** + * Toggle a navigation panel + * + * @param {jQuery} $toggler The h6 toggler + */ + const toggleNav = function ($toggler) { + const $panel = $toggler.next('div.nav-panel'); + const isOpen = $panel.is(':visible'); + // open sidebar on interaction + setDefaultContent(); + // toggle the panel, focus first link after opening + $panel.dw_toggle(!isOpen, function () { + if (!isOpen) { + focusFirstSubLink($panel); + } }); + }; - }); + /** + * Initialize the content navigation + * + * It mangles the sidebar content and handles inline Icon configuration + */ + const initContentNav = function () { + const $main = $nav.find('nav.nav-main'); + if (!$main.length) return; + const ELEMENT = 'h1,h2,h3,h4,h5'; // FIXME move to config + const $elements = $main.find(ELEMENT); + $elements.each(function () { + const $me = jQuery(this); + + // prepare text and the optional icon + const data = $me.text().split('@', 2); + const text = data[0].trim(); + const $icon = jQuery('') + .text(text.substr(0, 1).toUpperCase() + text.substr(1, 1).toLowerCase()) + .wrapInner(''); + if (data[1]) { + const src = data[1].trim(); + $icon.load(DOKU_BASE + 'lib/tpl/sprintdoc/svg.php?svg=' + src + '&e=1'); // directly embed + } + + // make the new toggler + const $toggler = jQuery('
') + .attr('role', 'heading') + .attr('aria-level', '2') + .text(text) + .prepend($icon) + ; + + // wrap all following siblings til the next element in a wrapper + const $wrap = jQuery('
') + .addClass('nav-panel'); + const $sibs = $me.nextAll(); + for (let i = 0; i < $sibs.length; i++) { + const $sib = jQuery($sibs[i]); + if ($sib.is(ELEMENT)) break; + $sib.detach().appendTo($wrap); + } + $wrap.insertAfter($me); + + // replace element with toggler + $me.replaceWith($toggler); + }); + }; + + /** + * Initialize the open/close toggling of menu entries + */ + const initMenuHandling = function () { + $nav.on('click', 'h6', function () { + toggleNav(jQuery(this)); + }); + }; + + /** + * Make sure the content area is always as high as the sidebar + */ + const initContentMinHeight = function () { + const $sidebar = jQuery('.page-wrapper').find('> .tools').find('.col-xs-12'); + if ($sidebar.length == 1) { + const num = parseFloat($sidebar.height()); + if (!isNaN(num)) { + jQuery('#dokuwiki__content').css('minHeight', num + 100); + } + } + }; + + /** + * Initialize the sidebar handle behaviour + */ + const initSidebarToggling = function () { + const $toggler = jQuery('.togglelink.page_main-content').find('a'); + $toggler.click(function (e) { + e.preventDefault(); + if (jQuery('body').hasClass('wide-content')) { + setDefaultContent(); + } else { + setWideContent(); + } + }); + }; + + // main + initContentNav(); + initSidebarToggling(); + initMenuHandling(); + initContentMinHeight(); }); + diff --git a/main.php b/main.php index 4527e46..941f24b 100755 --- a/main.php +++ b/main.php @@ -168,59 +168,14 @@ $classWideContent = ($ACT === "show") ? "": "wide-content "; ?>
-
diff --git a/script.js b/script.js index af31b5a..2b8a288 100755 --- a/script.js +++ b/script.js @@ -3,8 +3,5 @@ /* DOKUWIKI:include js/plugins/do_tasks.js */ -/* DOKUWIKI:include js/sidebar-menu.js */ /* DOKUWIKI:include js/meta-box.js */ - - /* DOKUWIKI:include js/sidebar.js */ diff --git a/tpl/main-sidebar-nav.php b/tpl/main-sidebar-nav.php new file mode 100644 index 0000000..81a985c --- /dev/null +++ b/tpl/main-sidebar-nav.php @@ -0,0 +1,71 @@ + + + + + + + + + + + + diff --git a/tpl/nav-main.php b/tpl/nav-main.php deleted file mode 100755 index 4349a58..0000000 --- a/tpl/nav-main.php +++ /dev/null @@ -1,12 +0,0 @@ -"; - echo "
".tpl_getLang('head_menu_main')."
"; - echo PHP_EOL; - tpl_include_page($conf['sidebar'], 1, 1); - echo PHP_EOL; - echo ""; - - endif ?> diff --git a/tpl/nav-sitetools.php b/tpl/nav-sitetools.php deleted file mode 100755 index cb57c7e..0000000 --- a/tpl/nav-sitetools.php +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/tpl/nav-trace.php b/tpl/nav-trace.php deleted file mode 100755 index 52ade4f..0000000 --- a/tpl/nav-trace.php +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/tpl/nav-usermenu.php b/tpl/nav-usermenu.php deleted file mode 100755 index f6fe3fe..0000000 --- a/tpl/nav-usermenu.php +++ /dev/null @@ -1,23 +0,0 @@ - - - - - From 83e6372c7d3f6cbe45ad2361c0dfc93169455296 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Thu, 16 Feb 2017 15:10:50 +0100 Subject: [PATCH 14/20] some style adjustments for the sidebar There is no need to differentiate between site tools and the navigation anymore. Instead now it's differentiated between the navigation tools and the displayed content. --- css/area_main-sidebar-content.less | 14 + ...idebar.less => area_main-sidebar-nav.less} | 4 +- css/area_sidetools.less | 342 ------------------ style.ini | 5 +- tpl/main-sidebar-nav.php | 2 +- 5 files changed, 20 insertions(+), 347 deletions(-) create mode 100644 css/area_main-sidebar-content.less rename css/{area_main-sidebar.less => area_main-sidebar-nav.less} (96%) delete mode 100755 css/area_sidetools.less diff --git a/css/area_main-sidebar-content.less b/css/area_main-sidebar-content.less new file mode 100644 index 0000000..60f6e1b --- /dev/null +++ b/css/area_main-sidebar-content.less @@ -0,0 +1,14 @@ +/** + * This file provides the design styles the non-navigational elements in the sidebar + */ +#dokuwiki__aside { + + // do not indent the first level of lists in the sidebar + ul, ol { + padding-left: 0; + + ul, ol { + padding-left: 1.5em; // same padding as defined in base styles + } + } +} diff --git a/css/area_main-sidebar.less b/css/area_main-sidebar-nav.less similarity index 96% rename from css/area_main-sidebar.less rename to css/area_main-sidebar-nav.less index 107f76a..8bc479e 100755 --- a/css/area_main-sidebar.less +++ b/css/area_main-sidebar-nav.less @@ -1,9 +1,9 @@ /** - * This file provides the design styles for the sidebar (navmain). + * This file provides the design styles the navigational elements in the sidebar * * @author Jana Deutschlaender + * @author Andreas Gohr */ - #dokuwiki__aside { @icon-size: @font-size-big; @menu-margin: @icon-size + @margin-small*2; // FIXME this is still wrong diff --git a/css/area_sidetools.less b/css/area_sidetools.less deleted file mode 100755 index 7d005da..0000000 --- a/css/area_sidetools.less +++ /dev/null @@ -1,342 +0,0 @@ -/** - * This file provides the design styles for the sitetools (nav). - * - * @author Jana Deutschlaender - */ - - -.side-tools.main-sidebar { - @icon-size: @margin-default; - - counter-increment: bar-counter; - - ul, - .trace { - padding-left: (@margin-big + .5); - } - - ul { - border-bottom: 1px solid @color-border; - } - - li { - list-style-type: none; - - a { - display: list-item; - list-style-type: square; - color: @color-nav; - padding-top: .1em; - padding-bottom: .1em; - transition: @transition color; - - &:hover, - &:focus, - &:active { - color: @button_background; - } - } - - &:first-of-type { - padding-top: .3em; - } - } - - > nav { - > ul { - margin: 0; - padding: 0 0 0 (@margin-big + .5); - - li { - list-style-type: none; - color: @color-nav; - - &:first-of-type { - padding-top: .3em; - } - } - } - } - - -/* + + + + + headlines for linklists + + + + + */ - h6 { - position: relative; - width: auto; - height: auto; - color: @color-nav; - font-weight: normal; - padding: .7em 0 .7em (@margin-big + 1.1); - - &[class="sr-only"] { - border-bottom: solid 1px @color-border; - } - - * { - color: inherit; - } - - -/* + + + icon + + + */ - &::before { - .display-flex(); - .flex-direction(); - .justify-content(); - - content: counter(bar-counter, lower-alpha); - position: absolute; - top: 0; - left: 0; - height: 100%; - width: @margin-big; - overflow: hidden; - text-align: center; - margin-top: auto; - margin-bottom: auto; - } - - -/* + + + line bottom + + + */ - &::after { - content: ''; - position: absolute; - top: 15%; - bottom: 15%; - width: 1px; - left: (@margin-big + .6); - background-color: @color-border; - transition: @transition background-color; - } - - + ul, - + div { - height: auto; - overflow: hidden; - } - - -/* + + + toggle + + + */ - &.opened, - &.closed { - padding: 0; - - &::before, - &::after { - display: none; - } - - a { - position: relative; - display: block; - border: 1px solid transparent; - border-radius: @fix_border-radius; - padding: .6em 0 .6em (@margin-big + 1); - transition: @transition color, @transition background-color, @transition border-color; - - &::before { - .display-flex(); - .flex-direction(); - .justify-content(); - - content: counter(bar-counter, lower-alpha); - position: absolute; - top: 1px; - bottom: 1px; - left: 0; - height: 100%; - width: @margin-big; - overflow: hidden; - color: @color-nav; - text-align: center; - margin-top: auto; - margin-bottom: auto; - transition: @transition color; - } - - &::after { - content: ''; - position: absolute; - top: 15%; - bottom: 15%; - width: 1px; - left: @margin-big; - background-color: @color-border; - transition: @transition background-color; - } - - &:hover, - &:focus, - &:active { - background-color: @button_color; - border-color: @button_background; - color: @button_background; - text-decoration: none; - - &::before { - color: @button_background; - } - - &::after { - background-color: @button_background; - } - } - } - } - - &.opened { - a { - background-color: @button_background; - color: @button_color; - - &::before { - background-color: @color-site-bg; - border-radius: @fix_border-radius 0 0 @fix_border-radius; - } - - &::after { - display: none; - } - - &:hover, - &:focus, - &:active { - background-color: @button_color; - border-color: @button_background; - color: @button_background; - text-decoration: none; - - &::before { - background-color: @button_color; - color: @button_background; - } - - &::after { - display: block; - background-color: @button_background; - } - } - } - - + ul, - + div { - height: auto; - overflow: hidden; - } - - + ul { - margin-top: .5em; - padding-bottom: .4rem; - } - } - - &.closed { - + ul, - + div { - height: 0; - a{ - display: none; - } - } - } - } - - -/* + + + + + f.i. trace + + + + + */ - p { - max-height: 6rem; - overflow-y: auto; - background-color: @color-content-bg; - border: 1px solid @button_background; - border-radius: @fix_border-radius; - font-size: @font-size-small; - margin-top: .1rem; - padding: .3rem .6em; - - * { - font-size: inherit; - } - - .bchead { - display: none; - } - - .bcsep { - float: left; - clear: both; - display: block; - width: 3%; - vertical-align: top; - color: @color-link; - padding-top: .3em; - } - - .breadcrumbs { - border: 0 none; - padding: 0; - margin: 0; - } - - bdi { - display: block; - float: left; - width: 94%; - line-height: 125%; - padding: .1rem 0; - margin-left: 2%; - } - - a { - cursor: pointer; - } - } -} - - -/* + + + + + + + + + + + + + + + + + + + + + + + + + + */ -/* min-width: 1440px */ - -@media @screen_min-xlg { - .side-tools.main-sidebar { - h6 { - font-size: @font-size-default; - - * { - font-size: inherit; - } - } - - li { - font-size: @font-size-small; - padding: .1em 0 .1em .1rem; - - > * { - font-size: inherit; - } - } - } -} - - -/* + + + + + + + + + + + + + + + + + + + + + + + + + + */ -/* max-width: 1439px */ - -@media @screen_max-xlg { - .side-tools.main-sidebar { - h6 { - font-size: @font-size-default; - } - - li { - font-size: @font-size-small; - padding: .15em 0 .15em .23rem; - - > * { - font-size: inherit; - } - } - - p { - font-size: @font-size-small; - } - } -} diff --git a/style.ini b/style.ini index 6785b2e..2e9d166 100755 --- a/style.ini +++ b/style.ini @@ -71,8 +71,9 @@ css/area_nav-usertools.less = all css/area_sidebar-search.less = all css/area_nav-pagetools.less = all css/area_nav-metabox.less = all -css/area_main-sidebar.less = all -css/area_sidetools.less = all +css/area_main-sidebar-nav.less = all +css/area_main-sidebar-content.less = all +;css/area_sidetools.less = all css/area_main-content.less = all css/area_togglelink.less = all diff --git a/tpl/main-sidebar-nav.php b/tpl/main-sidebar-nav.php index 81a985c..71597ea 100644 --- a/tpl/main-sidebar-nav.php +++ b/tpl/main-sidebar-nav.php @@ -61,7 +61,7 @@ $crumbs = array_reverse($crumbs, true); foreach($crumbs as $id => $name) { echo '
  • '; - tpl_link(wl($id), hsc($name), 'class="breadcrumbs" title="' . $id . '"'); + tpl_link(wl($id), hsc($name), 'title="' . $id . '"'); echo '
  • '; } ?> From 9eec2b7fe285e1884442b5912f9d6c2d7066faf3 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Thu, 16 Feb 2017 15:13:48 +0100 Subject: [PATCH 15/20] adjusted language --- lang/de/lang.php | 2 +- lang/en/lang.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/de/lang.php b/lang/de/lang.php index 21178ba..a45586b 100755 --- a/lang/de/lang.php +++ b/lang/de/lang.php @@ -18,7 +18,7 @@ $lang['head_quick_search'] = 'wikiübergreifende Schnellsuche'; $lang['head_menu_main'] = 'Hauptmenü'; $lang['head_menu_status'] = 'Seitenstatus'; $lang['head_breadcrumb'] = 'Standortanzeiger'; -$lang['head_menu_trace'] = 'zuletzt angesehen'; +$lang['head_menu_trace'] = 'Zuletzt Angesehen'; $lang['head_meta_box'] = 'Metainformationen zur Seite'; $lang['jump_to_quicksearch'] = 'Springe zur Schnellsuche'; diff --git a/lang/en/lang.php b/lang/en/lang.php index 20da35f..bc0a7c7 100755 --- a/lang/en/lang.php +++ b/lang/en/lang.php @@ -18,7 +18,7 @@ $lang['head_quick_search'] = 'quick search'; $lang['head_menu_main'] = 'main menu'; $lang['head_menu_status'] = 'site status'; $lang['head_breadcrumb'] = 'location indicator'; -$lang['head_menu_trace'] = 'trace / visited last'; +$lang['head_menu_trace'] = 'Last Visited Pages'; $lang['head_meta_box'] = 'meta data for this page'; $lang['jump_to_quicksearch'] = 'Jump to quick search'; From 373f4a8f3acebfaad275008f8efbc8353c69558f Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Thu, 16 Feb 2017 15:48:36 +0100 Subject: [PATCH 16/20] add level1 class to fake inpage lists --- tpl/main-sidebar-nav.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tpl/main-sidebar-nav.php b/tpl/main-sidebar-nav.php index 71597ea..ae3046b 100644 --- a/tpl/main-sidebar-nav.php +++ b/tpl/main-sidebar-nav.php @@ -10,7 +10,7 @@
    -