UNPKG

ts-query

Version:

its simple library for dom control.

561 lines (489 loc) 17.9 kB
(function (W, D) { $.addPlugin(function ($, E) { "use strict"; var pr = "prototype", p = $[pr], ep = E[pr], A = Array, ID = "_ID_"; /** * Функция наследования * @param child * @param parent */ var extend = function (child, parent) { var P = function () { }; P.prototype = parent.prototype; child.prototype = new P(); child.prototype.constructor = child; }; ///IMPORT:src/events/BaseEvent.js ///IMPORT:src/events/Touch.js ///IMPORT:src/events/Move.js ///IMPORT:src/events/Swipe.js ///IMPORT:src/events/Tap.js ///IMPORT:src/events/MultiTouch.js E[pr] = ep; /** * Подписываемся на событие * @param {string} eventName * @param {function} handler * @param {*} [context] */ ep.on = function (eventName, handler, context) { if (!this._events) this._events = {}; if (!this._ID) { this._ID = $._add(this); this._node[ID] = this._ID; } if (!this._events[eventName]) this._events[eventName] = []; var event_obj = { event: eventName, handler: handler, context: context || new $(this) }; this._events[eventName].push(event_obj); if (eventName.indexOf("User") != 0) { var $handler = function (event) { return handler.call(context || this, event); }; event_obj.originHandler = $handler; this._node.addEventListener(eventName, $handler, false); } else { if (!this._hasCustom) { this._addCustom(); } } }; /** * Отписываемся от события * @param {string} [eventName] * @param {function} [handler] * @returns {ep} */ ep.off = function (eventName, handler) { if (!this._events) return this; $.forEach(this._events, function (events, $eventName) { if (!eventName || eventName == $eventName) { events = events.filter(function (eventObject) { if (!handler || handler == eventObject.handler) { this._node.removeEventListener($eventName, eventObject.originHandler || eventObject.handler); return false; } else { return true; } }, this); } if (events.length == 0) { delete this._events[$eventName]; } }, this); if (this._hasCustom && !$.some(this._events, function (events, eventName) { return eventName.indexOf("User") == 0; })) { this._removeCustom(); } if (!Object.keys(this._events).length) { $._remove(this._ID); this._node[ID] = null; this._ID = null; } }; /** * Запускаем событие * @param {string} eventName * @param {Array} [args] * @returns {ep} */ ep.trigger = function (eventName, args) { if (!this._events) return this; if (!eventName) { console.log("Неверные параметры!"); return this; } if (!args) args = []; if (!A.isArray(args)) args = [args]; E._splitEvents(eventName).forEach(function (event, index) { if (this._events[event]) { this._startUserEvent(event, E._currentArgs(eventName, index, args.slice())); } }, this); return this; }; /** * Не забываем снять все обрботчики с убиваемого элемента * @private */ ep._remove = function () { this.off(); }; /** * Запускаем событие (для trigger) * @param {string} event * @param {Array} localArgs * @private */ ep._startUserEvent = function (event, localArgs) { this._events[event].forEach(function (eventObject) { eventObject.handler.apply(eventObject.context, localArgs); }); }; /** * Добавляем приватные обработчики для создания тач событий * @private */ ep._addCustom = function () { this._hasCustom = true; var $this = this; /** * @param {IqueryEvent} e * @private */ this._start = function (e) { e.preventDefault(); $this._onStart(e); }; this._node.addEventListener($.events.start, this._start, false); }; /** * Обрабатываем прикосновение * @param {IqueryEvent} e * @private */ ep._onStart = function (e) { var $this = this; E._fingers = E._fingers.concat(E._getTouches(e, true)); this._createTouchEvent(e, E._fingers.slice(), "start"); /** * Функция для подписки на движение пальца * @param {IqueryEvent} e */ var move = function (e) { e.preventDefault(); $this._onMove(e); }; /** * Функция для подписки на отрыв пальца * @param {IqueryEvent} e */ var end = function (e) { e.preventDefault(); $this._onEnd(e); if (!E._fingers.length) { D.removeEventListener($.events.move, move); D.removeEventListener($.events.end, end); $this.__hasEvents = false; } }; if (!this.__hasEvents) { D.addEventListener($.events.move, move, false); D.addEventListener($.events.end, end, false); this.__hasEvents = true; } }; /** * Обрабатываем движение пальца * @param {IqueryEvent} e * @private */ ep._onMove = function (e) { var fingers = E._getFingers(e); if (fingers.length == 1) { this._createMoveEvent(e, fingers); } else { this._createMultitouch(e, fingers); } }; /** * Обрабатываем отрыв пальца * @param {IqueryEvent} e * @private */ ep._onEnd = function (e) { var touches = E._getTouches(e, true); var fingers = E._getFingers(e, touches, true); this._createSwipe(e, fingers); this._createTapEvent(e, fingers); this._createTouchEvent(e, fingers, "end"); E._clearEvents(fingers); E._fingers = E._fingers.filter(function (finger) { return !$.some(touches, E._getFingerById, E, finger.identifier); }); }; /** * Создаем событие прикосновения * @param {IqueryEvent} e * @param {Array} fingers массив пальцев * @param {string} state * @private */ ep._createTouchEvent = function (e, fingers, state) { fingers.forEach(function (finger) { this._startEvent(new Touch(e, finger, state), finger); }, this); }; /** * Создаем событие движения * @param {IqueryEvent} e * @param {Array} fingers * @private */ ep._createMoveEvent = function (e, fingers) { var finger = fingers[0]; this._startEvent(new Move(e, finger, E._Event.fingers[finger.identifier]), finger); }; /** * Создаем мультитач событие * @param {IqueryEvent} e * @param {Array} fingers * @private */ ep._createMultitouch = function (e, fingers) { fingers = fingers.sort(function (a, b) { return a.time - b.time }); this._startEvent(new MultiTouch(e, fingers[0], fingers[1], E._Event.multitouch)); }; /** * Создаем тап событие * @param {IqueryEvent} e * @param {Array} fingers * @private */ ep._createTapEvent = function (e, fingers) { fingers.forEach(function (finger) { if (E._isTap(finger)) { this._startEvent(new Tap(e, finger), finger); } }, this); }; /** * Создаем свайп событие * @param {IqueryEvent} e * @param {Array} fingers * @private */ ep._createSwipe = function (e, fingers) { fingers.forEach(function (finger) { if (E._isSwipe(finger)) { this._startEvent(new Swipe(e, finger, E._Event.fingers[finger.identifier]), finger); } }, this); }; /** * Отписываемся от приватных событий * @private */ ep._removeCustom = function () { this._hasCustom = false; this._node.removeEventListener($.events.start, this._start); }; /** * Запускаем событие * @param EVENT * @param [finger] * @private */ ep._startEvent = function (EVENT, finger) { if (!finger) { E._Event.multitouch = EVENT; } else { if (!E._Event.fingers[finger.identifier]) E._Event.fingers[finger.identifier] = {}; E._Event.fingers[finger.identifier][EVENT.type] = EVENT; } this.trigger(EVENT.getEventString(), [EVENT.getClone()]); }; /** * Проверяем надо ли запускать тап для этого пальца * @param finger * @returns {boolean} * @private */ E._isTap = function (finger) { if (!E._Event.fingers[finger.identifier]) return false; if (!E._Event.fingers[finger.identifier].touch) return E._message("Нет тача!", "warn"); if (E._Event.fingers[finger.identifier].move && E._Event.fingers[finger.identifier].move.distance > 15) return false; return (Date.now() - E._Event.fingers[finger.identifier].touch.time < E._tapTime); }; /** * Проверяем надо ли запускать свайп для этого пальца * @param finger * @returns {boolean} * @private */ E._isSwipe = function (finger) { return ((E._Event.fingers[finger.identifier]) && (E._Event.fingers[finger.identifier].move) && (E._Event.fingers[finger.identifier].move.distance > 30) && (E._Event.fingers[finger.identifier].move.hasSwipeSpeed) && (Date.now() - E._Event.fingers[finger.identifier].touch.time < E._tapTime)); }; /** * Получаем список пальцев из события * @param {IqueryEvent} e * @param {boolean} [startOrEnd] * @returns {Array} * @private */ E._getTouches = function (e, startOrEnd) { var result = []; if ($.isPointer) { if (!e.pointerId) return E._message("Нет идентификатора пальца!"); result.push(E._getFingerData(e)); } else if (e.touches) { var touches = startOrEnd ? e.changedTouches : e.touches; result = A[pr].map.call(touches, function (touch) { return E._getFingerData(touch); }); } else { result.push(E._getFingerData(e)); } return result; }; /** * Получаем список пальцев * @param {IqueryEvent} e * @param {Array} [fingers] * @param {boolean} [isEnd] * @returns {Array} * @private */ E._getFingers = function (e, fingers, isEnd) { var result; fingers = fingers || E._getTouches(e); if (isEnd) { result = E._fingers.filter(function (finger) { return $.some(fingers, E._getFingerById, E, finger.identifier); }); } E._fingers = E._fingers.map(function (finger) { var fingerData = $.findFirst(result || fingers, E._getFingerById, E, finger.identifier); if (fingerData.id != null) { return E._getFingerData(fingerData.value, finger.time); } else { return finger; } }); return result || E._fingers.slice(); }; /** * Функция для итератора из статики $ (проверяем наличие пальца в E._fingers) * @param finger * @param index * @param id * @returns {boolean} * @private */ E._getFingerById = function (finger, index, id) { return finger.identifier == id; }; /** * Активные пальцы на экране * @type {Array} * @private */ E._fingers = []; /** * Вывадит сообщение в лог * @param {string} message * @param {string} [type] * @param {*} [returns] * @returns {*} * @private */ E._message = function (message, type, returns) { console[type || "error"](message); return returns || false; }; /** * Время на тап * @type {number} * @private */ E._tapTime = 300; E._Event = { multitouch: null, fingers: {} }; /** * Очищаем события * @private */ E._clearEvents = function (fingers) { if (fingers.length == 1) { E._Event.multitouch = null; } fingers.forEach(function (finger) { delete E._Event.fingers[finger.identifier]; }); }; /** * Получаем данные по событию * @param data * @param [time] * @returns {{time: (*|number|Date), identifier: (*|number), pageX: (*|number|Number), pageY: (*|number|Number), target: (*|HTMLElement|string|Node|EventTarget)}} * @private */ E._getFingerData = function (data, time) { return { time: time || data.timeStamp || Date.now(), identifier: data.identifier || data.pointerId || 0, pageX: data.pageX, pageY: data.pageY, target: data.target } }; /** * Рассчитываем аргументы для trigger * @param {string} eventName * @param {number} index * @param {Array} args * @returns {Array} * @private */ E._currentArgs = function (eventName, index, args) { var arr = eventName.split(":"); arr.splice(0, index + 1); return arr.concat(args); }; /** * Получаем список событий для запуска для trigger * @param {string} eventName * @returns {Array} * @private */ E._splitEvents = function (eventName) { var events = eventName.split(":"), result = []; for (var i = 0; i < events.length; i++) { result.push(events.slice(0, i + 1).join(":")); } return result; }; /** * Получаем обёртку над элементом * @param node * @returns {*} * @private */ E._createE = function (node) { return $._getById(node[ID]) || new E(node); }; $._elems = {}; $._add = function (element) { var uuid; while (!(uuid && !(uuid in this._elems))) { uuid = $.guuid(); } this._elems[uuid] = element; return uuid; }; $._remove = function (uuid) { delete this._elems[uuid]; }; $._getById = function (uuid) { return this._elems[uuid] || null; }; ["on", "off", "trigger"].forEach(function (method) { p[method] = function () { return this._toAll(method, A[pr].slice.call(arguments)); } }); }); })(window["$window"] || window, window["$document"] || document);