Index: openacs-4/packages/xowiki/tcl/form-field-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/form-field-procs.tcl,v diff -u -r1.145 -r1.146 --- openacs-4/packages/xowiki/tcl/form-field-procs.tcl 3 Apr 2009 15:21:29 -0000 1.145 +++ openacs-4/packages/xowiki/tcl/form-field-procs.tcl 8 Apr 2009 10:59:42 -0000 1.146 @@ -1398,7 +1398,7 @@ if {![my exists options]} {my options [list]} } select instproc render_input {} { - set atts [my get_attributes id name disabled] + set atts [my get_attributes id name disabled {CSSclass class}] if {[my multiple]} {lappend atts multiple [my multiple]} set options [my options] if {![my required]} { @@ -1466,6 +1466,59 @@ } } + abstract_page instproc render_input {} { + if {[my multiple]} { + # utilities.js aggregates "yahoo, dom, event, connection, animation, dragdrop" + set ajaxhelper 0 + ::xowiki::Includelet require_YUI_JS -ajaxhelper $ajaxhelper "utilities/utilities.js" + ::xowiki::Includelet require_YUI_JS -ajaxhelper $ajaxhelper "selector/selector-min.js" + ::xo::Page requireJS "/resources/xowiki/yui-selection-area.js" + + set js "" + foreach o [my options] { + foreach {label rep} $o break + set js_label [::xowiki::Includelet js_encode $label] + set js_rep [::xowiki::Includelet js_encode $rep] + append js "YAHOO.xo_sel_area.DDApp.values\['$js_label'\] = '$js_rep';\n" + append js "YAHOO.xo_sel_area.DDApp.dict\['$js_rep'\] = '$js_label';\n" + } + + ::html::div -class workarea { + ::html::h3 { ::html::t "Selection"} + set values "" + foreach v [my value] { + append values $v \n + set __values($v) 1 + } + my CSSclass selection + my set cols 30 + set atts [my get_attributes id name disabled {CSSclass class}] + # TODO what todo with DISABLED? + ::html::textarea [my get_attributes id name cols rows style {CSSclass class} disabled] { + ::html::t $values + } + } + ::html::div -class workarea { + ::html::h3 { ::html::t "Candidates"} + ::html::ul -id [my id]_candidates -class region { + #my msg [my options] + foreach o [my options] { + foreach {label rep} $o break + # Don't show current values under candidates + if {[info exists __values($rep)]} continue + ::html::li -class candidates {::html::t $rep} + } + } + } + ::html::div -class visual-clear { + ;# maybe some comment + } + ::html::script { html::t $js } + } else { + next + } + } + ########################################################### # # ::xowiki::formfield::form_page Index: openacs-4/packages/xowiki/tcl/includelet-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/includelet-procs.tcl,v diff -u -r1.103 -r1.104 --- openacs-4/packages/xowiki/tcl/includelet-procs.tcl 3 Apr 2009 15:16:24 -0000 1.103 +++ openacs-4/packages/xowiki/tcl/includelet-procs.tcl 8 Apr 2009 10:59:43 -0000 1.104 @@ -97,6 +97,10 @@ return [string map [list : _ # _] $name] } + ::xowiki::Includelet proc js_encode {string} { + string map [list \n \\n \" {\"} ' {\'}] $string + } + ::xowiki::Includelet proc html_id {name} { # Construct a valid HTML id or name. # For details, see http://www.w3.org/TR/html4/types.html @@ -129,30 +133,6 @@ return $link } - ::xowiki::Includelet proc incr_page_order {p} { - regexp {^(.*[.]?)([^.])$} $p _ prefix suffix - if {[string is integer -strict $suffix]} { - incr suffix - } elseif {[string is lower -strict $suffix]} { - regexp {^(.*)(.)$} $suffix _ before last - if {$last eq "z"} { - set last "aa" - } else { - set last [format %c [expr {[scan $last %c] + 1}]] - } - set suffix $before$last - } elseif {[string is upper -strict $suffix]} { - regexp {^(.*)(.)$} $suffix _ before last - if {$last eq "Z"} { - set last "AA" - } else { - set last [format %c [expr {[scan $last %c] + 1}]] - } - set suffix $before$last - } - return $prefix$suffix - } - ::xowiki::Includelet proc publish_status_clause {{-base_table ci} value} { if {$value eq "all"} { # legacy @@ -2305,6 +2285,7 @@ {-menu_buttons edit} {-locale ""} {-range ""} + {-allow_reorder ""} }} } @@ -2315,6 +2296,23 @@ lappend ::xowiki_page_item_id_rendered [$__including_page item_id] $__including_page set __is_book_page 1 + if {$allow_reorder ne ""} { + set granted [$package_id check_permissions \ + -user_id [[$package_id context] user_id] \ + -package_id $package_id \ + $package_id change_page_order] + #my msg "granted=$granted" + if {$granted} { + set ajaxhelper 0 + ::xowiki::Includelet require_YUI_JS -ajaxhelper $ajaxhelper "utilities/utilities.js" + ::xowiki::Includelet require_YUI_JS -ajaxhelper $ajaxhelper "selector/selector-min.js" + ::xo::Page requireJS "/resources/xowiki/yui-page-order-region.js" + } else { + # the user has not enough permissions, so disallow + set allow_reorder "" + } + } + set extra_where_clause "" set cnames "" if {[info exists category_id]} { @@ -2350,11 +2348,53 @@ } } + if {$allow_reorder ne ""} { + set js "YAHOO.xo_page_order_region.DDApp.report_link = '[$package_id package_url]';\n" + set last_level 0 + set ID [my js_name] + if {[string is integer -strict $allow_reorder]} { + set min_level $allow_reorder + } else { + set min_level 1 + } + } + foreach o [$pages children] { $o instvar page_order title page_id name title - set level [expr {[regsub -all {[.]} $page_order . page_order] + 1}] - set p [::xo::db::CrClass get_instance_from_db -item_id 0 -revision_id $page_id] + set level [expr {[regsub -all {[.]} $page_order _ page_order_js] + 1}] + if {$allow_reorder ne ""} { + # + # Build a (nested) list structure mirroring the hierarchy + # implied by the page_order. In essence, we provide CSS + # classes for the ULs and provide IDs for ULs and LI elements, + # and pass the associated page_order to javascript. + # + if {![regexp {^(.*)[.][^.]+$} $page_order _ prefix]} {set prefix ""} + + # First, insert the appropriate opening and closing of ULs. We + # could handle here prefix changes as well as different lists + # (e.g. 1.1 1.2 2.1) + # + if {$last_level != $level} { + for {set l $last_level} {$l > $level} {incr l -1} {append output "\n" } + for {set l $last_level} {$l < $level} {incr l} { + regsub -all {[.]} $prefix _ prefix_js + set id ${ID}__l${level}_${prefix_js} + set css_class [expr {$l+1 >= $min_level ? "page_order_region" : "page_order_region_no_target"}] + append output "\n" } + append output "\n" + } + return $output } } @@ -2528,7 +2574,7 @@ my get_parameters my instvar __including_page return_url set page [expr {[info exists page_id] ? $page_id : $__including_page}] - set page_order [::xowiki::Includelet incr_page_order [$page page_order]] + set page_order [::xowiki::utility incr_page_order [$page page_order]] if {[$page istype ::xowiki::FormPage]} { set template [$page page_template] return [my render_button \ Index: openacs-4/packages/xowiki/tcl/package-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/package-procs.tcl,v diff -u -r1.168 -r1.169 --- openacs-4/packages/xowiki/tcl/package-procs.tcl 26 Mar 2009 13:19:31 -0000 1.168 +++ openacs-4/packages/xowiki/tcl/package-procs.tcl 8 Apr 2009 10:59:43 -0000 1.169 @@ -609,8 +609,8 @@ # set exported [[my set policy] defined_methods Package] foreach m $exported { - #my msg "--QP my exists_query_parameter $m = [my exists_query_parameter $m]" - if {[my exists_query_parameter $m]} { + #my log "--QP my exists_query_parameter $m = [my exists_query_parameter $m] || [my exists_form_parameter $m]" + if {[my exists_query_parameter $m] || [my exists_form_parameter $m]} { set method $m ;# determining the method, similar file extensions return [self] } @@ -971,6 +971,93 @@ } # + # change_page_order (normally called via ajax POSTs) + # + Package ad_instproc change_page_order {} { + change_page_order for pages + } { + my instvar folder_id + set to [string trim [my form_parameter to ""]] + set from [string trim [my form_parameter from ""]] + set clean [string trim [my form_parameter clean ""]] ;# only for inserts + + #set from {1.2 1.3 1.4}; set to {1.3 1.4 1.2}; set clean {...} + #set from {1.2 1.3 1.4}; set to {1.3 1.4 2.1 1.2}; set clean {2.1} + #set from {1 2}; set to {1 1.2 2}; set clean {1.2 1.3 1.4} + + if {$from eq "" || $to eq "" || [llength $to]-[llength $from] >1 || [llength $to]-[llength $from]<0} { + my log "unreasonable request from='$from', to='$to'" + return + } + my log "--cpo from=$from, to=$to, clean=$clean" + set gap_renames [list] + # + # We distinguish two cases: + # - pure reordering: length(to) == length(from) + # - insert from another section: length(to) == length(from)+1 + # + if {[llength $to] == [llength $from]} { + my log "--cpo reorder" + } elseif {[llength $clean] > 1} { + my log "--cpo insert" + # + # We have to fill the gap. First, find the newly inserted + # element in $to. + # + foreach e $to { + if {[lsearch -exact $from $e] == -1} { + set inserted $e + break + } + } + if {![info exists inserted]} {error "invalid 'to' list (no inserted element detected)"} + # + # compute the remaining list + # + set remaining [list] + foreach e $clean {if {$e ne $inserted} {lappend remaining $e}} + # + # compute rename rename commands for it + # + set gap_renames [::xowiki::utility page_order_renames -parent_id $folder_id \ + -start [lindex $clean 0] -from $remaining -to $remaining] + foreach {page_id item_id name old_page_order new_page_order} $gap_renames { + my log "--cpo gap $page_id (name) rename $old_page_order to $new_page_order" + } + } + # + # Compute the rename commands for the drop target + # + set drop_renames [::xowiki::utility page_order_renames -parent_id $folder_id \ + -start [lindex $from 0] -from $from -to $to] + #my log "--cpo drops l=[llength $drop_renames]" + foreach {page_id item_id name old_page_order new_page_order} $drop_renames { + my log "--cpo drop $page_id ($name) rename $old_page_order to $new_page_order" + } + + # + # Perform the actual renames + # + set temp_obj [::xowiki::Page new -name dummy -volatile] + set slot [$temp_obj find_slot page_order] + db_transaction { + foreach {page_id item_id name old_page_order new_page_order} [concat $drop_renames $gap_renames] { + #my log "--cpo UPDATE $page_id new_page_order $new_page_order" + $temp_obj update_attribute_from_slot -revision_id $page_id $slot $new_page_order + ::xo::clusterwide ns_cache flush xotcl_object_cache ::$item_id + ::xo::clusterwide ns_cache flush xotcl_object_cache ::$page_id + } + } + # + # Flush the page fragement caches (page fragments based on page_order might be sufficient) + my flush_page_fragment_cache -scope agg + + ns_return 200 text/plain ok + } + + + + # # RSS 2.0 support # Package ad_instproc rss { @@ -1177,7 +1264,7 @@ if {![string is integer -strict $object_id]} { return [my error_msg "No valid object_id provided!"] } # flush could be made more precise in the future - [my id] flush_page_fragment_cache -scope agg + my flush_page_fragment_cache -scope agg my returnredirect [site_node::get_package_url -package_key categories]cadmin/object-map?ctx_id=$object_id&object_id=$object_id } @@ -1193,7 +1280,7 @@ if {![string is integer -strict $tree_id]} { return [my error_msg "No valid tree_id provided!"] } # flush could be made more precise in the future - [my id] flush_page_fragment_cache -scope agg + my flush_page_fragment_cache -scope agg my returnredirect [site_node::get_package_url -package_key categories]cadmin/tree-view?tree_id=$tree_id&ctx_id=$object_id&object_id=$object_id } @@ -1308,7 +1395,7 @@ switch -- $scope { agg {set key PF-[my id]-agg-*} all {set key PF-[my id]-*} - default {error "unknown cope for flushing page fragment cache"} + default {error "unknown scope for flushing page fragment cache"} } foreach entry [ns_cache names xowiki_cache $key] { ns_log notice "::xo::clusterwide ns_cache flush xowiki_cache $entry" @@ -1350,6 +1437,7 @@ Class Package -array set require_permission { reindex swa + change_page_order {{id admin}} import_prototype_page swa rss none google-sitemap none @@ -1406,6 +1494,7 @@ rss none google-sitemap none google-sitemapindex none + change_page_order {{id admin}} manage-categories {{id admin}} edit-category-tree {{id admin}} delete swa @@ -1459,6 +1548,7 @@ rss none google-sitemap none google-sitemapindex none + change_page_order {{id admin}} manage-categories {{id admin}} edit-category-tree {{id admin}} delete swa @@ -1515,6 +1605,7 @@ rss none google-sitemap none google-sitemapindex none + change_page_order {{id admin}} manage-categories {{id admin}} edit-category-tree {{id admin}} delete swa Index: openacs-4/packages/xowiki/tcl/xowiki-utility-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/xowiki-utility-procs.tcl,v diff -u -r1.1 -r1.2 --- openacs-4/packages/xowiki/tcl/xowiki-utility-procs.tcl 6 Jan 2009 01:10:59 -0000 1.1 +++ openacs-4/packages/xowiki/tcl/xowiki-utility-procs.tcl 8 Apr 2009 10:59:43 -0000 1.2 @@ -308,4 +308,100 @@ return $msg } + ::xowiki::utility proc incr_page_order {p} { + regexp {^(.*[.]?)([^.])$} $p _ prefix suffix + if {[string is integer -strict $suffix]} { + incr suffix + } elseif {[string is lower -strict $suffix]} { + regexp {^(.*)(.)$} $suffix _ before last + if {$last eq "z"} { + set last "aa" + } else { + set last [format %c [expr {[scan $last %c] + 1}]] + } + set suffix $before$last + } elseif {[string is upper -strict $suffix]} { + regexp {^(.*)(.)$} $suffix _ before last + if {$last eq "Z"} { + set last "AA" + } else { + set last [format %c [expr {[scan $last %c] + 1}]] + } + set suffix $before$last + } + return $prefix$suffix + } + + ::xowiki::utility proc page_order_compute_new_names {start page_orders} { + lappend pairs [lindex $page_orders 0] $start + foreach p [lrange $page_orders 1 end] { + lappend pairs $p [set start [my incr_page_order $start]] + } + return $pairs + } + + ::xowiki::utility proc get_page_order_items {-parent_id page_orders} { + set likes [list] + foreach page_order $page_orders { + if {[::xo::db::has_ltree]} { + lappend likes "p.page_order <@ '$page_order'" + } else { + lappend likes "p.page_order = '$page_order'" "p.page_order like '$page_order.%'" + } + } + set sql "select p.page_order, p.page_id, cr.item_id, ci.name + from xowiki_page p, cr_items ci, cr_revisions cr \ + where p.page_id = ci.live_revision \ + and p.page_id = cr.revision_id \ + and ci.publish_status <> 'production' \ + and ci.parent_id = $parent_id \ + and ([join $likes { or }])" + my log $sql + set pages [db_list_of_lists [my qn get_pages_with_page_order] $sql] + return $pages + } + + ::xowiki::utility proc page_order_renames {-parent_id -start -from -to} { + set pages [my get_page_order_items -parent_id $parent_id $to] + my log "pages=$pages" + array set npo [::xowiki::utility page_order_compute_new_names $start $to] + my log npo=[array get npo]=>to='$to' + set renames [list] + foreach tuple $pages { + foreach {old_page_order page_id item_id name} $tuple break + if {[info exists npo($old_page_order)]} { + # + # We have a name in the translation list + # + if {$npo($old_page_order) eq $old_page_order} { + # Nothing to do + #my log "--cpo name $old_page_order not changed" + } else { + #my log "--cpo name $old_page_order changed to '$npo($old_page_order)'" + lappend renames $page_id $item_id $name $old_page_order $npo($old_page_order) + } + } else { + # + # We have no translation in the list. This must be an item + # from a subtree of changed page_orders. + # + #my log "--cpo no translation for $old_page_order, check prefix" + foreach new_name [array names npo] { + if {[string match $new_name.* $old_page_order]} { + # + # The name matches. Add to the rename list if the prefix name actually changed. + # + if {$npo($new_name) ne $new_name} { + set l [string length $new_name] + set new_page_order "$npo($new_name)[string range $old_page_order $l end]" + my log "--cpo tree name $old_page_order changed to '$new_page_order'" + lappend renames $page_id $item_id $name $old_page_order $new_page_order + } + break + } + } + } + } + return $renames + } } Index: openacs-4/packages/xowiki/www/resources/xowiki.css =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/xowiki.css,v diff -u -r1.44 -r1.45 --- openacs-4/packages/xowiki/www/resources/xowiki.css 16 Feb 2009 12:11:55 -0000 1.44 +++ openacs-4/packages/xowiki/www/resources/xowiki.css 8 Apr 2009 10:59:43 -0000 1.45 @@ -314,7 +314,65 @@ margin: 0px; padding: 0px; } -/* child selectors are apparently not supported by IE 5 and 6, +/* for yui dnd stuff */ +div.xowiki-content div.workarea { padding-right: 10px; float:left; } +div.xowiki-content div.workarea h3 { margin-top: 0px; margin-bottom: 0.5ex;} + +div.xowiki-content ul.region { + border: 1px solid gray; + position: relative; + width: 300px; + list-style: none; + margin:0; + padding:0; + /* + The bottom padding provides the cushion that makes the empty + list targetable. Alternatively, we could leave the padding + off by default, adding it when we detect that the list is empty. + */ + padding-bottom:3ex; +} + +div.xowiki-content ul.region li { + margin: 1px; + cursor: move; +} + +div.xowiki-content li.candidates { + background-color: #D1E6EC; + border:1px solid #7EA6B2; +} + +div.xowiki-content li.selection { + background-color: #D8D4E2; + border:1px solid #6B4C86; +} + +div.xowiki-content div.book ul.page_order_region { + border: 0px solid gray; + width: 100%; + list-style: none; + margin: 10px; + padding:0; + /* + The bottom padding provides the cushion that makes the empty + list targetable. Alternatively, we could leave the padding + off by default, adding it when we detect that the list is empty. + */ + padding-bottom:3ex; +} +div.xowiki-content div.book ul.page_order_region_no_target { + border: 0px solid gray; + width: 100%; + list-style: none; + margin: 10px; + padding: 0; +} + +/* + Handling hidden field-sets: + + child selectors are apparently not supported by IE 5 and 6, but the content is usable in IE anyhow; maybe we have to use the parent-node hack Index: openacs-4/packages/xowiki/www/resources/yui-page-order-region.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/yui-page-order-region.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/yui-page-order-region.js 8 Apr 2009 10:59:43 -0000 1.1 @@ -0,0 +1,284 @@ +/* + * A simple drag and drop handler based on YUI for lists to be used + * e.g. with the book includelet. + * + * The handler knows about the two CSS + * classes page_order_region and page_order_region_no_target, where + * page_order_regions are used for drag and drop. The handler + * maintains client data (cd) associated with the list items, which + * has to be initialized by the application. + * + * Note: it might not be a good idea to use this with large list + * structures asi it is, since the drag-proxy might be quite large. + * + * Example usage with the book includelet: + * + * {{book -menu_buttons "edit create delete" -allow_reorder 2}} + * + * which means: only support drag&drop starting with the 2nd level + * + * Gustaf Neumann fecit April 2009 + */ + +YAHOO.namespace('xo_page_order_region'); + +YAHOO.xo_page_order_region.DragnDrop = function() { + + var Dom = YAHOO.util.Dom; + var Event = YAHOO.util.Event; + var DDM = YAHOO.util.DragDropMgr; + +YAHOO.xo_page_order_region.DDApp = { + with_DDTarget: 1, + + report_link: "", + cd: new Array(), + source_region: new Array(), + level: new Array(), + highest_level: 0, + highest_region: 0, + + init: function() { + // Ok, now we use standard drag and drop, modeled around the + // example from YUI dnd. We have now two HTML lists to be + // handled as regions for drag and drop. + //var regions = YAHOO.util.Selector.query('ul.page_order_region'); + var regions = YAHOO.util.Selector.query('ul.page_order_region, ul.page_order_region_no_target'); + for (var i = 0; i < regions.length; i++) { + + if (this.with_DDTarget) { + new YAHOO.util.DDTarget(regions[i].id); + } + + // compute the order of items per region + var order = ""; + var items = regions[i].childNodes; + + for (var j = 0; j < items.length; j++) { + if (items[j].nodeName != 'LI') {continue;} + + // add the DDList only for list items in UL with class + // "page_order_region" + if (YAHOO.util.Dom.hasClass(regions[i],"page_order_region")) { + new YAHOO.xo_page_order_region.DDList(iid); + } + + var iid = items[j].id; + // console.log(iid + " => " + this.cd[iid] ); + order += this.cd[iid] + " "; + // Keep as well the source regions + this.source_region[iid] = regions[i]; + } + // finally, remeber the level and store the original order + this.level[regions[i].id] = this.cd[iid].split(/[.]/g).length; + this.cd[regions[i].id] = order; + //console.log("initial region: " + regions[i].id + " => " + order); + } + }, + + report_level: function(id, e) { + if (this.level[id] != undefined) { + // Keep the highest level in case we drop on nested targets + if (this.highest_level < this.level[id]) { + this.highest_level = this.level[id]; + this.highest_region = id; + } + } + }, + + finish: function(e) { + + //console.log("finish " + this.highest_region); + // if we have no highest_region, the drop was not on a drop target + if (this.highest_region == 0) { + // get the ul via the provided element. This might not be the + // list you are expecting, if there are nested lists. + var ul = document.getElementById(e.id).parentNode; + //console.info(ul); + } else { + // The variable highest_region is only used, if ul's are + // defined as DDTargets + var ul = document.getElementById(this.highest_region); + } + + //console.log(this.cd[e.id] + " landed in region "+ ul.id + " => " + this.cd[ul.id]); + + // Process childNodes of the ul simply to avoid to include + // nested items + var order = ""; + var items = ul.childNodes; + + for (var j = 0; j < items.length; j++) { + if (items[j].nodeName != 'LI') {continue;} + var iid = items[j].id; + order += this.cd[iid] + " "; + //console.log(iid + " => " + this.cd[iid]); + } + + //console.log(this.cd[ul.id] + " => " + order + " => " + this.cd[this.source_region[e.id].id]); + + if (this.report_link != '' && order != '') { + this.callback = { + success: function(o) { + //window.location.href=window.location.href; + window.location.reload(); + } + } + YAHOO.util.Connect.asyncRequest('POST', this.report_link, this.callback, + 'change_page_order=1' + + '&from=' + escape(this.cd[ul.id]) + + '&to=' + escape(order) + + '&clean=' + escape(this.cd[this.source_region[e.id].id])); + } + }, + +}; + +///////////////////////////////////////////////////////////////////////////// +// custom drag and drop implementation +////////////////////////////////////////////////////////////////////////////// + +YAHOO.xo_page_order_region.DDList = function(id, sGroup, config) { + + YAHOO.xo_page_order_region.DDList.superclass.constructor.call(this, id, sGroup, config); + + var el = this.getDragEl(); + Dom.setStyle(el, "opacity", 0.67); // The proxy is slightly transparent + + this.goingUp = false; + this.lastY = 0; +}; + +YAHOO.extend(YAHOO.xo_page_order_region.DDList, YAHOO.util.DDProxy, { + + startDrag: function(x, y) { + + // make the proxy look like the source element + var dragEl = this.getDragEl(); + var clickEl = this.getEl(); + Dom.setStyle(clickEl, "visibility", "hidden"); + + dragEl.innerHTML = clickEl.innerHTML; + + Dom.setStyle(dragEl, "color", Dom.getStyle(clickEl, "color")); + Dom.setStyle(dragEl, "backgroundColor", Dom.getStyle(clickEl, "backgroundColor")); + Dom.setStyle(dragEl, "border", "2px solid gray"); + + // make sure, this is always initialized + YAHOO.xo_page_order_region.DDApp.highest_region = 0; + }, + + endDrag: function(e) { + //console.log("endDrag"); + var srcEl = this.getEl(); + var proxy = this.getDragEl(); + + // Show the proxy element and animate it to the src element's location + Dom.setStyle(proxy, "visibility", ""); + var a = new YAHOO.util.Motion( + proxy, { + points: { + to: Dom.getXY(srcEl) + } + }, + 0.2, + YAHOO.util.Easing.easeOut + ) + var proxyid = proxy.id; + var thisid = this.id; + + // Hide the proxy and show the source element when finished with the animation + a.onComplete.subscribe(function() { + Dom.setStyle(proxyid, "visibility", "hidden"); + Dom.setStyle(thisid, "visibility", ""); + }); + a.animate(); + YAHOO.xo_page_order_region.DDApp.finish(this); + }, + + onDragDrop: function(e, id) { + //console.log("onDragDrop"); + // If there is one drop interaction, the li was dropped either on the list, + // or it was dropped on the current location of the source element. + if (DDM.interactionInfo.drop.length === 1) { + + // The position of the cursor at the time of the drop (YAHOO.util.Point) + var pt = DDM.interactionInfo.point; + + // The region occupied by the source element at the time of the drop + var region = DDM.interactionInfo.sourceRegion; + + // Check to see if we are over the source element's location. We will + // append to the bottom of the list once we are sure it was a drop in + // the negative space (the area of the list without any list items) + if (!region.intersect(pt)) { + var destEl = Dom.get(id); + var destDD = DDM.getDDById(id); + destEl.appendChild(this.getEl()); + destDD.isEmpty = false; + DDM.refreshCache(); + //console.log('no intersect'); + } else { + // console.log('intersect'); + } + } else { + // console.log('interactions l=' + DDM.interactionInfo.drop.length); + } + YAHOO.xo_page_order_region.DDApp.report_level(id, this); + }, + + onDrag: function(e) { + + // Keep track of the direction of the drag for use during onDragOver + var y = Event.getPageY(e); + + if (y < this.lastY) { + this.goingUp = true; + } else if (y > this.lastY) { + this.goingUp = false; + } + + this.lastY = y; + }, + + onDragEnter: function(e, id) { + var destEl = Dom.get(id); + var p = destEl.parentNode; + Dom.setStyle(p, "border", "1px dotted green"); + Dom.setStyle(p, "margin", "10px"); + }, + + onDragOut: function(e, id) { + var destEl = Dom.get(id); + var p = destEl.parentNode; + Dom.setStyle(p, "border", "0px"); + Dom.setStyle(p, "margin", "0px"); + }, + + onDragOver: function(e, id) { + + var srcEl = this.getEl(); + var destEl = Dom.get(id); + + // We are only concerned with list items, we ignore the dragover + // notifications for the list. + if (destEl.nodeName.toLowerCase() == "li") { + var orig_p = srcEl.parentNode; + var p = destEl.parentNode; + + if (this.goingUp) { + p.insertBefore(srcEl, destEl); // insert above + } else { + p.insertBefore(srcEl, destEl.nextSibling); // insert below + } + + DDM.refreshCache(); + } + } +}); + +Event.onDOMReady(YAHOO.xo_page_order_region.DDApp.init, YAHOO.xo_page_order_region.DDApp, true); +}; + +YAHOO.xo_page_order_region.DragnDrop(); + Index: openacs-4/packages/xowiki/www/resources/yui-selection-area.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/Attic/yui-selection-area.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/yui-selection-area.js 8 Apr 2009 10:59:43 -0000 1.1 @@ -0,0 +1,237 @@ +/* + * A simple drag and drop handler based on YUI for lists to be used + * e.g. with the the form-fields of type abstract_page (e.g. form_page + * or page) with mutliple entries. + * + * The handler knows about the CSS class textarea.selection, makes it + * invisible and replaces it by a list (selected items). It handles + * actually ULs of class region which are treated as candidates. The + * candidates list is build by the application. On drag and drop, this code + * maintains the entries in textarea. + * + * + * Gustaf Neumann fecit April 2009 + */ +YAHOO.namespace('xo_sel_area'); + +YAHOO.xo_sel_area.DragnDrop = function() { + + var Dom = YAHOO.util.Dom; + var Event = YAHOO.util.Event; + var DDM = YAHOO.util.DragDropMgr; + +YAHOO.xo_sel_area.DDApp = { + dict: new Array(), + values: new Array(), + + init: function() { + // console.log("init called"); + // console.info(this); + // + // Find all textareas which class selection (maybe further restrict in the future) + var textareas = YAHOO.util.Selector.query('textarea.selection'); + for (var i = 0; i < textareas.length; i++) { + var textarea = textareas[i]; + // We found such an textarea. The lines of the textarea are + // treated as the selected values (internal representations). + var items = textarea.value.split(/\n/g); + var selected_LIs = ""; + // For all these items, build an HTML list with the labels + // (external representations). + for (var j = 0; j < items.length; j++) { + var o = items[j]; + if (o == "") continue; + selected_LIs += "
  • " + this.dict[o] + "
  • \n"; + } + + // Map in the candidates the internal representation to an + // external one. + var candidates = document.getElementById(textarea.id + "_candidates"); + var items = candidates.getElementsByTagName("li"); + for (var j = 0; j < items.length; j++) { + items[j].innerHTML = this.dict[items[j].innerHTML]; + } + + // Insert the generated HTML list and hide the textarea + var html = "\n"; + textarea.style.display = "none"; + var div = document.createElement('div'); + div.innerHTML = html; + Dom.insertBefore(div,textarea); + //console.log(html); + } + + // Ok, now we use standard drag and drop, modeled around the + // example from YUI dnd. We have now two HTML lists to be + // handled as regions for drag and drop. + var regions = YAHOO.util.Selector.query('ul.region'); + for (var i = 0; i < regions.length; i++) { + new YAHOO.util.DDTarget(regions[i].id); + var items = regions[i].getElementsByTagName("li"); + for (var j = 0; j < items.length; j++) { + var id = regions[i].id + '__' + j; + // setting the ids (note: will overwrite prexising ids on dnd items) + items[j].id = id; + new YAHOO.xo_sel_area.DDList(id); + } + } + }, + + build_selection: function(id) { + // We get called with the id of the drop target (the list) + var selection_id; + if (id.match(/_selection$/)) { + selection_id = id.replace(/_selection$/,""); + } else { + selection_id = id.replace(/_candidates$/,""); + } + // console.log("selection_id " + selection_id); + + var textarea = document.getElementById(selection_id); + var selection_list = document.getElementById(selection_id + "_selection"); + var items = selection_list.getElementsByTagName("li"); + var values = ""; + for (var j = 0; j < items.length; j++) { + var item = items[j]; + if (this.values[item.innerHTML] == undefined ) { + console.log(" undefined : "+ item.innerHTML); + console.info(this); + console.info(YAHOO.xo_sel_area.DDApp); + for (var i = 0; i < this.values.length; i++) { + console.log(" defined : "+ this.values[i]); + } + } else { + values += this.values[item.innerHTML] + "\n"; + } + } + textarea.value = values; + }, + +}; + +///////////////////////////////////////////////////////////////////////////// +// custom drag and drop implementation +////////////////////////////////////////////////////////////////////////////// + +YAHOO.xo_sel_area.DDList = function(id, sGroup, config) { + + YAHOO.xo_sel_area.DDList.superclass.constructor.call(this, id, sGroup, config); + + var el = this.getDragEl(); + Dom.setStyle(el, "opacity", 0.67); // The proxy is slightly transparent + + this.goingUp = false; + this.lastY = 0; +}; + +YAHOO.extend(YAHOO.xo_sel_area.DDList, YAHOO.util.DDProxy, { + + startDrag: function(x, y) { + + // make the proxy look like the source element + var dragEl = this.getDragEl(); + var clickEl = this.getEl(); + Dom.setStyle(clickEl, "visibility", "hidden"); + + dragEl.innerHTML = clickEl.innerHTML; + + Dom.setStyle(dragEl, "color", Dom.getStyle(clickEl, "color")); + Dom.setStyle(dragEl, "backgroundColor", Dom.getStyle(clickEl, "backgroundColor")); + Dom.setStyle(dragEl, "border", "2px solid gray"); + }, + + endDrag: function(e) { + + var srcEl = this.getEl(); + var proxy = this.getDragEl(); + + // Show the proxy element and animate it to the src element's location + Dom.setStyle(proxy, "visibility", ""); + var a = new YAHOO.util.Motion( + proxy, { + points: { + to: Dom.getXY(srcEl) + } + }, + 0.2, + YAHOO.util.Easing.easeOut + ) + var proxyid = proxy.id; + var thisid = this.id; + + // Hide the proxy and show the source element when finished with the animation + a.onComplete.subscribe(function() { + Dom.setStyle(proxyid, "visibility", "hidden"); + Dom.setStyle(thisid, "visibility", ""); + }); + a.animate(); + }, + + onDragDrop: function(e, id) { + + // If there is one drop interaction, the li was dropped either on the list, + // or it was dropped on the current location of the source element. + if (DDM.interactionInfo.drop.length === 1) { + + // The position of the cursor at the time of the drop (YAHOO.util.Point) + var pt = DDM.interactionInfo.point; + + // The region occupied by the source element at the time of the drop + var region = DDM.interactionInfo.sourceRegion; + + // Check to see if we are over the source element's location. We will + // append to the bottom of the list once we are sure it was a drop in + // the negative space (the area of the list without any list items) + if (!region.intersect(pt)) { + var destEl = Dom.get(id); + var destDD = DDM.getDDById(id); + destEl.appendChild(this.getEl()); + destDD.isEmpty = false; + DDM.refreshCache(); + } + + } + YAHOO.xo_sel_area.DDApp.build_selection(id); + }, + + onDrag: function(e) { + + // Keep track of the direction of the drag for use during onDragOver + var y = Event.getPageY(e); + + if (y < this.lastY) { + this.goingUp = true; + } else if (y > this.lastY) { + this.goingUp = false; + } + + this.lastY = y; + }, + + onDragOver: function(e, id) { + + var srcEl = this.getEl(); + var destEl = Dom.get(id); + + // We are only concerned with list items, we ignore the dragover + // notifications for the list. + if (destEl.nodeName.toLowerCase() == "li") { + var orig_p = srcEl.parentNode; + var p = destEl.parentNode; + + if (this.goingUp) { + p.insertBefore(srcEl, destEl); // insert above + } else { + p.insertBefore(srcEl, destEl.nextSibling); // insert below + } + + DDM.refreshCache(); + } + } +}); + +Event.onDOMReady(YAHOO.xo_sel_area.DDApp.init, YAHOO.xo_sel_area.DDApp, true); +}; + +YAHOO.xo_sel_area.DragnDrop(); +