diff --git a/event-helpers.js b/event-helpers.js index 4ddccf3..18c9fad 100644 --- a/event-helpers.js +++ b/event-helpers.js @@ -7,7 +7,47 @@ */ export const EventHelpers = function(superClass) { return class extends superClass { - listen(node, eventName, cbName) { + listen(node, eventName, cbName, options) { + const boundListener = this.__registerListener(cbName, node, eventName); + + if (eventName === 'track') { + this._track(node, boundListener); + return; + } + + node.addEventListener(eventName, boundListener, options); + } + + /** + * Remove an event listener bound to the context of the superClass. + * + * @param {HTMLElement} node Element to attach the event listener to. + * @param {String} eventName Name of the event. + * @param {String} cbName Name of the handler. + */ + unlisten(node, eventName, cbName, options) { + this.__boundEventListeners = this.__boundEventListeners || new WeakMap(); + const listeners = this.__boundEventListeners.get(node); + const eventKey = `${eventName}_${cbName}`; + + if (listeners && typeof listeners[eventKey]) { + node.removeEventListener(eventName, listeners[eventKey], options); + listeners[eventKey] = null; + } + } + + once(node, eventName, cbName, options) { + const wrappedCbName = `__onceCb__${cbName}`; + + this[wrappedCbName] = (...args) => { + this[cbName](...args); + this.unlisten(node, eventName, wrappedCbName, options); + }; + + this.listen(node, eventName, wrappedCbName, options); + } + + __registerListener(cbName, node, eventName) { this.__boundEventListeners = this.__boundEventListeners || new WeakMap(); const boundListener = this[cbName].bind(this); const eventKey = `${eventName}_${cbName}`; @@ -22,36 +62,49 @@ export const EventHelpers = function(superClass) { listeners[eventKey] = boundListener; this.__boundEventListeners.set(node, listeners); - node.addEventListener(eventName, boundListener); + + return boundListener; } - /** - * Remove an event listener bound to the context of the superClass. - * - * @param {HTMLElement} node Element to attach the event listener to. - * @param {String} eventName Name of the event. - * @param {String} cbName Name of the handler. - */ - unlisten(node, eventName, cbName) { - this.__boundEventListeners = this.__boundEventListeners || new WeakMap(); - const listeners = this.__boundEventListeners.get(node); - const eventKey = `${eventName}_${cbName}`; + _track(node, boundListener) { + this._boundTrackingListener = boundListener; + this.listen(node, 'mousedown', '_trackMouseDown'); + } - if (listeners && typeof listeners[eventKey]) { - node.removeEventListener(eventName, listeners[eventKey]); - listeners[eventKey] = null; + _trackMouseDown(e) { + this._trackInfo = { + originX: e.clientX, + originY: e.clientY, + started: false + }; + + this.listen(window, 'mousemove', '_trackMouseMove', true); + this.once(window, 'mouseup', '_trackMouseUp'); + } + + _trackMouseMove(e) { + if (!this._trackInfo.started && trackHasMovedEnough(this._trackInfo, e.clientX, e.clientY)) { + this._trackInfo.started = true; + this._boundTrackingListener(new CustomEvent('track', { detail: { state: 'start', dx: e.clientX - this._trackInfo.originX, dy: e.clientY - this._trackInfo.originY }, bubbles: true, composed: true })); + } + + if (this._trackInfo.started) { + this._boundTrackingListener(new CustomEvent('track', { detail: { state: 'track', dx: e.clientX - this._trackInfo.originX, dy: e.clientY - this._trackInfo.originY }, bubbles: true, composed: true })); } } - once(node, eventName, cbName) { - const wrappedCbName = `__onceCb__${cbName}`; - - this[wrappedCbName] = (...args) => { - this[cbName](...args); - this.unlisten(node, eventName, wrappedCbName); - }; - - this.listen(node, eventName, wrappedCbName); + _trackMouseUp(e) { + this._boundTrackingListener(new CustomEvent('track', { detail: { state: 'end', dx: e.clientX - this._trackInfo.originX, dy: e.clientY - this._trackInfo.originY }, bubbles: true, composed: true })); + this.unlisten(window, 'mousemove', '_trackMouseMove', true); + this._trackInfo = null; } } -} \ No newline at end of file +} + +const TRACK_DISTANCE = 5; + +function trackHasMovedEnough(info, x, y) { + let dx = Math.abs(info.originX - x); + let dy = Math.abs(info.originY - y); + return (dx >= TRACK_DISTANCE || dy >= TRACK_DISTANCE); +} diff --git a/sort.js b/sort.js new file mode 100644 index 0000000..29661e5 --- /dev/null +++ b/sort.js @@ -0,0 +1,104 @@ +import { reach } from './reach.js'; + +/** + * Sort collection asc by some field. + * + * @param {String} field Field to sort by. + * @param {Array} collection Collection to sort. + * @return {Array} The sorted collection. + */ +const sortAsc = (field, collection) => { + return collection.sort(function(a, b) { + let s1 = reach(field, a); + let s2 = reach(field, b); + + if (typeof s1 === 'string') { + s1 = s1.toLowerCase(); + } + + if (typeof s2 === 'string') { + s2 = s2.toLowerCase(); + } + + return naturalCompare(s1, s2); + }.bind(this)); +} + +/** + * Sort collection desc by some field. + * + * @param {String} field Field to sort by. + * @param {Array} collection Collection to sort. + * @return {Array} The sorted collection. + */ +const sortDesc = (field, collection) => { + return collection.sort(function(a, b) { + let s1 = reach(field, a); + let s2 = reach(field, b); + + if (typeof s1 === 'string') { + s1 = s1.toLowerCase(); + } + + if (typeof s2 === 'string') { + s2 = s2.toLowerCase(); + } + + return naturalCompare(s2, s1); + }.bind(this)); +} + +/** +* Sort strings naturally. +* +* @version 1.4.0 +* @date 2015-10-26 +* @stability 3 - Stable +* @author Lauri Rooden (https://github.com/litejs/natural-compare-lite) +* @license MIT License +* +* @param {String} a +* @param {String} b +* @return {Number} -1, 1 or 0. +*/ +const naturalCompare = (a, b) => { + let i; + let codeA; + let codeB = 1; + let posA = 0; + let posB = 0; + const alphabet = String.alphabet; + + function getCode(str, pos, code) { + if (code) { + for (i = pos; code = getCode(str, i), code < 76 && code > 65;) ++i; + return Number(str.slice(pos - 1, i)); + } + code = alphabet && alphabet.indexOf(str.charAt(pos)); + return code > -1 ? code + 76 : ((code = str.charCodeAt(pos) || 0), code < 45 || code > 127) ? code + : code < 46 ? 65 // - + : code < 48 ? code - 1 + : code < 58 ? code + 18 // 0-9 + : code < 65 ? code - 11 + : code < 91 ? code + 11 // A-Z + : code < 97 ? code - 37 + : code < 123 ? code + 5 // a-z + : code - 63; + } + + if ((a += '') != (b += '')) for (;codeB;) { + codeA = getCode(a, posA++); + codeB = getCode(b, posB++); + + if (codeA < 76 && codeB < 76 && codeA > 66 && codeB > 66) { + codeA = getCode(a, posA, posA); + codeB = getCode(b, posB, posA = i); + posB = i; + } + + if (codeA != codeB) return (codeA < codeB) ? -1 : 1; + } + return 0; +} + +export { sortAsc, sortDesc, naturalCompare };