dragselect
Advanced tools
Comparing version 1.8.1 to 1.9.0
@@ -0,1 +1,9 @@ | ||
# 1.9.0 | ||
- Fix non scrollable document element issue. See [#35](https://github.com/ThibaultJanBeyer/DragSelect/issues/35) | ||
- Added `onDragStartBegin` callback. Helpful for my friends at issue [#24](https://github.com/ThibaultJanBeyer/DragSelect/issues/24) | ||
- Increase performance by calculating the selection rectangle outside of the `_isElementTouching` function. Tested on 30.000 Nodes. The selection is still usable although a bit laggy on 30k nodes. | ||
- Rename `isElementTouching` to private `_isElementTouching`. You also have to pass a `selectionRect` as second argument now instead of just passing the container node. This is not considered a breaking change because that function was not exposed before. | ||
- Add prettier & eslint + prettify code | ||
# 1.8.1 | ||
@@ -2,0 +10,0 @@ |
@@ -1,2 +0,2 @@ | ||
// v 1.8.1 | ||
// v 1.9.0 | ||
/* | ||
@@ -38,3 +38,4 @@ ____ _____ __ __ | ||
** @selectableClass string the class assigned to the elements that can be selected | ||
** @onDragStart function It is fired when the user clicks in the area. This callback gets the event object. Executed after DragSelect function code ran, befor the setup of event listeners. | ||
** @onDragStartBegin function Is fired when the user clicks in the area. This callback gets the event object. Executed *before* DragSelect function code ran. | ||
** @onDragStart function It is fired when the user clicks in the area. This callback gets the event object. Executed after DragSelect function code ran, before the setup of event listeners. | ||
** @onDragMove function It is fired when the user drags. This callback gets the event object. Executed before DragSelect function code ran, after getting the current mouse position. | ||
@@ -100,20 +101,18 @@ ** @onElementSelect function It is fired every time an element is selected. This callback gets a property which is the just selected node | ||
* DragSelect Class. | ||
* | ||
* | ||
* @constructor | ||
* @param {Object} options - The options object. | ||
*/ | ||
function DragSelect( options ) { | ||
function DragSelect(options) { | ||
this.multiSelectKeyPressed; | ||
this.initialCursorPos = {x: 0, y: 0}; | ||
this.newCursorPos = {x: 0, y: 0}; | ||
this.previousCursorPos = {x: 0, y: 0}; | ||
this.initialCursorPos = { x: 0, y: 0 }; | ||
this.newCursorPos = { x: 0, y: 0 }; | ||
this.previousCursorPos = { x: 0, y: 0 }; | ||
this.initialScroll; | ||
this.selected = []; | ||
this._prevSelected = []; // memory to fix #9 | ||
this._prevSelected = []; // memory to fix #9 | ||
this._createBindings(); | ||
this._setupOptions( options ); | ||
this._setupOptions(options); | ||
this.start(); | ||
} | ||
@@ -125,3 +124,2 @@ | ||
DragSelect.prototype._createBindings = function() { | ||
this._startUp = this._startUp.bind(this); | ||
@@ -131,3 +129,2 @@ this._handleMove = this._handleMove.bind(this); | ||
this._onClick = this._onClick.bind(this); | ||
}; | ||
@@ -138,4 +135,3 @@ | ||
*/ | ||
DragSelect.prototype._setupOptions = function( options ) { | ||
DragSelect.prototype._setupOptions = function(options) { | ||
this.selectedClass = options.selectedClass || 'ds-selected'; | ||
@@ -147,9 +143,14 @@ this.hoverClass = options.hoverClass || 'ds-hover'; | ||
this.selectables = []; | ||
this._handleSelectables( this.toArray( options.selectables ) ); | ||
this._handleSelectables(this.toArray(options.selectables)); | ||
this.multiSelectKeys = options.multiSelectKeys || ['ctrlKey', 'shiftKey', 'metaKey']; | ||
this.multiSelectKeys = options.multiSelectKeys || [ | ||
'ctrlKey', | ||
'shiftKey', | ||
'metaKey' | ||
]; | ||
this.multiSelectMode = options.multiSelectMode || false; | ||
this.autoScrollSpeed = options.autoScrollSpeed || 1; | ||
this.autoScrollSpeed = options.autoScrollSpeed === 0 ? 0 : options.autoScrollSpeed || 1; | ||
this.selectCallback = options.onElementSelect || function() {}; | ||
this.unselectCallback = options.onElementUnselect || function() {}; | ||
this.onDragStartBegin = options.onDragStartBegin || function() {}; | ||
this.moveStartCallback = options.onDragStart || function() {}; | ||
@@ -162,6 +163,11 @@ this.moveCallback = options.onDragMove || function() {}; | ||
// Area has to have a special position attribute for calculations | ||
if( this.area !== document ) { | ||
var computedArea = getComputedStyle( this.area ); | ||
var isPositioned = computedArea.position === 'absolute' || computedArea.position === 'relative' || computedArea.position === 'fixed'; | ||
if( !isPositioned ) { this.area.style.position = 'relative'; } | ||
if (this.area !== document) { | ||
var computedArea = getComputedStyle(this.area); | ||
var isPositioned = | ||
computedArea.position === 'absolute' || | ||
computedArea.position === 'relative' || | ||
computedArea.position === 'fixed'; | ||
if (!isPositioned) { | ||
this.area.style.position = 'relative'; | ||
} | ||
} | ||
@@ -171,4 +177,3 @@ | ||
this.selector = options.selector || this._createSelector(); | ||
this.addClass( this.selector, this.selectorClass ); | ||
this.addClass(this.selector, this.selectorClass); | ||
}; | ||
@@ -178,3 +183,3 @@ | ||
* Add/Remove Selectables also handles css classes and event listeners. | ||
* | ||
* | ||
* @param {Object} selectables - selectable elements. | ||
@@ -184,38 +189,38 @@ * @param {Boolean} remove - if elements should be removed. | ||
*/ | ||
DragSelect.prototype._handleSelectables = function( selectables, remove, fromSelection ) { | ||
for ( var index = 0; index < selectables.length; index++ ) { | ||
DragSelect.prototype._handleSelectables = function( | ||
selectables, | ||
remove, | ||
fromSelection | ||
) { | ||
for (var index = 0; index < selectables.length; index++) { | ||
var selectable = selectables[index]; | ||
var indexOf = this.selectables.indexOf( selectable ); | ||
var indexOf = this.selectables.indexOf(selectable); | ||
if( indexOf < 0 && !remove ) { // add | ||
this.addClass( selectable, this.selectableClass ); | ||
selectable.addEventListener( 'click', this._onClick ); | ||
this.selectables.push( selectable ); | ||
if (indexOf < 0 && !remove) { | ||
// add | ||
this.addClass(selectable, this.selectableClass); | ||
selectable.addEventListener('click', this._onClick); | ||
this.selectables.push(selectable); | ||
// also add to current selection | ||
if( fromSelection && this.selected.indexOf( selectable ) < 0 ) { | ||
this.addClass( selectable, this.selectedClass ); | ||
this.selected.push( selectable ); | ||
if (fromSelection && this.selected.indexOf(selectable) < 0) { | ||
this.addClass(selectable, this.selectedClass); | ||
this.selected.push(selectable); | ||
} | ||
} else if (indexOf > -1 && remove) { | ||
// remove | ||
} | ||
this.removeClass(selectable, this.hoverClass); | ||
this.removeClass(selectable, this.selectableClass); | ||
selectable.removeEventListener('click', this._onClick); | ||
this.selectables.splice(indexOf, 1); | ||
else if( indexOf > -1 && remove ) { // remove | ||
this.removeClass( selectable, this.hoverClass ); | ||
this.removeClass( selectable, this.selectableClass ); | ||
selectable.removeEventListener( 'click', this._onClick ); | ||
this.selectables.splice( indexOf, 1 ); | ||
// also remove from current selection | ||
if( fromSelection && this.selected.indexOf( selectable ) > -1 ) { | ||
this.removeClass( selectable, this.selectedClass ); | ||
this.selected.splice( this.selected.indexOf( selectable ), 1 ); | ||
if (fromSelection && this.selected.indexOf(selectable) > -1) { | ||
this.removeClass(selectable, this.selectedClass); | ||
this.selected.splice(this.selected.indexOf(selectable), 1); | ||
} | ||
} | ||
} | ||
}; | ||
@@ -225,26 +230,35 @@ | ||
* Triggers when a node is actively selected. | ||
* | ||
* | ||
* This might be an "onClick" method but it also triggers when | ||
* <button> nodes are pressed via the keyboard. | ||
* Making DragSelect accessible for everyone! | ||
* | ||
* | ||
* @param {Object} selectables - selectable elements. | ||
* @param {Boolean} remove - if elements were removed. | ||
*/ | ||
DragSelect.prototype._onClick = function( event ) { | ||
DragSelect.prototype._onClick = function(event) { | ||
if( this.mouseInteraction ) { return; } // fix firefox doubleclick issue | ||
if( this.isRightClick( event ) ) { return; } | ||
if (this.mouseInteraction) { | ||
return; | ||
} // fix firefox doubleclick issue | ||
if (this.isRightClick(event)) { | ||
return; | ||
} | ||
var node = event.target; | ||
if(this.isMultiSelectKeyPressed( event )) { this._prevSelected = this.selected.slice(); } // #9 | ||
else { this._prevSelected = []; } // #9 | ||
if (this.isMultiSelectKeyPressed(event)) { | ||
this._prevSelected = this.selected.slice(); | ||
} // #9 | ||
else { | ||
this._prevSelected = []; | ||
} // #9 | ||
this.checkIfInsideSelection( true ); // reset selection if no multiselectionkeypressed | ||
this.checkIfInsideSelection(true); // reset selection if no multiselectionkeypressed | ||
if( this.selectables.indexOf( node ) > -1 ) { this.toggle( node ); } | ||
if (this.selectables.indexOf(node) > -1) { | ||
this.toggle(node); | ||
} | ||
this.reset(); | ||
}; | ||
@@ -254,22 +268,20 @@ | ||
* Create the selector node when not provided by options object. | ||
* | ||
* | ||
* @return {Node} | ||
*/ | ||
DragSelect.prototype._createSelector = function() { | ||
var selector = document.createElement('div'); | ||
var selector = document.createElement( 'div' ); | ||
selector.style.position = 'absolute'; | ||
if( !this.customStyles ) { | ||
if (!this.customStyles) { | ||
selector.style.background = 'rgba(0, 0, 255, 0.1)'; | ||
selector.style.border = '1px solid rgba(0, 0, 255, 0.45)'; | ||
selector.style.display = 'none'; | ||
selector.style.pointerEvents = 'none'; // fix for issue #8 (ie11+) | ||
selector.style.pointerEvents = 'none'; // fix for issue #8 (ie11+) | ||
} | ||
var _area = this.area === document ? document.body : this.area; | ||
_area.appendChild( selector ); | ||
_area.appendChild(selector); | ||
return selector; | ||
}; | ||
@@ -284,5 +296,3 @@ | ||
DragSelect.prototype.start = function() { | ||
this.area.addEventListener( 'mousedown', this._startUp ); | ||
this.area.addEventListener('mousedown', this._startUp); | ||
}; | ||
@@ -292,30 +302,39 @@ | ||
* Startup when the area is clicked. | ||
* | ||
* | ||
* @param {Object} event - The event object. | ||
*/ | ||
DragSelect.prototype._startUp = function( event ) { | ||
DragSelect.prototype._startUp = function(event) { | ||
if( this.isRightClick( event ) ) { return; } | ||
// callback | ||
this.onDragStartBegin(event); | ||
if (this._breaked) { return false; } | ||
if (this.isRightClick(event)) { | ||
return; | ||
} | ||
this.mouseInteraction = true; | ||
this.selector.style.display = 'block'; | ||
if(this.isMultiSelectKeyPressed( event )) { this._prevSelected = this.selected.slice(); } // #9 | ||
else { this._prevSelected = []; } // #9 | ||
if (this.isMultiSelectKeyPressed(event)) { | ||
this._prevSelected = this.selected.slice(); | ||
} // #9 | ||
else { | ||
this._prevSelected = []; | ||
} // #9 | ||
// move element on location | ||
this._getStartingPositions( event ); | ||
this.checkIfInsideSelection( true ); | ||
this._getStartingPositions(event); | ||
this.checkIfInsideSelection(true); | ||
this.selector.style.display = 'none'; // hidden unless moved, fix for issue #8 | ||
this.selector.style.display = 'none'; // hidden unless moved, fix for issue #8 | ||
// callback | ||
this.moveStartCallback( event ); | ||
if( this._breaked ) { return false; } | ||
this.moveStartCallback(event); | ||
if (this._breaked) { return false; } | ||
// event listeners | ||
this.area.removeEventListener( 'mousedown', this._startUp ); | ||
this.area.addEventListener( 'mousemove', this._handleMove ); | ||
document.addEventListener( 'mouseup', this.reset ); | ||
this.area.removeEventListener('mousedown', this._startUp); | ||
this.area.addEventListener('mousemove', this._handleMove); | ||
document.addEventListener('mouseup', this.reset); | ||
}; | ||
@@ -325,16 +344,17 @@ | ||
* Check if some multiselection modifier key is pressed | ||
* | ||
* | ||
* @param {Object} event - The event object. | ||
* @return {Boolean} this.isMultiSelectKeyPressed | ||
*/ | ||
DragSelect.prototype.isMultiSelectKeyPressed = function( event ) { | ||
DragSelect.prototype.isMultiSelectKeyPressed = function(event) { | ||
this.multiSelectKeyPressed = false; | ||
if (this.multiSelectMode){ | ||
if (this.multiSelectMode) { | ||
this.multiSelectKeyPressed = true; | ||
} else { | ||
for ( var index = 0; index < this.multiSelectKeys.length; index++ ) { | ||
for (var index = 0; index < this.multiSelectKeys.length; index++) { | ||
var mKey = this.multiSelectKeys[index]; | ||
if( event[mKey] ) { this.multiSelectKeyPressed = true; } | ||
if (event[mKey]) { | ||
this.multiSelectKeyPressed = true; | ||
} | ||
} | ||
@@ -344,3 +364,2 @@ } | ||
return this.multiSelectKeyPressed; | ||
}; | ||
@@ -350,10 +369,12 @@ | ||
* Grabs the starting position of all needed elements | ||
* | ||
* | ||
* @param {Object} event - The event object. | ||
*/ | ||
DragSelect.prototype._getStartingPositions = function( event ) { | ||
DragSelect.prototype._getStartingPositions = function(event) { | ||
this.initialCursorPos = this.newCursorPos = this._getCursorPos( | ||
event, | ||
this.area | ||
); | ||
this.initialScroll = this.getScroll(this.area); | ||
this.initialCursorPos = this.newCursorPos = this._getCursorPos( event, this.area ); | ||
this.initialScroll = this.getScroll( this.area ); | ||
var selectorPos = {}; | ||
@@ -364,7 +385,5 @@ selectorPos.x = this.initialCursorPos.x + this.initialScroll.x; | ||
selectorPos.h = 0; | ||
this.updatePos( this.selector, selectorPos ); | ||
this.updatePos(this.selector, selectorPos); | ||
}; | ||
// Movements/Sizing of selection | ||
@@ -375,22 +394,22 @@ ////////////////////////////////////////////////////////////////////////////////////// | ||
* Handles what happens while the mouse is moved | ||
* | ||
* | ||
* @param {Object} event - The event object. | ||
*/ | ||
DragSelect.prototype._handleMove = function( event ) { | ||
DragSelect.prototype._handleMove = function(event) { | ||
var selectorPos = this.getPosition(event); | ||
var selectorPos = this.getPosition( event ); | ||
// callback | ||
this.moveCallback( event ); | ||
if( this._breaked ) { return false; } | ||
this.moveCallback(event); | ||
if (this._breaked) { | ||
return false; | ||
} | ||
this.selector.style.display = 'block'; // hidden unless moved, fix for issue #8 | ||
this.selector.style.display = 'block'; // hidden unless moved, fix for issue #8 | ||
// move element on location | ||
this.updatePos( this.selector, selectorPos ); | ||
this.updatePos(this.selector, selectorPos); | ||
this.checkIfInsideSelection(); | ||
// scroll area if area is scrollable | ||
this._autoScroll( event ); | ||
this._autoScroll(event); | ||
}; | ||
@@ -400,10 +419,9 @@ | ||
* Calculates and returns the exact x,y w,h positions of the selector element | ||
* | ||
* | ||
* @param {Object} event - The event object. | ||
*/ | ||
DragSelect.prototype.getPosition = function( event ) { | ||
DragSelect.prototype.getPosition = function(event) { | ||
var cursorPosNew = this._getCursorPos(event, this.area); | ||
var scrollNew = this.getScroll(this.area); | ||
var cursorPosNew = this._getCursorPos( event, this.area ); | ||
var scrollNew = this.getScroll( this.area ); | ||
// save for later retrieval | ||
@@ -429,3 +447,3 @@ this.newCursorPos = cursorPosNew; | ||
* by +10px width to fake a negative sizing. | ||
* | ||
* | ||
* One solution to this problem is using css-transforms scale() with | ||
@@ -435,3 +453,3 @@ * transform-origin of top left. BUT we can’t use this since it will size | ||
* get inanely huge. Also transforms are not widely supported in IE. | ||
* | ||
* | ||
* Example #1: | ||
@@ -449,3 +467,3 @@ * Unfortunately, things get even more complicated when we are inside a scrollable | ||
* 3. selectorPos.w = cursorPosNew.x (5) - initialCursorPos.x (0) + scrollAmount.x (10) === 15; | ||
* | ||
* | ||
* let’s say after that movement we now scroll 20px to the left and move our cursor by 30px to the left: | ||
@@ -455,7 +473,7 @@ * 1b. cursorPosNew.x (-30) > initialCursorPos.x (0) - scrollAmount.x (-20) === -30 > -20 === false; | ||
* === -50; // move left position to cursor (for more info see Problem #1) | ||
* 3b. selectorPos.w = initialCursorPos.x (0) - cursorPosNew.x (-30) - scrollAmount.x (-20) | ||
* 3b. selectorPos.w = initialCursorPos.x (0) - cursorPosNew.x (-30) - scrollAmount.x (-20) | ||
* === 0--30--20 === 0+30+20 === 50; // scale width to original left position (for more info see Problem #1) | ||
* | ||
* | ||
* same thing has to be done for top/bottom | ||
* | ||
* | ||
* I hope that makes sence, try stuff out and play around with variables to get a hang of it. | ||
@@ -466,16 +484,18 @@ */ | ||
// right | ||
if( cursorPosNew.x > this.initialCursorPos.x - scrollAmount.x ) { // 1. | ||
selectorPos.x = this.initialCursorPos.x + this.initialScroll.x; // 2. | ||
selectorPos.w = cursorPosNew.x - this.initialCursorPos.x + scrollAmount.x; // 3. | ||
// left | ||
} else { // 1b. | ||
selectorPos.x = cursorPosNew.x + scrollNew.x; // 2b. | ||
selectorPos.w = this.initialCursorPos.x - cursorPosNew.x - scrollAmount.x; // 3b. | ||
if (cursorPosNew.x > this.initialCursorPos.x - scrollAmount.x) { | ||
// 1. | ||
selectorPos.x = this.initialCursorPos.x + this.initialScroll.x; // 2. | ||
selectorPos.w = cursorPosNew.x - this.initialCursorPos.x + scrollAmount.x; // 3. | ||
// left | ||
} else { | ||
// 1b. | ||
selectorPos.x = cursorPosNew.x + scrollNew.x; // 2b. | ||
selectorPos.w = this.initialCursorPos.x - cursorPosNew.x - scrollAmount.x; // 3b. | ||
} | ||
// bottom | ||
if( cursorPosNew.y > this.initialCursorPos.y - scrollAmount.y ) { | ||
if (cursorPosNew.y > this.initialCursorPos.y - scrollAmount.y) { | ||
selectorPos.y = this.initialCursorPos.y + this.initialScroll.y; | ||
selectorPos.h = cursorPosNew.y - this.initialCursorPos.y + scrollAmount.y; | ||
// top | ||
// top | ||
} else { | ||
@@ -487,6 +507,4 @@ selectorPos.y = cursorPosNew.y + scrollNew.y; | ||
return selectorPos; | ||
}; | ||
// Colision detection | ||
@@ -497,20 +515,24 @@ ////////////////////////////////////////////////////////////////////////////////////// | ||
* Checks if element is inside selection and takes action based on that | ||
* | ||
* | ||
* force handles first clicks and accessibility. Here is user is clicking directly onto | ||
* some element at start, (contrary to later hovers) we can assume that he | ||
* really wants to select/deselect that item. | ||
* | ||
* | ||
* @param {Boolean} force – forces through. | ||
* | ||
* @return {Boolean} | ||
*/ | ||
DragSelect.prototype.checkIfInsideSelection = function( force ) { | ||
DragSelect.prototype.checkIfInsideSelection = function(force) { | ||
var anyInside = false; | ||
for( var i = 0, il = this.selectables.length; i < il; i++ ) { | ||
var selectable = this.selectables[i]; | ||
if( this.isElementTouching( selectable, this.selector, this.area ) ) { | ||
var scroll = this.getScroll(this.area); | ||
var selectionRect = { | ||
y: this.selector.getBoundingClientRect().top + scroll.y, | ||
x: this.selector.getBoundingClientRect().left + scroll.x, | ||
h: this.selector.offsetHeight, | ||
w: this.selector.offsetWidth | ||
}; | ||
if( this._isElementTouching( selectable, selectionRect, scroll ) ) { | ||
this._handleSelection( selectable, force ); | ||
@@ -521,7 +543,4 @@ anyInside = true; | ||
} | ||
} | ||
return anyInside; | ||
}; | ||
@@ -531,19 +550,19 @@ | ||
* Logic when an item is selected | ||
* | ||
* | ||
* @param {Node} item – selected item. | ||
* @param {Boolean} force – forces through. | ||
*/ | ||
DragSelect.prototype._handleSelection = function( item, force ) { | ||
DragSelect.prototype._handleSelection = function(item, force) { | ||
if (this.hasClass(item, this.hoverClass) && !force) { | ||
return false; | ||
} | ||
var posInSelectedArray = this.selected.indexOf(item); | ||
if( this.hasClass( item, this.hoverClass ) && !force ) { return false; } | ||
var posInSelectedArray = this.selected.indexOf( item ); | ||
if( posInSelectedArray < 0 ) { | ||
this.select( item ); | ||
} else if( posInSelectedArray > -1 && this.multiSelectKeyPressed ) { | ||
this.unselect( item ); | ||
if (posInSelectedArray < 0) { | ||
this.select(item); | ||
} else if (posInSelectedArray > -1 && this.multiSelectKeyPressed) { | ||
this.unselect(item); | ||
} | ||
this.addClass( item, this.hoverClass ); | ||
this.addClass(item, this.hoverClass); | ||
}; | ||
@@ -553,12 +572,13 @@ | ||
* Logic when an item is de-selected | ||
* | ||
* | ||
* @param {Node} item – selected item. | ||
* @param {Boolean} force – forces through. | ||
*/ | ||
DragSelect.prototype._handleUnselection = function( item, force ) { | ||
DragSelect.prototype._handleUnselection = function(item, force) { | ||
if (!this.hasClass(item, this.hoverClass) && !force) { | ||
return false; | ||
} | ||
var posInSelectedArray = this.selected.indexOf(item); | ||
var isInPrevSelection = this._prevSelected.indexOf(item); // #9 | ||
if( !this.hasClass( item, this.hoverClass ) && !force ) { return false; } | ||
var posInSelectedArray = this.selected.indexOf( item ); | ||
var isInPrevSelection = this._prevSelected.indexOf( item ); // #9 | ||
/** | ||
@@ -571,10 +591,9 @@ * Special algorithm for issue #9. | ||
*/ | ||
if( posInSelectedArray > -1 && isInPrevSelection < 0 ) { | ||
this.unselect( item ); | ||
} else if ( posInSelectedArray < 0 && isInPrevSelection > -1 ) { | ||
this.select( item ); | ||
if (posInSelectedArray > -1 && isInPrevSelection < 0) { | ||
this.unselect(item); | ||
} else if (posInSelectedArray < 0 && isInPrevSelection > -1) { | ||
this.select(item); | ||
} | ||
this.removeClass( item, this.hoverClass ); | ||
this.removeClass(item, this.hoverClass); | ||
}; | ||
@@ -584,18 +603,20 @@ | ||
* Adds an item to the selection. | ||
* | ||
* | ||
* @param {Node} item – item to select. | ||
* @return {Node} item | ||
*/ | ||
DragSelect.prototype.select = function( item ) { | ||
DragSelect.prototype.select = function(item) { | ||
if (this.selected.indexOf(item) > -1) { | ||
return false; | ||
} | ||
if( this.selected.indexOf(item) > -1) { return false; } | ||
this.selected.push(item); | ||
this.addClass(item, this.selectedClass); | ||
this.selected.push( item ); | ||
this.addClass( item, this.selectedClass ); | ||
this.selectCallback(item); | ||
if (this._breaked) { | ||
return false; | ||
} | ||
this.selectCallback( item ); | ||
if( this._breaked ) { return false; } | ||
return item; | ||
}; | ||
@@ -605,18 +626,20 @@ | ||
* Removes an item from the selection. | ||
* | ||
* | ||
* @param {Node} item – item to select. | ||
* @return {Node} item | ||
*/ | ||
DragSelect.prototype.unselect = function( item ) { | ||
DragSelect.prototype.unselect = function(item) { | ||
if (this.selected.indexOf(item) < 0) { | ||
return false; | ||
} | ||
if( this.selected.indexOf(item) < 0) { return false; } | ||
this.selected.splice(this.selected.indexOf(item), 1); | ||
this.removeClass(item, this.selectedClass); | ||
this.selected.splice( this.selected.indexOf(item), 1 ); | ||
this.removeClass( item, this.selectedClass ); | ||
this.unselectCallback( item ); | ||
if( this._breaked ) { return false; } | ||
this.unselectCallback(item); | ||
if (this._breaked) { | ||
return false; | ||
} | ||
return item; | ||
}; | ||
@@ -627,41 +650,35 @@ | ||
* If it is already selected = remove, if not = add. | ||
* | ||
* | ||
* @param {Node} item – item to select. | ||
* @return {Node} item | ||
*/ | ||
DragSelect.prototype.toggle = function( item ) { | ||
if( this.selected.indexOf( item ) > -1) { | ||
this.unselect( item ); | ||
} else { | ||
this.select( item ); | ||
} | ||
return item; | ||
}; | ||
DragSelect.prototype.toggle = function(item) { | ||
if (this.selected.indexOf(item) > -1) { | ||
this.unselect(item); | ||
} else { | ||
this.select(item); | ||
} | ||
return item; | ||
}; | ||
/** | ||
* Checks if element is touched by the selector (and vice-versa) | ||
* | ||
* | ||
* @param {Node} element – item. | ||
* @param {Node} container – selector. | ||
* @param {Node} area – surrounding area. | ||
* @param {Object} selectionRect – Container bounds: | ||
Example: { | ||
y: this.selector.getBoundingClientRect().top + scroll.y, | ||
x: this.selector.getBoundingClientRect().left + scroll.x, | ||
h: this.selector.offsetHeight, | ||
w: this.selector.offsetWidth | ||
}; | ||
* @param {Object} scroll – Scroll x, y values. | ||
* @return {Boolean} | ||
*/ | ||
DragSelect.prototype.isElementTouching = function( element, container, area ) { | ||
/** | ||
* calculating everything here on every move consumes more performance | ||
* but makes sure to get the right positions even if the containers are | ||
* resized or moved on the fly. This also makes the function kinda context independant. | ||
*/ | ||
var scroll = this.getScroll( area ); | ||
var containerRect = { | ||
y: container.getBoundingClientRect().top + scroll.y, | ||
x: container.getBoundingClientRect().left + scroll.x, | ||
h: container.offsetHeight || element.getBoundingClientRect().height, | ||
w: container.offsetWidth || element.getBoundingClientRect().width | ||
}; | ||
DragSelect.prototype._isElementTouching = function( | ||
element, | ||
selectionRect, | ||
scroll | ||
) { | ||
var elementRect = { | ||
@@ -688,16 +705,13 @@ y: element.getBoundingClientRect().top + scroll.y, | ||
if ( | ||
containerRect.x < elementRect.x + elementRect.w && | ||
containerRect.x + containerRect.w > elementRect.x && | ||
containerRect.y < elementRect.y + elementRect.h && | ||
containerRect.h + containerRect.y > elementRect.y | ||
selectionRect.x < elementRect.x + elementRect.w && | ||
selectionRect.x + selectionRect.w > elementRect.x && | ||
selectionRect.y < elementRect.y + elementRect.h && | ||
selectionRect.h + selectionRect.y > elementRect.y | ||
) { | ||
return true; // collision detected! | ||
} | ||
else { | ||
} else { | ||
return false; | ||
} | ||
}; | ||
// Autoscroll | ||
@@ -708,16 +722,19 @@ ////////////////////////////////////////////////////////////////////////////////////// | ||
* Automatically Scroll the area by selecting | ||
* | ||
* | ||
* @param {Object} event – event object. | ||
*/ | ||
DragSelect.prototype._autoScroll = function( event ) { | ||
DragSelect.prototype._autoScroll = function(event) { | ||
var edge = this.isCursorNearEdge(event, this.area); | ||
var edge = this.isCursorNearEdge( event, this.area ); | ||
var _area = this.area === document ? document.documentElement || document.body : this.area; | ||
var _area = this.area === document ? this.area.body : this.area; | ||
if( edge === 'top' && _area.scrollTop > 0 ) { _area.scrollTop -= 1 * this.autoScrollSpeed; } | ||
else if( edge === 'bottom' ) { _area.scrollTop += 1 * this.autoScrollSpeed; } | ||
else if( edge === 'left' && _area.scrollLeft > 0 ) { _area.scrollLeft -= 1 * this.autoScrollSpeed; } | ||
else if( edge === 'right' ) { _area.scrollLeft += 1 * this.autoScrollSpeed; } | ||
if (edge === 'top' && _area.scrollTop > 0) { | ||
_area.scrollTop -= 1 * this.autoScrollSpeed; | ||
} else if (edge === 'bottom') { | ||
_area.scrollTop += 1 * this.autoScrollSpeed; | ||
} else if (edge === 'left' && _area.scrollLeft > 0) { | ||
_area.scrollLeft -= 1 * this.autoScrollSpeed; | ||
} else if (edge === 'right') { | ||
_area.scrollLeft += 1 * this.autoScrollSpeed; | ||
} | ||
}; | ||
@@ -727,3 +744,3 @@ | ||
* Check if the selector is near an edge of the area | ||
* | ||
* | ||
* @param {Object} event – event object. | ||
@@ -733,7 +750,6 @@ * @param {Node} area – the area. | ||
*/ | ||
DragSelect.prototype.isCursorNearEdge = function( event, area ) { | ||
DragSelect.prototype.isCursorNearEdge = function(event, area) { | ||
var cursorPosition = this._getCursorPos(event, area); | ||
var areaRect = this.getAreaRect(area); | ||
var cursorPosition = this._getCursorPos( event, area ); | ||
var areaRect = this.getAreaRect( area ); | ||
var tolerance = { | ||
@@ -744,12 +760,15 @@ x: Math.max(areaRect.width / 10, 30), | ||
if( cursorPosition.y < tolerance.y ) { return 'top'; } | ||
else if( areaRect.height - cursorPosition.y < tolerance.y ) { return 'bottom'; } | ||
else if( areaRect.width - cursorPosition.x < tolerance.x ) { return 'right'; } | ||
else if( cursorPosition.x < tolerance.x ) { return 'left'; } | ||
if (cursorPosition.y < tolerance.y) { | ||
return 'top'; | ||
} else if (areaRect.height - cursorPosition.y < tolerance.y) { | ||
return 'bottom'; | ||
} else if (areaRect.width - cursorPosition.x < tolerance.x) { | ||
return 'right'; | ||
} else if (cursorPosition.x < tolerance.x) { | ||
return 'left'; | ||
} | ||
return false; | ||
}; | ||
// Ending | ||
@@ -761,12 +780,13 @@ ////////////////////////////////////////////////////////////////////////////////////// | ||
*/ | ||
DragSelect.prototype.reset = function( event ) { | ||
DragSelect.prototype.reset = function(event) { | ||
this.previousCursorPos = this._getCursorPos(event, this.area); | ||
document.removeEventListener('mouseup', this.reset); | ||
this.area.removeEventListener('mousemove', this._handleMove); | ||
this.area.addEventListener('mousedown', this._startUp); | ||
this.previousCursorPos = this._getCursorPos( event, this.area ); | ||
document.removeEventListener( 'mouseup', this.reset ); | ||
this.area.removeEventListener( 'mousemove', this._handleMove ); | ||
this.area.addEventListener( 'mousedown', this._startUp ); | ||
this.callback(this.selected, event); | ||
if (this._breaked) { | ||
return false; | ||
} | ||
this.callback( this.selected, event ); | ||
if( this._breaked ) { return false; } | ||
this.selector.style.width = '0'; | ||
@@ -776,6 +796,9 @@ this.selector.style.height = '0'; | ||
setTimeout(function() { // debounce in order "onClick" to work | ||
this.mouseInteraction = false; | ||
}.bind(this), 100); | ||
setTimeout( | ||
function() { | ||
// debounce in order "onClick" to work | ||
this.mouseInteraction = false; | ||
}.bind(this), | ||
100 | ||
); | ||
}; | ||
@@ -789,8 +812,10 @@ | ||
DragSelect.prototype.break = function() { | ||
this._breaked = true; | ||
setTimeout(function() { // debounce the break should only break once instantly after call | ||
this._breaked = false; | ||
}.bind(this), 100); | ||
setTimeout( | ||
function() { | ||
// debounce the break should only break once instantly after call | ||
this._breaked = false; | ||
}.bind(this), | ||
100 | ||
); | ||
}; | ||
@@ -802,10 +827,7 @@ | ||
DragSelect.prototype.stop = function() { | ||
this.reset(); | ||
this.area.removeEventListener( 'mousedown', this._startUp ); | ||
document.removeEventListener( 'mouseup', this.reset ); | ||
this.area.removeEventListener('mousedown', this._startUp); | ||
document.removeEventListener('mouseup', this.reset); | ||
}; | ||
// Usefull methods for user | ||
@@ -816,9 +838,7 @@ ////////////////////////////////////////////////////////////////////////////////////// | ||
* Returns the current selected nodes | ||
* | ||
* | ||
* @return {Nodes} | ||
*/ | ||
DragSelect.prototype.getSelection = function() { | ||
return this.selected; | ||
}; | ||
@@ -829,3 +849,3 @@ | ||
* Will be relative to an area including the scroll unless advised otherwise | ||
* | ||
* | ||
* @param {Object} event | ||
@@ -836,10 +856,11 @@ * @param {Node} _area – containing area / this.area if none / document if === false | ||
*/ | ||
DragSelect.prototype.getCursorPos = function( event, _area, ignoreScroll ) { | ||
DragSelect.prototype.getCursorPos = function(event, _area, ignoreScroll) { | ||
if (!event) { | ||
return false; | ||
} | ||
if(!event) { return false; } | ||
var area = _area || (_area !== false && this.area); | ||
var pos = this._getCursorPos(event, area); | ||
var scroll = ignoreScroll ? { x: 0, y: 0 } : this.getScroll(area); | ||
var area = _area || _area !== false && this.area; | ||
var pos = this._getCursorPos( event, area ); | ||
var scroll = ignoreScroll ? { x: 0, y: 0 } : this.getScroll( area ); | ||
return { | ||
@@ -857,3 +878,3 @@ x: pos.x + scroll.x, | ||
* Can add multiple nodes at once, in contrary to .select | ||
* | ||
* | ||
* @param {Nodes} _nodes one or multiple nodes | ||
@@ -864,16 +885,22 @@ * @param {Boolean} _callback - if callback should be called | ||
*/ | ||
DragSelect.prototype.addSelection = function( _nodes, _callback, dontAddToSelectables ) { | ||
var nodes = this.toArray( _nodes ); | ||
DragSelect.prototype.addSelection = function( | ||
_nodes, | ||
_callback, | ||
dontAddToSelectables | ||
) { | ||
var nodes = this.toArray(_nodes); | ||
for (var index = 0, il = nodes.length; index < il; index++) { | ||
var node = nodes[index]; | ||
this.select( node ); | ||
this.select(node); | ||
} | ||
if( !dontAddToSelectables ) { this.addSelectables( nodes ); } | ||
if( _callback ) { this.callback( this.selected, false ); } | ||
if (!dontAddToSelectables) { | ||
this.addSelectables(nodes); | ||
} | ||
if (_callback) { | ||
this.callback(this.selected, false); | ||
} | ||
return this.selected; | ||
}; | ||
@@ -884,3 +911,3 @@ | ||
* Multiple nodes can be given at once, in contrary to unselect | ||
* | ||
* | ||
* @param {Nodes} _nodes one or multiple nodes | ||
@@ -891,16 +918,22 @@ * @param {Boolean} _callback - if callback should be called | ||
*/ | ||
DragSelect.prototype.removeSelection = function( _nodes, _callback, removeFromSelectables ) { | ||
var nodes = this.toArray( _nodes ); | ||
DragSelect.prototype.removeSelection = function( | ||
_nodes, | ||
_callback, | ||
removeFromSelectables | ||
) { | ||
var nodes = this.toArray(_nodes); | ||
for (var index = 0, il = nodes.length; index < il; index++) { | ||
var node = nodes[index]; | ||
this.unselect( node ); | ||
this.unselect(node); | ||
} | ||
if( removeFromSelectables ) { this.removeSelectables( nodes ); } | ||
if( _callback ) { this.callback( this.selected, false ); } | ||
if (removeFromSelectables) { | ||
this.removeSelectables(nodes); | ||
} | ||
if (_callback) { | ||
this.callback(this.selected, false); | ||
} | ||
return this.selected; | ||
}; | ||
@@ -912,3 +945,3 @@ | ||
* Multiple nodes can be given at once. | ||
* | ||
* | ||
* @param {Nodes} _nodes one or multiple nodes | ||
@@ -919,23 +952,16 @@ * @param {Boolean} _callback - if callback should be called | ||
*/ | ||
DragSelect.prototype.toggleSelection = function( _nodes, _callback, _special ) { | ||
var nodes = this.toArray( _nodes ); | ||
DragSelect.prototype.toggleSelection = function(_nodes, _callback, _special) { | ||
var nodes = this.toArray(_nodes); | ||
for (var index = 0, il = nodes.length; index < il; index++) { | ||
var node = nodes[index]; | ||
if( this.selected.indexOf( node ) < 0 ) { | ||
this.addSelection( node, _callback, _special ); | ||
if (this.selected.indexOf(node) < 0) { | ||
this.addSelection(node, _callback, _special); | ||
} else { | ||
this.removeSelection( node, _callback, _special ); | ||
this.removeSelection(node, _callback, _special); | ||
} | ||
} | ||
return this.selected; | ||
}; | ||
@@ -945,3 +971,3 @@ | ||
* Sets the current selected nodes and optionally run the callback | ||
* | ||
* | ||
* @param {Nodes} _nodes – dom nodes | ||
@@ -952,9 +978,11 @@ * @param {Boolean} runCallback - if callback should be called | ||
*/ | ||
DragSelect.prototype.setSelection = function( _nodes, runCallback, dontAddToSelectables ) { | ||
DragSelect.prototype.setSelection = function( | ||
_nodes, | ||
runCallback, | ||
dontAddToSelectables | ||
) { | ||
this.clearSelection(); | ||
this.addSelection( _nodes, runCallback, dontAddToSelectables ); | ||
this.addSelection(_nodes, runCallback, dontAddToSelectables); | ||
return this.selected; | ||
}; | ||
@@ -964,18 +992,18 @@ | ||
* Unselect / Deselect all current selected Nodes | ||
* | ||
* | ||
* @param {Boolean} runCallback - if callback should be called | ||
* @return {Array} this.selected, should be empty | ||
*/ | ||
DragSelect.prototype.clearSelection = function( runCallback ) { | ||
DragSelect.prototype.clearSelection = function(runCallback) { | ||
var selection = this.selected.slice(); | ||
for (var index = 0, il = selection.length; index < il; index++) { | ||
var node = selection[index]; | ||
this.unselect( node ); | ||
this.unselect(node); | ||
} | ||
if( runCallback ) { this.callback( this.selected, false ); } | ||
if (runCallback) { | ||
this.callback(this.selected, false); | ||
} | ||
return this.selected; | ||
}; | ||
@@ -986,3 +1014,3 @@ | ||
* The algorithm makes sure that no node is added twice | ||
* | ||
* | ||
* @param {Nodes} _nodes – dom nodes | ||
@@ -992,8 +1020,6 @@ * @param {Boolean} addToSelection – if elements should also be added to current selection | ||
*/ | ||
DragSelect.prototype.addSelectables = function( _nodes, addToSelection ) { | ||
var nodes = this.toArray( _nodes ); | ||
this._handleSelectables( nodes, false, addToSelection ); | ||
DragSelect.prototype.addSelectables = function(_nodes, addToSelection) { | ||
var nodes = this.toArray(_nodes); | ||
this._handleSelectables(nodes, false, addToSelection); | ||
return _nodes; | ||
}; | ||
@@ -1003,9 +1029,7 @@ | ||
* Gets all nodes that can be selected | ||
* | ||
* | ||
* @return {Nodes} this.selectables | ||
*/ | ||
DragSelect.prototype.getSelectables = function() { | ||
return this.selectables; | ||
}; | ||
@@ -1018,3 +1042,3 @@ | ||
* thus replacing the original set. | ||
* | ||
* | ||
* @param {Nodes} _nodes – dom nodes | ||
@@ -1025,7 +1049,9 @@ * @param {Boolean} removeFromSelection – if elements should also be removed from current selection | ||
*/ | ||
DragSelect.prototype.setSelectables = function( _nodes, removeFromSelection, addToSelection ) { | ||
this.removeSelectables( this.getSelectables(), removeFromSelection ); | ||
return this.addSelectables( _nodes, addToSelection ); | ||
DragSelect.prototype.setSelectables = function( | ||
_nodes, | ||
removeFromSelection, | ||
addToSelection | ||
) { | ||
this.removeSelectables(this.getSelectables(), removeFromSelection); | ||
return this.addSelectables(_nodes, addToSelection); | ||
}; | ||
@@ -1035,3 +1061,3 @@ | ||
* Remove nodes from the nodes that can be selected. | ||
* | ||
* | ||
* @param {Nodes} _nodes – dom nodes | ||
@@ -1041,11 +1067,8 @@ * @param {Boolean} removeFromSelection – if elements should also be removed from current selection | ||
*/ | ||
DragSelect.prototype.removeSelectables = function( _nodes, removeFromSelection ) { | ||
var nodes = this.toArray( _nodes ); | ||
this._handleSelectables( nodes, true, removeFromSelection ); | ||
DragSelect.prototype.removeSelectables = function(_nodes, removeFromSelection) { | ||
var nodes = this.toArray(_nodes); | ||
this._handleSelectables(nodes, true, removeFromSelection); | ||
return _nodes; | ||
}; | ||
// Helpers | ||
@@ -1058,23 +1081,24 @@ ////////////////////////////////////////////////////////////////////////////////////// | ||
* (found @ https://stackoverflow.com/a/2405835) | ||
* | ||
* @param {Object} event | ||
* | ||
* @param {Object} event | ||
* @return {Boolean} | ||
*/ | ||
DragSelect.prototype.isRightClick = function( event ) { | ||
DragSelect.prototype.isRightClick = function(event) { | ||
if (!event) { | ||
return false; | ||
} | ||
if( !event ) { return false; } | ||
var isRightMB = false; | ||
if ('which' in event) { // Gecko (Firefox), WebKit (Safari/Chrome) & Opera | ||
isRightMB = event.which === 3; | ||
} else if ('button' in event) { // IE, Opera | ||
isRightMB = event.button === 2; | ||
if ('which' in event) { | ||
// Gecko (Firefox), WebKit (Safari/Chrome) & Opera | ||
isRightMB = event.which === 3; | ||
} else if ('button' in event) { | ||
// IE, Opera | ||
isRightMB = event.button === 2; | ||
} | ||
return isRightMB; | ||
}; | ||
/** | ||
@@ -1084,17 +1108,21 @@ * Adds a class to an element | ||
* all credits to http://clubmate.fi/javascript-adding-and-removing-class-names-from-elements/ | ||
* | ||
* @param {Node} element | ||
* @param {String} classname | ||
* | ||
* @param {Node} element | ||
* @param {String} classname | ||
* @return {Node} element | ||
*/ | ||
DragSelect.prototype.addClass = function( element, classname ) { | ||
DragSelect.prototype.addClass = function(element, classname) { | ||
if (element.classList) { | ||
return element.classList.add(classname); | ||
} | ||
if(element.classList) { return element.classList.add(classname); } | ||
var cn = element.getAttribute('class') || ''; | ||
if( cn.indexOf(classname) !== -1 ) { return element; } // test for existance | ||
if( cn !== '' ) { classname = ' ' + classname; } // add a space if the element already has class | ||
element.setAttribute('class', cn+classname); | ||
if (cn.indexOf(classname) !== -1) { | ||
return element; | ||
} // test for existance | ||
if (cn !== '') { | ||
classname = ' ' + classname; | ||
} // add a space if the element already has class | ||
element.setAttribute('class', cn + classname); | ||
return element; | ||
}; | ||
@@ -1106,17 +1134,17 @@ | ||
* all credits to http://clubmate.fi/javascript-adding-and-removing-class-names-from-elements/ | ||
* | ||
* @param {Node} element | ||
* @param {String} classname | ||
* | ||
* @param {Node} element | ||
* @param {String} classname | ||
* @return {Node} element | ||
*/ | ||
DragSelect.prototype.removeClass = function( element, classname ) { | ||
DragSelect.prototype.removeClass = function(element, classname) { | ||
if (element.classList) { | ||
return element.classList.remove(classname); | ||
} | ||
if(element.classList) { return element.classList.remove(classname); } | ||
var cn = element.getAttribute('class') || ''; | ||
var rxp = new RegExp( classname + '\\b', 'g' ); | ||
cn = cn.replace( rxp, '' ); | ||
var rxp = new RegExp(classname + '\\b', 'g'); | ||
cn = cn.replace(rxp, ''); | ||
element.setAttribute('class', cn); | ||
return element; | ||
}; | ||
@@ -1127,15 +1155,18 @@ | ||
* sadly legacy phones/browsers don’t support .classlist so we use this workaround | ||
* | ||
* @param {Node} element | ||
* @param {String} classname | ||
* | ||
* @param {Node} element | ||
* @param {String} classname | ||
* @return {Boolean} | ||
*/ | ||
DragSelect.prototype.hasClass = function( element, classname ) { | ||
DragSelect.prototype.hasClass = function(element, classname) { | ||
if (element.classList) { | ||
return element.classList.contains(classname); | ||
} | ||
if(element.classList) { return element.classList.contains(classname); } | ||
var cn = element.getAttribute('class') || ''; | ||
if( cn.indexOf( classname ) > -1 ) { return true; } | ||
else { return false; } | ||
if (cn.indexOf(classname) > -1) { | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
}; | ||
@@ -1146,13 +1177,16 @@ | ||
* so user doesn’t have to care. | ||
* | ||
* | ||
* @param {Node} nodes | ||
* @return {array} | ||
*/ | ||
DragSelect.prototype.toArray = function( nodes ) { | ||
DragSelect.prototype.toArray = function(nodes) { | ||
if (!nodes) { | ||
return false; | ||
} | ||
if (!nodes.length && this.isElement(nodes)) { | ||
return [nodes]; | ||
} | ||
if( !nodes ) { return false; } | ||
if( !nodes.length && this.isElement( nodes ) ) { return [nodes]; } | ||
var array = []; | ||
for ( var i = nodes.length - 1; i >= 0; i-- ) { | ||
for (var i = nodes.length - 1; i >= 0; i--) { | ||
array[i] = nodes[i]; | ||
@@ -1162,3 +1196,2 @@ } | ||
return array; | ||
}; | ||
@@ -1168,22 +1201,22 @@ | ||
* Checks if a node is of type element | ||
* all credits to vikynandha: https://gist.github.com/vikynandha/6539809 | ||
* | ||
* all credits to vikynandha: https://gist.github.com/vikynandha/6539809 | ||
* | ||
* @param {Node} node | ||
* @return {Boolean} | ||
*/ | ||
DragSelect.prototype.isElement = function( node ) { | ||
try { // Using W3 DOM2 (works for FF, Opera and Chrome), also checking for SVGs | ||
DragSelect.prototype.isElement = function(node) { | ||
try { | ||
// Using W3 DOM2 (works for FF, Opera and Chrome), also checking for SVGs | ||
return node instanceof HTMLElement || node instanceof SVGElement; | ||
} | ||
catch( e ){ | ||
} catch (e) { | ||
// Browsers not supporting W3 DOM2 don't have HTMLElement and | ||
// an exception is thrown and we end up here. Testing some | ||
// properties that all elements have. (works even on IE7) | ||
return ( typeof node === 'object' ) && | ||
( node.nodeType === 1 ) && | ||
( typeof node.style === 'object' ) && | ||
( typeof node.ownerDocument === 'object' ); | ||
return ( | ||
typeof node === 'object' && | ||
node.nodeType === 1 && | ||
typeof node.style === 'object' && | ||
typeof node.ownerDocument === 'object' | ||
); | ||
} | ||
}; | ||
@@ -1196,3 +1229,3 @@ | ||
* Use the public .getCursorPos() from outside, it’s more flexible | ||
* | ||
* | ||
* @param {Object} event | ||
@@ -1202,6 +1235,9 @@ * @param {Node} area – containing area / document if none | ||
*/ | ||
DragSelect.prototype._getCursorPos = function( event, area ) { | ||
if(!event) { return { x: 0, y: 0 }; } | ||
DragSelect.prototype._getCursorPos = function(event, area) { | ||
if (!event) { | ||
return { x: 0, y: 0 }; | ||
} | ||
var cPos = { // event.clientX/Y fallback for <IE8 | ||
var cPos = { | ||
// event.clientX/Y fallback for <IE8 | ||
x: event.pageX || event.clientX, | ||
@@ -1211,10 +1247,10 @@ y: event.pageY || event.clientY | ||
var areaRect = this.getAreaRect( area || document ); | ||
var docScroll = this.getScroll(); // needed when document is scrollable but area is not | ||
var areaRect = this.getAreaRect(area || document); | ||
var docScroll = this.getScroll(); // needed when document is scrollable but area is not | ||
return { // if it’s constrained in an area the area should be substracted calculate | ||
return { | ||
// if it’s constrained in an area the area should be substracted calculate | ||
x: cPos.x - areaRect.left - docScroll.x, | ||
y: cPos.y - areaRect.top - docScroll.y | ||
}; | ||
}; | ||
@@ -1224,7 +1260,7 @@ | ||
* Returns the starting/initial position of the cursor/selector | ||
* | ||
* | ||
* @return {Object} initialPos. | ||
*/ | ||
DragSelect.prototype.getInitialCursorPosition = function() { | ||
return this.initialCursorPos; | ||
return this.initialCursorPos; | ||
}; | ||
@@ -1234,3 +1270,3 @@ | ||
* Returns the last seen position of the cursor/selector | ||
* | ||
* | ||
* @return {Object} initialPos. | ||
@@ -1248,3 +1284,3 @@ */ | ||
DragSelect.prototype.getPreviousCursorPosition = function() { | ||
return this.previousCursorPos; | ||
return this.previousCursorPos; | ||
}; | ||
@@ -1260,6 +1296,9 @@ | ||
*/ | ||
DragSelect.prototype.getCursorPositionDifference = function( usePreviousCursorDifference ) { | ||
DragSelect.prototype.getCursorPositionDifference = function( | ||
usePreviousCursorDifference | ||
) { | ||
var posA = this.getCurrentCursorPosition(); | ||
var posB = usePreviousCursorDifference ? this.getPreviousCursorPosition() : this.getInitialCursorPosition(); | ||
var posB = usePreviousCursorDifference | ||
? this.getPreviousCursorPosition() | ||
: this.getInitialCursorPosition(); | ||
@@ -1270,3 +1309,2 @@ return { | ||
}; | ||
}; | ||
@@ -1277,14 +1315,20 @@ | ||
* If container has no scroll it will return 0 | ||
* | ||
* | ||
* @param {Node} area | ||
* @return {Object} scroll X/Y | ||
*/ | ||
DragSelect.prototype.getScroll = function( area ) { | ||
DragSelect.prototype.getScroll = function(area) { | ||
var body = { | ||
top: document.body.scrollTop > 0 ? document.body.scrollTop : document.documentElement.scrollTop, | ||
left: document.body.scrollLeft > 0 ? document.body.scrollLeft : document.documentElement.scrollLeft | ||
top: | ||
document.body.scrollTop > 0 | ||
? document.body.scrollTop | ||
: document.documentElement.scrollTop, | ||
left: | ||
document.body.scrollLeft > 0 | ||
? document.body.scrollLeft | ||
: document.documentElement.scrollLeft | ||
}; | ||
var scroll = { // when the rectangle is bound to the document, no scroll is needed | ||
var scroll = { | ||
// when the rectangle is bound to the document, no scroll is needed | ||
y: area && area.scrollTop >= 0 ? area.scrollTop : body.top, | ||
@@ -1295,3 +1339,2 @@ x: area && area.scrollLeft >= 0 ? area.scrollLeft : body.left | ||
return scroll; | ||
}; | ||
@@ -1303,14 +1346,26 @@ | ||
* except the sizes will be nulled. | ||
* | ||
* @param {Node} area | ||
* | ||
* @param {Node} area | ||
* @return {Object} | ||
*/ | ||
DragSelect.prototype.getAreaRect = function( area ) { | ||
if(area === document) { | ||
DragSelect.prototype.getAreaRect = function(area) { | ||
if (area === document) { | ||
var size = { | ||
y: area.documentElement.clientHeight > 0 ? area.documentElement.clientHeight : window.innerHeight, | ||
x: area.documentElement.clientWidth > 0 ? area.documentElement.clientWidth : window.innerWidth | ||
y: | ||
area.documentElement.clientHeight > 0 | ||
? area.documentElement.clientHeight | ||
: window.innerHeight, | ||
x: | ||
area.documentElement.clientWidth > 0 | ||
? area.documentElement.clientWidth | ||
: window.innerWidth | ||
}; | ||
return { top: 0, left: 0, bottom: 0, right: 0, width: size.x, height: size.y }; | ||
return { | ||
top: 0, | ||
left: 0, | ||
bottom: 0, | ||
right: 0, | ||
width: size.x, | ||
height: size.y | ||
}; | ||
} | ||
@@ -1326,3 +1381,2 @@ | ||
}; | ||
}; | ||
@@ -1333,10 +1387,9 @@ | ||
* height values accordingly. | ||
* | ||
* | ||
* @param {Node} node | ||
* @param {Object} pos { x, y, w, h } | ||
* | ||
* | ||
* @return {Node} | ||
*/ | ||
DragSelect.prototype.updatePos = function( node, pos ) { | ||
DragSelect.prototype.updatePos = function(node, pos) { | ||
node.style.left = pos.x + 'px'; | ||
@@ -1347,24 +1400,23 @@ node.style.top = pos.y + 'px'; | ||
return node; | ||
}; | ||
// Make exportable | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
// jshint -W117 | ||
/* eslint-disable no-undef */ | ||
// Module exporting | ||
if ( typeof module !== 'undefined' && module !== null ) { | ||
if (typeof module !== 'undefined' && module !== null) { | ||
module.exports = DragSelect; | ||
// AMD Modules | ||
} else if( typeof define !== 'undefined' && typeof define === 'function' && define ) { | ||
define(function() { return DragSelect; }); | ||
// AMD Modules | ||
} else if ( | ||
typeof define !== 'undefined' && | ||
typeof define === 'function' && | ||
define | ||
) { | ||
define(function() { | ||
return DragSelect; | ||
}); | ||
} else { | ||
window.DragSelect = DragSelect; | ||
} |
@@ -1,1 +0,1 @@ | ||
function DragSelect(e){this.multiSelectKeyPressed,this.initialCursorPos={x:0,y:0},this.newCursorPos={x:0,y:0},this.previousCursorPos={x:0,y:0},this.initialScroll,this.selected=[],this._prevSelected=[],this._createBindings(),this._setupOptions(e),this.start()}DragSelect.prototype._createBindings=function(){this._startUp=this._startUp.bind(this),this._handleMove=this._handleMove.bind(this),this.reset=this.reset.bind(this),this._onClick=this._onClick.bind(this)},DragSelect.prototype._setupOptions=function(e){if(this.selectedClass=e.selectedClass||"ds-selected",this.hoverClass=e.hoverClass||"ds-hover",this.selectorClass=e.selectorClass||"ds-selector",this.selectableClass=e.selectableClass||"ds-selectable",this.selectables=[],this._handleSelectables(this.toArray(e.selectables)),this.multiSelectKeys=e.multiSelectKeys||["ctrlKey","shiftKey","metaKey"],this.multiSelectMode=e.multiSelectMode||!1,this.autoScrollSpeed=e.autoScrollSpeed||1,this.selectCallback=e.onElementSelect||function(){},this.unselectCallback=e.onElementUnselect||function(){},this.moveStartCallback=e.onDragStart||function(){},this.moveCallback=e.onDragMove||function(){},this.callback=e.callback||function(){},this.area=e.area||document,this.customStyles=e.customStyles,this.area!==document){var t=getComputedStyle(this.area);"absolute"===t.position||"relative"===t.position||"fixed"===t.position||(this.area.style.position="relative")}this.selector=e.selector||this._createSelector(),this.addClass(this.selector,this.selectorClass)},DragSelect.prototype._handleSelectables=function(e,t,s){for(var i=0;i<e.length;i++){var o=e[i],r=this.selectables.indexOf(o);r<0&&!t?(this.addClass(o,this.selectableClass),o.addEventListener("click",this._onClick),this.selectables.push(o),s&&this.selected.indexOf(o)<0&&(this.addClass(o,this.selectedClass),this.selected.push(o))):r>-1&&t&&(this.removeClass(o,this.hoverClass),this.removeClass(o,this.selectableClass),o.removeEventListener("click",this._onClick),this.selectables.splice(r,1),s&&this.selected.indexOf(o)>-1&&(this.removeClass(o,this.selectedClass),this.selected.splice(this.selected.indexOf(o),1)))}},DragSelect.prototype._onClick=function(e){if(!this.mouseInteraction&&!this.isRightClick(e)){var t=e.target;this.isMultiSelectKeyPressed(e)?this._prevSelected=this.selected.slice():this._prevSelected=[],this.checkIfInsideSelection(!0),this.selectables.indexOf(t)>-1&&this.toggle(t),this.reset()}},DragSelect.prototype._createSelector=function(){var e=document.createElement("div");return e.style.position="absolute",this.customStyles||(e.style.background="rgba(0, 0, 255, 0.1)",e.style.border="1px solid rgba(0, 0, 255, 0.45)",e.style.display="none",e.style.pointerEvents="none"),(this.area===document?document.body:this.area).appendChild(e),e},DragSelect.prototype.start=function(){this.area.addEventListener("mousedown",this._startUp)},DragSelect.prototype._startUp=function(e){if(!this.isRightClick(e)){if(this.mouseInteraction=!0,this.selector.style.display="block",this.isMultiSelectKeyPressed(e)?this._prevSelected=this.selected.slice():this._prevSelected=[],this._getStartingPositions(e),this.checkIfInsideSelection(!0),this.selector.style.display="none",this.moveStartCallback(e),this._breaked)return!1;this.area.removeEventListener("mousedown",this._startUp),this.area.addEventListener("mousemove",this._handleMove),document.addEventListener("mouseup",this.reset)}},DragSelect.prototype.isMultiSelectKeyPressed=function(e){if(this.multiSelectKeyPressed=!1,this.multiSelectMode)this.multiSelectKeyPressed=!0;else for(var t=0;t<this.multiSelectKeys.length;t++){var s=this.multiSelectKeys[t];e[s]&&(this.multiSelectKeyPressed=!0)}return this.multiSelectKeyPressed},DragSelect.prototype._getStartingPositions=function(e){this.initialCursorPos=this.newCursorPos=this._getCursorPos(e,this.area),this.initialScroll=this.getScroll(this.area);var t={};t.x=this.initialCursorPos.x+this.initialScroll.x,t.y=this.initialCursorPos.y+this.initialScroll.y,t.w=0,t.h=0,this.updatePos(this.selector,t)},DragSelect.prototype._handleMove=function(e){var t=this.getPosition(e);if(this.moveCallback(e),this._breaked)return!1;this.selector.style.display="block",this.updatePos(this.selector,t),this.checkIfInsideSelection(),this._autoScroll(e)},DragSelect.prototype.getPosition=function(e){var t=this._getCursorPos(e,this.area),s=this.getScroll(this.area);this.newCursorPos=t;var i={x:s.x-this.initialScroll.x,y:s.y-this.initialScroll.y},o={};return t.x>this.initialCursorPos.x-i.x?(o.x=this.initialCursorPos.x+this.initialScroll.x,o.w=t.x-this.initialCursorPos.x+i.x):(o.x=t.x+s.x,o.w=this.initialCursorPos.x-t.x-i.x),t.y>this.initialCursorPos.y-i.y?(o.y=this.initialCursorPos.y+this.initialScroll.y,o.h=t.y-this.initialCursorPos.y+i.y):(o.y=t.y+s.y,o.h=this.initialCursorPos.y-t.y-i.y),o},DragSelect.prototype.checkIfInsideSelection=function(e){for(var t=!1,s=0,i=this.selectables.length;s<i;s++){var o=this.selectables[s];this.isElementTouching(o,this.selector,this.area)?(this._handleSelection(o,e),t=!0):this._handleUnselection(o,e)}return t},DragSelect.prototype._handleSelection=function(e,t){if(this.hasClass(e,this.hoverClass)&&!t)return!1;var s=this.selected.indexOf(e);s<0?this.select(e):s>-1&&this.multiSelectKeyPressed&&this.unselect(e),this.addClass(e,this.hoverClass)},DragSelect.prototype._handleUnselection=function(e,t){if(!this.hasClass(e,this.hoverClass)&&!t)return!1;var s=this.selected.indexOf(e),i=this._prevSelected.indexOf(e);s>-1&&i<0?this.unselect(e):s<0&&i>-1&&this.select(e),this.removeClass(e,this.hoverClass)},DragSelect.prototype.select=function(e){return!(this.selected.indexOf(e)>-1)&&(this.selected.push(e),this.addClass(e,this.selectedClass),this.selectCallback(e),!this._breaked&&e)},DragSelect.prototype.unselect=function(e){return!(this.selected.indexOf(e)<0)&&(this.selected.splice(this.selected.indexOf(e),1),this.removeClass(e,this.selectedClass),this.unselectCallback(e),!this._breaked&&e)},DragSelect.prototype.toggle=function(e){return this.selected.indexOf(e)>-1?this.unselect(e):this.select(e),e},DragSelect.prototype.isElementTouching=function(e,t,s){var i=this.getScroll(s),o={y:t.getBoundingClientRect().top+i.y,x:t.getBoundingClientRect().left+i.x,h:t.offsetHeight||e.getBoundingClientRect().height,w:t.offsetWidth||e.getBoundingClientRect().width},r={y:e.getBoundingClientRect().top+i.y,x:e.getBoundingClientRect().left+i.x,h:e.offsetHeight||e.getBoundingClientRect().height,w:e.offsetWidth||e.getBoundingClientRect().width};return o.x<r.x+r.w&&o.x+o.w>r.x&&o.y<r.y+r.h&&o.h+o.y>r.y},DragSelect.prototype._autoScroll=function(e){var t=this.isCursorNearEdge(e,this.area),s=this.area===document?this.area.body:this.area;"top"===t&&s.scrollTop>0?s.scrollTop-=1*this.autoScrollSpeed:"bottom"===t?s.scrollTop+=1*this.autoScrollSpeed:"left"===t&&s.scrollLeft>0?s.scrollLeft-=1*this.autoScrollSpeed:"right"===t&&(s.scrollLeft+=1*this.autoScrollSpeed)},DragSelect.prototype.isCursorNearEdge=function(e,t){var s=this._getCursorPos(e,t),i=this.getAreaRect(t),o={x:Math.max(i.width/10,30),y:Math.max(i.height/10,30)};return s.y<o.y?"top":i.height-s.y<o.y?"bottom":i.width-s.x<o.x?"right":s.x<o.x&&"left"},DragSelect.prototype.reset=function(e){if(this.previousCursorPos=this._getCursorPos(e,this.area),document.removeEventListener("mouseup",this.reset),this.area.removeEventListener("mousemove",this._handleMove),this.area.addEventListener("mousedown",this._startUp),this.callback(this.selected,e),this._breaked)return!1;this.selector.style.width="0",this.selector.style.height="0",this.selector.style.display="none",setTimeout(function(){this.mouseInteraction=!1}.bind(this),100)},DragSelect.prototype.break=function(){this._breaked=!0,setTimeout(function(){this._breaked=!1}.bind(this),100)},DragSelect.prototype.stop=function(){this.reset(),this.area.removeEventListener("mousedown",this._startUp),document.removeEventListener("mouseup",this.reset)},DragSelect.prototype.getSelection=function(){return this.selected},DragSelect.prototype.getCursorPos=function(e,t,s){if(!e)return!1;var i=t||!1!==t&&this.area,o=this._getCursorPos(e,i),r=s?{x:0,y:0}:this.getScroll(i);return{x:o.x+r.x,y:o.y+r.y}},DragSelect.prototype.addSelection=function(e,t,s){for(var i=this.toArray(e),o=0,r=i.length;o<r;o++){var l=i[o];this.select(l)}return s||this.addSelectables(i),t&&this.callback(this.selected,!1),this.selected},DragSelect.prototype.removeSelection=function(e,t,s){for(var i=this.toArray(e),o=0,r=i.length;o<r;o++){var l=i[o];this.unselect(l)}return s&&this.removeSelectables(i),t&&this.callback(this.selected,!1),this.selected},DragSelect.prototype.toggleSelection=function(e,t,s){for(var i=this.toArray(e),o=0,r=i.length;o<r;o++){var l=i[o];this.selected.indexOf(l)<0?this.addSelection(l,t,s):this.removeSelection(l,t,s)}return this.selected},DragSelect.prototype.setSelection=function(e,t,s){return this.clearSelection(),this.addSelection(e,t,s),this.selected},DragSelect.prototype.clearSelection=function(e){for(var t=this.selected.slice(),s=0,i=t.length;s<i;s++){var o=t[s];this.unselect(o)}return e&&this.callback(this.selected,!1),this.selected},DragSelect.prototype.addSelectables=function(e,t){var s=this.toArray(e);return this._handleSelectables(s,!1,t),e},DragSelect.prototype.getSelectables=function(){return this.selectables},DragSelect.prototype.setSelectables=function(e,t,s){return this.removeSelectables(this.getSelectables(),t),this.addSelectables(e,s)},DragSelect.prototype.removeSelectables=function(e,t){var s=this.toArray(e);return this._handleSelectables(s,!0,t),e},DragSelect.prototype.isRightClick=function(e){if(!e)return!1;var t=!1;return"which"in e?t=3===e.which:"button"in e&&(t=2===e.button),t},DragSelect.prototype.addClass=function(e,t){if(e.classList)return e.classList.add(t);var s=e.getAttribute("class")||"";return-1!==s.indexOf(t)?e:(""!==s&&(t=" "+t),e.setAttribute("class",s+t),e)},DragSelect.prototype.removeClass=function(e,t){if(e.classList)return e.classList.remove(t);var s=e.getAttribute("class")||"",i=new RegExp(t+"\\b","g");return s=s.replace(i,""),e.setAttribute("class",s),e},DragSelect.prototype.hasClass=function(e,t){return e.classList?e.classList.contains(t):(e.getAttribute("class")||"").indexOf(t)>-1},DragSelect.prototype.toArray=function(e){if(!e)return!1;if(!e.length&&this.isElement(e))return[e];for(var t=[],s=e.length-1;s>=0;s--)t[s]=e[s];return t},DragSelect.prototype.isElement=function(e){try{return e instanceof HTMLElement||e instanceof SVGElement}catch(t){return"object"==typeof e&&1===e.nodeType&&"object"==typeof e.style&&"object"==typeof e.ownerDocument}},DragSelect.prototype._getCursorPos=function(e,t){if(!e)return{x:0,y:0};var s={x:e.pageX||e.clientX,y:e.pageY||e.clientY},i=this.getAreaRect(t||document),o=this.getScroll();return{x:s.x-i.left-o.x,y:s.y-i.top-o.y}},DragSelect.prototype.getInitialCursorPosition=function(){return this.initialCursorPos},DragSelect.prototype.getCurrentCursorPosition=function(){return this.newCursorPos},DragSelect.prototype.getPreviousCursorPosition=function(){return this.previousCursorPos},DragSelect.prototype.getCursorPositionDifference=function(e){var t=this.getCurrentCursorPosition(),s=e?this.getPreviousCursorPosition():this.getInitialCursorPosition();return{x:t.x-s.x,y:t.y-s.y}},DragSelect.prototype.getScroll=function(e){var t={top:document.body.scrollTop>0?document.body.scrollTop:document.documentElement.scrollTop,left:document.body.scrollLeft>0?document.body.scrollLeft:document.documentElement.scrollLeft};return{y:e&&e.scrollTop>=0?e.scrollTop:t.top,x:e&&e.scrollLeft>=0?e.scrollLeft:t.left}},DragSelect.prototype.getAreaRect=function(e){if(e===document){var t={y:e.documentElement.clientHeight>0?e.documentElement.clientHeight:window.innerHeight,x:e.documentElement.clientWidth>0?e.documentElement.clientWidth:window.innerWidth};return{top:0,left:0,bottom:0,right:0,width:t.x,height:t.y}}return{top:e.getBoundingClientRect().top,left:e.getBoundingClientRect().left,bottom:e.getBoundingClientRect().bottom,right:e.getBoundingClientRect().right,width:e.offsetWidth,height:e.offsetHeight}},DragSelect.prototype.updatePos=function(e,t){return e.style.left=t.x+"px",e.style.top=t.y+"px",e.style.width=t.w+"px",e.style.height=t.h+"px",e},"undefined"!=typeof module&&null!==module?module.exports=DragSelect:"undefined"!=typeof define&&"function"==typeof define&&define?define(function(){return DragSelect}):window.DragSelect=DragSelect; | ||
function DragSelect(e){this.multiSelectKeyPressed,this.initialCursorPos={x:0,y:0},this.newCursorPos={x:0,y:0},this.previousCursorPos={x:0,y:0},this.initialScroll,this.selected=[],this._prevSelected=[],this._createBindings(),this._setupOptions(e),this.start()}DragSelect.prototype._createBindings=function(){this._startUp=this._startUp.bind(this),this._handleMove=this._handleMove.bind(this),this.reset=this.reset.bind(this),this._onClick=this._onClick.bind(this)},DragSelect.prototype._setupOptions=function(e){if(this.selectedClass=e.selectedClass||"ds-selected",this.hoverClass=e.hoverClass||"ds-hover",this.selectorClass=e.selectorClass||"ds-selector",this.selectableClass=e.selectableClass||"ds-selectable",this.selectables=[],this._handleSelectables(this.toArray(e.selectables)),this.multiSelectKeys=e.multiSelectKeys||["ctrlKey","shiftKey","metaKey"],this.multiSelectMode=e.multiSelectMode||!1,this.autoScrollSpeed=0===e.autoScrollSpeed?0:e.autoScrollSpeed||1,this.selectCallback=e.onElementSelect||function(){},this.unselectCallback=e.onElementUnselect||function(){},this.onDragStartBegin=e.onDragStartBegin||function(){},this.moveStartCallback=e.onDragStart||function(){},this.moveCallback=e.onDragMove||function(){},this.callback=e.callback||function(){},this.area=e.area||document,this.customStyles=e.customStyles,this.area!==document){var t=getComputedStyle(this.area);"absolute"===t.position||"relative"===t.position||"fixed"===t.position||(this.area.style.position="relative")}this.selector=e.selector||this._createSelector(),this.addClass(this.selector,this.selectorClass)},DragSelect.prototype._handleSelectables=function(e,t,s){for(var i=0;i<e.length;i++){var o=e[i],r=this.selectables.indexOf(o);r<0&&!t?(this.addClass(o,this.selectableClass),o.addEventListener("click",this._onClick),this.selectables.push(o),s&&this.selected.indexOf(o)<0&&(this.addClass(o,this.selectedClass),this.selected.push(o))):r>-1&&t&&(this.removeClass(o,this.hoverClass),this.removeClass(o,this.selectableClass),o.removeEventListener("click",this._onClick),this.selectables.splice(r,1),s&&this.selected.indexOf(o)>-1&&(this.removeClass(o,this.selectedClass),this.selected.splice(this.selected.indexOf(o),1)))}},DragSelect.prototype._onClick=function(e){if(!this.mouseInteraction&&!this.isRightClick(e)){var t=e.target;this.isMultiSelectKeyPressed(e)?this._prevSelected=this.selected.slice():this._prevSelected=[],this.checkIfInsideSelection(!0),this.selectables.indexOf(t)>-1&&this.toggle(t),this.reset()}},DragSelect.prototype._createSelector=function(){var e=document.createElement("div");return e.style.position="absolute",this.customStyles||(e.style.background="rgba(0, 0, 255, 0.1)",e.style.border="1px solid rgba(0, 0, 255, 0.45)",e.style.display="none",e.style.pointerEvents="none"),(this.area===document?document.body:this.area).appendChild(e),e},DragSelect.prototype.start=function(){this.area.addEventListener("mousedown",this._startUp)},DragSelect.prototype._startUp=function(e){if(this.onDragStartBegin(e),this._breaked)return!1;if(!this.isRightClick(e)){if(this.mouseInteraction=!0,this.selector.style.display="block",this.isMultiSelectKeyPressed(e)?this._prevSelected=this.selected.slice():this._prevSelected=[],this._getStartingPositions(e),this.checkIfInsideSelection(!0),this.selector.style.display="none",this.moveStartCallback(e),this._breaked)return!1;this.area.removeEventListener("mousedown",this._startUp),this.area.addEventListener("mousemove",this._handleMove),document.addEventListener("mouseup",this.reset)}},DragSelect.prototype.isMultiSelectKeyPressed=function(e){if(this.multiSelectKeyPressed=!1,this.multiSelectMode)this.multiSelectKeyPressed=!0;else for(var t=0;t<this.multiSelectKeys.length;t++){var s=this.multiSelectKeys[t];e[s]&&(this.multiSelectKeyPressed=!0)}return this.multiSelectKeyPressed},DragSelect.prototype._getStartingPositions=function(e){this.initialCursorPos=this.newCursorPos=this._getCursorPos(e,this.area),this.initialScroll=this.getScroll(this.area);var t={};t.x=this.initialCursorPos.x+this.initialScroll.x,t.y=this.initialCursorPos.y+this.initialScroll.y,t.w=0,t.h=0,this.updatePos(this.selector,t)},DragSelect.prototype._handleMove=function(e){var t=this.getPosition(e);if(this.moveCallback(e),this._breaked)return!1;this.selector.style.display="block",this.updatePos(this.selector,t),this.checkIfInsideSelection(),this._autoScroll(e)},DragSelect.prototype.getPosition=function(e){var t=this._getCursorPos(e,this.area),s=this.getScroll(this.area);this.newCursorPos=t;var i={x:s.x-this.initialScroll.x,y:s.y-this.initialScroll.y},o={};return t.x>this.initialCursorPos.x-i.x?(o.x=this.initialCursorPos.x+this.initialScroll.x,o.w=t.x-this.initialCursorPos.x+i.x):(o.x=t.x+s.x,o.w=this.initialCursorPos.x-t.x-i.x),t.y>this.initialCursorPos.y-i.y?(o.y=this.initialCursorPos.y+this.initialScroll.y,o.h=t.y-this.initialCursorPos.y+i.y):(o.y=t.y+s.y,o.h=this.initialCursorPos.y-t.y-i.y),o},DragSelect.prototype.checkIfInsideSelection=function(e){for(var t=!1,s=0,i=this.selectables.length;s<i;s++){var o=this.selectables[s],r=this.getScroll(this.area),l={y:this.selector.getBoundingClientRect().top+r.y,x:this.selector.getBoundingClientRect().left+r.x,h:this.selector.offsetHeight,w:this.selector.offsetWidth};this._isElementTouching(o,l,r)?(this._handleSelection(o,e),t=!0):this._handleUnselection(o,e)}return t},DragSelect.prototype._handleSelection=function(e,t){if(this.hasClass(e,this.hoverClass)&&!t)return!1;var s=this.selected.indexOf(e);s<0?this.select(e):s>-1&&this.multiSelectKeyPressed&&this.unselect(e),this.addClass(e,this.hoverClass)},DragSelect.prototype._handleUnselection=function(e,t){if(!this.hasClass(e,this.hoverClass)&&!t)return!1;var s=this.selected.indexOf(e),i=this._prevSelected.indexOf(e);s>-1&&i<0?this.unselect(e):s<0&&i>-1&&this.select(e),this.removeClass(e,this.hoverClass)},DragSelect.prototype.select=function(e){return!(this.selected.indexOf(e)>-1)&&(this.selected.push(e),this.addClass(e,this.selectedClass),this.selectCallback(e),!this._breaked&&e)},DragSelect.prototype.unselect=function(e){return!(this.selected.indexOf(e)<0)&&(this.selected.splice(this.selected.indexOf(e),1),this.removeClass(e,this.selectedClass),this.unselectCallback(e),!this._breaked&&e)},DragSelect.prototype.toggle=function(e){return this.selected.indexOf(e)>-1?this.unselect(e):this.select(e),e},DragSelect.prototype._isElementTouching=function(e,t,s){var i={y:e.getBoundingClientRect().top+s.y,x:e.getBoundingClientRect().left+s.x,h:e.offsetHeight||e.getBoundingClientRect().height,w:e.offsetWidth||e.getBoundingClientRect().width};return t.x<i.x+i.w&&t.x+t.w>i.x&&t.y<i.y+i.h&&t.h+t.y>i.y},DragSelect.prototype._autoScroll=function(e){var t=this.isCursorNearEdge(e,this.area),s=this.area===document?document.documentElement||document.body:this.area;"top"===t&&s.scrollTop>0?s.scrollTop-=1*this.autoScrollSpeed:"bottom"===t?s.scrollTop+=1*this.autoScrollSpeed:"left"===t&&s.scrollLeft>0?s.scrollLeft-=1*this.autoScrollSpeed:"right"===t&&(s.scrollLeft+=1*this.autoScrollSpeed)},DragSelect.prototype.isCursorNearEdge=function(e,t){var s=this._getCursorPos(e,t),i=this.getAreaRect(t),o={x:Math.max(i.width/10,30),y:Math.max(i.height/10,30)};return s.y<o.y?"top":i.height-s.y<o.y?"bottom":i.width-s.x<o.x?"right":s.x<o.x&&"left"},DragSelect.prototype.reset=function(e){if(this.previousCursorPos=this._getCursorPos(e,this.area),document.removeEventListener("mouseup",this.reset),this.area.removeEventListener("mousemove",this._handleMove),this.area.addEventListener("mousedown",this._startUp),this.callback(this.selected,e),this._breaked)return!1;this.selector.style.width="0",this.selector.style.height="0",this.selector.style.display="none",setTimeout(function(){this.mouseInteraction=!1}.bind(this),100)},DragSelect.prototype.break=function(){this._breaked=!0,setTimeout(function(){this._breaked=!1}.bind(this),100)},DragSelect.prototype.stop=function(){this.reset(),this.area.removeEventListener("mousedown",this._startUp),document.removeEventListener("mouseup",this.reset)},DragSelect.prototype.getSelection=function(){return this.selected},DragSelect.prototype.getCursorPos=function(e,t,s){if(!e)return!1;var i=t||!1!==t&&this.area,o=this._getCursorPos(e,i),r=s?{x:0,y:0}:this.getScroll(i);return{x:o.x+r.x,y:o.y+r.y}},DragSelect.prototype.addSelection=function(e,t,s){for(var i=this.toArray(e),o=0,r=i.length;o<r;o++){var l=i[o];this.select(l)}return s||this.addSelectables(i),t&&this.callback(this.selected,!1),this.selected},DragSelect.prototype.removeSelection=function(e,t,s){for(var i=this.toArray(e),o=0,r=i.length;o<r;o++){var l=i[o];this.unselect(l)}return s&&this.removeSelectables(i),t&&this.callback(this.selected,!1),this.selected},DragSelect.prototype.toggleSelection=function(e,t,s){for(var i=this.toArray(e),o=0,r=i.length;o<r;o++){var l=i[o];this.selected.indexOf(l)<0?this.addSelection(l,t,s):this.removeSelection(l,t,s)}return this.selected},DragSelect.prototype.setSelection=function(e,t,s){return this.clearSelection(),this.addSelection(e,t,s),this.selected},DragSelect.prototype.clearSelection=function(e){for(var t=this.selected.slice(),s=0,i=t.length;s<i;s++){var o=t[s];this.unselect(o)}return e&&this.callback(this.selected,!1),this.selected},DragSelect.prototype.addSelectables=function(e,t){var s=this.toArray(e);return this._handleSelectables(s,!1,t),e},DragSelect.prototype.getSelectables=function(){return this.selectables},DragSelect.prototype.setSelectables=function(e,t,s){return this.removeSelectables(this.getSelectables(),t),this.addSelectables(e,s)},DragSelect.prototype.removeSelectables=function(e,t){var s=this.toArray(e);return this._handleSelectables(s,!0,t),e},DragSelect.prototype.isRightClick=function(e){if(!e)return!1;var t=!1;return"which"in e?t=3===e.which:"button"in e&&(t=2===e.button),t},DragSelect.prototype.addClass=function(e,t){if(e.classList)return e.classList.add(t);var s=e.getAttribute("class")||"";return-1!==s.indexOf(t)?e:(""!==s&&(t=" "+t),e.setAttribute("class",s+t),e)},DragSelect.prototype.removeClass=function(e,t){if(e.classList)return e.classList.remove(t);var s=e.getAttribute("class")||"",i=new RegExp(t+"\\b","g");return s=s.replace(i,""),e.setAttribute("class",s),e},DragSelect.prototype.hasClass=function(e,t){return e.classList?e.classList.contains(t):(e.getAttribute("class")||"").indexOf(t)>-1},DragSelect.prototype.toArray=function(e){if(!e)return!1;if(!e.length&&this.isElement(e))return[e];for(var t=[],s=e.length-1;s>=0;s--)t[s]=e[s];return t},DragSelect.prototype.isElement=function(e){try{return e instanceof HTMLElement||e instanceof SVGElement}catch(t){return"object"==typeof e&&1===e.nodeType&&"object"==typeof e.style&&"object"==typeof e.ownerDocument}},DragSelect.prototype._getCursorPos=function(e,t){if(!e)return{x:0,y:0};var s={x:e.pageX||e.clientX,y:e.pageY||e.clientY},i=this.getAreaRect(t||document),o=this.getScroll();return{x:s.x-i.left-o.x,y:s.y-i.top-o.y}},DragSelect.prototype.getInitialCursorPosition=function(){return this.initialCursorPos},DragSelect.prototype.getCurrentCursorPosition=function(){return this.newCursorPos},DragSelect.prototype.getPreviousCursorPosition=function(){return this.previousCursorPos},DragSelect.prototype.getCursorPositionDifference=function(e){var t=this.getCurrentCursorPosition(),s=e?this.getPreviousCursorPosition():this.getInitialCursorPosition();return{x:t.x-s.x,y:t.y-s.y}},DragSelect.prototype.getScroll=function(e){var t={top:document.body.scrollTop>0?document.body.scrollTop:document.documentElement.scrollTop,left:document.body.scrollLeft>0?document.body.scrollLeft:document.documentElement.scrollLeft};return{y:e&&e.scrollTop>=0?e.scrollTop:t.top,x:e&&e.scrollLeft>=0?e.scrollLeft:t.left}},DragSelect.prototype.getAreaRect=function(e){if(e===document){var t={y:e.documentElement.clientHeight>0?e.documentElement.clientHeight:window.innerHeight,x:e.documentElement.clientWidth>0?e.documentElement.clientWidth:window.innerWidth};return{top:0,left:0,bottom:0,right:0,width:t.x,height:t.y}}return{top:e.getBoundingClientRect().top,left:e.getBoundingClientRect().left,bottom:e.getBoundingClientRect().bottom,right:e.getBoundingClientRect().right,width:e.offsetWidth,height:e.offsetHeight}},DragSelect.prototype.updatePos=function(e,t){return e.style.left=t.x+"px",e.style.top=t.y+"px",e.style.width=t.w+"px",e.style.height=t.h+"px",e},"undefined"!=typeof module&&null!==module?module.exports=DragSelect:"undefined"!=typeof define&&"function"==typeof define&&define?define(function(){return DragSelect}):window.DragSelect=DragSelect; |
var gulp = require('gulp'); | ||
var jshint = require('gulp-jshint'); | ||
var uglify = require('gulp-uglify'); | ||
var rename = require('gulp-rename'); | ||
var htmlhint = require('gulp-htmlhint'); | ||
var htmlmin = require('gulp-htmlmin'); | ||
@@ -10,6 +8,5 @@ var autoprefixer = require('gulp-autoprefixer'); | ||
gulp.task('js', function () { | ||
gulp.src('./src/DragSelect.js') | ||
.pipe(jshint()) | ||
.pipe(jshint.reporter('default')) | ||
gulp.task('js', function() { | ||
gulp | ||
.src('./src/DragSelect.js') | ||
.pipe(gulp.dest('./dist/')) | ||
@@ -21,4 +18,5 @@ .pipe(uglify()) | ||
gulp.task('css', function () { | ||
gulp.src('./src/example.css') | ||
gulp.task('css', function() { | ||
gulp | ||
.src('./src/example.css') | ||
.pipe(autoprefixer()) | ||
@@ -29,14 +27,13 @@ .pipe(csso()) | ||
gulp.task('html', function () { | ||
gulp.src('./src/index.html') | ||
.pipe(htmlhint()) | ||
.pipe(htmlmin({collapseWhitespace: true})) | ||
gulp.task('html', function() { | ||
gulp | ||
.src('./src/index.html') | ||
.pipe(htmlmin({ collapseWhitespace: true })) | ||
.pipe(gulp.dest('./dist/')); | ||
}); | ||
gulp.task('quicktest', function () { | ||
gulp.src('./src/quicktest.html') | ||
.pipe(gulp.dest('./dist/')); | ||
gulp.task('quicktest', function() { | ||
gulp.src('./src/quicktest.html').pipe(gulp.dest('./dist/')); | ||
}); | ||
gulp.task('default', ['js', 'html', 'css', 'quicktest']); |
{ | ||
"name": "dragselect", | ||
"version": "1.8.1", | ||
"version": "1.9.0", | ||
"description": "easy javascript drag select functionality for your projects", | ||
@@ -21,3 +21,2 @@ "main": "./dist/ds.min.js", | ||
"gulp-csso": "^2.0.0", | ||
"gulp-htmlhint": "^0.3.1", | ||
"gulp-htmlmin": "^3.0.0", | ||
@@ -24,0 +23,0 @@ "gulp-jshint": "^2.0.4", |
@@ -86,3 +86,3 @@ ``` | ||
multiSelectMode: false, // If set to true, the multiselection behavior will be turned on by default without the need of modifier keys. Default: false | ||
autoScrollSpeed: 3, // Speed in which the area scrolls while selecting (if available). Unit is pixel per movement. Set to 0.0001 to disable autoscrolling. Default = 1 | ||
autoScrollSpeed: 3, // Speed in which the area scrolls while selecting (if available). Unit is pixel per movement. Set to 0 to disable autoscrolling. Default = 1 | ||
onDragStart: function(element) {}, // fired when the user clicks in the area. This callback gets the event object. Executed after DragSelect function code ran, befor the setup of event listeners. | ||
@@ -132,2 +132,3 @@ onDragMove: function(element) {}, // fired when the user drags. This callback gets the event object. Executed before DragSelect function code ran, after getting the current mouse position. | ||
|selectableClass |string |OPTIONAL. The class name assigned to the elements that can be selected. Default = [see classes](#classes) | | ||
|onDragStartBegin |function |OPTIONAL. Fired when the user clicks in the area. This callback gets the event object. Executed **before** DragSelect function code runs | | ||
|onDragStart |function |OPTIONAL. Fired when the user clicks in the area. This callback gets the event object. Executed after DragSelect function code ran, befor the setup of event listeners | | ||
@@ -134,0 +135,0 @@ |onDragMove |function |OPTIONAL. Fired when the user drags. This callback gets the event object. Executed before DragSelect function code ran, after getting the current mouse position | |
@@ -1,2 +0,2 @@ | ||
// v 1.8.1 | ||
// v 1.9.0 | ||
/* | ||
@@ -38,3 +38,4 @@ ____ _____ __ __ | ||
** @selectableClass string the class assigned to the elements that can be selected | ||
** @onDragStart function It is fired when the user clicks in the area. This callback gets the event object. Executed after DragSelect function code ran, befor the setup of event listeners. | ||
** @onDragStartBegin function Is fired when the user clicks in the area. This callback gets the event object. Executed *before* DragSelect function code ran. | ||
** @onDragStart function It is fired when the user clicks in the area. This callback gets the event object. Executed after DragSelect function code ran, before the setup of event listeners. | ||
** @onDragMove function It is fired when the user drags. This callback gets the event object. Executed before DragSelect function code ran, after getting the current mouse position. | ||
@@ -100,20 +101,18 @@ ** @onElementSelect function It is fired every time an element is selected. This callback gets a property which is the just selected node | ||
* DragSelect Class. | ||
* | ||
* | ||
* @constructor | ||
* @param {Object} options - The options object. | ||
*/ | ||
function DragSelect( options ) { | ||
function DragSelect(options) { | ||
this.multiSelectKeyPressed; | ||
this.initialCursorPos = {x: 0, y: 0}; | ||
this.newCursorPos = {x: 0, y: 0}; | ||
this.previousCursorPos = {x: 0, y: 0}; | ||
this.initialCursorPos = { x: 0, y: 0 }; | ||
this.newCursorPos = { x: 0, y: 0 }; | ||
this.previousCursorPos = { x: 0, y: 0 }; | ||
this.initialScroll; | ||
this.selected = []; | ||
this._prevSelected = []; // memory to fix #9 | ||
this._prevSelected = []; // memory to fix #9 | ||
this._createBindings(); | ||
this._setupOptions( options ); | ||
this._setupOptions(options); | ||
this.start(); | ||
} | ||
@@ -125,3 +124,2 @@ | ||
DragSelect.prototype._createBindings = function() { | ||
this._startUp = this._startUp.bind(this); | ||
@@ -131,3 +129,2 @@ this._handleMove = this._handleMove.bind(this); | ||
this._onClick = this._onClick.bind(this); | ||
}; | ||
@@ -138,4 +135,3 @@ | ||
*/ | ||
DragSelect.prototype._setupOptions = function( options ) { | ||
DragSelect.prototype._setupOptions = function(options) { | ||
this.selectedClass = options.selectedClass || 'ds-selected'; | ||
@@ -147,9 +143,14 @@ this.hoverClass = options.hoverClass || 'ds-hover'; | ||
this.selectables = []; | ||
this._handleSelectables( this.toArray( options.selectables ) ); | ||
this._handleSelectables(this.toArray(options.selectables)); | ||
this.multiSelectKeys = options.multiSelectKeys || ['ctrlKey', 'shiftKey', 'metaKey']; | ||
this.multiSelectKeys = options.multiSelectKeys || [ | ||
'ctrlKey', | ||
'shiftKey', | ||
'metaKey' | ||
]; | ||
this.multiSelectMode = options.multiSelectMode || false; | ||
this.autoScrollSpeed = options.autoScrollSpeed || 1; | ||
this.autoScrollSpeed = options.autoScrollSpeed === 0 ? 0 : options.autoScrollSpeed || 1; | ||
this.selectCallback = options.onElementSelect || function() {}; | ||
this.unselectCallback = options.onElementUnselect || function() {}; | ||
this.onDragStartBegin = options.onDragStartBegin || function() {}; | ||
this.moveStartCallback = options.onDragStart || function() {}; | ||
@@ -162,6 +163,11 @@ this.moveCallback = options.onDragMove || function() {}; | ||
// Area has to have a special position attribute for calculations | ||
if( this.area !== document ) { | ||
var computedArea = getComputedStyle( this.area ); | ||
var isPositioned = computedArea.position === 'absolute' || computedArea.position === 'relative' || computedArea.position === 'fixed'; | ||
if( !isPositioned ) { this.area.style.position = 'relative'; } | ||
if (this.area !== document) { | ||
var computedArea = getComputedStyle(this.area); | ||
var isPositioned = | ||
computedArea.position === 'absolute' || | ||
computedArea.position === 'relative' || | ||
computedArea.position === 'fixed'; | ||
if (!isPositioned) { | ||
this.area.style.position = 'relative'; | ||
} | ||
} | ||
@@ -171,4 +177,3 @@ | ||
this.selector = options.selector || this._createSelector(); | ||
this.addClass( this.selector, this.selectorClass ); | ||
this.addClass(this.selector, this.selectorClass); | ||
}; | ||
@@ -178,3 +183,3 @@ | ||
* Add/Remove Selectables also handles css classes and event listeners. | ||
* | ||
* | ||
* @param {Object} selectables - selectable elements. | ||
@@ -184,38 +189,38 @@ * @param {Boolean} remove - if elements should be removed. | ||
*/ | ||
DragSelect.prototype._handleSelectables = function( selectables, remove, fromSelection ) { | ||
for ( var index = 0; index < selectables.length; index++ ) { | ||
DragSelect.prototype._handleSelectables = function( | ||
selectables, | ||
remove, | ||
fromSelection | ||
) { | ||
for (var index = 0; index < selectables.length; index++) { | ||
var selectable = selectables[index]; | ||
var indexOf = this.selectables.indexOf( selectable ); | ||
var indexOf = this.selectables.indexOf(selectable); | ||
if( indexOf < 0 && !remove ) { // add | ||
this.addClass( selectable, this.selectableClass ); | ||
selectable.addEventListener( 'click', this._onClick ); | ||
this.selectables.push( selectable ); | ||
if (indexOf < 0 && !remove) { | ||
// add | ||
this.addClass(selectable, this.selectableClass); | ||
selectable.addEventListener('click', this._onClick); | ||
this.selectables.push(selectable); | ||
// also add to current selection | ||
if( fromSelection && this.selected.indexOf( selectable ) < 0 ) { | ||
this.addClass( selectable, this.selectedClass ); | ||
this.selected.push( selectable ); | ||
if (fromSelection && this.selected.indexOf(selectable) < 0) { | ||
this.addClass(selectable, this.selectedClass); | ||
this.selected.push(selectable); | ||
} | ||
} else if (indexOf > -1 && remove) { | ||
// remove | ||
} | ||
this.removeClass(selectable, this.hoverClass); | ||
this.removeClass(selectable, this.selectableClass); | ||
selectable.removeEventListener('click', this._onClick); | ||
this.selectables.splice(indexOf, 1); | ||
else if( indexOf > -1 && remove ) { // remove | ||
this.removeClass( selectable, this.hoverClass ); | ||
this.removeClass( selectable, this.selectableClass ); | ||
selectable.removeEventListener( 'click', this._onClick ); | ||
this.selectables.splice( indexOf, 1 ); | ||
// also remove from current selection | ||
if( fromSelection && this.selected.indexOf( selectable ) > -1 ) { | ||
this.removeClass( selectable, this.selectedClass ); | ||
this.selected.splice( this.selected.indexOf( selectable ), 1 ); | ||
if (fromSelection && this.selected.indexOf(selectable) > -1) { | ||
this.removeClass(selectable, this.selectedClass); | ||
this.selected.splice(this.selected.indexOf(selectable), 1); | ||
} | ||
} | ||
} | ||
}; | ||
@@ -225,26 +230,35 @@ | ||
* Triggers when a node is actively selected. | ||
* | ||
* | ||
* This might be an "onClick" method but it also triggers when | ||
* <button> nodes are pressed via the keyboard. | ||
* Making DragSelect accessible for everyone! | ||
* | ||
* | ||
* @param {Object} selectables - selectable elements. | ||
* @param {Boolean} remove - if elements were removed. | ||
*/ | ||
DragSelect.prototype._onClick = function( event ) { | ||
DragSelect.prototype._onClick = function(event) { | ||
if( this.mouseInteraction ) { return; } // fix firefox doubleclick issue | ||
if( this.isRightClick( event ) ) { return; } | ||
if (this.mouseInteraction) { | ||
return; | ||
} // fix firefox doubleclick issue | ||
if (this.isRightClick(event)) { | ||
return; | ||
} | ||
var node = event.target; | ||
if(this.isMultiSelectKeyPressed( event )) { this._prevSelected = this.selected.slice(); } // #9 | ||
else { this._prevSelected = []; } // #9 | ||
if (this.isMultiSelectKeyPressed(event)) { | ||
this._prevSelected = this.selected.slice(); | ||
} // #9 | ||
else { | ||
this._prevSelected = []; | ||
} // #9 | ||
this.checkIfInsideSelection( true ); // reset selection if no multiselectionkeypressed | ||
this.checkIfInsideSelection(true); // reset selection if no multiselectionkeypressed | ||
if( this.selectables.indexOf( node ) > -1 ) { this.toggle( node ); } | ||
if (this.selectables.indexOf(node) > -1) { | ||
this.toggle(node); | ||
} | ||
this.reset(); | ||
}; | ||
@@ -254,22 +268,20 @@ | ||
* Create the selector node when not provided by options object. | ||
* | ||
* | ||
* @return {Node} | ||
*/ | ||
DragSelect.prototype._createSelector = function() { | ||
var selector = document.createElement('div'); | ||
var selector = document.createElement( 'div' ); | ||
selector.style.position = 'absolute'; | ||
if( !this.customStyles ) { | ||
if (!this.customStyles) { | ||
selector.style.background = 'rgba(0, 0, 255, 0.1)'; | ||
selector.style.border = '1px solid rgba(0, 0, 255, 0.45)'; | ||
selector.style.display = 'none'; | ||
selector.style.pointerEvents = 'none'; // fix for issue #8 (ie11+) | ||
selector.style.pointerEvents = 'none'; // fix for issue #8 (ie11+) | ||
} | ||
var _area = this.area === document ? document.body : this.area; | ||
_area.appendChild( selector ); | ||
_area.appendChild(selector); | ||
return selector; | ||
}; | ||
@@ -284,5 +296,3 @@ | ||
DragSelect.prototype.start = function() { | ||
this.area.addEventListener( 'mousedown', this._startUp ); | ||
this.area.addEventListener('mousedown', this._startUp); | ||
}; | ||
@@ -292,30 +302,39 @@ | ||
* Startup when the area is clicked. | ||
* | ||
* | ||
* @param {Object} event - The event object. | ||
*/ | ||
DragSelect.prototype._startUp = function( event ) { | ||
DragSelect.prototype._startUp = function(event) { | ||
if( this.isRightClick( event ) ) { return; } | ||
// callback | ||
this.onDragStartBegin(event); | ||
if (this._breaked) { return false; } | ||
if (this.isRightClick(event)) { | ||
return; | ||
} | ||
this.mouseInteraction = true; | ||
this.selector.style.display = 'block'; | ||
if(this.isMultiSelectKeyPressed( event )) { this._prevSelected = this.selected.slice(); } // #9 | ||
else { this._prevSelected = []; } // #9 | ||
if (this.isMultiSelectKeyPressed(event)) { | ||
this._prevSelected = this.selected.slice(); | ||
} // #9 | ||
else { | ||
this._prevSelected = []; | ||
} // #9 | ||
// move element on location | ||
this._getStartingPositions( event ); | ||
this.checkIfInsideSelection( true ); | ||
this._getStartingPositions(event); | ||
this.checkIfInsideSelection(true); | ||
this.selector.style.display = 'none'; // hidden unless moved, fix for issue #8 | ||
this.selector.style.display = 'none'; // hidden unless moved, fix for issue #8 | ||
// callback | ||
this.moveStartCallback( event ); | ||
if( this._breaked ) { return false; } | ||
this.moveStartCallback(event); | ||
if (this._breaked) { return false; } | ||
// event listeners | ||
this.area.removeEventListener( 'mousedown', this._startUp ); | ||
this.area.addEventListener( 'mousemove', this._handleMove ); | ||
document.addEventListener( 'mouseup', this.reset ); | ||
this.area.removeEventListener('mousedown', this._startUp); | ||
this.area.addEventListener('mousemove', this._handleMove); | ||
document.addEventListener('mouseup', this.reset); | ||
}; | ||
@@ -325,16 +344,17 @@ | ||
* Check if some multiselection modifier key is pressed | ||
* | ||
* | ||
* @param {Object} event - The event object. | ||
* @return {Boolean} this.isMultiSelectKeyPressed | ||
*/ | ||
DragSelect.prototype.isMultiSelectKeyPressed = function( event ) { | ||
DragSelect.prototype.isMultiSelectKeyPressed = function(event) { | ||
this.multiSelectKeyPressed = false; | ||
if (this.multiSelectMode){ | ||
if (this.multiSelectMode) { | ||
this.multiSelectKeyPressed = true; | ||
} else { | ||
for ( var index = 0; index < this.multiSelectKeys.length; index++ ) { | ||
for (var index = 0; index < this.multiSelectKeys.length; index++) { | ||
var mKey = this.multiSelectKeys[index]; | ||
if( event[mKey] ) { this.multiSelectKeyPressed = true; } | ||
if (event[mKey]) { | ||
this.multiSelectKeyPressed = true; | ||
} | ||
} | ||
@@ -344,3 +364,2 @@ } | ||
return this.multiSelectKeyPressed; | ||
}; | ||
@@ -350,10 +369,12 @@ | ||
* Grabs the starting position of all needed elements | ||
* | ||
* | ||
* @param {Object} event - The event object. | ||
*/ | ||
DragSelect.prototype._getStartingPositions = function( event ) { | ||
DragSelect.prototype._getStartingPositions = function(event) { | ||
this.initialCursorPos = this.newCursorPos = this._getCursorPos( | ||
event, | ||
this.area | ||
); | ||
this.initialScroll = this.getScroll(this.area); | ||
this.initialCursorPos = this.newCursorPos = this._getCursorPos( event, this.area ); | ||
this.initialScroll = this.getScroll( this.area ); | ||
var selectorPos = {}; | ||
@@ -364,7 +385,5 @@ selectorPos.x = this.initialCursorPos.x + this.initialScroll.x; | ||
selectorPos.h = 0; | ||
this.updatePos( this.selector, selectorPos ); | ||
this.updatePos(this.selector, selectorPos); | ||
}; | ||
// Movements/Sizing of selection | ||
@@ -375,22 +394,22 @@ ////////////////////////////////////////////////////////////////////////////////////// | ||
* Handles what happens while the mouse is moved | ||
* | ||
* | ||
* @param {Object} event - The event object. | ||
*/ | ||
DragSelect.prototype._handleMove = function( event ) { | ||
DragSelect.prototype._handleMove = function(event) { | ||
var selectorPos = this.getPosition(event); | ||
var selectorPos = this.getPosition( event ); | ||
// callback | ||
this.moveCallback( event ); | ||
if( this._breaked ) { return false; } | ||
this.moveCallback(event); | ||
if (this._breaked) { | ||
return false; | ||
} | ||
this.selector.style.display = 'block'; // hidden unless moved, fix for issue #8 | ||
this.selector.style.display = 'block'; // hidden unless moved, fix for issue #8 | ||
// move element on location | ||
this.updatePos( this.selector, selectorPos ); | ||
this.updatePos(this.selector, selectorPos); | ||
this.checkIfInsideSelection(); | ||
// scroll area if area is scrollable | ||
this._autoScroll( event ); | ||
this._autoScroll(event); | ||
}; | ||
@@ -400,10 +419,9 @@ | ||
* Calculates and returns the exact x,y w,h positions of the selector element | ||
* | ||
* | ||
* @param {Object} event - The event object. | ||
*/ | ||
DragSelect.prototype.getPosition = function( event ) { | ||
DragSelect.prototype.getPosition = function(event) { | ||
var cursorPosNew = this._getCursorPos(event, this.area); | ||
var scrollNew = this.getScroll(this.area); | ||
var cursorPosNew = this._getCursorPos( event, this.area ); | ||
var scrollNew = this.getScroll( this.area ); | ||
// save for later retrieval | ||
@@ -429,3 +447,3 @@ this.newCursorPos = cursorPosNew; | ||
* by +10px width to fake a negative sizing. | ||
* | ||
* | ||
* One solution to this problem is using css-transforms scale() with | ||
@@ -435,3 +453,3 @@ * transform-origin of top left. BUT we can’t use this since it will size | ||
* get inanely huge. Also transforms are not widely supported in IE. | ||
* | ||
* | ||
* Example #1: | ||
@@ -449,3 +467,3 @@ * Unfortunately, things get even more complicated when we are inside a scrollable | ||
* 3. selectorPos.w = cursorPosNew.x (5) - initialCursorPos.x (0) + scrollAmount.x (10) === 15; | ||
* | ||
* | ||
* let’s say after that movement we now scroll 20px to the left and move our cursor by 30px to the left: | ||
@@ -455,7 +473,7 @@ * 1b. cursorPosNew.x (-30) > initialCursorPos.x (0) - scrollAmount.x (-20) === -30 > -20 === false; | ||
* === -50; // move left position to cursor (for more info see Problem #1) | ||
* 3b. selectorPos.w = initialCursorPos.x (0) - cursorPosNew.x (-30) - scrollAmount.x (-20) | ||
* 3b. selectorPos.w = initialCursorPos.x (0) - cursorPosNew.x (-30) - scrollAmount.x (-20) | ||
* === 0--30--20 === 0+30+20 === 50; // scale width to original left position (for more info see Problem #1) | ||
* | ||
* | ||
* same thing has to be done for top/bottom | ||
* | ||
* | ||
* I hope that makes sence, try stuff out and play around with variables to get a hang of it. | ||
@@ -466,16 +484,18 @@ */ | ||
// right | ||
if( cursorPosNew.x > this.initialCursorPos.x - scrollAmount.x ) { // 1. | ||
selectorPos.x = this.initialCursorPos.x + this.initialScroll.x; // 2. | ||
selectorPos.w = cursorPosNew.x - this.initialCursorPos.x + scrollAmount.x; // 3. | ||
// left | ||
} else { // 1b. | ||
selectorPos.x = cursorPosNew.x + scrollNew.x; // 2b. | ||
selectorPos.w = this.initialCursorPos.x - cursorPosNew.x - scrollAmount.x; // 3b. | ||
if (cursorPosNew.x > this.initialCursorPos.x - scrollAmount.x) { | ||
// 1. | ||
selectorPos.x = this.initialCursorPos.x + this.initialScroll.x; // 2. | ||
selectorPos.w = cursorPosNew.x - this.initialCursorPos.x + scrollAmount.x; // 3. | ||
// left | ||
} else { | ||
// 1b. | ||
selectorPos.x = cursorPosNew.x + scrollNew.x; // 2b. | ||
selectorPos.w = this.initialCursorPos.x - cursorPosNew.x - scrollAmount.x; // 3b. | ||
} | ||
// bottom | ||
if( cursorPosNew.y > this.initialCursorPos.y - scrollAmount.y ) { | ||
if (cursorPosNew.y > this.initialCursorPos.y - scrollAmount.y) { | ||
selectorPos.y = this.initialCursorPos.y + this.initialScroll.y; | ||
selectorPos.h = cursorPosNew.y - this.initialCursorPos.y + scrollAmount.y; | ||
// top | ||
// top | ||
} else { | ||
@@ -487,6 +507,4 @@ selectorPos.y = cursorPosNew.y + scrollNew.y; | ||
return selectorPos; | ||
}; | ||
// Colision detection | ||
@@ -497,20 +515,24 @@ ////////////////////////////////////////////////////////////////////////////////////// | ||
* Checks if element is inside selection and takes action based on that | ||
* | ||
* | ||
* force handles first clicks and accessibility. Here is user is clicking directly onto | ||
* some element at start, (contrary to later hovers) we can assume that he | ||
* really wants to select/deselect that item. | ||
* | ||
* | ||
* @param {Boolean} force – forces through. | ||
* | ||
* @return {Boolean} | ||
*/ | ||
DragSelect.prototype.checkIfInsideSelection = function( force ) { | ||
DragSelect.prototype.checkIfInsideSelection = function(force) { | ||
var anyInside = false; | ||
for( var i = 0, il = this.selectables.length; i < il; i++ ) { | ||
var selectable = this.selectables[i]; | ||
if( this.isElementTouching( selectable, this.selector, this.area ) ) { | ||
var scroll = this.getScroll(this.area); | ||
var selectionRect = { | ||
y: this.selector.getBoundingClientRect().top + scroll.y, | ||
x: this.selector.getBoundingClientRect().left + scroll.x, | ||
h: this.selector.offsetHeight, | ||
w: this.selector.offsetWidth | ||
}; | ||
if( this._isElementTouching( selectable, selectionRect, scroll ) ) { | ||
this._handleSelection( selectable, force ); | ||
@@ -521,7 +543,4 @@ anyInside = true; | ||
} | ||
} | ||
return anyInside; | ||
}; | ||
@@ -531,19 +550,19 @@ | ||
* Logic when an item is selected | ||
* | ||
* | ||
* @param {Node} item – selected item. | ||
* @param {Boolean} force – forces through. | ||
*/ | ||
DragSelect.prototype._handleSelection = function( item, force ) { | ||
DragSelect.prototype._handleSelection = function(item, force) { | ||
if (this.hasClass(item, this.hoverClass) && !force) { | ||
return false; | ||
} | ||
var posInSelectedArray = this.selected.indexOf(item); | ||
if( this.hasClass( item, this.hoverClass ) && !force ) { return false; } | ||
var posInSelectedArray = this.selected.indexOf( item ); | ||
if( posInSelectedArray < 0 ) { | ||
this.select( item ); | ||
} else if( posInSelectedArray > -1 && this.multiSelectKeyPressed ) { | ||
this.unselect( item ); | ||
if (posInSelectedArray < 0) { | ||
this.select(item); | ||
} else if (posInSelectedArray > -1 && this.multiSelectKeyPressed) { | ||
this.unselect(item); | ||
} | ||
this.addClass( item, this.hoverClass ); | ||
this.addClass(item, this.hoverClass); | ||
}; | ||
@@ -553,12 +572,13 @@ | ||
* Logic when an item is de-selected | ||
* | ||
* | ||
* @param {Node} item – selected item. | ||
* @param {Boolean} force – forces through. | ||
*/ | ||
DragSelect.prototype._handleUnselection = function( item, force ) { | ||
DragSelect.prototype._handleUnselection = function(item, force) { | ||
if (!this.hasClass(item, this.hoverClass) && !force) { | ||
return false; | ||
} | ||
var posInSelectedArray = this.selected.indexOf(item); | ||
var isInPrevSelection = this._prevSelected.indexOf(item); // #9 | ||
if( !this.hasClass( item, this.hoverClass ) && !force ) { return false; } | ||
var posInSelectedArray = this.selected.indexOf( item ); | ||
var isInPrevSelection = this._prevSelected.indexOf( item ); // #9 | ||
/** | ||
@@ -571,10 +591,9 @@ * Special algorithm for issue #9. | ||
*/ | ||
if( posInSelectedArray > -1 && isInPrevSelection < 0 ) { | ||
this.unselect( item ); | ||
} else if ( posInSelectedArray < 0 && isInPrevSelection > -1 ) { | ||
this.select( item ); | ||
if (posInSelectedArray > -1 && isInPrevSelection < 0) { | ||
this.unselect(item); | ||
} else if (posInSelectedArray < 0 && isInPrevSelection > -1) { | ||
this.select(item); | ||
} | ||
this.removeClass( item, this.hoverClass ); | ||
this.removeClass(item, this.hoverClass); | ||
}; | ||
@@ -584,18 +603,20 @@ | ||
* Adds an item to the selection. | ||
* | ||
* | ||
* @param {Node} item – item to select. | ||
* @return {Node} item | ||
*/ | ||
DragSelect.prototype.select = function( item ) { | ||
DragSelect.prototype.select = function(item) { | ||
if (this.selected.indexOf(item) > -1) { | ||
return false; | ||
} | ||
if( this.selected.indexOf(item) > -1) { return false; } | ||
this.selected.push(item); | ||
this.addClass(item, this.selectedClass); | ||
this.selected.push( item ); | ||
this.addClass( item, this.selectedClass ); | ||
this.selectCallback(item); | ||
if (this._breaked) { | ||
return false; | ||
} | ||
this.selectCallback( item ); | ||
if( this._breaked ) { return false; } | ||
return item; | ||
}; | ||
@@ -605,18 +626,20 @@ | ||
* Removes an item from the selection. | ||
* | ||
* | ||
* @param {Node} item – item to select. | ||
* @return {Node} item | ||
*/ | ||
DragSelect.prototype.unselect = function( item ) { | ||
DragSelect.prototype.unselect = function(item) { | ||
if (this.selected.indexOf(item) < 0) { | ||
return false; | ||
} | ||
if( this.selected.indexOf(item) < 0) { return false; } | ||
this.selected.splice(this.selected.indexOf(item), 1); | ||
this.removeClass(item, this.selectedClass); | ||
this.selected.splice( this.selected.indexOf(item), 1 ); | ||
this.removeClass( item, this.selectedClass ); | ||
this.unselectCallback( item ); | ||
if( this._breaked ) { return false; } | ||
this.unselectCallback(item); | ||
if (this._breaked) { | ||
return false; | ||
} | ||
return item; | ||
}; | ||
@@ -627,41 +650,35 @@ | ||
* If it is already selected = remove, if not = add. | ||
* | ||
* | ||
* @param {Node} item – item to select. | ||
* @return {Node} item | ||
*/ | ||
DragSelect.prototype.toggle = function( item ) { | ||
if( this.selected.indexOf( item ) > -1) { | ||
this.unselect( item ); | ||
} else { | ||
this.select( item ); | ||
} | ||
return item; | ||
}; | ||
DragSelect.prototype.toggle = function(item) { | ||
if (this.selected.indexOf(item) > -1) { | ||
this.unselect(item); | ||
} else { | ||
this.select(item); | ||
} | ||
return item; | ||
}; | ||
/** | ||
* Checks if element is touched by the selector (and vice-versa) | ||
* | ||
* | ||
* @param {Node} element – item. | ||
* @param {Node} container – selector. | ||
* @param {Node} area – surrounding area. | ||
* @param {Object} selectionRect – Container bounds: | ||
Example: { | ||
y: this.selector.getBoundingClientRect().top + scroll.y, | ||
x: this.selector.getBoundingClientRect().left + scroll.x, | ||
h: this.selector.offsetHeight, | ||
w: this.selector.offsetWidth | ||
}; | ||
* @param {Object} scroll – Scroll x, y values. | ||
* @return {Boolean} | ||
*/ | ||
DragSelect.prototype.isElementTouching = function( element, container, area ) { | ||
/** | ||
* calculating everything here on every move consumes more performance | ||
* but makes sure to get the right positions even if the containers are | ||
* resized or moved on the fly. This also makes the function kinda context independant. | ||
*/ | ||
var scroll = this.getScroll( area ); | ||
var containerRect = { | ||
y: container.getBoundingClientRect().top + scroll.y, | ||
x: container.getBoundingClientRect().left + scroll.x, | ||
h: container.offsetHeight || element.getBoundingClientRect().height, | ||
w: container.offsetWidth || element.getBoundingClientRect().width | ||
}; | ||
DragSelect.prototype._isElementTouching = function( | ||
element, | ||
selectionRect, | ||
scroll | ||
) { | ||
var elementRect = { | ||
@@ -688,16 +705,13 @@ y: element.getBoundingClientRect().top + scroll.y, | ||
if ( | ||
containerRect.x < elementRect.x + elementRect.w && | ||
containerRect.x + containerRect.w > elementRect.x && | ||
containerRect.y < elementRect.y + elementRect.h && | ||
containerRect.h + containerRect.y > elementRect.y | ||
selectionRect.x < elementRect.x + elementRect.w && | ||
selectionRect.x + selectionRect.w > elementRect.x && | ||
selectionRect.y < elementRect.y + elementRect.h && | ||
selectionRect.h + selectionRect.y > elementRect.y | ||
) { | ||
return true; // collision detected! | ||
} | ||
else { | ||
} else { | ||
return false; | ||
} | ||
}; | ||
// Autoscroll | ||
@@ -708,16 +722,19 @@ ////////////////////////////////////////////////////////////////////////////////////// | ||
* Automatically Scroll the area by selecting | ||
* | ||
* | ||
* @param {Object} event – event object. | ||
*/ | ||
DragSelect.prototype._autoScroll = function( event ) { | ||
DragSelect.prototype._autoScroll = function(event) { | ||
var edge = this.isCursorNearEdge(event, this.area); | ||
var edge = this.isCursorNearEdge( event, this.area ); | ||
var _area = this.area === document ? document.documentElement || document.body : this.area; | ||
var _area = this.area === document ? this.area.body : this.area; | ||
if( edge === 'top' && _area.scrollTop > 0 ) { _area.scrollTop -= 1 * this.autoScrollSpeed; } | ||
else if( edge === 'bottom' ) { _area.scrollTop += 1 * this.autoScrollSpeed; } | ||
else if( edge === 'left' && _area.scrollLeft > 0 ) { _area.scrollLeft -= 1 * this.autoScrollSpeed; } | ||
else if( edge === 'right' ) { _area.scrollLeft += 1 * this.autoScrollSpeed; } | ||
if (edge === 'top' && _area.scrollTop > 0) { | ||
_area.scrollTop -= 1 * this.autoScrollSpeed; | ||
} else if (edge === 'bottom') { | ||
_area.scrollTop += 1 * this.autoScrollSpeed; | ||
} else if (edge === 'left' && _area.scrollLeft > 0) { | ||
_area.scrollLeft -= 1 * this.autoScrollSpeed; | ||
} else if (edge === 'right') { | ||
_area.scrollLeft += 1 * this.autoScrollSpeed; | ||
} | ||
}; | ||
@@ -727,3 +744,3 @@ | ||
* Check if the selector is near an edge of the area | ||
* | ||
* | ||
* @param {Object} event – event object. | ||
@@ -733,7 +750,6 @@ * @param {Node} area – the area. | ||
*/ | ||
DragSelect.prototype.isCursorNearEdge = function( event, area ) { | ||
DragSelect.prototype.isCursorNearEdge = function(event, area) { | ||
var cursorPosition = this._getCursorPos(event, area); | ||
var areaRect = this.getAreaRect(area); | ||
var cursorPosition = this._getCursorPos( event, area ); | ||
var areaRect = this.getAreaRect( area ); | ||
var tolerance = { | ||
@@ -744,12 +760,15 @@ x: Math.max(areaRect.width / 10, 30), | ||
if( cursorPosition.y < tolerance.y ) { return 'top'; } | ||
else if( areaRect.height - cursorPosition.y < tolerance.y ) { return 'bottom'; } | ||
else if( areaRect.width - cursorPosition.x < tolerance.x ) { return 'right'; } | ||
else if( cursorPosition.x < tolerance.x ) { return 'left'; } | ||
if (cursorPosition.y < tolerance.y) { | ||
return 'top'; | ||
} else if (areaRect.height - cursorPosition.y < tolerance.y) { | ||
return 'bottom'; | ||
} else if (areaRect.width - cursorPosition.x < tolerance.x) { | ||
return 'right'; | ||
} else if (cursorPosition.x < tolerance.x) { | ||
return 'left'; | ||
} | ||
return false; | ||
}; | ||
// Ending | ||
@@ -761,12 +780,13 @@ ////////////////////////////////////////////////////////////////////////////////////// | ||
*/ | ||
DragSelect.prototype.reset = function( event ) { | ||
DragSelect.prototype.reset = function(event) { | ||
this.previousCursorPos = this._getCursorPos(event, this.area); | ||
document.removeEventListener('mouseup', this.reset); | ||
this.area.removeEventListener('mousemove', this._handleMove); | ||
this.area.addEventListener('mousedown', this._startUp); | ||
this.previousCursorPos = this._getCursorPos( event, this.area ); | ||
document.removeEventListener( 'mouseup', this.reset ); | ||
this.area.removeEventListener( 'mousemove', this._handleMove ); | ||
this.area.addEventListener( 'mousedown', this._startUp ); | ||
this.callback(this.selected, event); | ||
if (this._breaked) { | ||
return false; | ||
} | ||
this.callback( this.selected, event ); | ||
if( this._breaked ) { return false; } | ||
this.selector.style.width = '0'; | ||
@@ -776,6 +796,9 @@ this.selector.style.height = '0'; | ||
setTimeout(function() { // debounce in order "onClick" to work | ||
this.mouseInteraction = false; | ||
}.bind(this), 100); | ||
setTimeout( | ||
function() { | ||
// debounce in order "onClick" to work | ||
this.mouseInteraction = false; | ||
}.bind(this), | ||
100 | ||
); | ||
}; | ||
@@ -789,8 +812,10 @@ | ||
DragSelect.prototype.break = function() { | ||
this._breaked = true; | ||
setTimeout(function() { // debounce the break should only break once instantly after call | ||
this._breaked = false; | ||
}.bind(this), 100); | ||
setTimeout( | ||
function() { | ||
// debounce the break should only break once instantly after call | ||
this._breaked = false; | ||
}.bind(this), | ||
100 | ||
); | ||
}; | ||
@@ -802,10 +827,7 @@ | ||
DragSelect.prototype.stop = function() { | ||
this.reset(); | ||
this.area.removeEventListener( 'mousedown', this._startUp ); | ||
document.removeEventListener( 'mouseup', this.reset ); | ||
this.area.removeEventListener('mousedown', this._startUp); | ||
document.removeEventListener('mouseup', this.reset); | ||
}; | ||
// Usefull methods for user | ||
@@ -816,9 +838,7 @@ ////////////////////////////////////////////////////////////////////////////////////// | ||
* Returns the current selected nodes | ||
* | ||
* | ||
* @return {Nodes} | ||
*/ | ||
DragSelect.prototype.getSelection = function() { | ||
return this.selected; | ||
}; | ||
@@ -829,3 +849,3 @@ | ||
* Will be relative to an area including the scroll unless advised otherwise | ||
* | ||
* | ||
* @param {Object} event | ||
@@ -836,10 +856,11 @@ * @param {Node} _area – containing area / this.area if none / document if === false | ||
*/ | ||
DragSelect.prototype.getCursorPos = function( event, _area, ignoreScroll ) { | ||
DragSelect.prototype.getCursorPos = function(event, _area, ignoreScroll) { | ||
if (!event) { | ||
return false; | ||
} | ||
if(!event) { return false; } | ||
var area = _area || (_area !== false && this.area); | ||
var pos = this._getCursorPos(event, area); | ||
var scroll = ignoreScroll ? { x: 0, y: 0 } : this.getScroll(area); | ||
var area = _area || _area !== false && this.area; | ||
var pos = this._getCursorPos( event, area ); | ||
var scroll = ignoreScroll ? { x: 0, y: 0 } : this.getScroll( area ); | ||
return { | ||
@@ -857,3 +878,3 @@ x: pos.x + scroll.x, | ||
* Can add multiple nodes at once, in contrary to .select | ||
* | ||
* | ||
* @param {Nodes} _nodes one or multiple nodes | ||
@@ -864,16 +885,22 @@ * @param {Boolean} _callback - if callback should be called | ||
*/ | ||
DragSelect.prototype.addSelection = function( _nodes, _callback, dontAddToSelectables ) { | ||
var nodes = this.toArray( _nodes ); | ||
DragSelect.prototype.addSelection = function( | ||
_nodes, | ||
_callback, | ||
dontAddToSelectables | ||
) { | ||
var nodes = this.toArray(_nodes); | ||
for (var index = 0, il = nodes.length; index < il; index++) { | ||
var node = nodes[index]; | ||
this.select( node ); | ||
this.select(node); | ||
} | ||
if( !dontAddToSelectables ) { this.addSelectables( nodes ); } | ||
if( _callback ) { this.callback( this.selected, false ); } | ||
if (!dontAddToSelectables) { | ||
this.addSelectables(nodes); | ||
} | ||
if (_callback) { | ||
this.callback(this.selected, false); | ||
} | ||
return this.selected; | ||
}; | ||
@@ -884,3 +911,3 @@ | ||
* Multiple nodes can be given at once, in contrary to unselect | ||
* | ||
* | ||
* @param {Nodes} _nodes one or multiple nodes | ||
@@ -891,16 +918,22 @@ * @param {Boolean} _callback - if callback should be called | ||
*/ | ||
DragSelect.prototype.removeSelection = function( _nodes, _callback, removeFromSelectables ) { | ||
var nodes = this.toArray( _nodes ); | ||
DragSelect.prototype.removeSelection = function( | ||
_nodes, | ||
_callback, | ||
removeFromSelectables | ||
) { | ||
var nodes = this.toArray(_nodes); | ||
for (var index = 0, il = nodes.length; index < il; index++) { | ||
var node = nodes[index]; | ||
this.unselect( node ); | ||
this.unselect(node); | ||
} | ||
if( removeFromSelectables ) { this.removeSelectables( nodes ); } | ||
if( _callback ) { this.callback( this.selected, false ); } | ||
if (removeFromSelectables) { | ||
this.removeSelectables(nodes); | ||
} | ||
if (_callback) { | ||
this.callback(this.selected, false); | ||
} | ||
return this.selected; | ||
}; | ||
@@ -912,3 +945,3 @@ | ||
* Multiple nodes can be given at once. | ||
* | ||
* | ||
* @param {Nodes} _nodes one or multiple nodes | ||
@@ -919,23 +952,16 @@ * @param {Boolean} _callback - if callback should be called | ||
*/ | ||
DragSelect.prototype.toggleSelection = function( _nodes, _callback, _special ) { | ||
var nodes = this.toArray( _nodes ); | ||
DragSelect.prototype.toggleSelection = function(_nodes, _callback, _special) { | ||
var nodes = this.toArray(_nodes); | ||
for (var index = 0, il = nodes.length; index < il; index++) { | ||
var node = nodes[index]; | ||
if( this.selected.indexOf( node ) < 0 ) { | ||
this.addSelection( node, _callback, _special ); | ||
if (this.selected.indexOf(node) < 0) { | ||
this.addSelection(node, _callback, _special); | ||
} else { | ||
this.removeSelection( node, _callback, _special ); | ||
this.removeSelection(node, _callback, _special); | ||
} | ||
} | ||
return this.selected; | ||
}; | ||
@@ -945,3 +971,3 @@ | ||
* Sets the current selected nodes and optionally run the callback | ||
* | ||
* | ||
* @param {Nodes} _nodes – dom nodes | ||
@@ -952,9 +978,11 @@ * @param {Boolean} runCallback - if callback should be called | ||
*/ | ||
DragSelect.prototype.setSelection = function( _nodes, runCallback, dontAddToSelectables ) { | ||
DragSelect.prototype.setSelection = function( | ||
_nodes, | ||
runCallback, | ||
dontAddToSelectables | ||
) { | ||
this.clearSelection(); | ||
this.addSelection( _nodes, runCallback, dontAddToSelectables ); | ||
this.addSelection(_nodes, runCallback, dontAddToSelectables); | ||
return this.selected; | ||
}; | ||
@@ -964,18 +992,18 @@ | ||
* Unselect / Deselect all current selected Nodes | ||
* | ||
* | ||
* @param {Boolean} runCallback - if callback should be called | ||
* @return {Array} this.selected, should be empty | ||
*/ | ||
DragSelect.prototype.clearSelection = function( runCallback ) { | ||
DragSelect.prototype.clearSelection = function(runCallback) { | ||
var selection = this.selected.slice(); | ||
for (var index = 0, il = selection.length; index < il; index++) { | ||
var node = selection[index]; | ||
this.unselect( node ); | ||
this.unselect(node); | ||
} | ||
if( runCallback ) { this.callback( this.selected, false ); } | ||
if (runCallback) { | ||
this.callback(this.selected, false); | ||
} | ||
return this.selected; | ||
}; | ||
@@ -986,3 +1014,3 @@ | ||
* The algorithm makes sure that no node is added twice | ||
* | ||
* | ||
* @param {Nodes} _nodes – dom nodes | ||
@@ -992,8 +1020,6 @@ * @param {Boolean} addToSelection – if elements should also be added to current selection | ||
*/ | ||
DragSelect.prototype.addSelectables = function( _nodes, addToSelection ) { | ||
var nodes = this.toArray( _nodes ); | ||
this._handleSelectables( nodes, false, addToSelection ); | ||
DragSelect.prototype.addSelectables = function(_nodes, addToSelection) { | ||
var nodes = this.toArray(_nodes); | ||
this._handleSelectables(nodes, false, addToSelection); | ||
return _nodes; | ||
}; | ||
@@ -1003,9 +1029,7 @@ | ||
* Gets all nodes that can be selected | ||
* | ||
* | ||
* @return {Nodes} this.selectables | ||
*/ | ||
DragSelect.prototype.getSelectables = function() { | ||
return this.selectables; | ||
}; | ||
@@ -1018,3 +1042,3 @@ | ||
* thus replacing the original set. | ||
* | ||
* | ||
* @param {Nodes} _nodes – dom nodes | ||
@@ -1025,7 +1049,9 @@ * @param {Boolean} removeFromSelection – if elements should also be removed from current selection | ||
*/ | ||
DragSelect.prototype.setSelectables = function( _nodes, removeFromSelection, addToSelection ) { | ||
this.removeSelectables( this.getSelectables(), removeFromSelection ); | ||
return this.addSelectables( _nodes, addToSelection ); | ||
DragSelect.prototype.setSelectables = function( | ||
_nodes, | ||
removeFromSelection, | ||
addToSelection | ||
) { | ||
this.removeSelectables(this.getSelectables(), removeFromSelection); | ||
return this.addSelectables(_nodes, addToSelection); | ||
}; | ||
@@ -1035,3 +1061,3 @@ | ||
* Remove nodes from the nodes that can be selected. | ||
* | ||
* | ||
* @param {Nodes} _nodes – dom nodes | ||
@@ -1041,11 +1067,8 @@ * @param {Boolean} removeFromSelection – if elements should also be removed from current selection | ||
*/ | ||
DragSelect.prototype.removeSelectables = function( _nodes, removeFromSelection ) { | ||
var nodes = this.toArray( _nodes ); | ||
this._handleSelectables( nodes, true, removeFromSelection ); | ||
DragSelect.prototype.removeSelectables = function(_nodes, removeFromSelection) { | ||
var nodes = this.toArray(_nodes); | ||
this._handleSelectables(nodes, true, removeFromSelection); | ||
return _nodes; | ||
}; | ||
// Helpers | ||
@@ -1058,23 +1081,24 @@ ////////////////////////////////////////////////////////////////////////////////////// | ||
* (found @ https://stackoverflow.com/a/2405835) | ||
* | ||
* @param {Object} event | ||
* | ||
* @param {Object} event | ||
* @return {Boolean} | ||
*/ | ||
DragSelect.prototype.isRightClick = function( event ) { | ||
DragSelect.prototype.isRightClick = function(event) { | ||
if (!event) { | ||
return false; | ||
} | ||
if( !event ) { return false; } | ||
var isRightMB = false; | ||
if ('which' in event) { // Gecko (Firefox), WebKit (Safari/Chrome) & Opera | ||
isRightMB = event.which === 3; | ||
} else if ('button' in event) { // IE, Opera | ||
isRightMB = event.button === 2; | ||
if ('which' in event) { | ||
// Gecko (Firefox), WebKit (Safari/Chrome) & Opera | ||
isRightMB = event.which === 3; | ||
} else if ('button' in event) { | ||
// IE, Opera | ||
isRightMB = event.button === 2; | ||
} | ||
return isRightMB; | ||
}; | ||
/** | ||
@@ -1084,17 +1108,21 @@ * Adds a class to an element | ||
* all credits to http://clubmate.fi/javascript-adding-and-removing-class-names-from-elements/ | ||
* | ||
* @param {Node} element | ||
* @param {String} classname | ||
* | ||
* @param {Node} element | ||
* @param {String} classname | ||
* @return {Node} element | ||
*/ | ||
DragSelect.prototype.addClass = function( element, classname ) { | ||
DragSelect.prototype.addClass = function(element, classname) { | ||
if (element.classList) { | ||
return element.classList.add(classname); | ||
} | ||
if(element.classList) { return element.classList.add(classname); } | ||
var cn = element.getAttribute('class') || ''; | ||
if( cn.indexOf(classname) !== -1 ) { return element; } // test for existance | ||
if( cn !== '' ) { classname = ' ' + classname; } // add a space if the element already has class | ||
element.setAttribute('class', cn+classname); | ||
if (cn.indexOf(classname) !== -1) { | ||
return element; | ||
} // test for existance | ||
if (cn !== '') { | ||
classname = ' ' + classname; | ||
} // add a space if the element already has class | ||
element.setAttribute('class', cn + classname); | ||
return element; | ||
}; | ||
@@ -1106,17 +1134,17 @@ | ||
* all credits to http://clubmate.fi/javascript-adding-and-removing-class-names-from-elements/ | ||
* | ||
* @param {Node} element | ||
* @param {String} classname | ||
* | ||
* @param {Node} element | ||
* @param {String} classname | ||
* @return {Node} element | ||
*/ | ||
DragSelect.prototype.removeClass = function( element, classname ) { | ||
DragSelect.prototype.removeClass = function(element, classname) { | ||
if (element.classList) { | ||
return element.classList.remove(classname); | ||
} | ||
if(element.classList) { return element.classList.remove(classname); } | ||
var cn = element.getAttribute('class') || ''; | ||
var rxp = new RegExp( classname + '\\b', 'g' ); | ||
cn = cn.replace( rxp, '' ); | ||
var rxp = new RegExp(classname + '\\b', 'g'); | ||
cn = cn.replace(rxp, ''); | ||
element.setAttribute('class', cn); | ||
return element; | ||
}; | ||
@@ -1127,15 +1155,18 @@ | ||
* sadly legacy phones/browsers don’t support .classlist so we use this workaround | ||
* | ||
* @param {Node} element | ||
* @param {String} classname | ||
* | ||
* @param {Node} element | ||
* @param {String} classname | ||
* @return {Boolean} | ||
*/ | ||
DragSelect.prototype.hasClass = function( element, classname ) { | ||
DragSelect.prototype.hasClass = function(element, classname) { | ||
if (element.classList) { | ||
return element.classList.contains(classname); | ||
} | ||
if(element.classList) { return element.classList.contains(classname); } | ||
var cn = element.getAttribute('class') || ''; | ||
if( cn.indexOf( classname ) > -1 ) { return true; } | ||
else { return false; } | ||
if (cn.indexOf(classname) > -1) { | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
}; | ||
@@ -1146,13 +1177,16 @@ | ||
* so user doesn’t have to care. | ||
* | ||
* | ||
* @param {Node} nodes | ||
* @return {array} | ||
*/ | ||
DragSelect.prototype.toArray = function( nodes ) { | ||
DragSelect.prototype.toArray = function(nodes) { | ||
if (!nodes) { | ||
return false; | ||
} | ||
if (!nodes.length && this.isElement(nodes)) { | ||
return [nodes]; | ||
} | ||
if( !nodes ) { return false; } | ||
if( !nodes.length && this.isElement( nodes ) ) { return [nodes]; } | ||
var array = []; | ||
for ( var i = nodes.length - 1; i >= 0; i-- ) { | ||
for (var i = nodes.length - 1; i >= 0; i--) { | ||
array[i] = nodes[i]; | ||
@@ -1162,3 +1196,2 @@ } | ||
return array; | ||
}; | ||
@@ -1168,22 +1201,22 @@ | ||
* Checks if a node is of type element | ||
* all credits to vikynandha: https://gist.github.com/vikynandha/6539809 | ||
* | ||
* all credits to vikynandha: https://gist.github.com/vikynandha/6539809 | ||
* | ||
* @param {Node} node | ||
* @return {Boolean} | ||
*/ | ||
DragSelect.prototype.isElement = function( node ) { | ||
try { // Using W3 DOM2 (works for FF, Opera and Chrome), also checking for SVGs | ||
DragSelect.prototype.isElement = function(node) { | ||
try { | ||
// Using W3 DOM2 (works for FF, Opera and Chrome), also checking for SVGs | ||
return node instanceof HTMLElement || node instanceof SVGElement; | ||
} | ||
catch( e ){ | ||
} catch (e) { | ||
// Browsers not supporting W3 DOM2 don't have HTMLElement and | ||
// an exception is thrown and we end up here. Testing some | ||
// properties that all elements have. (works even on IE7) | ||
return ( typeof node === 'object' ) && | ||
( node.nodeType === 1 ) && | ||
( typeof node.style === 'object' ) && | ||
( typeof node.ownerDocument === 'object' ); | ||
return ( | ||
typeof node === 'object' && | ||
node.nodeType === 1 && | ||
typeof node.style === 'object' && | ||
typeof node.ownerDocument === 'object' | ||
); | ||
} | ||
}; | ||
@@ -1196,3 +1229,3 @@ | ||
* Use the public .getCursorPos() from outside, it’s more flexible | ||
* | ||
* | ||
* @param {Object} event | ||
@@ -1202,6 +1235,9 @@ * @param {Node} area – containing area / document if none | ||
*/ | ||
DragSelect.prototype._getCursorPos = function( event, area ) { | ||
if(!event) { return { x: 0, y: 0 }; } | ||
DragSelect.prototype._getCursorPos = function(event, area) { | ||
if (!event) { | ||
return { x: 0, y: 0 }; | ||
} | ||
var cPos = { // event.clientX/Y fallback for <IE8 | ||
var cPos = { | ||
// event.clientX/Y fallback for <IE8 | ||
x: event.pageX || event.clientX, | ||
@@ -1211,10 +1247,10 @@ y: event.pageY || event.clientY | ||
var areaRect = this.getAreaRect( area || document ); | ||
var docScroll = this.getScroll(); // needed when document is scrollable but area is not | ||
var areaRect = this.getAreaRect(area || document); | ||
var docScroll = this.getScroll(); // needed when document is scrollable but area is not | ||
return { // if it’s constrained in an area the area should be substracted calculate | ||
return { | ||
// if it’s constrained in an area the area should be substracted calculate | ||
x: cPos.x - areaRect.left - docScroll.x, | ||
y: cPos.y - areaRect.top - docScroll.y | ||
}; | ||
}; | ||
@@ -1224,7 +1260,7 @@ | ||
* Returns the starting/initial position of the cursor/selector | ||
* | ||
* | ||
* @return {Object} initialPos. | ||
*/ | ||
DragSelect.prototype.getInitialCursorPosition = function() { | ||
return this.initialCursorPos; | ||
return this.initialCursorPos; | ||
}; | ||
@@ -1234,3 +1270,3 @@ | ||
* Returns the last seen position of the cursor/selector | ||
* | ||
* | ||
* @return {Object} initialPos. | ||
@@ -1248,3 +1284,3 @@ */ | ||
DragSelect.prototype.getPreviousCursorPosition = function() { | ||
return this.previousCursorPos; | ||
return this.previousCursorPos; | ||
}; | ||
@@ -1260,6 +1296,9 @@ | ||
*/ | ||
DragSelect.prototype.getCursorPositionDifference = function( usePreviousCursorDifference ) { | ||
DragSelect.prototype.getCursorPositionDifference = function( | ||
usePreviousCursorDifference | ||
) { | ||
var posA = this.getCurrentCursorPosition(); | ||
var posB = usePreviousCursorDifference ? this.getPreviousCursorPosition() : this.getInitialCursorPosition(); | ||
var posB = usePreviousCursorDifference | ||
? this.getPreviousCursorPosition() | ||
: this.getInitialCursorPosition(); | ||
@@ -1270,3 +1309,2 @@ return { | ||
}; | ||
}; | ||
@@ -1277,14 +1315,20 @@ | ||
* If container has no scroll it will return 0 | ||
* | ||
* | ||
* @param {Node} area | ||
* @return {Object} scroll X/Y | ||
*/ | ||
DragSelect.prototype.getScroll = function( area ) { | ||
DragSelect.prototype.getScroll = function(area) { | ||
var body = { | ||
top: document.body.scrollTop > 0 ? document.body.scrollTop : document.documentElement.scrollTop, | ||
left: document.body.scrollLeft > 0 ? document.body.scrollLeft : document.documentElement.scrollLeft | ||
top: | ||
document.body.scrollTop > 0 | ||
? document.body.scrollTop | ||
: document.documentElement.scrollTop, | ||
left: | ||
document.body.scrollLeft > 0 | ||
? document.body.scrollLeft | ||
: document.documentElement.scrollLeft | ||
}; | ||
var scroll = { // when the rectangle is bound to the document, no scroll is needed | ||
var scroll = { | ||
// when the rectangle is bound to the document, no scroll is needed | ||
y: area && area.scrollTop >= 0 ? area.scrollTop : body.top, | ||
@@ -1295,3 +1339,2 @@ x: area && area.scrollLeft >= 0 ? area.scrollLeft : body.left | ||
return scroll; | ||
}; | ||
@@ -1303,14 +1346,26 @@ | ||
* except the sizes will be nulled. | ||
* | ||
* @param {Node} area | ||
* | ||
* @param {Node} area | ||
* @return {Object} | ||
*/ | ||
DragSelect.prototype.getAreaRect = function( area ) { | ||
if(area === document) { | ||
DragSelect.prototype.getAreaRect = function(area) { | ||
if (area === document) { | ||
var size = { | ||
y: area.documentElement.clientHeight > 0 ? area.documentElement.clientHeight : window.innerHeight, | ||
x: area.documentElement.clientWidth > 0 ? area.documentElement.clientWidth : window.innerWidth | ||
y: | ||
area.documentElement.clientHeight > 0 | ||
? area.documentElement.clientHeight | ||
: window.innerHeight, | ||
x: | ||
area.documentElement.clientWidth > 0 | ||
? area.documentElement.clientWidth | ||
: window.innerWidth | ||
}; | ||
return { top: 0, left: 0, bottom: 0, right: 0, width: size.x, height: size.y }; | ||
return { | ||
top: 0, | ||
left: 0, | ||
bottom: 0, | ||
right: 0, | ||
width: size.x, | ||
height: size.y | ||
}; | ||
} | ||
@@ -1326,3 +1381,2 @@ | ||
}; | ||
}; | ||
@@ -1333,10 +1387,9 @@ | ||
* height values accordingly. | ||
* | ||
* | ||
* @param {Node} node | ||
* @param {Object} pos { x, y, w, h } | ||
* | ||
* | ||
* @return {Node} | ||
*/ | ||
DragSelect.prototype.updatePos = function( node, pos ) { | ||
DragSelect.prototype.updatePos = function(node, pos) { | ||
node.style.left = pos.x + 'px'; | ||
@@ -1347,24 +1400,23 @@ node.style.top = pos.y + 'px'; | ||
return node; | ||
}; | ||
// Make exportable | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
// jshint -W117 | ||
/* eslint-disable no-undef */ | ||
// Module exporting | ||
if ( typeof module !== 'undefined' && module !== null ) { | ||
if (typeof module !== 'undefined' && module !== null) { | ||
module.exports = DragSelect; | ||
// AMD Modules | ||
} else if( typeof define !== 'undefined' && typeof define === 'function' && define ) { | ||
define(function() { return DragSelect; }); | ||
// AMD Modules | ||
} else if ( | ||
typeof define !== 'undefined' && | ||
typeof define === 'function' && | ||
define | ||
) { | ||
define(function() { | ||
return DragSelect; | ||
}); | ||
} else { | ||
window.DragSelect = DragSelect; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1253046
12
40
2883
174