Chromium Code Reviews| Index: chrome/browser/resources/file_manager/js/drag_selector.js |
| diff --git a/chrome/browser/resources/file_manager/js/drag_selector.js b/chrome/browser/resources/file_manager/js/drag_selector.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8797719b352ecff72517b290ec30fe67780a2d78 |
| --- /dev/null |
| +++ b/chrome/browser/resources/file_manager/js/drag_selector.js |
| @@ -0,0 +1,184 @@ |
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +'use strict'; |
| + |
| +/** |
| + * Drag selector used on the file list or the grid table. |
| + * TODO(hirono): Support drag selection for grid view. crbug.com/224832 |
| + * @constructor |
| + */ |
| +function DragSelector() { |
| + /** |
| + * Target list of drag selection. |
| + * @private |
| + * @type {cr.ui.list?} |
|
mtomasz
2013/05/31 08:15:31
Shouldn't be list? -> List
hirono
2013/06/03 04:40:33
Done.
|
| + */ |
| + this.target_ = null; |
| + |
| + /** |
| + * Border element of drag handle. |
| + * @private |
|
mtomasz
2013/05/31 08:15:31
@private after @type
hirono
2013/06/03 04:40:33
Done.
|
| + * @type {HtmlElement?} |
|
mtomasz
2013/05/31 08:15:31
Isn't '?' redundant? Objects do not need ? for nul
hirono
2013/06/03 04:40:33
Done.
|
| + */ |
| + this.border_ = null; |
| + |
| + /** |
| + * Start point of dragging. |
| + * @private |
| + * @type {number?} |
| + */ |
| + this.startX_ = null; |
| + |
| + /** |
| + * Start point of dragging. |
| + * @private |
| + * @type {number?} |
|
mtomasz
2013/05/31 08:15:31
For example, here ? is necessary, since it is not
hirono
2013/06/03 04:40:33
Done.
|
| + */ |
| + this.startY_ = null; |
| + |
| + /** |
| + * Indexes of selected items by dragging at the last update. |
| + * @private |
| + * @type {Array.<number>?} |
| + */ |
| + this.lastSelection_ = null; |
| + |
| + /** |
| + * Indexes of selected items at the start of dragging. |
| + * @private |
| + * @type {Array.<number>?} |
| + */ |
| + this.originalSelection_ = null; |
| + |
| + // Bind handlers to make them removable. |
| + this.onMouseMove_ = DragSelector.prototype.onMouseMove_.bind(this); |
|
mtomasz
2013/05/31 08:15:31
This looks tricky. For that we use:
this.onMouseMo
hirono
2013/06/03 04:40:33
Done.
|
| + this.onMouseUp_ = DragSelector.prototype.onMouseUp_.bind(this); |
| +} |
| + |
| +/** |
| + * Starts drag selection by reacting dragstart event. |
| + * This function must be called from handlers of dragstart event. |
|
mtomasz
2013/05/31 08:15:31
When @jsdoc is long, you may want to add an empty
hirono
2013/06/03 04:40:33
Done.
|
| + * @this {DragSelector} |
| + * @param {cr.ui.list} list List where the drag selection starts. |
| + * @param {Event} event The dragstart event. |
| + */ |
| +DragSelector.prototype.startDragSelection = function(list, event) { |
| + // Precondition check |
| + if (!list.selectionModel_.multiple || this.target_) |
| + return; |
| + // Set the target of drag selection |
|
mtomasz
2013/05/31 08:15:31
of -> of the
hirono
2013/06/03 04:40:33
Done.
|
| + this.target_ = list; |
|
mtomasz
2013/05/31 08:15:31
Please add empty lines in large blocks of code for
hirono
2013/06/03 04:40:33
Done.
|
| + // Create and add the border element |
| + if (!this.border_) { |
| + this.border_ = this.target_.ownerDocument.createElement('div'); |
| + this.border_.className = 'drag-selection-border'; |
| + } |
| + list.appendChild(this.border_); |
| + // Prevent default action |
|
mtomasz
2013/05/31 08:15:31
Add periods at the end of sentences.
hirono
2013/06/03 04:40:33
Done.
|
| + event.preventDefault(); |
| + // Handle modifier keys. |
|
mtomasz
2013/05/31 08:15:31
Please change to more descriptive comment, eg. wha
hirono
2013/06/03 04:40:33
Done.
|
| + if (!event.modifiers & Event.SHIFT_MASK && |
|
mtomasz
2013/05/31 08:15:31
Does it work as intended? I can start selection us
hirono
2013/06/03 04:40:33
These modifier keys just specified that this selec
|
| + !event.modifiers & Event.CONTROL_MASK) { |
| + this.target_.selectionModel_.unselectAll(); |
| + } |
| + // Save the start state. |
| + var rect = list.getBoundingClientRect(); |
| + this.startX_ = event.clientX - rect.left + list.scrollLeft; |
| + this.startY_ = event.clientY - rect.top + list.scrollTop; |
| + this.border_.style.left = this.startX_ + 'px'; |
| + this.border_.style.top = this.startY_ + 'px'; |
| + this.lastSelection_ = []; |
| + this.originalSelection_ = this.target_.selectionModel_.selectedIndexes; |
| + // Register evnet handlers. |
| + // The handlers are bounded at the constructor. |
| + this.target_.ownerDocument.addEventListener( |
| + 'mousemove', this.onMouseMove_, true); |
| + this.target_.ownerDocument.addEventListener( |
| + 'mouseup', this.onMouseUp_, true); |
| +}; |
| + |
| +/** |
| + * Handle the mousemove evnet. |
|
mtomasz
2013/05/31 08:15:31
evnet -> event
hirono
2013/06/03 04:40:33
Done.
|
| + * @private |
| + * @param {MouseEvent} event The mousemove event. |
| + */ |
| +DragSelector.prototype.onMouseMove_ = function(event) { |
| + // Get current selection and leadIndex from geometry. |
| + var inRect = this.target_.getBoundingClientRect(); |
| + var x = event.clientX - inRect.left + this.target_.scrollLeft; |
| + var y = event.clientY - inRect.top + this.target_.scrollTop; |
| + var borderMetrics = { |
|
mtomasz
2013/05/31 08:15:31
Metrics -> Bounds / Rect / Geometry?
(optional) bo
hirono
2013/06/03 04:40:33
Done.
|
| + left: Math.max(Math.min(this.startX_, x), 0), |
| + top: Math.max(Math.min(this.startY_, y), 0), |
| + right: Math.min(Math.max(this.startX_, x), this.target_.scrollWidth), |
| + bottom: Math.min(Math.max(this.startY_, y), this.target_.scrollHeight) |
| + }; |
| + borderMetrics.width = borderMetrics.right - borderMetrics.left; |
| + borderMetrics.height = borderMetrics.bottom - borderMetrics.top; |
| + this.border_.style.left = borderMetrics.left + 'px'; |
| + this.border_.style.top = borderMetrics.top + 'px'; |
| + this.border_.style.width = borderMetrics.width + 'px'; |
| + this.border_.style.height = borderMetrics.height + 'px'; |
| + var currentSelection = []; |
| + var leadIndex = -1; |
|
mtomasz
2013/05/31 08:15:31
Can we add a comment, eg. Collect items within the
hirono
2013/06/03 04:40:33
Done.
|
| + for (var i = 0; i < this.target_.selectionModel_.length; i++) { |
| + var itemMetrics = this.target_.getHeightsForIndex_(i); |
| + itemMetrics.bottom = itemMetrics.top + itemMetrics.height; |
| + if (itemMetrics.top < borderMetrics.bottom && |
| + itemMetrics.bottom >= borderMetrics.top) { |
| + currentSelection.push(i); |
| + } |
| + var pointed = itemMetrics.top <= y && y < itemMetrics.bottom; |
| + if (pointed) |
| + leadIndex = i; |
| + } |
| + // Diff the selection between currentSelection and this.lastSelection_. |
| + var selectionFlag = []; |
| + for (var i = 0; i < this.lastSelection_.length; i++) { |
| + var index = this.lastSelection_[i]; |
| + // Bit operator can be used for undefined value. |
| + selectionFlag[index] = selectionFlag[index] | 1; |
|
mtomasz
2013/05/31 08:15:31
1, 2 -> enum?
hirono
2013/06/03 04:40:33
Done.
|
| + } |
| + for (var i = 0; i < currentSelection.length; i++) { |
| + var index = currentSelection[i]; |
| + // Bit operator can be used for undefined value. |
| + selectionFlag[index] = selectionFlag[index] | 2; |
| + } |
| + // Update selection |
|
mtomasz
2013/05/31 08:15:31
selection -> the selection
hirono
2013/06/03 04:40:33
Done.
|
| + this.target_.selectionModel_.beginChange(); |
| + for (var name in selectionFlag) { |
| + var index = parseInt(name); |
|
mtomasz
2013/05/31 08:15:31
Is parseInt necessary here?
hirono
2013/06/03 04:40:33
Yes, actually, I had used the name directly at fir
|
| + var flag = selectionFlag[name]; |
| + if (flag == 1) { |
| + // This is included in lastSelection but currentSelection. |
| + // Revert the selection state to this.originalSelection_. |
| + this.target_.selectionModel_.setIndexSelected( |
| + index, this.originalSelection_.indexOf(index) != -1); |
| + } else if (flag == 2) { |
| + // This is included in currentSelection but lastSelection. |
| + this.target_.selectionModel_.setIndexSelected(index, true); |
| + } |
| + } |
| + this.target_.selectionModel_.leadIndex = leadIndex; |
| + this.target_.selectionModel_.anchorIndex = leadIndex; |
| + this.target_.selectionModel_.endChange(); |
| + this.lastSelection_ = currentSelection; |
| +}; |
| + |
| +/** |
| + * Handle the mouseup event. |
| + * @private |
| + * @param {MouseEvent} event The mouseup event. |
| + */ |
| +DragSelector.prototype.onMouseUp_ = function(event) { |
| + this.onMouseMove_(event); |
| + this.target_.removeChild(this.border_); |
| + this.target_.ownerDocument.removeEventListener( |
| + 'mousemove', this.onMouseMove_, true); |
| + this.target_.ownerDocument.removeEventListener( |
| + 'mouseup', this.onMouseUp_, true); |
| + event.stopPropagation(); |
| + this.target_ = null; |
| +}; |