Index: openacs-4/packages/xowiki/tcl/tree-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/tree-procs.tcl,v diff -u -r1.23 -r1.24 --- openacs-4/packages/xowiki/tcl/tree-procs.tcl 11 Jul 2018 13:46:15 -0000 1.23 +++ openacs-4/packages/xowiki/tcl/tree-procs.tcl 3 Sep 2024 15:37:55 -0000 1.24 @@ -17,11 +17,12 @@ -superclass ::xo::OrderedComposite \ -parameter { {name ""} + {owner} {verbose 0} id } - # + # # Class methods # Tree proc renderer {style} { @@ -50,18 +51,18 @@ -category -orderby -itemobj - {-increasing:boolean true} + {-increasing:boolean true} {-open_item:boolean false} } { set items ${category}::items - if {![:isobject $items]} { + if {![nsf::is object $items]} { ::xo::OrderedComposite create $items if {[info exists orderby]} { - if {$orderby eq "page_order"} { - $items mixin add ::xo::OrderedComposite::IndexCompare - } set direction [expr {$increasing ? "increasing" : "decreasing"}] - $items orderby -order $direction $orderby + $items orderby \ + -order $direction \ + -type [ad_decode $orderby page_order index dictionary] \ + $orderby } } $items add $itemobj @@ -71,12 +72,18 @@ } } Tree instproc open_tree {} {;} - Tree instproc render {{-style mktree} {-js ""} {-context ""}} { + + Tree instproc render { + {-style mktree} + {-js ""} + {-context ""} + {-properties ""} + } { set renderer [[self class] renderer $style] $renderer set context $context $renderer set js $js TreeNode instmixin $renderer - set content [$renderer render [self]] + set content [$renderer render -properties $properties [self]] TreeNode instmixin "" if {[$renderer set js] ne ""} { template::add_body_script -script [$renderer set js] @@ -85,47 +92,70 @@ } Tree instproc add_pages { - {-full false} - {-remove_levels 0} - {-book_mode false} - {-open_page ""} + {-full:boolean false} + {-remove_levels:integer 0} + {-book_mode:boolean false} + {-open_page ""} {-expand_all false} - -owner - pages + {-properties ""} + -owner:object + pages:object } { + # + # Add the pages of the ordered composite to the tree. Note that + # it is intended to preserve the order criterion of the provided + # ordered composite in "pages". If you want to change the order + # set it already for the passed-in ordered composite via the + # method "orderby" of the OrderedComposite. + # set tree(-1) [self] set :open_node($tree(-1)) 1 set pos 0 - if {${:verbose}} {:log "add_pages want to add [llength [$pages children]] pages"} + if {${:verbose}} { + :log "add_pages want to add [llength [$pages children]] pages" + } + set ul_class [expr {[dict exists $properties CSSclass_ul] ? + [dict get $properties CSSclass_ul] : ""}] foreach o [$pages children] { $o instvar page_order title name - if {![regexp {^(.*)[.]([^.]+)} $page_order _ parent]} {set parent ""} + if {![regexp {^(.*)[.]([^.]+)} $page_order _ parent]} { + set parent "" + } set page_number [$owner page_number $page_order $remove_levels] - set level [regsub -all {[.]} [$o set page_order] _ page_order_js] - if {${:verbose}} {:log "... work on [$o set page_order] level $level full $full"} + set level [regsub -all -- {[.]} [$o set page_order] _ page_order_js] + if {${:verbose}} { + :log "... work on [$o set page_order] level $level full $full" + } if {$full || [info exists :open_node($parent)] || [info exists :open_node($page_order)]} { set href [$owner href $book_mode $name] set is_current [expr {$open_page eq $name}] set is_open [expr {$is_current || $expand_all}] - set c [::xowiki::TreeNode new -orderby pos -pos [incr pos] -level $level \ - -object $o -owner [self] \ - -label $title -prefix $page_number -href $href \ + set c [::xowiki::TreeNode new \ + -level $level \ + -object $o \ + -owner [self] \ + -label $title \ + -prefix $page_number \ + -href $href \ -highlight $is_current \ -expanded $is_open \ -open_requests 1 \ - -verbose ${:verbose}] + -verbose ${:verbose} \ + -ul_class $ul_class] set tree($level) $c for {set l [expr {$level - 1}]} {![info exists tree($l)]} {incr l -1} {} $tree($l) add $c - if {$is_open} {$c open_tree} + if {$is_open} { + $c open_tree + } } } return $tree(-1) } # - # ::xowiki::TreeNode + # ::xowiki::TreeNode # # The TreeNode represents an n-ary node storing its child nodes in # an ordered composite. In addition to its children, every node may @@ -137,7 +167,7 @@ level label pos {open_requests 0} count - {href ""} + {href ""} object owner li_id ul_id ul_class {prefix ""} {expanded false} @@ -152,15 +182,15 @@ TreeNode instproc some_child_has_items {} { foreach i [:children] { - if {[:isobject ${i}::items]} {return 1} + if {[nsf::is object ${i}::items]} {return 1} if {[$i some_child_has_items]} {return 1} } return 0 } TreeNode instproc render {} { set content "" - if {[:isobject [self]::items]} { + if {[nsf::is object [self]::items]} { foreach i [[self]::items children] { append cat_content [:render_item -highlight [$i exists open_item] $i ] } @@ -172,15 +202,17 @@ append content [:render_node -open true $cat_content] } - if {${:verbose}} {:log "TreeNode items [:isobject [self]::items] render open_requests ${:open_requests} -> $content"} + if {${:verbose}} { + :log "TreeNode items [nsf::is object [self]::items] render open_requests ${:open_requests} -> $content" + } return $content } # # The rendering of trees is performed via rendering classes. All # renderers are created and configured via the meta-class # TreeRenderer. This meta-class defines the common attributes and - # behavior of all TreeRenders. + # behavior of all TreeRenders. # # In particular, the TreeRenders are defined to work with xowiki's # page fragment caching. Via page fragment caching, the result of @@ -201,11 +233,18 @@ TreeRenderer instproc include_head_entries {args} { # to be overloaded } - TreeRenderer instproc render {tree} { + TreeRenderer instproc render {{-properties ""} tree} { set content "" foreach c [$tree children] {append content [$c render] \n} return $content } + TreeRenderer instproc get_property {properties property {default ""}} { + set value $default + if {[dict exists $properties $property]} { + set value [dict get $properties $property] + } + return $value + } # # The renderers should provide the following methods as procs @@ -214,7 +253,7 @@ # - render {tree} # # (both are optional) and the following methods as instprocs - # + # # - render_node {{-open:boolean false} cat_content} # - render_item {{-highlight:boolean false} item} # @@ -224,20 +263,24 @@ #-------------------------------------------------------------------------------- # List-specific renderer - # + # # This is a very common render that maps the tree structure into an # unordered HTML list. The rendered is specialized by e.g. the - # mktree an yuitree render below. - #-------------------------------------------------------------------------------- + # mktree a yuitree render below. + #-------------------------------------------------------------------------------- - TreeRenderer create TreeRenderer=list + TreeRenderer create TreeRenderer=list TreeRenderer=list proc include_head_entries {args} { # In the general case, we have nothing to include. More - # specialized renders will provide their head entries. + # specialized renders could provide their head entries. } - TreeRenderer=list proc render {tree} { - return "" + TreeRenderer=list proc render {{-properties ""} tree} { + set CSSclass [:get_property $properties CSSclass_ul \ + [:get_property $properties CSSclass_top_ul]] + set my_ul_class [expr {$CSSclass ne "" ? "class='$CSSclass' " : ""}] + return "" } + TreeRenderer=list instproc render_item {{-highlight:boolean false} item} { $item instvar title href set prefix [$item set prefix] @@ -259,8 +302,10 @@ set h_atts [lindex [$cl highlight_atts] [expr {${:highlight} ? 0 : 1}]] set u_atts "" - if {[info exists :li_id]} {append o_atts " id='${:li_id}'"} - if {[info exists :ul_id]} {append u_atts " id='${:ul_id}'"} + if {[info exists :li_id]} {append o_atts " id='${:li_id}'"} + if {[info exists :li_atts]} {append o_atts " ${:li_atts}"} + if {[info exists :ul_id]} {append u_atts " id='${:ul_id}'"} + if {[info exists :ul_atts]} {append u_atts " ${:ul_atts}"} if {[info exists :ul_class]} {append u_atts " class='${:ul_class}'"} set label [::xowiki::Includelet html_encode [:label]] @@ -281,9 +326,9 @@ } else { set content "" } - return "
  • ${:prefix} $entry$content" + return "
  • ${:prefix} $entry$content
  • " } - + #-------------------------------------------------------------------------------- # List-specific renderer based on mktree #-------------------------------------------------------------------------------- @@ -296,13 +341,13 @@ #::xo::Page requireJS "/resources/acs-templating/mktree.js" template::add_body_script -src "/resources/acs-templating/mktree.js" } - TreeRenderer=mktree proc render {tree} { + TreeRenderer=mktree proc render {{-properties ""} tree} { return "" } #-------------------------------------------------------------------------------- # List-specific renderer based for some menus - #-------------------------------------------------------------------------------- + #-------------------------------------------------------------------------------- TreeRenderer create TreeRenderer=samplemenu \ -superclass TreeRenderer=list \ -li_expanded_atts [list "class='menu-open'" "class='menu-closed'"] \ @@ -311,7 +356,7 @@ TreeRenderer=samplemenu proc include_head_entries {args} { # add your CSS here... } - TreeRenderer=samplemenu proc render {tree} { + TreeRenderer=samplemenu proc render {{-properties ""} tree} { return "" } @@ -337,7 +382,7 @@ append entry \ "" \ [ns_quotehtml $label] \ - "" + "" set o_atts "class='dropdown'" set u_atts "class='dropdown-menu'" @@ -362,8 +407,8 @@ } return "
  • $entry $content" } - - TreeRenderer=bootstrap3horizontal proc render {tree} { + + TreeRenderer=bootstrap3horizontal proc render {{-properties ""} tree} { set name [$tree name] if {$name ne ""} { set navbarLabel [subst { @@ -374,27 +419,27 @@ } else { set navbarLabel "" } - + return [subst { }] } - - + + #-------------------------------------------------------------------------------- # List-specific renderer based on yuitree #-------------------------------------------------------------------------------- TreeRenderer create TreeRenderer=yuitree \ -superclass TreeRenderer=list \ -li_expanded_atts [list "class='expanded'" ""] - TreeRenderer=yuitree proc include_head_entries {{-style ""} {-ajax 1} args} { + TreeRenderer=yuitree proc include_head_entries {{-style ""} {-ajax:boolean 1} args} { ::xo::Page requireCSS urn:ad:css:yui2:fonts/fonts-min ::xo::Page requireCSS urn:ad:css:yui2:treeview/assets/skins/sam/treeview if {$style ne ""} { @@ -405,64 +450,111 @@ ::xo::Page requireCSS urn:ad:css:yui2:treeview/assets/$style/tree } } + ::xo::Page requireJS urn:ad:js:yui2:yahoo/yahoo-min ::xo::Page requireJS urn:ad:js:yui2:yahoo-dom-event/yahoo-dom-event if {$ajax} { + ::xo::Page requireJS urn:ad:js:yui2:event/event-min ::xo::Page requireJS urn:ad:js:yui2:connection/connection-min ::xo::Page requireJS urn:ad:js:yui2:animation/animation-min } ::xo::Page requireJS urn:ad:js:yui2:treeview/treeview-min } - TreeRenderer=yuitree proc render {tree} { + TreeRenderer=yuitree proc render {{-properties ""} tree} { return "
    " } #-------------------------------------------------------------------------------- - # list-specific render with YUI drag and drop functionality - #-------------------------------------------------------------------------------- + # list-specific render with drag and drop functionality + #-------------------------------------------------------------------------------- TreeRenderer create TreeRenderer=listdnd \ -superclass TreeRenderer=list \ -li_expanded_atts [list "" ""] TreeRenderer=listdnd proc include_head_entries {args} { - set ajaxhelper 0 - ::xo::Page requireJS urn:ad:js:yui2:utilities/utilities - ::xo::Page requireJS urn:ad:js:yui2:selector/selector-min - ::xo::Page requireJS "/resources/xowiki/yui-page-order-region.js" + ::xo::Page requireJS "/resources/xowiki/listdnd.js" } - TreeRenderer=listdnd proc render {tree} { - array set "" ${:context} - if {[info exists (min_level)] && $(min_level) == 1} { - set css_class "page_order_region" + TreeRenderer=listdnd proc min_level {} { + if {[dict exists ${:context} min_level]} { + return [dict get ${:context} min_level] + } + return "" + } + TreeRenderer=listdnd proc add_handler {-id -event} { + template::add_event_listener \ + -id $id \ + -event $event \ + -preventdefault=false \ + -script "listdnd_${event}_handler(event);" + } + + TreeRenderer=listdnd proc render {{-properties ""} tree} { + #:log "=== TreeRenderer=listdnd render $tree" + # + # Do we allow reorder on the top-level? + # + set id [$tree id]-topul + if {[:min_level] == -1} { + set css_class "page_order_region" + foreach event {drop dragover dragleave} { + :add_handler -id $id -event $event + } } else { set css_class "page_order_region_no_target" } - return "
    " + #:log "=== TreeRenderer=listdnd render $tree min_level <[:min_level]>" + if {[$tree exists owner]} { + # + # assume, the "owner" is an includelet. + set owner [$tree set owner] + set page [$owner set __including_page] + set package_url [::[$page package_id] package_url] + set package_url_data " data-package_url='$package_url' data-folder_id='[$page parent_id]'" + } else { + set package_url_data "" + } + + return [subst {
    +
    + }] } TreeRenderer=listdnd instproc render_node {{-open:boolean false} cat_content} { + #:log "=== TreeRenderer=listdnd render_node $cat_content" #set open_state [expr {${:open_requests} > 0 ?"class='liOpen'" : "class='liClosed'"}] - #set cl [lindex [:info precedence] 0] - set obj ${:object} - set o [:owner] - $obj instvar page_order - set :li_id [::xowiki::Includelet js_name [$o set id]_$page_order] - set :ul_id [::xowiki::Includelet js_name [$o set id]__l${:level}_$page_order] + ${:object} instvar page_order - set cl [self class] - $cl append js "\nYAHOO.xo_page_order_region.DDApp.cd\['${:li_id}'\] = '$page_order';" + set :li_id [::xowiki::Includelet js_name [${:owner} set id]_$page_order] + set :ul_id [::xowiki::Includelet js_name [${:owner} set id]__l${:level}_$page_order] - array set "" [$cl set context] - set :ul_class [expr {[info exists (min_level)] && ${:level} >= $(min_level) ? - "page_order_region" : "page_order_region_no_target"}] + set min_level [[self class] min_level] + set reorder_child [expr {$min_level ne "" && ${:level} >= $min_level}] + set reorder_self [expr {$min_level ne "" && ${:level} > $min_level}] + #:log "=== render_node $page_order min_level $min_level level ${:level} reorder_child $reorder_child reorder_self $reorder_self" + + if {$reorder_child} { + foreach event {drop dragover dragleave} { + [self class] add_handler -id ${:ul_id} -event $event + } + set :ul_class "page_order_region" + } else { + set :ul_class "page_order_region_no_target" + } + if {$reorder_self} { + set :li_atts [subst {data-value='$page_order' draggable='true'}] + [self class] add_handler -id ${:li_id} -event dragstart + } + set :ul_id [::xowiki::Includelet js_name [${:owner} set id]__l${:level}_$page_order] + return [next] } #-------------------------------------------------------------------------------- # Tree renderer based on a section structure #-------------------------------------------------------------------------------- TreeRenderer create TreeRenderer=sections \ - -superclass TreeRenderer=list + -superclass TreeRenderer=list TreeRenderer=sections instproc render_item {{-highlight:boolean false} item} { $item instvar title href set prefix [$item set prefix] @@ -487,7 +579,7 @@ #-------------------------------------------------------------------------------- # Bootstrap tree renderer based on # http://jonmiles.github.io/bootstrap-treeview/ - #-------------------------------------------------------------------------------- + #-------------------------------------------------------------------------------- TreeRenderer create TreeRenderer=bootstrap3 TreeRenderer=bootstrap3 proc include_head_entries {args} { @@ -498,8 +590,7 @@ security::csp::require script-src cdnjs.cloudflare.com } - TreeRenderer=bootstrap3 proc render {tree} { - if {${:verbose}} {:log "TreeRenderer=bootstrap3 p=[:info precedence]"} + TreeRenderer=bootstrap3 proc render {{-properties ""} tree} { set jsTree [string trimright [next] ", \n"] set id [$tree id] set options "" @@ -548,23 +639,29 @@ #-------------------------------------------------------------------------------- # Bootstrap3 tree renderer with folder structure - #-------------------------------------------------------------------------------- + #-------------------------------------------------------------------------------- TreeRenderer create TreeRenderer=bootstrap3-folders -superclass TreeRenderer=bootstrap3 - TreeRenderer=bootstrap3-folders proc render {tree} { + TreeRenderer=bootstrap3-folders proc render {{-properties ""} tree} { set jsTree [string trimright [next] ", \n"] set id [$tree id] - set options "" - lappend options \ - "expandIcon: 'glyphicon glyphicon-folder-close'" \ - "collapseIcon: 'glyphicon glyphicon-folder-open'" \ - "enableLinks: true" + set options [list "enableLinks: true"] + # see list of possible icons: https://github.com/jonmiles/bootstrap-treeview + if {[::xowiki::CSS toolkit] eq "bootstrap5"} { + lappend options \ + "expandIcon: 'glyphicon glyphicon-none'" \ + "collapseIcon: 'bi bi-folder2-open'" + #"expandIcon: 'bi bi-folder'" + } else { + lappend options \ + "expandIcon: 'glyphicon glyphicon-none'" \ + "collapseIcon: 'glyphicon glyphicon-folder-open'" + } template::add_body_script -script "\n\$('#$id').treeview({data: \[$jsTree\], [join $options ,] });" return "
    " } - } -::xo::library source_dependent +::xo::library source_dependent # # Local variables: