%3 ::xowiki::formfield::select ::xowiki::formfield::select initialize render_input ::xowiki::formfield::enumeration ::xowiki::formfield::enumeration add_statistics answer_is_correct config_from_category_tree get_labels ggw initialize make_correct pretty_value render_input render_label_classes render_label_text render_result_statistics scores stats_record_detail value_if_nothing_is_returned_from_form ::xowiki::formfield::select->::xowiki::formfield::enumeration ::xowiki::formfield::ShuffleField ::xowiki::formfield::ShuffleField ::xowiki::formfield::enumeration->::xowiki::formfield::ShuffleField ::xowiki::formfield::reorder_box ::xowiki::formfield::reorder_box answer_is_correct initialize render_input ::xowiki::formfield::reorder_box->::xowiki::formfield::select

Class ::xowiki::formfield::reorder_box

::xowiki::formfield::reorder_box[i] create ... \
           [ -shuffle:boolean (default "true") ]

Class Relations

  • class: ::xotcl::Class[i]
  • superclass: ::xowiki::formfield::select[i]
::xotcl::Class create ::xowiki::formfield::reorder_box \
     -superclass ::xowiki::formfield::select

Methods (to be applied on instances)

  • answer_is_correct (scripted)

    #
    # This method returns 0 (undecided), -1 (incorrect) or 1 (correct)
    # and sets as well the instance variable :correction with the
    # per-item correctness settings for correct/incorrect.
    #
    #:log "reorder_box CORRECT? ${:name} (value=[:value])"
    
    if {![info exists :answer]} {
      set result 0
    } else {
      set value [:value]
      if {[llength $value] != [llength ${:answer}]} {
        error "list length of value <$value> and answer <${:answer}> must be equal (${:name})"
      }
      set result 1
      set :correction {}
    
      set R 0; set W 0
      foreach v $value a ${:answer} {
        set ok [expr {$v eq $a}]
        lappend :correction $ok
        if {$ok} {
          incr R
        } else {
          set result -1
          incr W
        }
      }
    
      ns_log notice "${:name}: correction? have grading [info exists :grading]"
      set correct_relative {}
      #
      # Compare neighbors; when left neighbor is smaller, this is
      # counted as correct. Assumption: provided answer is ascending.
      #
      set Rr 0; set Wr 0
      for {set i 1} {$i < [llength $value]} {incr i} {
        if {[lindex $value $i-1] < [lindex $value $i]} {
          incr Rr
          lappend correction_relative 1
        } else {
          set result -1
          incr Wr
          lappend correction_relative 0
        }
      }
      #
      # We could provide a special correction based on the relative
      # data, but the rendering of this in e.g. the exam protocol is
      # would need more work (green and red should be between the
      # answer elements, not left of it).
      #
      #if {[info exists :grading] && ${:grading} eq "relative"} {
      #  set :correction $correction_relative
      #}
    
      #
      # An empty value for grading is the same as grading "exact"
      #
      set exact [expr {$result == 1 ? 1.0 : 0.0}]
      set scores {}
      lappend scores  exact $exact  "" $exact  position [:ggw $R $W]  relative [:ggw $Rr $Wr]
    
      dict set :correction_data scores $scores
    
      #:log "${:name} reorder_box CORRECT? answers [llength ${:answer}] "  "options [llength ${:options}] -> $result scores $scores"
    }
    return $result
  • initialize (scripted)

    #
    # The reorder_box must always be treated as a :multiple field
    #
    set :multiple 1
    next
  • render_input (scripted)

    
    if {${:value} eq ""} {
      #
      # When we have no value, provide a start value.
      #
      if {${:shuffle}} {
        #
        # When :shuffle is set, provide a randomized start value.
        #
        set :value [::xowiki::randomized_indices -seed [xo::cc user_id] [llength ${:options}]]
        #ns_log notice "=== reorder_box value '${:value}' shuffle ${:shuffle} value ${:value}"
      } else {
        #
        # Otherwise, take the internal representations as :value
        #
        set :value [lmap o ${:options} {lindex $o 1}]
      }
    }
    #
    # Make sure that value is feasible and bail out, if not.
    #
    set c -1; set indices [lmap o ${:options} {incr c}]
    if {[lsort -integer ${:value}] ne $indices} {
      error "internal representation of options ${:options} must be subsequent integers starting with 0\nwe have: ${:value}\noptions: ${:options}"
    }
    
    #
    # Provide an HTML ID for ".sortable" compatible with jquery
    #
    regsub -all -- {[.]} "${:id}.sortable" - jqID
    set textAreaID ${:id}.text
    
    if {![:is_disabled]} {
      #
      # If not disabled, let people move around the elements.
      #
      ::xo::Page requireCSS urn:ad:css:jquery-ui
      ::xo::Page requireJS urn:ad:js:jquery-ui
      if {[::xo::cc mobile]} {
        ::xo::Page requireJS urn:ad:js:jquery-ui-touch-punch
      }
    
      template::add_body_script -script  [subst {
        \$("#$jqID").sortable();
        \$("#$jqID").on( "sortupdate", function( event, ui ) {
          var ul       = event.target;
          var textarea = document.getElementById('$textAreaID');
          var items    = ul.getElementsByTagName('LI');
          var internalRep = "";
          for (var j = 0; j < items.length; j++) {
            internalRep += items\[j\].dataset.value + "\\n";
          }
          textarea.value = internalRep;
        } );
      }]
    }
    
    #
    # Make sure, we have :correction initialized, since the rendering
    # code below needs it.
    #
    if {![info exists :correction]} {
      set :correction {}
    }
    
    html::div -class reorder_box -id ${:id} {
      #
      # The input field of the internal representation.
      #
      ::html::textarea -id $textAreaID -name ${:name} {
        ::html::t [join ${:value} \n]
      }
    
      #
      # The box for reordering items.
      #
      ::html::div -class workarea {
        ::html::ul -class "list-group"  -id $jqID {
              foreach v ${:value} c ${:correction} {
                set cl "list-group-item"
                lappend cl [expr {$c eq "1" ? "correct" : $c eq "0" ? "incorrect" : ""} ]
                ::html::li -class $cl -style "min-width:200px;" -data-value $v {
                  ::html::t [lindex ${:options} $v 0]
                }
              }
            }
      }
    }
  • shuffle (setter)