From 12b9a472b5cdb98b1f30ef3df52d9067c2f38cfe Mon Sep 17 00:00:00 2001 From: =?utf8?q?Sven=20Sch=C3=B6ling?= Date: Fri, 14 May 2021 13:47:46 +0200 Subject: [PATCH] =?utf8?q?Layout:=20Javascript=20Men=C3=BC=20refactoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Das Javascript Menü war noch in einem Zustand aus der Anfangszeit von SL::Layout. Diese Änderungen teilen das in ein Main Layout (Javascript) und ein sub layout für das DHTMLMenu. Das DHTMLMenu Layout ist außerdem ein wenig optimiert. Es benutzt jetzt nicht mehr ein template sondern baut das DOM direkt zusammen (spart im hot path einen template aufruf und ist um faktor 5 schneller), und ausserdem werden die -f checks auf die icon Dateien jetzt mit SL::System::ResourceCache gecacht, so dass nicht für jeden Request ein paar Duzend stat() gemacht werden müssen. --- SL/Layout/DHTMLMenu.pm | 39 ++++++++++++++ SL/Layout/Javascript.pm | 59 ++------------------- SL/Presenter/JavascriptMenu.pm | 70 ++++++++++++++++++++++++ SL/System/ResourceCache.pm | 79 ++++++++++++++++++++++++++++ templates/webpages/menu/menunew.html | 39 -------------- 5 files changed, 191 insertions(+), 95 deletions(-) create mode 100644 SL/Layout/DHTMLMenu.pm create mode 100644 SL/Presenter/JavascriptMenu.pm create mode 100644 SL/System/ResourceCache.pm delete mode 100644 templates/webpages/menu/menunew.html diff --git a/SL/Layout/DHTMLMenu.pm b/SL/Layout/DHTMLMenu.pm new file mode 100644 index 000000000..43e83c153 --- /dev/null +++ b/SL/Layout/DHTMLMenu.pm @@ -0,0 +1,39 @@ +package SL::Layout::DHTMLMenu; + +use strict; +use parent qw(SL::Layout::Base); + +use SL::Presenter::JavascriptMenu qw(render_menu); + +sub static_javascripts { + qw(dhtmlsuite/menu-for-applications.js), +} + +sub javascripts_inline { +<<'EOJS', + DHTMLSuite.createStandardObjects(); + DHTMLSuite.configObj.setImagePath('image/dhtmlsuite/'); + var menu_model = new DHTMLSuite.menuModel(); + menu_model.addItemsFromMarkup('main_menu_model'); + menu_model.init(); + var menu_bar = new DHTMLSuite.menuBar(); + menu_bar.addMenuItems(menu_model); + menu_bar.setTarget('main_menu_div'); + menu_bar.init(); +EOJS +} + +sub pre_content { + render_menu($_[0]->menu), +} + +sub static_stylesheets { + qw( + dhtmlsuite/menu-item.css + dhtmlsuite/menu-bar.css + icons16.css + menu.css + ); +} + +1; diff --git a/SL/Layout/Javascript.pm b/SL/Layout/Javascript.pm index 85454c4ee..de3c7f3c0 100644 --- a/SL/Layout/Javascript.pm +++ b/SL/Layout/Javascript.pm @@ -4,6 +4,7 @@ use strict; use parent qw(SL::Layout::Base); use SL::Layout::None; +use SL::Layout::DHTMLMenu; use SL::Layout::Top; use SL::Layout::ActionBar; use SL::Layout::Content; @@ -17,64 +18,10 @@ sub init_sub_layouts { [ SL::Layout::None->new, SL::Layout::Top->new, + SL::Layout::DHTMLMenu->new, + $_[0]->sub_layouts_by_name->{actionbar}, SL::Layout::Content->new, ] } -sub javascripts { - my ($self) = @_; - - return uniq grep { $_ } map { $self->_find_javascript($_) } - map({ $_->javascripts } $self->sub_layouts), - qw(dhtmlsuite/menu-for-applications.js), - $_[0]->sub_layouts_by_name->{actionbar}->javascripts, - $self->use_javascript; -} - -sub javascripts_inline { - $_[0]->SUPER::javascripts_inline, -<<'EOJS', - DHTMLSuite.createStandardObjects(); - DHTMLSuite.configObj.setImagePath('image/dhtmlsuite/'); - var menu_model = new DHTMLSuite.menuModel(); - menu_model.addItemsFromMarkup('main_menu_model'); - menu_model.init(); - var menu_bar = new DHTMLSuite.menuBar(); - menu_bar.addMenuItems(menu_model); - menu_bar.setTarget('main_menu_div'); - menu_bar.init(); -EOJS - $_[0]->sub_layouts_by_name->{actionbar}->javascripts_inline, -} - -sub pre_content { - $_[0]->SUPER::pre_content . - $_[0]->presenter->render("menu/menunew", - force_ul_width => 1, - menu => $_[0]->menu, - icon_path => sub { my $simg = "image/icons/svg/$_[0].svg"; my $pimg="image/icons/16x16/$_[0].png"; -f $simg ? $simg : ( -f $pimg ? $pimg : ()) }, - max_width => sub { 10 * max map { length $::locale->text($_->{name}) } @{ $_[0]{children} || [] } }, - ) . - ($_[0]->sub_layouts_by_name->{actionbar}->pre_content // ''); -} - -sub stylesheets { - my ($self) = @_; - my $css_path = $self->get_stylesheet_for_user; - - return - uniq - grep { $_ } - map { $self->_find_stylesheet($_, $css_path) } - qw( - dhtmlsuite/menu-item.css - dhtmlsuite/menu-bar.css - icons16.css - menu.css - ), - ( map { $_->stylesheets } $_[0]->sub_layouts ), - $_[0]->sub_layouts_by_name->{actionbar}->stylesheets, - $_[0]->use_stylesheet; -} - 1; diff --git a/SL/Presenter/JavascriptMenu.pm b/SL/Presenter/JavascriptMenu.pm new file mode 100644 index 000000000..24999b20c --- /dev/null +++ b/SL/Presenter/JavascriptMenu.pm @@ -0,0 +1,70 @@ +package SL::Presenter::JavascriptMenu; + +use strict; +use SL::Presenter::EscapedText qw(escape is_escaped); +use SL::Presenter::Tag qw( html_tag link_tag); +use SL::Locale::String qw(t8); +use SL::System::ResourceCache; + +use List::Util qw(max); + +use Exporter qw(import); +our @EXPORT_OK = qw(render_menu); + +sub render_menu { + my ($menu) = @_; + + html_tag('div', '', id => 'main_menu_div') . + html_tag('ul', render_children($menu, 100, $menu->{tree}), + id => "main_menu_model", + style => 'display:none', + ); +} + +sub render_node { + my ($menu, $node, $id) = @_; + return '' if !$node->{visible}; + + my $icon = get_icon($node->{icon}); + my $link = $menu->href_for_node($node) || '#'; + my $name = $menu->name_for_node($node); + + html_tag('li', + link_tag($link, $name, target => $node->{target}) + . html_tag('ul', render_children($menu, $id * 100, $node->{children} // []), + width => max_width($node) + ), + id => $id, + (itemIcon => $icon)x!!$icon, + ) +} + +sub render_children { + my ($menu, $id, $children) = @_; + my $sub_id = 1; + + join '', map { + render_node($menu, $_, 100 * $id + $sub_id++) + } @$children +} + +sub max_width { + 11 * ( max( map { length $::locale->text($_->{name}) } @{ $_[0]{children} || [] } ) // 1 ) +} + +sub get_icon { + my $name = $_[0]; + + return undef if !defined $name; + + my $simg = "image/icons/svg/$name.svg"; + my $pimg = "image/icons/16x16/$name.png"; + + SL::System::ResourceCache->get($simg) ? $simg + : SL::System::ResourceCache->get($pimg) ? $pimg + : (); +} + +1; + + diff --git a/SL/System/ResourceCache.pm b/SL/System/ResourceCache.pm new file mode 100644 index 000000000..be7a1d4ec --- /dev/null +++ b/SL/System/ResourceCache.pm @@ -0,0 +1,79 @@ +package SL::System::ResourceCache; + +use strict; +use File::stat; +use File::Find; + +our @paths = qw(image css); +our $cache; + +sub generate_data { + return if $cache; + + $cache = {}; + + File::Find::find(sub { + $cache->{ $File::Find::name =~ s{^\./}{}r } = stat($_); + }, @paths); +} + +sub get { + my ($class, $file) = @_; + no warnings 'once'; + + return stat($file) if ($::dispatcher // { interface => 'cgi' })->{interface} eq 'cgi'; + + $class->generate_data; + $cache->{$file}; +} + +1; + + +__END__ + +=encoding utf-8 + +=head1 NAME + +SL::System::ResourceCache - provides access to resource files without having to access the filesystem all the time + +=head1 SYNOPSIS + + use SL::System::ResourceCache; + + SL::System::ResourceCache->get($filename); + +=head1 DESCRIPTION + +This will stat() all files in the configured paths at startup once, so that +subsequent calls can use the cached values. Particularly useful for icons in +the menu, which would otherwise generate a few hundred file sytem accesses per +request. + +The caching will not happen in CGI and script environments. + +=head1 FUNCTIONS + +=over 4 + +=item * C + +If the file exists, returns a L object. If it doesn't exists, returns undef. + +=back + +=head1 BUGS + +None yet :) + +=head1 TODO + +Make into instance cache and keep it as system wide object + +=head1 AUTHOR + +Sven Schöling Esven.schoeling@googlemail.comE + +=cut + diff --git a/templates/webpages/menu/menunew.html b/templates/webpages/menu/menunew.html deleted file mode 100644 index df3ed7b39..000000000 --- a/templates/webpages/menu/menunew.html +++ /dev/null @@ -1,39 +0,0 @@ -[%- USE T8 %] -[%- USE L %] -[%- USE HTML %] -[%- USE LxERP -%] - - [%- SET main_id = '100' %] - -- 2.20.1