UNPKG

@dragon/utils

Version:

Vanilla implementtation of utils functions used in dragon lib

321 lines (233 loc) 7.07 kB
import touchy from '@dragon/touchy' // cross dom event management import classes from '@dragon/dom-classes' /* global global */ let doc = document let docElm = doc.documentElement export default { // domEventManager: touchy, // domClassManager: classes, // getConfig: getConfig, // getImmediateChild: getImmediateChild, // getReference: getReference, // getCoord: getCoord, // getEventHost: getEventHost, // getOffset: getOffset, // getScroll: getScroll, // getElementBehindPoint: getElementBehindPoint, // getRectWidth: getRectWidth, // getRectHeight: getRectHeight, // getParent: getParent, // nextEl: nextEl, // toArray: toArray, // bind: bind, // domIndexOf: domIndexOf, // isInput: isInput, // isEditable: isEditable, // getIndexByElm: getIndexByElm, // ensureArray: ensureArray, } export { touchy as domEventManager, classes as domClassManager, } export function setConfig( parent, config ) { this.config = Object.assign( Object.create( parent.config ), config ) } export function getConfig( prop ) { prop = this.config[ prop ] return typeof prop == 'function' ? prop( this ) : prop } export function getImmediateChild( dropTarget, target ) { let immediate = target while ( immediate !== dropTarget && getParent( immediate ) !== dropTarget ) { immediate = getParent( immediate ) } if ( immediate === docElm ) { return null } return immediate } export function getReference( dropTarget, target, x, y, direction, abs ) { let horizontal = direction === 'horizontal' if ( abs ) { x = x - getScroll( 'scrollLeft', 'pageXOffset' ) y = y - getScroll( 'scrollTop', 'pageYOffset' ) } return target !== dropTarget ? inside() : outside() // reference function outside() { // slower, but able to figure out any position let len = dropTarget.children.length, i, el, rect for ( i = 0; i < len; i++ ) { el = dropTarget.children[ i ] rect = el.getBoundingClientRect() if ( horizontal && (rect.left + rect.width / 2) > x ) { return el } if ( !horizontal && (rect.top + rect.height / 2) > y ) { return el } } return null } function inside() { // faster, but only available if dropped inside a child element let rect = target.getBoundingClientRect() if ( horizontal ) { return resolve( x > rect.left + getRectWidth( rect ) / 2 ) } return resolve( y > rect.top + getRectHeight( rect ) / 2 ) } function resolve( after ) { return after ? nextEl( target ) : target } } export function getCoord( coord, e ) { let host = getEventHost( e ) let missMap = { pageX: 'clientX', // IE8 pageY: 'clientY' // IE8 } if ( coord in missMap && !(coord in host) && missMap[ coord ] in host ) { coord = missMap[ coord ] } return host[ coord ] } export function getEventHost( e ) { // on touchend event, we have to use `e.changedTouches` // see http://stackoverflow.com/questions/7192563/touchend-event-properties // see github.com/bevacqua/dragula/issues/34 if ( e.targetTouches && e.targetTouches.length ) { return e.targetTouches[ 0 ] } if ( e.changedTouches && e.changedTouches.length ) { return e.changedTouches[ 0 ] } return e } export function whichMouseButton (e) { // if (e.touches !== void 0) { return e.touches.length } if (e.touches !== void 0) { return 1 } // accept all touches if (e.which !== void 0 && e.which !== 0) { return e.which } // see github.com/bevacqua/dragula/issues/261 if (e.buttons !== void 0) { return e.buttons } let button = e.button if (button !== void 0) { // see github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/event.js#L573-L575 return button & 1 ? 1 : button & 2 ? 3 : (button & 4 ? 2 : 0) } } // get offset of element from top left corner of document export function getOffset( el, size ) { let rect = el.getBoundingClientRect() let result = { left: rect.left + getScroll( 'scrollLeft', 'pageXOffset' ), top: rect.top + getScroll( 'scrollTop', 'pageYOffset' ) } if ( size ) { result.width = getRectWidth( rect ) result.height = getRectHeight( rect ) } return result } export function getScroll( scrollProp, offsetProp ) { if ( typeof global[ offsetProp ] !== 'undefined' ) { return global[ offsetProp ] } if ( docElm.clientHeight ) { return docElm[ scrollProp ] } return doc.body[ scrollProp ] } export function getElementBehindPoint( elmToHide, x, y, abs ) { let state = elmToHide.className let el // hide elmToHide elmToHide.className += ' dragon-hide' // look at the position el = doc.elementFromPoint( abs ? x - getScroll( 'scrollLeft', 'pageXOffset' ) : x, abs ? y - getScroll( 'scrollTop', 'pageYOffset' ) : y ) // show elmToHide back elmToHide.className = state return el } export function getRectWidth( rect ) { return rect.width || (rect.right - rect.left) } export function getRectHeight( rect ) { return rect.height || (rect.bottom - rect.top) } export function getParent( el ) { return el.parentNode === doc ? null : el.parentNode } export function nextEl( el ) { return el.nextElementSibling || manually() function manually() { let sibling = el do { sibling = sibling.nextSibling } while ( sibling && sibling.nodeType !== 1 ) return sibling } } export function toArray( obj ) { return [].slice.call( obj ) } export function ensureArray( it ) { if ( Array.isArray( it ) ) return it else if ( it.length && it.length != 0 ) return toArray( it ) else return [ it ] } export function bind( obj, methodName ) { let bindedName = '_binded_' + methodName if ( !obj[ bindedName ] ) obj[ bindedName ] = function () { return obj[ methodName ].apply( obj, arguments ) } return obj[ bindedName ] } export function domIndexOf( parent, child ) { // Possible problems with IE8- ? https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/children#Browser_compatibility return Array.prototype.indexOf.call( parent.children, child ) } export function isInput( el ) { return el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT' || isEditable( el ) } export function isEditable( el ) { if ( !el ) { return false } // no parents were editable if ( el.contentEditable === 'false' ) { return false } // stop the lookup if ( el.contentEditable === 'true' ) { return true } // found a contentEditable element in the chain return isEditable( getParent( el ) ) // contentEditable is set to 'inherit' } export function getIndexByElm( sourceArray, elm ) { let len = sourceArray.length for ( let i = 0; i < len; i++ ) { if ( sourceArray[ i ].elm == elm ) return i } return -1 } export function hierarchySafe( fn, success, fail ) { try { // dom edit fn to protect fn() if ( success ) success() } catch( e ){ // console.dir(e) if ( e.name !== 'HierarchyRequestError') // fixing: Uncaught DOMException: Failed to execute 'insertBefore' on 'Node': The new child element contains the parent. console.error( e ) // eslint-disable-line no-console if ( fail ) fail() } }