dragselect
Advanced tools
Comparing version 1.3.6 to 1.4.0
@@ -9,53 +9,44 @@ /* | ||
Key-Features | ||
Key-Features | ||
- No dependencies | ||
- Add drag selection. | ||
- Choose which elements can be selected. | ||
- Awesome browser support, works even on IE7 | ||
- Ease of use | ||
- Lightweight, only 1KB gzipped | ||
- Free & open source under MIT License | ||
- No dependencies | ||
- Add drag selection. | ||
- Choose which elements can be selected. | ||
- Awesome browser support, works even on IE7 | ||
- Ease of use | ||
- Lightweight, only ~1KB gzipped | ||
- Free & open source under MIT License | ||
Classes | ||
.ds-selected on elements that are selected | ||
Properties | ||
** @selector node the square that will draw the selection | ||
** @selectables nodes the elements that can be selected | ||
** @onElementSelect function this is optional, it is fired every time an element is selected. This callback gets a property which is the just selected node | ||
** @onElementUnselect function this is optional, it is fired every time an element is de-selected. This callback gets a property which is the just de-selected node | ||
** @callback function a callback function that gets fired when the element is dropped. This callback gets a property which is an array that holds all selected nodes | ||
** @selector node the square that will draw the selection | ||
** @selectables nodes the elements that can be selected | ||
** @onElementSelect function this is optional, it is fired every time an element is selected. This callback gets a property which is the just selected node | ||
** @onElementUnselect function this is optional, it is fired every time an element is de-selected. This callback gets a property which is the just de-selected node | ||
** @callback function a callback function that gets fired when the element is dropped. This callback gets a property which is an array that holds all selected nodes | ||
Methods | ||
** .start () reset the functionality after a teardown | ||
** .stop () will teardown/stop the whole functionality | ||
** .getSelection () returns the current selection | ||
** .addSelectables ([nodes]) add elements that can be selected. Intelligent algorythm never adds elements twice. | ||
** .removeSelectables ([nodes]) remove elements that can be selected. Also removes the 'selected' class from those elements. | ||
** .start () reset the functionality after a teardown | ||
** .stop () will teardown/stop the whole functionality | ||
** .getSelection () returns the current selection | ||
** .addSelectables ([nodes]) add elements that can be selected. Intelligent algorythm never adds elements twice. | ||
** .removeSelectables ([nodes]) remove elements that can be selected. Also removes the 'selected' class from those elements. | ||
** and everything else | ||
STAR THIS PLUGIN ON GITHUB: | ||
https://github.com/ThibaultJanBeyer/dragSelect | ||
Please give it a like, this is what makes me happy :-) | ||
Thanks You | ||
****************************************** | ||
********* The MIT License (MIT) ********** | ||
****************************************** | ||
Copyright (c) 2017 ThibaultJanBeyer | ||
web: http://www.thibaultjanbeyer.com/ | ||
github: https://github.com/ThibaultJanBeyer/dragSelect | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
@@ -67,6 +58,4 @@ of this software and associated documentation files (the "Software"), to deal | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
@@ -79,12 +68,9 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
SOFTWARE. | ||
*/ | ||
var dragSelect = function(options) { | ||
// Errors | ||
if(!options) { | ||
console.log('ERROR: dragSelect: please provide an options object to the function. See reference at: https://github.com/ThibaultJanBeyer/dragSelect for more info'); | ||
} | ||
// Setup | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
var selector, | ||
@@ -95,7 +81,9 @@ selectables, | ||
callback, | ||
initialCursorPos, | ||
area, | ||
selected; | ||
selected, | ||
initialScroll; | ||
function setup() { | ||
selector = options.selector || document.getElementById("rectangle"); | ||
function _setup() { | ||
selector = options.selector || document.getElementById('rectangle'); | ||
selectables = toArray(options.selectables) || []; | ||
@@ -106,62 +94,145 @@ selectCallback = options.onElementSelect || function() {}; | ||
area = options.area || document; | ||
selected = []; | ||
} | ||
setup(); | ||
} _setup(); | ||
//- Start | ||
// Start | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
function start() { | ||
area.addEventListener('mousedown', startUp); | ||
} | ||
start(); | ||
area.addEventListener('mousedown', _startUp); | ||
} start(); | ||
var cursorPos; | ||
function startUp(e) { | ||
cursorPos = { // event.clientX/Y fallback for IE8- | ||
x: e.pageX || e.clientX, | ||
y: e.pageY || e.clientY | ||
}; | ||
// Startups | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
function _startUp(event) { | ||
selector.style.display = 'block'; | ||
// move element on location | ||
selector.style.display = 'block'; | ||
selector.style.top = cursorPos.y + 'px'; | ||
selector.style.left = cursorPos.x + 'px'; | ||
checkIfInside(); | ||
area.removeEventListener('mousedown', startUp); | ||
area.addEventListener('mousemove', move); | ||
getStartingPositions(event); | ||
checkIfInsideSelection(); | ||
area.removeEventListener('mousedown', _startUp); | ||
area.addEventListener('mousemove', handleMove); | ||
document.addEventListener('mouseup', reset); | ||
} | ||
function getStartingPositions(event) { | ||
initialCursorPos = getCursorPos(event); | ||
initialScroll = getScroll(area); | ||
var selectorPos = {}; | ||
selectorPos.x = initialCursorPos.x + initialScroll.x; | ||
selectorPos.y = initialCursorPos.y + initialScroll.y; | ||
selectorPos.w = 0; | ||
selectorPos.h = 0; | ||
_updatePos(selector, selectorPos); | ||
} | ||
// Movements/Sizing of selection | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
// resize that div while mouse is pressed | ||
var cursorPos2; | ||
function move(e) { | ||
cursorPos2 = { // event.clientX/Y fallback for IE8- | ||
x: e.pageX || e.clientX, | ||
y: e.pageY || e.clientY | ||
function handleMove(event) { | ||
// move element on location | ||
var selectorPos = getPosition(event); | ||
_updatePos(selector, selectorPos); | ||
checkIfInsideSelection(); | ||
// scroll area if area is | ||
autoScroll(event); | ||
} | ||
function getPosition(event) { | ||
var cursorPosNew = getCursorPos(event); | ||
var scrollNew = getScroll(area); | ||
// if area or document is scrolled those values have to be included aswell | ||
var scrollAmount = { | ||
x: scrollNew.x - initialScroll.x, | ||
y: scrollNew.y - initialScroll.y | ||
}; | ||
// check for direction | ||
if(cursorPos2.x > cursorPos.x) { | ||
selector.style.width = cursorPos2.x - cursorPos.x + 'px'; | ||
} else { | ||
selector.style.left = cursorPos2.x + 'px'; | ||
selector.style.width = cursorPos.x - cursorPos2.x + 'px'; | ||
/** check for direction | ||
* | ||
* This is quite complicated math, so also quite complicated to explain. Lemme’ try: | ||
* | ||
* Problem #1: | ||
* Sadly in HTML we can not have negative sizes. | ||
* so if we want to scale our element 10px to the right then it is easy, | ||
* we just have to add +10px to the width. But if we want to scale the element | ||
* -10px to the left then things become more complicated, we have to move | ||
* the element -10px to the left on the x axis and also scale the element | ||
* by +10px width to fake a negative sizing. | ||
* | ||
* One solution to this problem is using css-transforms scale() with | ||
* transform-origin of top left. BUT we can’t use this since it will size | ||
* everything, then when your element has a border for example, the border will | ||
* get inanely huge. Also transforms are not widely supported in IE. | ||
* | ||
* Example #1: | ||
* Unfortunately, things get even more complicated when we are inside a scrollable | ||
* DIV. Then, let’s say we scoll to the right by 10px and move the cursor right by 5px in our | ||
* checks we have to substract 10px from the initialcursor position in our check | ||
* (since the inital position is moved to the left by 10px) so in our example: | ||
* 1. cursorPosNew.x (5) > initialCursorPos.x (0) - scrollAmount.x (10) === 5 > -10 === true | ||
* then reset the x position to its initial position (since we might have changed that | ||
* position when scrolling to the left before going right) in our example: | ||
* 2. selectorPos.x = initialCursorPos.x (0) + initialScroll.x (0) === 0; | ||
* then we cann calculate the elements width, which is | ||
* the new cursor position minus the initial one plus the scroll amount, so in our example: | ||
* 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: | ||
* 1b. cursorPosNew.x (-30) > initialCursorPos.x (0) - scrollAmount.x (-20) === -30 > -20 === false; | ||
* 2b. selectorPos.x = cursorPosNew.x (-30) + scrollNew.x (-20) | ||
* === -50; // move left position to cursor (for more info see Problem #1) | ||
* 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. | ||
*/ | ||
var selectorPos = {}; | ||
// right | ||
if(cursorPosNew.x > initialCursorPos.x - scrollAmount.x) { // 1. | ||
selectorPos.x = initialCursorPos.x + initialScroll.x; // 2. | ||
selectorPos.w = cursorPosNew.x - initialCursorPos.x + scrollAmount.x; // 3. | ||
// left | ||
} else { // 1b. | ||
selectorPos.x = cursorPosNew.x + scrollNew.x; // 2b. | ||
selectorPos.w = initialCursorPos.x - cursorPosNew.x - scrollAmount.x; // 3b. | ||
} | ||
if(cursorPos2.y > cursorPos.y) { | ||
selector.style.height = cursorPos2.y - cursorPos.y + 'px'; | ||
// bottom | ||
if(cursorPosNew.y > initialCursorPos.y - scrollAmount.y) { | ||
selectorPos.y = initialCursorPos.y + initialScroll.y; | ||
selectorPos.h = cursorPosNew.y - initialCursorPos.y + scrollAmount.y; | ||
// top | ||
} else { | ||
selector.style.top = cursorPos2.y + 'px'; | ||
selector.style.height = cursorPos.y - cursorPos2.y + 'px'; | ||
selectorPos.y = cursorPosNew.y + scrollNew.y; | ||
selectorPos.h = initialCursorPos.y - cursorPosNew.y - scrollAmount.y; | ||
} | ||
checkIfInside(); | ||
return selectorPos; | ||
} | ||
function checkIfInside() { | ||
// return elements that are inside the container | ||
// Colision detection | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
function checkIfInsideSelection() { | ||
for(var i = 0, il = selectables.length; i < il; i++) { | ||
var selectable = selectables[i]; | ||
var index = selected.indexOf(selectable); | ||
var posInSelectedArray = selected.indexOf(selectable); | ||
if(isElementTouching(selectable, selector)) { | ||
if(index < 0) { | ||
if( isElementTouching(selectable, selector) ) { | ||
if( posInSelectedArray < 0 ) { | ||
selected.push(selectable); | ||
@@ -171,24 +242,16 @@ addClass(selectable, 'selected'); | ||
} | ||
} else { | ||
if(index > -1) { | ||
selected.splice(selected.indexOf(selectable), 1); | ||
if( posInSelectedArray > -1 ) { | ||
selected.splice(posInSelectedArray, 1); | ||
removeClass(selectable, 'selected'); | ||
unselectCallback(selectable); | ||
} | ||
} | ||
} | ||
} | ||
// and finally unbind those functions when mouse click is released | ||
function reset() { | ||
selector.style.width = '0'; | ||
selector.style.height = '0'; | ||
selector.style.display = 'none'; | ||
callback(selected); | ||
area.removeEventListener('mousemove', move); | ||
area.addEventListener('mousedown', startUp); | ||
} | ||
//- Is Element touching Selection? (and vice-versa) | ||
@@ -201,7 +264,4 @@ function isElementTouching(element, container) { | ||
*/ | ||
var scroll = { | ||
// fallback for IE9- | ||
x: area && area.scrollTop ? area.scrollTop : window.scrollY || document.documentElement.scrollTop, | ||
y: area && area.scrollLeft ? area.scrollLeft : window.scrollX || document.documentElement.scrollLeft | ||
}; | ||
var scroll = getScroll(area); | ||
var containerRect = { | ||
@@ -240,3 +300,4 @@ y: container.getBoundingClientRect().top + scroll.y, | ||
return true; // collision detected! | ||
} else { | ||
} | ||
else { | ||
return false; | ||
@@ -246,2 +307,69 @@ } | ||
// Autoscroll | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
//- Scroll the area by selecting | ||
function autoScroll(event) { | ||
var edge = isCursorNearEdge(event); | ||
var _area = area === document ? area.body : area; | ||
if( edge === 'top' && _area.scrollTop > 0 ) { _area.scrollTop -= 1; } | ||
else if( edge === 'bottom' ) { _area.scrollTop += 1; } | ||
else if( edge === 'left' && _area.scrollLeft > 0 ) { _area.scrollLeft -= 1; } | ||
else if( edge === 'right' ) { _area.scrollLeft += 1; } | ||
} | ||
// Check if the selector is near an edge of the area | ||
function isCursorNearEdge(event) { | ||
var cursorPosition = getCursorPos(event); | ||
var areaRect = getAreaRect(area); | ||
var tolerance = { | ||
x: Math.max(areaRect.width / 10, 20), | ||
y: Math.max(areaRect.height / 10, 20) | ||
}; | ||
// document body also changes the cursor position values so we have to take | ||
// the scroll amount into consideration for these calculations | ||
var scroll = area === document ? getScroll(document.body) : { x: 0, y: 0 }; | ||
if(cursorPosition.y < tolerance.y + scroll.y) { return 'top'; } | ||
else if(areaRect.height - cursorPosition.y + scroll.y < tolerance.y) { return 'bottom'; } | ||
else if(areaRect.width - cursorPosition.x + scroll.x < tolerance.x) { return 'right'; } | ||
else if(cursorPosition.x < tolerance.x + scroll.x) { return 'left'; } | ||
return false; | ||
} | ||
//- Reset | ||
function reset() { | ||
// unbind those functions when mouse click is released | ||
selector.style.width = '0'; | ||
selector.style.height = '0'; | ||
selector.style.display = 'none'; | ||
callback(selected); | ||
area.removeEventListener('mousemove', handleMove); | ||
area.addEventListener('mousedown', _startUp); | ||
} | ||
//- Stop | ||
function stop() { | ||
reset(); | ||
area.removeEventListener('mousedown', _startUp); | ||
document.removeEventListener('mouseup', reset); | ||
} | ||
// Usefull methods for user | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
//- getSelection | ||
function getSelection() { | ||
return selected; | ||
} | ||
//- Add/Remove Selectables | ||
@@ -269,41 +397,50 @@ function addSelectables(_nodes) { | ||
function getSelection() { | ||
return selected; | ||
} | ||
//- Stop | ||
function stop() { | ||
reset(); | ||
area.removeEventListener('mousedown', startUp); | ||
document.removeEventListener('mouseup', reset); | ||
} | ||
// Helpers | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
/* * * * * * | ||
* HELPERS * | ||
* * * * * */ | ||
// sadly old phones/browsers do not support the quite new .classlist | ||
// so we have to use this workaround to add/remove classes | ||
// all credits to http://clubmate.fi/javascript-adding-and-removing-class-names-from-elements/ | ||
/** | ||
* Adds a class to an element | ||
* sadly legacy phones/browsers don’t support .classlist so we use this workaround | ||
* all credits to http://clubmate.fi/javascript-adding-and-removing-class-names-from-elements/ | ||
* @param {*} element | ||
* @param {*} classname | ||
* @return {node} element | ||
*/ | ||
function addClass( element, classname ) { | ||
var cn = element.className; | ||
//test for existance | ||
if( cn.indexOf(classname) !== -1 ) { return; } | ||
//add a space if the element already has class | ||
if( cn !== '' ) { classname = ' ' + 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.className = cn+classname; | ||
return element; | ||
} | ||
/** | ||
* Removes a class of an element | ||
* sadly legacy phones/browsers don’t support .classlist so we use this workaround | ||
* all credits to http://clubmate.fi/javascript-adding-and-removing-class-names-from-elements/ | ||
* @param {*} element | ||
* @param {*} classname | ||
* @return {node} element | ||
*/ | ||
function removeClass( element, classname ) { | ||
var cn = element.className; | ||
var rxp = new RegExp( classname + '\\b', 'g' ); | ||
cn = cn.replace( classname, '' ); | ||
cn = cn.replace( rxp, '' ); | ||
element.className = cn; | ||
} | ||
function toArray(obj) { | ||
if(!obj) { return false; } | ||
if(!obj.length && isElement(obj)) { return [obj]; } | ||
/** | ||
* Transforms an Object to an array | ||
* this is mainly used to transform Nodelists | ||
* into arrays of nodes. So user doesn’t have to care | ||
* @param {*} obj | ||
* @return {array} | ||
*/ | ||
function toArray( obj ) { | ||
if( !obj ) { return false; } | ||
if( !obj.length && isElement( obj ) ) { return [obj]; } | ||
var array = []; | ||
for (var i = obj.length - 1; i >= 0; i--) { | ||
for ( var i = obj.length - 1; i >= 0; i-- ) { | ||
array[i] = obj[i]; | ||
@@ -315,21 +452,100 @@ } | ||
function isElement(obj) { | ||
try { | ||
//Using W3 DOM2 (works for FF, Opera and Chrom) | ||
/** | ||
* Checks if a node is of type element | ||
* all credits to vikynandha: https://gist.github.com/vikynandha/6539809 | ||
* @param {*} obj | ||
* @return {bool} | ||
*/ | ||
function isElement( obj ) { | ||
try { // Using W3 DOM2 (works for FF, Opera and Chrom) | ||
return obj instanceof HTMLElement; | ||
} | ||
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 on IE7) | ||
return (typeof obj==="object") && | ||
(obj.nodeType===1) && (typeof obj.style === "object") && | ||
(typeof obj.ownerDocument ==="object"); | ||
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 on IE7) | ||
return ( typeof obj === 'object' ) && | ||
( obj.nodeType === 1 ) && | ||
( typeof obj.style === 'object' ) && | ||
( typeof obj.ownerDocument === 'object' ); | ||
} | ||
} | ||
/** | ||
* Returns cursor x, y position based on event object | ||
* @param {obj} event | ||
* @return {obj} cursor X/Y | ||
*/ | ||
function getCursorPos( event ) { | ||
var cPos = { // event.clientX/Y fallback for <IE8 | ||
x: event.pageX || event.clientX, | ||
y: event.pageY || event.clientY | ||
}; | ||
//- Return | ||
var areaRect = getAreaRect( area ); | ||
return { // if it’s constrained in an area the area should be substracted calculate | ||
x: cPos.x - areaRect.left, | ||
y: cPos.y - areaRect.top | ||
}; | ||
} | ||
/** | ||
* Returns the current x, y scroll value of a container | ||
* If container has no scroll it will return the | ||
* window/document scroll values | ||
* @param {node} area | ||
* @return {obj} scroll X/Y | ||
*/ | ||
function getScroll( area ) { | ||
var scroll = { // when the rectangle is bound to the document, no scroll is needed | ||
y: area && area.scrollTop >= 0 ? area.scrollTop : 0, | ||
x: area && area.scrollLeft >= 0 ? area.scrollLeft : 0 | ||
}; | ||
return scroll; | ||
} | ||
/** | ||
* Returns the top/left/bottom/right/width/height | ||
* values of a node | ||
* @param {node} area | ||
* @return {object} | ||
*/ | ||
function getAreaRect( area ) { | ||
if(area === document) { | ||
return { top: 0, left: 0, bottom: 0, right: 0, width: area.body.offsetWidth, height: area.body.offsetHeight }; | ||
} | ||
return { | ||
top: area.getBoundingClientRect().top, | ||
left: area.getBoundingClientRect().left, | ||
bottom: area.getBoundingClientRect().bottom, | ||
right: area.getBoundingClientRect().right, | ||
width: area.offsetWidth, | ||
height: area.offsetHeight | ||
}; | ||
} | ||
/** | ||
* Updates the node style left, top, width, | ||
* height values accordingly. | ||
* @param {node} node | ||
* @param {object} pos { x, y, w, h } | ||
* @return {node} | ||
*/ | ||
function _updatePos( node, pos ) { | ||
node.style.left = pos.x + 'px'; | ||
node.style.top = pos.y + 'px'; | ||
node.style.width = pos.w + 'px'; | ||
node.style.height = pos.h + 'px'; | ||
return node; | ||
} | ||
// Return | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
var DS = { | ||
_updatePos: _updatePos, | ||
isElement: isElement, | ||
@@ -342,5 +558,5 @@ toArray: toArray, | ||
reset: reset, | ||
checkIfInside: checkIfInside, | ||
move: move, | ||
startUp: startUp, | ||
checkIfInsideSelection: checkIfInsideSelection, | ||
handleMove: handleMove, | ||
_startUp: _startUp, | ||
start: start, | ||
@@ -355,3 +571,6 @@ getSelection: getSelection, | ||
// make exportable | ||
// Make exportable | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
if (typeof module !== 'undefined' && module !== null) { | ||
@@ -363,2 +582,3 @@ module.exports = dragSelect; | ||
// Relevant Discussions: | ||
@@ -365,0 +585,0 @@ // https://stackoverflow.com/questions/11979586/select-and-drag-to-get-selected-elements |
@@ -1,1 +0,1 @@ | ||
var dragSelect=function(e){function t(){w.addEventListener("mousedown",n)}function n(e){b={x:e.pageX||e.clientX,y:e.pageY||e.clientY},p.style.display="block",p.style.top=b.y+"px",p.style.left=b.x+"px",l(),w.removeEventListener("mousedown",n),w.addEventListener("mousemove",o),document.addEventListener("mouseup",c)}function o(e){L={x:e.pageX||e.clientX,y:e.pageY||e.clientY},L.x>b.x?p.style.width=L.x-b.x+"px":(p.style.left=L.x+"px",p.style.width=b.x-L.x+"px"),L.y>b.y?p.style.height=L.y-b.y+"px":(p.style.top=L.y+"px",p.style.height=b.y-L.y+"px"),l()}function l(){for(var e=0,t=g.length;e<t;e++){var n=g[e],o=E.indexOf(n);s(n,p)?o<0&&(E.push(n),a(n,"selected"),x(n)):o>-1&&(E.splice(E.indexOf(n),1),f(n,"selected"),h(n))}}function c(){p.style.width="0",p.style.height="0",p.style.display="none",v(E),w.removeEventListener("mousemove",o),w.addEventListener("mousedown",n)}function s(e,t){var n={x:w&&w.scrollTop?w.scrollTop:window.scrollY||document.documentElement.scrollTop,y:w&&w.scrollLeft?w.scrollLeft:window.scrollX||document.documentElement.scrollLeft},o={y:t.getBoundingClientRect().top+n.y,x:t.getBoundingClientRect().left+n.x,h:t.offsetHeight,w:t.offsetWidth},l={y:e.getBoundingClientRect().top+n.y,x:e.getBoundingClientRect().left+n.x,h:e.offsetHeight,w:e.offsetWidth};return o.x<l.x+l.w&&o.x+o.w>l.x&&o.y<l.y+l.h&&o.h+o.y>l.y}function r(e){for(var t=m(e),n=0,o=t.length;n<o;n++){var l=t[n];g.indexOf(l)<0&&g.push(l)}}function i(e){for(var t=m(e),n=0,o=t.length;n<o;n++){var l=t[n];g.indexOf(l)>0&&(f(l,"selected"),g.splice(g.indexOf(l),1))}}function d(){return E}function u(){c(),w.removeEventListener("mousedown",n),document.removeEventListener("mouseup",c)}function a(e,t){var n=e.className;-1===n.indexOf(t)&&(""!==n&&(t=" "+t),e.className=n+t)}function f(e,t){var n=e.className;new RegExp(t+"\\b","g");n=n.replace(t,""),e.className=n}function m(e){if(!e)return!1;if(!e.length&&y(e))return[e];for(var t=[],n=e.length-1;n>=0;n--)t[n]=e[n];return t}function y(e){try{return e instanceof HTMLElement}catch(t){return"object"==typeof e&&1===e.nodeType&&"object"==typeof e.style&&"object"==typeof e.ownerDocument}}e||console.log("ERROR: dragSelect: please provide an options object to the function. See reference at: https://github.com/ThibaultJanBeyer/dragSelect for more info");var p,g,x,h,v,w,E;!function(){p=e.selector||document.getElementById("rectangle"),g=m(e.selectables)||[],x=e.onElementSelect||function(){},h=e.onElementUnselect||function(){},v=e.callback||function(){},w=e.area||document,E=[]}(),t();var b,L;return{isElement:y,toArray:m,removeClass:f,addClass:a,stop:u,isElementTouching:s,reset:c,checkIfInside:l,move:o,startUp:n,start:t,getSelection:d,removeSelectables:i,addSelectables:r}};"undefined"!=typeof module&&null!==module?module.exports=dragSelect:window.dragSelect=dragSelect; | ||
var dragSelect=function(e){function t(){H.addEventListener("mousedown",n)}function n(e){L.style.display="block",o(e),r(),H.removeEventListener("mousedown",n),H.addEventListener("mousemove",i),document.addEventListener("mouseup",f)}function o(e){T=v(e),M=w(H);var t={};t.x=T.x+M.x,t.y=T.y+M.y,t.w=0,t.h=0,E(L,t)}function i(e){var t=l(e);E(L,t),r(),s(e)}function l(e){var t=v(e),n=w(H),o={x:n.x-M.x,y:n.y-M.y},i={};return t.x>T.x-o.x?(i.x=T.x+M.x,i.w=t.x-T.x+o.x):(i.x=t.x+n.x,i.w=T.x-t.x-o.x),t.y>T.y-o.y?(i.y=T.y+M.y,i.h=t.y-T.y+o.y):(i.y=t.y+n.y,i.h=T.y-t.y-o.y),i}function r(){for(var e=0,t=C.length;e<t;e++){var n=C[e],o=O.indexOf(n);c(n,L)?o<0&&(O.push(n),h(n,"selected"),B(n)):o>-1&&(O.splice(o,1),g(n,"selected"),R(n))}}function c(e,t){var n=w(H),o={y:t.getBoundingClientRect().top+n.y,x:t.getBoundingClientRect().left+n.x,h:t.offsetHeight,w:t.offsetWidth},i={y:e.getBoundingClientRect().top+n.y,x:e.getBoundingClientRect().left+n.x,h:e.offsetHeight,w:e.offsetWidth};return o.x<i.x+i.w&&o.x+o.w>i.x&&o.y<i.y+i.h&&o.h+o.y>i.y}function s(e){var t=u(e),n=H===document?H.body:H;"top"===t&&n.scrollTop>0?n.scrollTop-=1:"bottom"===t?n.scrollTop+=1:"left"===t&&n.scrollLeft>0?n.scrollLeft-=1:"right"===t&&(n.scrollLeft+=1)}function u(e){var t=v(e),n=b(H),o={x:Math.max(n.width/10,20),y:Math.max(n.height/10,20)},i=H===document?w(document.body):{x:0,y:0};return t.y<o.y+i.y?"top":n.height-t.y+i.y<o.y?"bottom":n.width-t.x+i.x<o.x?"right":t.x<o.x+i.x&&"left"}function f(){L.style.width="0",L.style.height="0",L.style.display="none",S(O),H.removeEventListener("mousemove",i),H.addEventListener("mousedown",n)}function d(){f(),H.removeEventListener("mousedown",n),document.removeEventListener("mouseup",f)}function y(){return O}function a(e){for(var t=m(e),n=0,o=t.length;n<o;n++){var i=t[n];C.indexOf(i)<0&&C.push(i)}}function x(e){for(var t=m(e),n=0,o=t.length;n<o;n++){var i=t[n];C.indexOf(i)>0&&(g(i,"selected"),C.splice(C.indexOf(i),1))}}function h(e,t){var n=e.className;return-1!==n.indexOf(t)?e:(""!==n&&(t=" "+t),e.className=n+t,e)}function g(e,t){var n=e.className,o=new RegExp(t+"\\b","g");n=n.replace(o,""),e.className=n}function m(e){if(!e)return!1;if(!e.length&&p(e))return[e];for(var t=[],n=e.length-1;n>=0;n--)t[n]=e[n];return t}function p(e){try{return e instanceof HTMLElement}catch(t){return"object"==typeof e&&1===e.nodeType&&"object"==typeof e.style&&"object"==typeof e.ownerDocument}}function v(e){var t={x:e.pageX||e.clientX,y:e.pageY||e.clientY},n=b(H);return{x:t.x-n.left,y:t.y-n.top}}function w(e){return{y:e&&e.scrollTop>=0?e.scrollTop:0,x:e&&e.scrollLeft>=0?e.scrollLeft:0}}function b(e){return e===document?{top:0,left:0,bottom:0,right:0,width:e.body.offsetWidth,height:e.body.offsetHeight}:{top:e.getBoundingClientRect().top,left:e.getBoundingClientRect().left,bottom:e.getBoundingClientRect().bottom,right:e.getBoundingClientRect().right,width:e.offsetWidth,height:e.offsetHeight}}function E(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}var L,C,B,R,S,T,H,O,M;return function(){L=e.selector||document.getElementById("rectangle"),C=m(e.selectables)||[],B=e.onElementSelect||function(){},R=e.onElementUnselect||function(){},S=e.callback||function(){},H=e.area||document,O=[]}(),t(),{_updatePos:E,isElement:p,toArray:m,removeClass:g,addClass:h,stop:d,isElementTouching:c,reset:f,checkIfInsideSelection:r,handleMove:i,_startUp:n,start:t,getSelection:y,removeSelectables:x,addSelectables:a}};"undefined"!=typeof module&&null!==module?module.exports=dragSelect:window.dragSelect=dragSelect; |
{ | ||
"name": "dragselect", | ||
"version": "1.3.6", | ||
"version": "1.4.0", | ||
"description": "easy javascript drag select functionality to your projects", | ||
"main": "./dist/ds.min.js", | ||
"dependencies": {}, | ||
"scripts": { | ||
"start": "npm install; npm test", | ||
"gulp": "gulp", | ||
"deploy": "gulp; gulp deploy;", | ||
"test": "open src/quicktest.html -a 'Google Chrome'" | ||
}, | ||
"devDependencies": { | ||
@@ -8,0 +14,0 @@ "gulp": "^3.9.1", |
/* | ||
@TODO: rewrite it in a OOP manner so that people can extend/mixin the dragselect | ||
__ _____ __ __ | ||
@@ -9,53 +11,44 @@ ____/ /________ _____ _/ ___/___ / /__ _____/ /_ | ||
Key-Features | ||
Key-Features | ||
- No dependencies | ||
- Add drag selection. | ||
- Choose which elements can be selected. | ||
- Awesome browser support, works even on IE7 | ||
- Ease of use | ||
- Lightweight, only 1KB gzipped | ||
- Free & open source under MIT License | ||
- No dependencies | ||
- Add drag selection. | ||
- Choose which elements can be selected. | ||
- Awesome browser support, works even on IE7 | ||
- Ease of use | ||
- Lightweight, only ~1KB gzipped | ||
- Free & open source under MIT License | ||
Classes | ||
.ds-selected on elements that are selected | ||
Properties | ||
** @selector node the square that will draw the selection | ||
** @selectables nodes the elements that can be selected | ||
** @onElementSelect function this is optional, it is fired every time an element is selected. This callback gets a property which is the just selected node | ||
** @onElementUnselect function this is optional, it is fired every time an element is de-selected. This callback gets a property which is the just de-selected node | ||
** @callback function a callback function that gets fired when the element is dropped. This callback gets a property which is an array that holds all selected nodes | ||
** @selector node the square that will draw the selection | ||
** @selectables nodes the elements that can be selected | ||
** @onElementSelect function this is optional, it is fired every time an element is selected. This callback gets a property which is the just selected node | ||
** @onElementUnselect function this is optional, it is fired every time an element is de-selected. This callback gets a property which is the just de-selected node | ||
** @callback function a callback function that gets fired when the element is dropped. This callback gets a property which is an array that holds all selected nodes | ||
Methods | ||
** .start () reset the functionality after a teardown | ||
** .stop () will teardown/stop the whole functionality | ||
** .getSelection () returns the current selection | ||
** .addSelectables ([nodes]) add elements that can be selected. Intelligent algorythm never adds elements twice. | ||
** .removeSelectables ([nodes]) remove elements that can be selected. Also removes the 'selected' class from those elements. | ||
** .start () reset the functionality after a teardown | ||
** .stop () will teardown/stop the whole functionality | ||
** .getSelection () returns the current selection | ||
** .addSelectables ([nodes]) add elements that can be selected. Intelligent algorythm never adds elements twice. | ||
** .removeSelectables ([nodes]) remove elements that can be selected. Also removes the 'selected' class from those elements. | ||
** and everything else | ||
STAR THIS PLUGIN ON GITHUB: | ||
https://github.com/ThibaultJanBeyer/dragSelect | ||
Please give it a like, this is what makes me happy :-) | ||
Thanks You | ||
****************************************** | ||
********* The MIT License (MIT) ********** | ||
****************************************** | ||
Copyright (c) 2017 ThibaultJanBeyer | ||
web: http://www.thibaultjanbeyer.com/ | ||
github: https://github.com/ThibaultJanBeyer/dragSelect | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
@@ -67,6 +60,4 @@ of this software and associated documentation files (the "Software"), to deal | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
@@ -79,12 +70,9 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
SOFTWARE. | ||
*/ | ||
var dragSelect = function(options) { | ||
// Errors | ||
if(!options) { | ||
console.log('ERROR: dragSelect: please provide an options object to the function. See reference at: https://github.com/ThibaultJanBeyer/dragSelect for more info'); | ||
} | ||
// Setup | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
var selector, | ||
@@ -95,7 +83,9 @@ selectables, | ||
callback, | ||
initialCursorPos, | ||
area, | ||
selected; | ||
selected, | ||
initialScroll; | ||
function setup() { | ||
selector = options.selector || document.getElementById("rectangle"); | ||
function _setup() { | ||
selector = options.selector || document.getElementById('rectangle'); | ||
selectables = toArray(options.selectables) || []; | ||
@@ -106,62 +96,146 @@ selectCallback = options.onElementSelect || function() {}; | ||
area = options.area || document; | ||
selected = []; | ||
} | ||
setup(); | ||
} _setup(); | ||
//- Start | ||
// Start | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
function start() { | ||
area.addEventListener('mousedown', startUp); | ||
} | ||
start(); | ||
area.addEventListener('mousedown', _startUp); | ||
} start(); | ||
var cursorPos; | ||
function startUp(e) { | ||
cursorPos = { // event.clientX/Y fallback for IE8- | ||
x: e.pageX || e.clientX, | ||
y: e.pageY || e.clientY | ||
}; | ||
// Startups | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
function _startUp(event) { | ||
selector.style.display = 'block'; | ||
// move element on location | ||
selector.style.display = 'block'; | ||
selector.style.top = cursorPos.y + 'px'; | ||
selector.style.left = cursorPos.x + 'px'; | ||
checkIfInside(); | ||
area.removeEventListener('mousedown', startUp); | ||
area.addEventListener('mousemove', move); | ||
getStartingPositions(event); | ||
checkIfInsideSelection(); | ||
area.removeEventListener('mousedown', _startUp); | ||
area.addEventListener('mousemove', handleMove); | ||
document.addEventListener('mouseup', reset); | ||
} | ||
function getStartingPositions(event) { | ||
initialCursorPos = getCursorPos(event); | ||
initialScroll = getScroll(area); | ||
var selectorPos = {}; | ||
selectorPos.x = initialCursorPos.x + initialScroll.x; | ||
selectorPos.y = initialCursorPos.y + initialScroll.y; | ||
selectorPos.w = 0; | ||
selectorPos.h = 0; | ||
_updatePos(selector, selectorPos); | ||
} | ||
// Movements/Sizing of selection | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
// resize that div while mouse is pressed | ||
var cursorPos2; | ||
function move(e) { | ||
cursorPos2 = { // event.clientX/Y fallback for IE8- | ||
x: e.pageX || e.clientX, | ||
y: e.pageY || e.clientY | ||
function handleMove(event) { | ||
// move element on location | ||
var selectorPos = getPosition(event); | ||
_updatePos(selector, selectorPos); | ||
checkIfInsideSelection(); | ||
// scroll area if area is | ||
autoScroll(event); | ||
} | ||
function getPosition(event) { | ||
var cursorPosNew = getCursorPos(event); | ||
var scrollNew = getScroll(area); | ||
// if area or document is scrolled those values have to be included aswell | ||
var scrollAmount = { | ||
x: scrollNew.x - initialScroll.x, | ||
y: scrollNew.y - initialScroll.y | ||
}; | ||
// check for direction | ||
if(cursorPos2.x > cursorPos.x) { | ||
selector.style.width = cursorPos2.x - cursorPos.x + 'px'; | ||
} else { | ||
selector.style.left = cursorPos2.x + 'px'; | ||
selector.style.width = cursorPos.x - cursorPos2.x + 'px'; | ||
/** check for direction | ||
* | ||
* This is quite complicated math, so also quite complicated to explain. Lemme’ try: | ||
* | ||
* Problem #1: | ||
* Sadly in HTML we can not have negative sizes. | ||
* so if we want to scale our element 10px to the right then it is easy, | ||
* we just have to add +10px to the width. But if we want to scale the element | ||
* -10px to the left then things become more complicated, we have to move | ||
* the element -10px to the left on the x axis and also scale the element | ||
* by +10px width to fake a negative sizing. | ||
* | ||
* One solution to this problem is using css-transforms scale() with | ||
* transform-origin of top left. BUT we can’t use this since it will size | ||
* everything, then when your element has a border for example, the border will | ||
* get inanely huge. Also transforms are not widely supported in IE. | ||
* | ||
* Example #1: | ||
* Unfortunately, things get even more complicated when we are inside a scrollable | ||
* DIV. Then, let’s say we scoll to the right by 10px and move the cursor right by 5px in our | ||
* checks we have to substract 10px from the initialcursor position in our check | ||
* (since the inital position is moved to the left by 10px) so in our example: | ||
* 1. cursorPosNew.x (5) > initialCursorPos.x (0) - scrollAmount.x (10) === 5 > -10 === true | ||
* then reset the x position to its initial position (since we might have changed that | ||
* position when scrolling to the left before going right) in our example: | ||
* 2. selectorPos.x = initialCursorPos.x (0) + initialScroll.x (0) === 0; | ||
* then we cann calculate the elements width, which is | ||
* the new cursor position minus the initial one plus the scroll amount, so in our example: | ||
* 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: | ||
* 1b. cursorPosNew.x (-30) > initialCursorPos.x (0) - scrollAmount.x (-20) === -30 > -20 === false; | ||
* 2b. selectorPos.x = cursorPosNew.x (-30) + scrollNew.x (-20) | ||
* === -50; // move left position to cursor (for more info see Problem #1) | ||
* 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. | ||
*/ | ||
var selectorPos = {}; | ||
// console.log('yala', cursorPosNew.y, initialCursorPos.y, scrollAmount.y, initialScroll.y); | ||
// right | ||
if(cursorPosNew.x > initialCursorPos.x - scrollAmount.x) { // 1. | ||
selectorPos.x = initialCursorPos.x + initialScroll.x; // 2. | ||
selectorPos.w = cursorPosNew.x - initialCursorPos.x + scrollAmount.x; // 3. | ||
// left | ||
} else { // 1b. | ||
selectorPos.x = cursorPosNew.x + scrollNew.x; // 2b. | ||
selectorPos.w = initialCursorPos.x - cursorPosNew.x - scrollAmount.x; // 3b. | ||
} | ||
if(cursorPos2.y > cursorPos.y) { | ||
selector.style.height = cursorPos2.y - cursorPos.y + 'px'; | ||
// bottom | ||
if(cursorPosNew.y > initialCursorPos.y - scrollAmount.y) { | ||
selectorPos.y = initialCursorPos.y + initialScroll.y; | ||
selectorPos.h = cursorPosNew.y - initialCursorPos.y + scrollAmount.y; | ||
// top | ||
} else { | ||
selector.style.top = cursorPos2.y + 'px'; | ||
selector.style.height = cursorPos.y - cursorPos2.y + 'px'; | ||
selectorPos.y = cursorPosNew.y + scrollNew.y; | ||
selectorPos.h = initialCursorPos.y - cursorPosNew.y - scrollAmount.y; | ||
} | ||
checkIfInside(); | ||
return selectorPos; | ||
} | ||
function checkIfInside() { | ||
// return elements that are inside the container | ||
// Colision detection | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
function checkIfInsideSelection() { | ||
for(var i = 0, il = selectables.length; i < il; i++) { | ||
var selectable = selectables[i]; | ||
var index = selected.indexOf(selectable); | ||
var posInSelectedArray = selected.indexOf(selectable); | ||
if(isElementTouching(selectable, selector)) { | ||
if(index < 0) { | ||
if( isElementTouching(selectable, selector) ) { | ||
if( posInSelectedArray < 0 ) { | ||
selected.push(selectable); | ||
@@ -171,24 +245,16 @@ addClass(selectable, 'selected'); | ||
} | ||
} else { | ||
if(index > -1) { | ||
selected.splice(selected.indexOf(selectable), 1); | ||
if( posInSelectedArray > -1 ) { | ||
selected.splice(posInSelectedArray, 1); | ||
removeClass(selectable, 'selected'); | ||
unselectCallback(selectable); | ||
} | ||
} | ||
} | ||
} | ||
// and finally unbind those functions when mouse click is released | ||
function reset() { | ||
selector.style.width = '0'; | ||
selector.style.height = '0'; | ||
selector.style.display = 'none'; | ||
callback(selected); | ||
area.removeEventListener('mousemove', move); | ||
area.addEventListener('mousedown', startUp); | ||
} | ||
//- Is Element touching Selection? (and vice-versa) | ||
@@ -201,7 +267,4 @@ function isElementTouching(element, container) { | ||
*/ | ||
var scroll = { | ||
// fallback for IE9- | ||
x: area && area.scrollTop ? area.scrollTop : window.scrollY || document.documentElement.scrollTop, | ||
y: area && area.scrollLeft ? area.scrollLeft : window.scrollX || document.documentElement.scrollLeft | ||
}; | ||
var scroll = getScroll(area); | ||
var containerRect = { | ||
@@ -240,3 +303,4 @@ y: container.getBoundingClientRect().top + scroll.y, | ||
return true; // collision detected! | ||
} else { | ||
} | ||
else { | ||
return false; | ||
@@ -246,2 +310,69 @@ } | ||
// Autoscroll | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
//- Scroll the area by selecting | ||
function autoScroll(event) { | ||
var edge = isCursorNearEdge(event); | ||
var _area = area === document ? area.body : area; | ||
if( edge === 'top' && _area.scrollTop > 0 ) { _area.scrollTop -= 1; } | ||
else if( edge === 'bottom' ) { _area.scrollTop += 1; } | ||
else if( edge === 'left' && _area.scrollLeft > 0 ) { _area.scrollLeft -= 1; } | ||
else if( edge === 'right' ) { _area.scrollLeft += 1; } | ||
} | ||
// Check if the selector is near an edge of the area | ||
function isCursorNearEdge(event) { | ||
var cursorPosition = getCursorPos(event); | ||
var areaRect = getAreaRect(area); | ||
var tolerance = { | ||
x: Math.max(areaRect.width / 10, 20), | ||
y: Math.max(areaRect.height / 10, 20) | ||
}; | ||
// document body also changes the cursor position values so we have to take | ||
// the scroll amount into consideration for these calculations | ||
var scroll = area === document ? getScroll(document.body) : { x: 0, y: 0 }; | ||
if(cursorPosition.y < tolerance.y + scroll.y) { return 'top'; } | ||
else if(areaRect.height - cursorPosition.y + scroll.y < tolerance.y) { return 'bottom'; } | ||
else if(areaRect.width - cursorPosition.x + scroll.x < tolerance.x) { return 'right'; } | ||
else if(cursorPosition.x < tolerance.x + scroll.x) { return 'left'; } | ||
return false; | ||
} | ||
//- Reset | ||
function reset() { | ||
// unbind those functions when mouse click is released | ||
selector.style.width = '0'; | ||
selector.style.height = '0'; | ||
selector.style.display = 'none'; | ||
callback(selected); | ||
area.removeEventListener('mousemove', handleMove); | ||
area.addEventListener('mousedown', _startUp); | ||
} | ||
//- Stop | ||
function stop() { | ||
reset(); | ||
area.removeEventListener('mousedown', _startUp); | ||
document.removeEventListener('mouseup', reset); | ||
} | ||
// Usefull methods for user | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
//- getSelection | ||
function getSelection() { | ||
return selected; | ||
} | ||
//- Add/Remove Selectables | ||
@@ -269,41 +400,50 @@ function addSelectables(_nodes) { | ||
function getSelection() { | ||
return selected; | ||
} | ||
//- Stop | ||
function stop() { | ||
reset(); | ||
area.removeEventListener('mousedown', startUp); | ||
document.removeEventListener('mouseup', reset); | ||
} | ||
// Helpers | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
/* * * * * * | ||
* HELPERS * | ||
* * * * * */ | ||
// sadly old phones/browsers do not support the quite new .classlist | ||
// so we have to use this workaround to add/remove classes | ||
// all credits to http://clubmate.fi/javascript-adding-and-removing-class-names-from-elements/ | ||
/** | ||
* Adds a class to an element | ||
* sadly legacy phones/browsers don’t support .classlist so we use this workaround | ||
* all credits to http://clubmate.fi/javascript-adding-and-removing-class-names-from-elements/ | ||
* @param {*} element | ||
* @param {*} classname | ||
* @return {node} element | ||
*/ | ||
function addClass( element, classname ) { | ||
var cn = element.className; | ||
//test for existance | ||
if( cn.indexOf(classname) !== -1 ) { return; } | ||
//add a space if the element already has class | ||
if( cn !== '' ) { classname = ' ' + 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.className = cn+classname; | ||
return element; | ||
} | ||
/** | ||
* Removes a class of an element | ||
* sadly legacy phones/browsers don’t support .classlist so we use this workaround | ||
* all credits to http://clubmate.fi/javascript-adding-and-removing-class-names-from-elements/ | ||
* @param {*} element | ||
* @param {*} classname | ||
* @return {node} element | ||
*/ | ||
function removeClass( element, classname ) { | ||
var cn = element.className; | ||
var rxp = new RegExp( classname + '\\b', 'g' ); | ||
cn = cn.replace( classname, '' ); | ||
cn = cn.replace( rxp, '' ); | ||
element.className = cn; | ||
} | ||
function toArray(obj) { | ||
if(!obj) { return false; } | ||
if(!obj.length && isElement(obj)) { return [obj]; } | ||
/** | ||
* Transforms an Object to an array | ||
* this is mainly used to transform Nodelists | ||
* into arrays of nodes. So user doesn’t have to care | ||
* @param {*} obj | ||
* @return {array} | ||
*/ | ||
function toArray( obj ) { | ||
if( !obj ) { return false; } | ||
if( !obj.length && isElement( obj ) ) { return [obj]; } | ||
var array = []; | ||
for (var i = obj.length - 1; i >= 0; i--) { | ||
for ( var i = obj.length - 1; i >= 0; i-- ) { | ||
array[i] = obj[i]; | ||
@@ -315,21 +455,100 @@ } | ||
function isElement(obj) { | ||
try { | ||
//Using W3 DOM2 (works for FF, Opera and Chrom) | ||
/** | ||
* Checks if a node is of type element | ||
* all credits to vikynandha: https://gist.github.com/vikynandha/6539809 | ||
* @param {*} obj | ||
* @return {bool} | ||
*/ | ||
function isElement( obj ) { | ||
try { // Using W3 DOM2 (works for FF, Opera and Chrom) | ||
return obj instanceof HTMLElement; | ||
} | ||
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 on IE7) | ||
return (typeof obj==="object") && | ||
(obj.nodeType===1) && (typeof obj.style === "object") && | ||
(typeof obj.ownerDocument ==="object"); | ||
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 on IE7) | ||
return ( typeof obj === 'object' ) && | ||
( obj.nodeType === 1 ) && | ||
( typeof obj.style === 'object' ) && | ||
( typeof obj.ownerDocument === 'object' ); | ||
} | ||
} | ||
/** | ||
* Returns cursor x, y position based on event object | ||
* @param {obj} event | ||
* @return {obj} cursor X/Y | ||
*/ | ||
function getCursorPos( event ) { | ||
var cPos = { // event.clientX/Y fallback for <IE8 | ||
x: event.pageX || event.clientX, | ||
y: event.pageY || event.clientY | ||
}; | ||
//- Return | ||
var areaRect = getAreaRect( area ); | ||
return { // if it’s constrained in an area the area should be substracted calculate | ||
x: cPos.x - areaRect.left, | ||
y: cPos.y - areaRect.top | ||
}; | ||
} | ||
/** | ||
* Returns the current x, y scroll value of a container | ||
* If container has no scroll it will return the | ||
* window/document scroll values | ||
* @param {node} area | ||
* @return {obj} scroll X/Y | ||
*/ | ||
function getScroll( area ) { | ||
var scroll = { // when the rectangle is bound to the document, no scroll is needed | ||
y: area && area.scrollTop >= 0 ? area.scrollTop : 0, | ||
x: area && area.scrollLeft >= 0 ? area.scrollLeft : 0 | ||
}; | ||
return scroll; | ||
} | ||
/** | ||
* Returns the top/left/bottom/right/width/height | ||
* values of a node | ||
* @param {node} area | ||
* @return {object} | ||
*/ | ||
function getAreaRect( area ) { | ||
if(area === document) { | ||
return { top: 0, left: 0, bottom: 0, right: 0, width: area.body.offsetWidth, height: area.body.offsetHeight }; | ||
} | ||
return { | ||
top: area.getBoundingClientRect().top, | ||
left: area.getBoundingClientRect().left, | ||
bottom: area.getBoundingClientRect().bottom, | ||
right: area.getBoundingClientRect().right, | ||
width: area.offsetWidth, | ||
height: area.offsetHeight | ||
}; | ||
} | ||
/** | ||
* Updates the node style left, top, width, | ||
* height values accordingly. | ||
* @param {node} node | ||
* @param {object} pos { x, y, w, h } | ||
* @return {node} | ||
*/ | ||
function _updatePos( node, pos ) { | ||
node.style.left = pos.x + 'px'; | ||
node.style.top = pos.y + 'px'; | ||
node.style.width = pos.w + 'px'; | ||
node.style.height = pos.h + 'px'; | ||
return node; | ||
} | ||
// Return | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
var DS = { | ||
_updatePos: _updatePos, | ||
isElement: isElement, | ||
@@ -342,5 +561,5 @@ toArray: toArray, | ||
reset: reset, | ||
checkIfInside: checkIfInside, | ||
move: move, | ||
startUp: startUp, | ||
checkIfInsideSelection: checkIfInsideSelection, | ||
handleMove: handleMove, | ||
_startUp: _startUp, | ||
start: start, | ||
@@ -355,3 +574,6 @@ getSelection: getSelection, | ||
// make exportable | ||
// Make exportable | ||
////////////////////////////////////////////////////////////////////////////////////// | ||
if (typeof module !== 'undefined' && module !== null) { | ||
@@ -363,2 +585,3 @@ module.exports = dragSelect; | ||
// Relevant Discussions: | ||
@@ -365,0 +588,0 @@ // https://stackoverflow.com/questions/11979586/select-and-drag-to-get-selected-elements |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
1240102
18
4278
1