Uploaded image for project: 'ZK'
  1. ZK
  2. ZK-5022

missing safety checks on shift-ctrl selection on listbox

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: Normal Normal
    • 9.6.1
    • 9.6.0.1
    • None
    • Security Level: Jimmy

      Steps to Reproduce

      https://zkfiddle.org/sample/2pvqgk0/18-Another-new-ZK-fiddle

      Run fiddle

      1. Select last Listitem
      2. Click button (delete last item)
      3. Shift+Click any Listitem

       

      Current Result

      Error: Index: 6, Size: 6

      Expected Result

      no error

      Debug Information

      caused by the Listbox._selectUpto function (from SelectWidget)

      https://github.com/zkoss/zk/blob/c787afdb2caee30df173da7fa45e00d2c658df08/zul/src/archive/web/js/zul/sel/SelectWidget.js#L878

       

      this._lastSelectedItem is not deleted from Listbox when listitem is removed, Listbox tries to use a non-existing index

      Workaround

      (see fiddle, commented out)

          zk.afterLoad("zul.sel, zkmax", function () {
              var _xListbox = {};
              zk.override(zul.sel.Listbox.prototype, _xListbox, {
                  _focus: function (row) {
                      this.shiftAnchor = null;
                      return _xListbox._focus.apply(this, arguments);
                  },
                  _selectOne: function (row, skipFocus) {
                      this.shiftAnchor = null;
                      return _xListbox._selectOne.apply(this, arguments);
                  },
                  _toggleSelect: function (row, toSel, evt, skipFocus) {
                      this.shiftAnchor = null;
                      return _xListbox._toggleSelect.apply(this, arguments);
                  },
                  _selectUpto: function (row, evt, skipFocus) {
                      /* don't touch behavior for rod listbox */
                      if (this._listbox$rod) {
                          return _xListbox._selectUpto.apply(this, arguments);
                      }
      
                      var shiftAnchor = this.shiftAnchor
                              || this._focusItem
                              || ((this._lastSelectedItem && this._lastSelectedItem.parent) ?
                                   this._lastSelectedItem : this.firstItem);
      
                      this._unsetSelectAllExcept();
                      this._changeSelect(shiftAnchor, true);
      
                      if (row.isSelected()) {
                          if (!skipFocus)
                              this._focus(row);
                          return;
                      }
      
                      var focusfound = false, rowfound = false,
                          lastSelected = shiftAnchor;
      
                      if (!lastSelected.isSelected()) {
                          var rowIndex = this.indexOfItem(row),
                              min = Number.MAX_VALUE,
                              closestSelItem;
                          for (var i = 0; i < this._selItems.length; ++i) {
                              var item = this._selItems[i],
                                  index = this.indexOfItem(item),
                                  diff = rowIndex - index,
                                  oldmin = min;
                              if ((diff <= 0) && closestSelItem)
                                  break;
                              min = Math.min(diff, min);
                              if (min != oldmin)
                                  lastSelected = item;
                          }
                      }
                      for (var it = this.getBodyWidgetIterator(), si = this.getSelectedItem(), w; (w = it.next());) {
                          if (w.isDisabled() || !w.isSelectable()) continue;
                          if (focusfound) {
                              this._changeSelect(w, true);
                              if (w == row)
                                  break;
                          } else if (rowfound) {
                              this._changeSelect(w, true);
                              if (this._isFocus(w) || w == lastSelected)
                                  break;
                          } else if (!si) {
                              if (w != row)
                                  continue;
                              this._changeSelect(w, true);
                              break;
                          } else {
                              rowfound = w == row;
                              focusfound = this._isFocus(w) || w == lastSelected;
                              if (rowfound || focusfound) {
                                  this._changeSelect(w, true);
                                  if (rowfound && focusfound)
                                      break;
                              }
                          }
                      }
      
                      if (!skipFocus)
                          this._focus(row);
      
                      this.shiftAnchor = shiftAnchor;
      
                      this.fireOnSelect(row, evt);
                  },
                  onChildRemoved_: function (child) {
                      if(this.shiftAnchor === child) {
                          this.shiftAnchor = null;
                      }
                      return _xListbox.onChildRemoved_.apply(this, arguments);
                  }
               });
          });
      

       

      Secondary issue, related to main issue

      If there is no selected element (due to component just having been rendered, and no selection triggered), doing shift+click on an item will select all items from index 0 to target  (see 2nd listbox with selected items content filled onSelect)

      However, only the targeted Listitem is marked as selected.

      (This is due to the same line of code as the main issue, if no bounding item is found)

            jumperchen jumperchen
            MDuchemin MDuchemin
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: