From 88ffff0853897ce442a34788f9b589ff2996295f Mon Sep 17 00:00:00 2001 From: easylogic Date: Sun, 2 Aug 2020 17:53:48 +0900 Subject: [PATCH] fixed Dom export --- dist/sapa.esm.js | 2090 +++++++++++++------------- dist/sapa.js | 2091 ++++++++++++++------------- package-lock.json | 18 + package.json | 3 +- src/util/App.js | 3 +- src/util/Dom.js | 2 +- src/util/EventMachine.js | 2 +- src/util/handler/DomEventHandler.js | 3 +- src/util/index.js | 1 - 9 files changed, 2116 insertions(+), 2097 deletions(-) diff --git a/dist/sapa.esm.js b/dist/sapa.esm.js index 7d4a719..59872b1 100644 --- a/dist/sapa.esm.js +++ b/dist/sapa.esm.js @@ -153,1421 +153,1421 @@ function uuidShort(){ return uuid; } -const setBooleanProp = (el, name, value) => { - if (value) { - el.setAttribute(name, name); - el[name] = value; - } else { - el.removeAttribute(name); - el[name] = value; - } - }; - -const setProp = (el, name, value) => { - if (typeof value === 'boolean') { - setBooleanProp(el, name, value); - } else { - el.setAttribute(name, value); - } -}; - - -const removeBooleanProp = (node, name) => { - node.removeAttribute(name); - node[name] = false; -}; - -const removeUndefinedProp = (node, name) => { - node.removeAttribute(name); -}; - -const removeProp = (node, name, value) => { - if (typeof value === 'boolean') { - removeBooleanProp(node, name); - } else if (name) { - removeUndefinedProp(node, name); - } -}; - - -const updateProp = (node, name, newValue, oldValue) => { - if (!newValue) { - removeProp(node, name, oldValue); - } else if (!oldValue || newValue !== oldValue) { - setProp(node, name, newValue); - } - }; - - -const updateProps = (node, newProps = {}, oldProps = {}) => { - const props = {...newProps,...oldProps}; - - Object.keys(props).forEach((name) => { - updateProp(node, name, newProps[name], oldProps[name]); - }); +const makeEventChecker = (value, split = CHECK_SAPARATOR) => { + return ` ${split} ${value}`; }; -function changed(node1, node2) { - return ( - (node1.nodeType === Node.TEXT_NODE && node1 !== node2) - || node1.nodeName !== node2.nodeName - ) -} - -function getProps (attributes) { - var results = {}; - for(var t of attributes) { - results[t.name] = t.value; - } - - return results; - -} - -function updateElement (parentElement, oldEl, newEl, i) { - if (!oldEl) { - parentElement.appendChild(newEl.cloneNode(true)); - } else if (!newEl) { - parentElement.removeChild(oldEl); - } else if (changed(newEl, oldEl)) { - parentElement.replaceChild(newEl.cloneNode(true), oldEl); - } else if (newEl.nodeType !== Node.TEXT_NODE) { - updateProps(oldEl, getProps(newEl.attributes), getProps(oldEl.attributes)); // added - var oldChildren = children(oldEl); - var newChildren = children(newEl); - var max = Math.max(oldChildren.length, newChildren.length); - - for (var i = 0; i < max; i++) { - updateElement(oldEl, oldChildren[i], newChildren[i], i); - } - } +// event name regular expression +const CHECK_DOM_EVENT_PATTERN = /^dom (.*)/gi; +const CHECK_LOAD_PATTERN = /^load (.*)/gi; +const CHECK_BIND_PATTERN = /^bind (.*)/gi; -} -const children = (el) => { - var element = el.firstChild; - if (!element) { - return [] - } +const NAME_SAPARATOR = ":"; +const CHECK_SAPARATOR = "|"; +const DOM_EVENT_SAPARATOR = "dom "; +const LOAD_SAPARATOR = "load "; +const BIND_SAPARATOR = "bind "; - var results = []; +const SAPARATOR = ' '; - do { - results.push(element); - element = element.nextSibling; - } while (element); +const refManager = {}; - return results; +const DOM_EVENT_MAKE = (...keys) => { + var key = keys.join(NAME_SAPARATOR); + return (...args) => { + return DOM_EVENT_SAPARATOR + [key, ...args].join(SAPARATOR); + }; }; +const CUSTOM = DOM_EVENT_MAKE; +const CLICK = DOM_EVENT_MAKE("click"); +const DOUBLECLICK = DOM_EVENT_MAKE("dblclick"); +const MOUSEDOWN = DOM_EVENT_MAKE("mousedown"); +const MOUSEUP = DOM_EVENT_MAKE("mouseup"); +const MOUSEMOVE = DOM_EVENT_MAKE("mousemove"); +const MOUSEOVER = DOM_EVENT_MAKE("mouseover"); +const MOUSEOUT = DOM_EVENT_MAKE("mouseout"); +const MOUSEENTER = DOM_EVENT_MAKE("mouseenter"); +const MOUSELEAVE = DOM_EVENT_MAKE("mouseleave"); +const TOUCHSTART = DOM_EVENT_MAKE("touchstart"); +const TOUCHMOVE = DOM_EVENT_MAKE("touchmove"); +const TOUCHEND = DOM_EVENT_MAKE("touchend"); +const KEYDOWN = DOM_EVENT_MAKE("keydown"); +const KEYUP = DOM_EVENT_MAKE("keyup"); +const KEYPRESS = DOM_EVENT_MAKE("keypress"); +const DRAG = DOM_EVENT_MAKE("drag"); +const DRAGSTART = DOM_EVENT_MAKE("dragstart"); +const DROP = DOM_EVENT_MAKE("drop"); +const DRAGOVER = DOM_EVENT_MAKE("dragover"); +const DRAGENTER = DOM_EVENT_MAKE("dragenter"); +const DRAGLEAVE = DOM_EVENT_MAKE("dragleave"); +const DRAGEXIT = DOM_EVENT_MAKE("dragexit"); +const DRAGOUT = DOM_EVENT_MAKE("dragout"); +const DRAGEND = DOM_EVENT_MAKE("dragend"); +const CONTEXTMENU = DOM_EVENT_MAKE("contextmenu"); +const CHANGE = DOM_EVENT_MAKE("change"); +const INPUT = DOM_EVENT_MAKE("input"); +const FOCUS = DOM_EVENT_MAKE("focus"); +const FOCUSIN = DOM_EVENT_MAKE("focusin"); +const FOCUSOUT = DOM_EVENT_MAKE("focusout"); +const BLUR = DOM_EVENT_MAKE("blur"); +const PASTE = DOM_EVENT_MAKE("paste"); +const RESIZE = DOM_EVENT_MAKE("resize"); +const SCROLL = DOM_EVENT_MAKE("scroll"); +const SUBMIT = DOM_EVENT_MAKE("submit"); +const POINTERSTART = CUSTOM("pointerdown"); +const POINTERMOVE = CUSTOM("pointermove"); +const POINTEREND = CUSTOM("pointerup"); +const CHANGEINPUT = CUSTOM("change", "input"); +const WHEEL = CUSTOM("wheel", "mousewheel", "DOMMouseScroll"); +const ANIMATIONSTART = DOM_EVENT_MAKE('animationstart'); +const ANIMATIONEND = DOM_EVENT_MAKE('animationend'); +const ANIMATIONITERATION = DOM_EVENT_MAKE('animationiteration'); +const TRANSITIONSTART = DOM_EVENT_MAKE('transitionstart'); +const TRANSITIONEND = DOM_EVENT_MAKE('transitionend'); +const TRANSITIONRUN = DOM_EVENT_MAKE('transitionrun'); +const TRANSITIONCANCEL = DOM_EVENT_MAKE('transitioncancel'); -function DomDiff (A, B) { - - A = A.el || A; - B = B.el || B; - - var childrenA = children(A); - var childrenB = children(B); - - var len = Math.max(childrenA.length, childrenB.length); - for (var i = 0; i < len; i++) { - updateElement(A, childrenA[i], childrenB[i], i); - } -} - -class Dom { - constructor(tag, className, attr) { - if (isNotString(tag)) { - this.el = tag; - } else { - var el = document.createElement(tag); - - if (className) { - el.className = className; - } - - attr = attr || {}; - - for (var k in attr) { - el.setAttribute(k, attr[k]); - } - - this.el = el; - } - } - - static create (tag, className, attr) { - return new Dom(tag, className, attr); - } - - static createByHTML (htmlString) { - var div = Dom.create('div'); - var list = div.html(htmlString).children(); +// Predefined CHECKER +const CHECKER = (value, split = CHECK_SAPARATOR) => { + return makeEventChecker(value, split); +}; - if (list.length) { - return Dom.create(list[0].el); - } +const AFTER = (value, split = CHECK_SAPARATOR) => { + return makeEventChecker(`after(${value})`, split); +}; - return null; - } +const BEFORE = (value, split = CHECK_SAPARATOR) => { + return makeEventChecker(`before(${value})`, split); +}; - static createFragment (htmlString) { - var div = Dom.create('div'); - return Dom.create(div.html(htmlString).createChildrenFragment()) - } +const IF = CHECKER; +const KEY = CHECKER; - static getScrollTop() { - return Math.max( - window.pageYOffset, - document.documentElement.scrollTop, - document.body.scrollTop - ); - } +const ARROW_UP = CHECKER('ArrowUp'); +const ARROW_DOWN = CHECKER('ArrowDown'); +const ARROW_LEFT = CHECKER('ArrowLeft'); +const ARROW_RIGHT = CHECKER('ArrowRight'); +const ENTER = CHECKER('Enter'); +const SPACE = CHECKER('Space'); +const ESCAPE = CHECKER('Escape'); - static getScrollLeft() { - return Math.max( - window.pageXOffset, - document.documentElement.scrollLeft, - document.body.scrollLeft - ); - } +const ALT = CHECKER("isAltKey"); +const SHIFT = CHECKER("isShiftKey"); +const META = CHECKER("isMetaKey"); +const CONTROL = CHECKER("isCtrlKey"); +const SELF = CHECKER("self"); - static parse(html) { - var parser = DOMParser(); - return parser.parseFromString(html, "text/htmll"); - } +const FIT = CHECKER("fit"); +const PASSIVE = CHECKER("passive"); +const VDOM = CHECKER('vdom'); - static body () { - return Dom.create(document.body) - } +// event config method +const DEBOUNCE = (t = 100) => { + return CHECKER(`debounce(${t})`); +}; - isFragment () { - return this.el.nodeType === 11; - } +const D1000 = DEBOUNCE(1000); - setAttr (obj) { - if (this.isFragment()) return; - Object.keys(obj).forEach(key => { - this.attr(key, obj[key]); - }); - return this; - } +const THROTTLE = (t = 100) => { + return CHECKER(`throttle(${t})`); +}; - attr(key, value) { - if (this.isFragment()) return; - if (arguments.length == 1) { - if (this.el.getAttribute) { - return this.el.getAttribute(key); - } else { - return undefined; - } +const CAPTURE = CHECKER("capture()"); +// event config method - } +// before method - this.el.setAttribute(key, value); +// after method +const MOVE = (method = "move") => { + return AFTER(`bodyMouseMove ${method}`); +}; +const END = (method = "end") => { + return AFTER(`bodyMouseUp ${method}`); +}; - return this; - } +const PREVENT = AFTER(`preventDefault`); +const STOP = AFTER(`stopPropagation`); - attrKeyValue(keyField) { - if (this.isFragment()) return {}; - return { - [this.el.getAttribute(keyField)]: this.val() - } - } +// Predefined LOADER +const LOAD = (value = "$el") => { + return LOAD_SAPARATOR + value; +}; - attrs(...args) { - if (this.isFragment()) return []; - return args.map(key => { - return this.el.getAttribute(key); - }); - } +const createRef = value => { + if (value === '') return ''; - styles(...args) { - return args.map(key => { - return this.el.style[key]; - }); - } + var id = uuid(); + refManager[id] = value; - removeAttr(key) { - this.el.removeAttribute(key); + return id; +}; - return this; - } +const getRef = id => { + return refManager[id] || ''; +}; - removeStyle(key) { - this.el.style.removeProperty(key); - return this; - } +const BIND_CHECK_FUNCTION = field => { + return function() { + return this.prevState[field] != this.state[field]; + }; +}; - is(checkElement) { - return this.el === (checkElement.el || checkElement); - } +const BIND_CHECK_DEFAULT_FUNCTION = () => { + return true; +}; - isTag(tag) { - return this.el.tagName.toLowerCase() === tag.toLowerCase() - } +const BIND = (value = "$el", checkFieldOrCallback = '') => { + return ( + BIND_SAPARATOR + value + ( + checkFieldOrCallback ? CHECK_SAPARATOR + createRef(checkFieldOrCallback) : '' + ) + ); +}; - closest(cls) { - var temp = this; - var checkCls = false; +var Event = { + addEvent(dom, eventName, callback, useCapture = false) { + if (dom) { + dom.addEventListener(eventName, callback, useCapture); + } + }, - while (!(checkCls = temp.hasClass(cls))) { - if (temp.el.parentNode) { - temp = Dom.create(temp.el.parentNode); - } else { - return null; - } + removeEvent(dom, eventName, callback) { + if (dom) { + dom.removeEventListener(eventName, callback); } + }, - if (checkCls) { - return temp; + pos(e) { + if (e.touches && e.touches[0]) { + return e.touches[0]; } - return null; + return e; + }, + + posXY(e) { + var pos = this.pos(e); + return { + x: pos.pageX, + y: pos.pageY + }; } +}; - parent() { - return Dom.create(this.el.parentNode); +class BaseStore { + constructor(opt = {}) { + this.cachedCallback = {}; + this.callbacks = {}; + this.commandes = []; } - hasParent () { - return !!this.el.parentNode + getCallbacks(event) { + if (!this.callbacks[event]) { + this.callbacks[event] = []; + } + + return this.callbacks[event] } - removeClass(...args) { - this.el.classList.remove(...args); - return this; + setCallbacks(event, list = []) { + this.callbacks[event] = list; } - hasClass(cls) { - if (!this.el.classList) return false; - return this.el.classList.contains(cls); + on(event, originalCallback, context, delay = 0) { + var callback = delay > 0 ? debounce(originalCallback, delay) : originalCallback; + + this.getCallbacks(event).push({ event, callback, context, originalCallback }); } - addClass(...args) { - this.el.classList.add(...args); + off(event, originalCallback) { - return this; + if (arguments.length == 1) { + this.setCallbacks(event); + } else if (arguments.length == 2) { + this.setCallbacks(event, this.getCallbacks(event).filter(f => { + return f.originalCallback !== originalCallback + })); + } } - onlyOneClass(cls) { - var parent = this.parent(); + offAll (context) { - parent.children().forEach(it => { - it.removeClass(cls); + Object.keys(this.callbacks).forEach(event => { + this.setCallbacks(event, this.getCallbacks(event).filter(f => { + return f.context !== context; + })); }); + } - this.addClass(cls); + getCachedCallbacks (event) { + return this.getCallbacks(event); } - toggleClass(cls, isForce) { - this.el.classList.toggle(cls, isForce); + sendMessage(source, event, $2, $3, $4, $5) { + Promise.resolve().then(() => { + var list = this.getCachedCallbacks(event); + if (list) { + list + .filter(f => f.originalCallback.source !== source) + .forEach(f => { + f.callback($2, $3, $4, $5); + }); + } + + }); } - html(html) { - if (isUndefined(html)) { - return this.el.innerHTML; - } + triggerMessage(source, event, $2, $3, $4, $5) { + Promise.resolve().then(() => { + var list = this.getCachedCallbacks(event); + if (list) { + list + .filter(f => f.originalCallback.source === source) + .forEach(f => { + f.callback($2, $3, $4, $5); + }); + } else { + console.warn(event, ' is not valid event'); + } - if (isString(html)) { - this.el.innerHTML = html; - } else { - this.empty().append(html); - } - return this; + }); } - htmlDiff(fragment) { - DomDiff(this, fragment); - } - updateDiff (html, rootElement = 'div') { - DomDiff(this, Dom.create(rootElement).html(html)); - } - updateSVGDiff (html, rootElement = 'div') { - DomDiff(this, Dom.create(rootElement).html(`${html}`).firstChild); - } - find(selector) { - return this.el.querySelector(selector); + emit($1, $2, $3, $4, $5) { + this.sendMessage(this.source, $1, $2, $3, $4, $5); } - $(selector) { - var node = this.find(selector); - return node ? Dom.create(node) : null; + trigger($1, $2, $3, $4, $5) { + this.triggerMessage(this.source, $1, $2, $3, $4, $5); } - findAll(selector) { - return this.el.querySelectorAll(selector); + execute($1, $2, $3, $4, $5){ + this.runCommand(this.source, $1, $2, $3, $4, $5); } +} + +// collectProps 에서 제외될 메소드 목록 +const expectMethod = { + "constructor": true, + "initState": true, + "refresh": true, + "updateData": true, + "constructor": true, + "initializeProperty": true, + "created": true, + "getRealEventName": true, + "initializeStoreEvent": true, + "destoryStoreEvent": true, + "destroy": true, + "emit": true, + "trigger": true, + "on": true, + "off": true, + "setState": true, + "_reload": true, + "render": true, + "initialize": true, + "afterRender": true, + "components": true, + "getRef": true, + "parseTemplate": true, + "childrenIds": true, + "exists": true, + "parseProperty": true, + "parseSourceName": true, + "parseComponent": true, + "clean": true, + "refresh": true, + "template": true, + "eachChildren": true, + "initializeEvent": true, + "destroy": true, + "self": true, + "isAltKey": true, + "isCtrlKey": true, + "isShiftKey": true, + "isMetaKey": true, + "preventDefault": true, + "stopPropagation": true, + "bodyMouseMove": true, + "bodyMouseUp": true, + }; - $$(selector) { - var arr = this.findAll(selector); - return Object.keys(arr).map(key => { - return Dom.create(arr[key]); - }); - } +class BaseHandler { + constructor (context, options = {}) { + this.context = context; + this.options = options; + } - empty() { - while (this.el.firstChild) this.el.removeChild(this.el.firstChild); - return this; - } + // 초기화 설정 + initialize () { - append(el) { - if (isString(el)) { - this.el.appendChild(document.createTextNode(el)); - } else { - this.el.appendChild(el.el || el); } - return this; - } + // html 을 로드 할 때 + load () { - prepend(el) { - if (isString(el)) { - this.el.prepend(document.createTextNode(el)); - } else { - this.el.prepend(el.el || el); } - return this; - } + // 새로고침 할 때 + refresh () { - prependHTML(html) { - var $dom = Dom.create("div").html(html); + } + + // 화면에 그린 이후에 실행 되는 로직들 + render () { - this.prepend($dom.createChildrenFragment()); + } - return $dom; - } + getRef(id) { + return this.context.getRef(id); + } + + splitMethodByKeyword (arr, keyword) { + var filterKeys = arr.filter(code => code.indexOf(`${keyword}(`) > -1); + var filterMaps = filterKeys.map(code => { + var [target, param] = code + .split(`${keyword}(`)[1] + .split(")")[0] + .trim() + .split(" "); + + return { target, param }; + }); + + return [filterKeys, filterMaps]; + } - appendHTML(html) { - var $dom = Dom.create("div").html(html); + /** + * property 수집하기 + * 상위 클래스의 모든 property 를 수집해서 리턴한다. + */ + collectProps() { - this.append($dom.createChildrenFragment()); + var context = this.context; + var p = context.__proto__; + var results = []; + do { + var isObject = p instanceof Object; - return $dom; - } + if (isObject === false) { + break; + } + const names = Object.getOwnPropertyNames(p).filter(name => { + return context && isFunction(context[name]) && !expectMethod[name]; + }); - /** - * create document fragment with children dom - */ - createChildrenFragment() { - const list = this.children(); + results.push(...names); + p = p.__proto__; + } while (p); - var fragment = document.createDocumentFragment(); - list.forEach($el => fragment.appendChild($el.el)); + return results; + } - return fragment; - } - appendTo(target) { - var t = target.el ? target.el : target; - t.appendChild(this.el); + filterProps(pattern) { + return this.collectProps().filter(key => { + return key.match(pattern); + }); + } - return this; - } + run () { - remove() { - if (this.el.parentNode) { - this.el.parentNode.removeChild(this.el); } - return this; - } + destroy() { - removeChild(el) { - this.el.removeChild(el.el || el); - return this; - } + } +} - text(value) { - if (isUndefined(value)) { - return this.el.textContent; +const setBooleanProp = (el, name, value) => { + if (value) { + el.setAttribute(name, name); + el[name] = value; } else { - var tempText = value; - - if (value instanceof Dom) { - tempText = value.text(); - } - - this.el.textContent = tempText; - return this; + el.removeAttribute(name); + el[name] = value; } - } + }; + +const setProp = (el, name, value) => { + if (typeof value === 'boolean') { + setBooleanProp(el, name, value); + } else { + el.setAttribute(name, value); + } +}; - /** - * - * $el.css` - * border-color: yellow; - * ` - * - * @param {*} key - * @param {*} value - */ - css(key, value) { - if (isNotUndefined(key) && isNotUndefined(value)) { - Object.assign(this.el.style, {[key]: value}); - } else if (isNotUndefined(key)) { - if (isString(key)) { - return getComputedStyle(this.el)[key]; - } else { - Object.assign(this.el.style, key); - } +const removeBooleanProp = (node, name) => { + node.removeAttribute(name); + node[name] = false; +}; + +const removeUndefinedProp = (node, name) => { + node.removeAttribute(name); +}; + +const removeProp = (node, name, value) => { + if (typeof value === 'boolean') { + removeBooleanProp(node, name); + } else if (name) { + removeUndefinedProp(node, name); } +}; + - return this; - } +const updateProp = (node, name, newValue, oldValue) => { + if (!newValue) { + removeProp(node, name, oldValue); + } else if (!oldValue || newValue !== oldValue) { + setProp(node, name, newValue); + } + }; - getComputedStyle (...list) { - var css = getComputedStyle(this.el); - var obj = {}; - list.forEach(it => { - obj[it] = css[it]; +const updateProps = (node, newProps = {}, oldProps = {}) => { + const props = {...newProps,...oldProps}; + + Object.keys(props).forEach((name) => { + updateProp(node, name, newProps[name], oldProps[name]); }); +}; - return obj; - } - - getStyleList(...list) { - var style = {}; - - var len = this.el.style.length; - for (var i = 0; i < len; i++) { - var key = this.el.style[i]; +function changed(node1, node2) { + return ( + (node1.nodeType === Node.TEXT_NODE && node1 !== node2) + || node1.nodeName !== node2.nodeName + ) +} - style[key] = this.el.style[key]; +function getProps (attributes) { + var results = {}; + for(var t of attributes) { + results[t.name] = t.value; } - list.forEach(key => { - style[key] = this.css(key); - }); - - return style; - } + return results; + +} - cssText(value) { - if (isUndefined(value)) { - return this.el.style.cssText; - } +function updateElement (parentElement, oldEl, newEl, i) { + if (!oldEl) { + parentElement.appendChild(newEl.cloneNode(true)); + } else if (!newEl) { + parentElement.removeChild(oldEl); + } else if (changed(newEl, oldEl)) { + parentElement.replaceChild(newEl.cloneNode(true), oldEl); + } else if (newEl.nodeType !== Node.TEXT_NODE) { + updateProps(oldEl, getProps(newEl.attributes), getProps(oldEl.attributes)); // added + var oldChildren = children(oldEl); + var newChildren = children(newEl); + var max = Math.max(oldChildren.length, newChildren.length); - if (value != this.el.tempCssText) { - this.el.style.cssText = value; - this.el.tempCssText = value; - } + for (var i = 0; i < max; i++) { + updateElement(oldEl, oldChildren[i], newChildren[i], i); + } + } - return this; - } +} - cssArray(arr) { - if (arr[0]) this.el.style[arr[0]] = arr[1]; - if (arr[2]) this.el.style[arr[2]] = arr[3]; - if (arr[4]) this.el.style[arr[4]] = arr[5]; - if (arr[6]) this.el.style[arr[6]] = arr[7]; - if (arr[8]) this.el.style[arr[8]] = arr[9]; +const children = (el) => { + var element = el.firstChild; - return this; - } + if (!element) { + return [] + } - cssFloat(key) { - return parseFloat(this.css(key)); - } + var results = []; - cssInt(key) { - return parseInt(this.css(key)); - } + do { + results.push(element); + element = element.nextSibling; + } while (element); - px(key, value) { - return this.css(key, value + 'px'); - } + return results; +}; - rect() { - return this.el.getBoundingClientRect(); - } - isSVG () { - return this.el.tagName.toUpperCase() === 'SVG'; - } +function DomDiff (A, B) { - offsetRect() { + A = A.el || A; + B = B.el || B; - if (this.isSVG()) { - const parentBox = this.parent().rect(); - const box = this.rect(); + var childrenA = children(A); + var childrenB = children(B); - return { - x: box.x - parentBox.x, - y: box.y - parentBox.y, - top: box.x - parentBox.x, - left: box.y - parentBox.y, - width: box.width, - height: box.height - } + var len = Math.max(childrenA.length, childrenB.length); + for (var i = 0; i < len; i++) { + updateElement(A, childrenA[i], childrenB[i], i); } +} - return { - x: this.el.offsetLeft, - y: this.el.offsetTop, - top: this.el.offsetTop, - left: this.el.offsetLeft, - width: this.el.offsetWidth, - height: this.el.offsetHeight - }; - } +class Dom { + constructor(tag, className, attr) { + if (isNotString(tag)) { + this.el = tag; + } else { + var el = document.createElement(tag); - offset() { - var rect = this.rect(); + if (className) { + el.className = className; + } - var scrollTop = Dom.getScrollTop(); - var scrollLeft = Dom.getScrollLeft(); + attr = attr || {}; - return { - top: rect.top + scrollTop, - left: rect.left + scrollLeft - }; - } + for (var k in attr) { + el.setAttribute(k, attr[k]); + } - offsetLeft() { - return this.offset().left; + this.el = el; + } } - offsetTop() { - return this.offset().top; + static create (tag, className, attr) { + return new Dom(tag, className, attr); } + + static createByHTML (htmlString) { + var div = Dom.create('div'); + var list = div.html(htmlString).children(); - position() { - if (this.el.style.top) { - return { - top: parseFloat(this.css("top")), - left: parseFloat(this.css("left")) - }; - } else { - return this.rect(); + if (list.length) { + return Dom.create(list[0].el); } + + return null; } - size() { - return [this.width(), this.height()]; + static createFragment (htmlString) { + var div = Dom.create('div'); + return Dom.create(div.html(htmlString).createChildrenFragment()) } - width() { - return this.el.offsetWidth || this.rect().width; + static getScrollTop() { + return Math.max( + window.pageYOffset, + document.documentElement.scrollTop, + document.body.scrollTop + ); } - contentWidth() { - return ( - this.width() - - this.cssFloat("padding-left") - - this.cssFloat("padding-right") + static getScrollLeft() { + return Math.max( + window.pageXOffset, + document.documentElement.scrollLeft, + document.body.scrollLeft ); } - height() { - return this.el.offsetHeight || this.rect().height; + static parse(html) { + var parser = DOMParser(); + return parser.parseFromString(html, "text/htmll"); } - contentHeight() { - return ( - this.height() - - this.cssFloat("padding-top") - - this.cssFloat("padding-bottom") - ); + static body () { + return Dom.create(document.body) } - val(value) { - if (isUndefined(value)) { - return this.el.value; - } else if (isNotUndefined(value)) { - var tempValue = value; + isFragment () { + return this.el.nodeType === 11; + } - if (value instanceof Dom) { - tempValue = value.val(); + setAttr (obj) { + if (this.isFragment()) return; + Object.keys(obj).forEach(key => { + this.attr(key, obj[key]); + }); + return this; + } + + attr(key, value) { + if (this.isFragment()) return; + if (arguments.length == 1) { + if (this.el.getAttribute) { + return this.el.getAttribute(key); + } else { + return undefined; } - this.el.value = tempValue; } + this.el.setAttribute(key, value); + return this; } - matches (selector) { - if (this.el) { - - if (!this.el.matches) return null; - - if (this.el.matches(selector)) { - return this; - } - return this.parent().matches(selector); + attrKeyValue(keyField) { + if (this.isFragment()) return {}; + return { + [this.el.getAttribute(keyField)]: this.val() } + } - return null; -} + attrs(...args) { + if (this.isFragment()) return []; + return args.map(key => { + return this.el.getAttribute(key); + }); + } + + styles(...args) { + return args.map(key => { + return this.el.style[key]; + }); + } + removeAttr(key) { + this.el.removeAttribute(key); - get value() { - return this.el.value; + return this; } - get naturalWidth () { - return this.el.naturalWidth + removeStyle(key) { + this.el.style.removeProperty(key); + return this; } - get naturalHeight () { - return this.el.naturalHeight - } + is(checkElement) { + return this.el === (checkElement.el || checkElement); + } - get files() { - return this.el.files ? [...this.el.files] : []; + isTag(tag) { + return this.el.tagName.toLowerCase() === tag.toLowerCase() } - realVal() { - switch (this.el.nodeType) { - case "INPUT": - var type = this.attr("type"); - if (type == "checkbox" || type == "radio") { - return this.checked(); - } - case "SELECT": - case "TEXTAREA": - return this.el.value; + closest(cls) { + var temp = this; + var checkCls = false; + + while (!(checkCls = temp.hasClass(cls))) { + if (temp.el.parentNode) { + temp = Dom.create(temp.el.parentNode); + } else { + return null; + } } - return ""; + if (checkCls) { + return temp; + } + + return null; } - show(displayType = "block") { - return this.css("display", displayType != "none" ? displayType : "block"); + parent() { + return Dom.create(this.el.parentNode); } - hide() { - return this.css("display", "none"); + hasParent () { + return !!this.el.parentNode } - isHide () { - return this.css("display") == "none" + removeClass(...args) { + this.el.classList.remove(...args); + return this; } - isShow () { - return !this.isHide(); + hasClass(cls) { + if (!this.el.classList) return false; + return this.el.classList.contains(cls); } - toggle(isForce) { - var currentHide = this.isHide(); + addClass(...args) { + this.el.classList.add(...args); - if (arguments.length == 1) { - if (isForce) { - return this.show(); - } else { - return this.hide(); - } - } else { - if (currentHide) { - return this.show(); - } else { - return this.hide(); - } - } + return this; } - get totalLength () { - return this.el.getTotalLength() - } + onlyOneClass(cls) { + var parent = this.parent(); - scrollIntoView () { - this.el.scrollIntoView(); + parent.children().forEach(it => { + it.removeClass(cls); + }); + + this.addClass(cls); } - addScrollLeft (dt) { - this.el.scrollLeft += dt; - return this; + toggleClass(cls, isForce) { + this.el.classList.toggle(cls, isForce); } - addScrollTop (dt) { - this.el.scrollTop += dt; - return this; - } + html(html) { + if (isUndefined(html)) { + return this.el.innerHTML; + } - setScrollTop(scrollTop) { - this.el.scrollTop = scrollTop; - return this; - } + if (isString(html)) { + this.el.innerHTML = html; + } else { + this.empty().append(html); + } - setScrollLeft(scrollLeft) { - this.el.scrollLeft = scrollLeft; return this; } - scrollTop() { - if (this.el === document.body) { - return Dom.getScrollTop(); - } - - return this.el.scrollTop; + htmlDiff(fragment) { + DomDiff(this, fragment); + } + updateDiff (html, rootElement = 'div') { + DomDiff(this, Dom.create(rootElement).html(html)); } - scrollLeft() { - if (this.el === document.body) { - return Dom.getScrollLeft(); - } + updateSVGDiff (html, rootElement = 'div') { - return this.el.scrollLeft; + DomDiff(this, Dom.create(rootElement).html(`${html}`).firstChild); + } + + find(selector) { + return this.el.querySelector(selector); } - scrollHeight() { - return this.el.scrollHeight; + $(selector) { + var node = this.find(selector); + return node ? Dom.create(node) : null; } - scrollWidth() { - return this.el.scrollWidth; - } + findAll(selector) { + return this.el.querySelectorAll(selector); + } - on(eventName, callback, opt1, opt2) { - this.el.addEventListener(eventName, callback, opt1, opt2); + $$(selector) { + var arr = this.findAll(selector); + return Object.keys(arr).map(key => { + return Dom.create(arr[key]); + }); + } + empty() { + while (this.el.firstChild) this.el.removeChild(this.el.firstChild); return this; } - off(eventName, callback) { - this.el.removeEventListener(eventName, callback); + append(el) { + if (isString(el)) { + this.el.appendChild(document.createTextNode(el)); + } else { + this.el.appendChild(el.el || el); + } return this; } - getElement() { - return this.el; + prepend(el) { + if (isString(el)) { + this.el.prepend(document.createTextNode(el)); + } else { + this.el.prepend(el.el || el); + } + + return this; } - createChild(tag, className = '', attrs = {}, css = {}) { - let $element = Dom.create(tag, className, attrs); - $element.css(css); + prependHTML(html) { + var $dom = Dom.create("div").html(html); - this.append($element); + this.prepend($dom.createChildrenFragment()); - return $element; + return $dom; } - get firstChild() { - return Dom.create(this.el.firstElementChild); - } + appendHTML(html) { + var $dom = Dom.create("div").html(html); - children() { - var element = this.el.firstElementChild; + this.append($dom.createChildrenFragment()); - if (!element) { - return []; - } + return $dom; + } - var results = []; + /** + * create document fragment with children dom + */ + createChildrenFragment() { + const list = this.children(); - do { - results.push(Dom.create(element)); - element = element.nextElementSibling; - } while (element); + var fragment = document.createDocumentFragment(); + list.forEach($el => fragment.appendChild($el.el)); - return results; + return fragment; } - childLength() { - return this.el.children.length; - } + appendTo(target) { + var t = target.el ? target.el : target; - replace(newElement) { + t.appendChild(this.el); + return this; + } + + remove() { if (this.el.parentNode) { - this.el.parentNode.replaceChild(newElement.el || newElement, this.el); + this.el.parentNode.removeChild(this.el); } return this; } - replaceChild(oldElement, newElement) { - this.el.replaceChild(newElement.el || newElement, oldElement.el || oldElement); - - return this; - } + removeChild(el) { + this.el.removeChild(el.el || el); + return this; + } - checked(isChecked = false) { - if (arguments.length == 0) { - return !!this.el.checked; - } + text(value) { + if (isUndefined(value)) { + return this.el.textContent; + } else { + var tempText = value; - this.el.checked = !!isChecked; + if (value instanceof Dom) { + tempText = value.text(); + } - return this; + this.el.textContent = tempText; + return this; + } } + /** + * + * $el.css` + * border-color: yellow; + * ` + * + * @param {*} key + * @param {*} value + */ - click () { - this.el.click(); - - return this; - } - - focus() { - this.el.focus(); + css(key, value) { + if (isNotUndefined(key) && isNotUndefined(value)) { + Object.assign(this.el.style, {[key]: value}); + } else if (isNotUndefined(key)) { + if (isString(key)) { + return getComputedStyle(this.el)[key]; + } else { + Object.assign(this.el.style, key); + } + } return this; } - select() { - this.el.select(); - return this; - } + getComputedStyle (...list) { + var css = getComputedStyle(this.el); - blur() { - this.el.blur(); + var obj = {}; + list.forEach(it => { + obj[it] = css[it]; + }); - return this; + return obj; } - select() { - this.el.select(); + getStyleList(...list) { + var style = {}; + + var len = this.el.style.length; + for (var i = 0; i < len; i++) { + var key = this.el.style[i]; + + style[key] = this.el.style[key]; + } - return this; - } + list.forEach(key => { + style[key] = this.css(key); + }); - // canvas functions + return style; + } - context(contextType = "2d") { - if (!this._initContext) { - this._initContext = this.el.getContext(contextType); + cssText(value) { + if (isUndefined(value)) { + return this.el.style.cssText; } - return this._initContext; - } + if (value != this.el.tempCssText) { + this.el.style.cssText = value; + this.el.tempCssText = value; + } - resize({ width, height }) { - // support hi-dpi for retina display - this._initContext = null; - var ctx = this.context(); - var scale = window.devicePixelRatio || 1; + return this; + } - this.px("width", +width); - this.px("height", +height); + cssArray(arr) { + if (arr[0]) this.el.style[arr[0]] = arr[1]; + if (arr[2]) this.el.style[arr[2]] = arr[3]; + if (arr[4]) this.el.style[arr[4]] = arr[5]; + if (arr[6]) this.el.style[arr[6]] = arr[7]; + if (arr[8]) this.el.style[arr[8]] = arr[9]; - this.el.width = width * scale; - this.el.height = height * scale; + return this; + } - ctx.scale(scale, scale); + cssFloat(key) { + return parseFloat(this.css(key)); } - toDataURL (type = 'image/png', quality = 1) { - return this.el.toDataURL(type, quality) + cssInt(key) { + return parseInt(this.css(key)); } - clear() { - this.context().clearRect(0, 0, this.el.width, this.el.height); + px(key, value) { + return this.css(key, value + 'px'); } - update(callback) { - this.clear(); - callback.call(this, this); + rect() { + return this.el.getBoundingClientRect(); } - drawImage (img, dx = 0, dy = 0) { - var ctx = this.context(); - var scale = window.devicePixelRatio || 1; - ctx.drawImage(img, dx, dy, img.width, img.height, 0, 0, this.el.width / scale, this.el.height / scale); + isSVG () { + return this.el.tagName.toUpperCase() === 'SVG'; } - drawOption(option = {}) { - var ctx = this.context(); + offsetRect() { - Object.assign(ctx, option); - } + if (this.isSVG()) { + const parentBox = this.parent().rect(); + const box = this.rect(); - drawLine(x1, y1, x2, y2) { - var ctx = this.context(); + return { + x: box.x - parentBox.x, + y: box.y - parentBox.y, + top: box.x - parentBox.x, + left: box.y - parentBox.y, + width: box.width, + height: box.height + } + } - ctx.beginPath(); - ctx.moveTo(x1, y1); - ctx.lineTo(x2, y2); - ctx.stroke(); - ctx.closePath(); + return { + x: this.el.offsetLeft, + y: this.el.offsetTop, + top: this.el.offsetTop, + left: this.el.offsetLeft, + width: this.el.offsetWidth, + height: this.el.offsetHeight + }; } - drawPath(...path) { - var ctx = this.context(); + offset() { + var rect = this.rect(); - ctx.beginPath(); + var scrollTop = Dom.getScrollTop(); + var scrollLeft = Dom.getScrollLeft(); - path.forEach((p, index) => { - if (index == 0) { - ctx.moveTo(p[0], p[1]); - } else { - ctx.lineTo(p[0], p[1]); - } - }); - ctx.stroke(); - ctx.fill(); - ctx.closePath(); + return { + top: rect.top + scrollTop, + left: rect.left + scrollLeft + }; } - drawCircle(cx, cy, r) { - var ctx = this.context(); - ctx.beginPath(); - ctx.arc(cx, cy, r, 0, 2 * Math.PI); - ctx.stroke(); - ctx.fill(); + offsetLeft() { + return this.offset().left; } - drawText(x, y, text) { - this.context().fillText(text, x, y); + offsetTop() { + return this.offset().top; } - /* utility */ - fullscreen () { - var element = this.el; - - if (element.requestFullscreen) { - element.requestFullscreen(); - } else if (element.wekitRequestFullscreen) { - element.wekitRequestFullscreen(); + position() { + if (this.el.style.top) { + return { + top: parseFloat(this.css("top")), + left: parseFloat(this.css("left")) + }; + } else { + return this.rect(); } } -} -const makeEventChecker = (value, split = CHECK_SAPARATOR) => { - return ` ${split} ${value}`; -}; + size() { + return [this.width(), this.height()]; + } -// event name regular expression -const CHECK_DOM_EVENT_PATTERN = /^dom (.*)/gi; -const CHECK_LOAD_PATTERN = /^load (.*)/gi; -const CHECK_BIND_PATTERN = /^bind (.*)/gi; + width() { + return this.el.offsetWidth || this.rect().width; + } + contentWidth() { + return ( + this.width() - + this.cssFloat("padding-left") - + this.cssFloat("padding-right") + ); + } + height() { + return this.el.offsetHeight || this.rect().height; + } -const NAME_SAPARATOR = ":"; -const CHECK_SAPARATOR = "|"; -const DOM_EVENT_SAPARATOR = "dom "; -const LOAD_SAPARATOR = "load "; -const BIND_SAPARATOR = "bind "; + contentHeight() { + return ( + this.height() - + this.cssFloat("padding-top") - + this.cssFloat("padding-bottom") + ); + } -const SAPARATOR = ' '; + val(value) { + if (isUndefined(value)) { + return this.el.value; + } else if (isNotUndefined(value)) { + var tempValue = value; -const refManager = {}; + if (value instanceof Dom) { + tempValue = value.val(); + } -const DOM_EVENT_MAKE = (...keys) => { - var key = keys.join(NAME_SAPARATOR); - return (...args) => { - return DOM_EVENT_SAPARATOR + [key, ...args].join(SAPARATOR); - }; -}; + this.el.value = tempValue; + } -const CUSTOM = DOM_EVENT_MAKE; -const CLICK = DOM_EVENT_MAKE("click"); -const DOUBLECLICK = DOM_EVENT_MAKE("dblclick"); -const MOUSEDOWN = DOM_EVENT_MAKE("mousedown"); -const MOUSEUP = DOM_EVENT_MAKE("mouseup"); -const MOUSEMOVE = DOM_EVENT_MAKE("mousemove"); -const MOUSEOVER = DOM_EVENT_MAKE("mouseover"); -const MOUSEOUT = DOM_EVENT_MAKE("mouseout"); -const MOUSEENTER = DOM_EVENT_MAKE("mouseenter"); -const MOUSELEAVE = DOM_EVENT_MAKE("mouseleave"); -const TOUCHSTART = DOM_EVENT_MAKE("touchstart"); -const TOUCHMOVE = DOM_EVENT_MAKE("touchmove"); -const TOUCHEND = DOM_EVENT_MAKE("touchend"); -const KEYDOWN = DOM_EVENT_MAKE("keydown"); -const KEYUP = DOM_EVENT_MAKE("keyup"); -const KEYPRESS = DOM_EVENT_MAKE("keypress"); -const DRAG = DOM_EVENT_MAKE("drag"); -const DRAGSTART = DOM_EVENT_MAKE("dragstart"); -const DROP = DOM_EVENT_MAKE("drop"); -const DRAGOVER = DOM_EVENT_MAKE("dragover"); -const DRAGENTER = DOM_EVENT_MAKE("dragenter"); -const DRAGLEAVE = DOM_EVENT_MAKE("dragleave"); -const DRAGEXIT = DOM_EVENT_MAKE("dragexit"); -const DRAGOUT = DOM_EVENT_MAKE("dragout"); -const DRAGEND = DOM_EVENT_MAKE("dragend"); -const CONTEXTMENU = DOM_EVENT_MAKE("contextmenu"); -const CHANGE = DOM_EVENT_MAKE("change"); -const INPUT = DOM_EVENT_MAKE("input"); -const FOCUS = DOM_EVENT_MAKE("focus"); -const FOCUSIN = DOM_EVENT_MAKE("focusin"); -const FOCUSOUT = DOM_EVENT_MAKE("focusout"); -const BLUR = DOM_EVENT_MAKE("blur"); -const PASTE = DOM_EVENT_MAKE("paste"); -const RESIZE = DOM_EVENT_MAKE("resize"); -const SCROLL = DOM_EVENT_MAKE("scroll"); -const SUBMIT = DOM_EVENT_MAKE("submit"); -const POINTERSTART = CUSTOM("pointerdown"); -const POINTERMOVE = CUSTOM("pointermove"); -const POINTEREND = CUSTOM("pointerup"); -const CHANGEINPUT = CUSTOM("change", "input"); -const WHEEL = CUSTOM("wheel", "mousewheel", "DOMMouseScroll"); -const ANIMATIONSTART = DOM_EVENT_MAKE('animationstart'); -const ANIMATIONEND = DOM_EVENT_MAKE('animationend'); -const ANIMATIONITERATION = DOM_EVENT_MAKE('animationiteration'); -const TRANSITIONSTART = DOM_EVENT_MAKE('transitionstart'); -const TRANSITIONEND = DOM_EVENT_MAKE('transitionend'); -const TRANSITIONRUN = DOM_EVENT_MAKE('transitionrun'); -const TRANSITIONCANCEL = DOM_EVENT_MAKE('transitioncancel'); + return this; + } -// Predefined CHECKER -const CHECKER = (value, split = CHECK_SAPARATOR) => { - return makeEventChecker(value, split); -}; + matches (selector) { + if (this.el) { -const AFTER = (value, split = CHECK_SAPARATOR) => { - return makeEventChecker(`after(${value})`, split); -}; + if (!this.el.matches) return null; -const BEFORE = (value, split = CHECK_SAPARATOR) => { - return makeEventChecker(`before(${value})`, split); -}; + if (this.el.matches(selector)) { + return this; + } + return this.parent().matches(selector); + } -const IF = CHECKER; -const KEY = CHECKER; + return null; +} -const ARROW_UP = CHECKER('ArrowUp'); -const ARROW_DOWN = CHECKER('ArrowDown'); -const ARROW_LEFT = CHECKER('ArrowLeft'); -const ARROW_RIGHT = CHECKER('ArrowRight'); -const ENTER = CHECKER('Enter'); -const SPACE = CHECKER('Space'); -const ESCAPE = CHECKER('Escape'); -const ALT = CHECKER("isAltKey"); -const SHIFT = CHECKER("isShiftKey"); -const META = CHECKER("isMetaKey"); -const CONTROL = CHECKER("isCtrlKey"); -const SELF = CHECKER("self"); + get value() { + return this.el.value; + } -const FIT = CHECKER("fit"); -const PASSIVE = CHECKER("passive"); -const VDOM = CHECKER('vdom'); + get naturalWidth () { + return this.el.naturalWidth + } -// event config method -const DEBOUNCE = (t = 100) => { - return CHECKER(`debounce(${t})`); -}; + get naturalHeight () { + return this.el.naturalHeight + } -const D1000 = DEBOUNCE(1000); + get files() { + return this.el.files ? [...this.el.files] : []; + } -const THROTTLE = (t = 100) => { - return CHECKER(`throttle(${t})`); -}; + realVal() { + switch (this.el.nodeType) { + case "INPUT": + var type = this.attr("type"); + if (type == "checkbox" || type == "radio") { + return this.checked(); + } + case "SELECT": + case "TEXTAREA": + return this.el.value; + } -const CAPTURE = CHECKER("capture()"); -// event config method + return ""; + } -// before method + show(displayType = "block") { + return this.css("display", displayType != "none" ? displayType : "block"); + } -// after method -const MOVE = (method = "move") => { - return AFTER(`bodyMouseMove ${method}`); -}; -const END = (method = "end") => { - return AFTER(`bodyMouseUp ${method}`); -}; + hide() { + return this.css("display", "none"); + } -const PREVENT = AFTER(`preventDefault`); -const STOP = AFTER(`stopPropagation`); + isHide () { + return this.css("display") == "none" + } -// Predefined LOADER -const LOAD = (value = "$el") => { - return LOAD_SAPARATOR + value; -}; + isShow () { + return !this.isHide(); + } -const createRef = value => { - if (value === '') return ''; + toggle(isForce) { + var currentHide = this.isHide(); - var id = uuid(); - refManager[id] = value; + if (arguments.length == 1) { + if (isForce) { + return this.show(); + } else { + return this.hide(); + } + } else { + if (currentHide) { + return this.show(); + } else { + return this.hide(); + } + } + } - return id; -}; + get totalLength () { + return this.el.getTotalLength() + } -const getRef = id => { - return refManager[id] || ''; -}; + scrollIntoView () { + this.el.scrollIntoView(); + } -const BIND_CHECK_FUNCTION = field => { - return function() { - return this.prevState[field] != this.state[field]; - }; -}; + addScrollLeft (dt) { + this.el.scrollLeft += dt; + return this; + } -const BIND_CHECK_DEFAULT_FUNCTION = () => { - return true; -}; + addScrollTop (dt) { + this.el.scrollTop += dt; + return this; + } -const BIND = (value = "$el", checkFieldOrCallback = '') => { - return ( - BIND_SAPARATOR + value + ( - checkFieldOrCallback ? CHECK_SAPARATOR + createRef(checkFieldOrCallback) : '' - ) - ); -}; + setScrollTop(scrollTop) { + this.el.scrollTop = scrollTop; + return this; + } -var Event = { - addEvent(dom, eventName, callback, useCapture = false) { - if (dom) { - dom.addEventListener(eventName, callback, useCapture); - } - }, + setScrollLeft(scrollLeft) { + this.el.scrollLeft = scrollLeft; + return this; + } - removeEvent(dom, eventName, callback) { - if (dom) { - dom.removeEventListener(eventName, callback); + scrollTop() { + if (this.el === document.body) { + return Dom.getScrollTop(); } - }, - pos(e) { - if (e.touches && e.touches[0]) { - return e.touches[0]; + return this.el.scrollTop; + } + + scrollLeft() { + if (this.el === document.body) { + return Dom.getScrollLeft(); } - return e; - }, + return this.el.scrollLeft; + } - posXY(e) { - var pos = this.pos(e); - return { - x: pos.pageX, - y: pos.pageY - }; + scrollHeight() { + return this.el.scrollHeight; } -}; -class BaseStore { - constructor(opt = {}) { - this.cachedCallback = {}; - this.callbacks = {}; - this.commandes = []; + scrollWidth() { + return this.el.scrollWidth; + } + + on(eventName, callback, opt1, opt2) { + this.el.addEventListener(eventName, callback, opt1, opt2); + + return this; } - getCallbacks(event) { - if (!this.callbacks[event]) { - this.callbacks[event] = []; - } + off(eventName, callback) { + this.el.removeEventListener(eventName, callback); - return this.callbacks[event] + return this; } - setCallbacks(event, list = []) { - this.callbacks[event] = list; + getElement() { + return this.el; } - on(event, originalCallback, context, delay = 0) { - var callback = delay > 0 ? debounce(originalCallback, delay) : originalCallback; + createChild(tag, className = '', attrs = {}, css = {}) { + let $element = Dom.create(tag, className, attrs); + $element.css(css); - this.getCallbacks(event).push({ event, callback, context, originalCallback }); + this.append($element); + + return $element; } - off(event, originalCallback) { + get firstChild() { + return Dom.create(this.el.firstElementChild); + } - if (arguments.length == 1) { - this.setCallbacks(event); - } else if (arguments.length == 2) { - this.setCallbacks(event, this.getCallbacks(event).filter(f => { - return f.originalCallback !== originalCallback - })); + children() { + var element = this.el.firstElementChild; + + if (!element) { + return []; } - } - offAll (context) { + var results = []; - Object.keys(this.callbacks).forEach(event => { - this.setCallbacks(event, this.getCallbacks(event).filter(f => { - return f.context !== context; - })); - }); + do { + results.push(Dom.create(element)); + element = element.nextElementSibling; + } while (element); + + return results; } - getCachedCallbacks (event) { - return this.getCallbacks(event); + childLength() { + return this.el.children.length; } - sendMessage(source, event, $2, $3, $4, $5) { - Promise.resolve().then(() => { - var list = this.getCachedCallbacks(event); - if (list) { - list - .filter(f => f.originalCallback.source !== source) - .forEach(f => { - f.callback($2, $3, $4, $5); - }); - } + replace(newElement) { - }); + if (this.el.parentNode) { + this.el.parentNode.replaceChild(newElement.el || newElement, this.el); + } + + return this; } - triggerMessage(source, event, $2, $3, $4, $5) { - Promise.resolve().then(() => { - var list = this.getCachedCallbacks(event); - if (list) { - list - .filter(f => f.originalCallback.source === source) - .forEach(f => { - f.callback($2, $3, $4, $5); - }); - } else { - console.warn(event, ' is not valid event'); - } + replaceChild(oldElement, newElement) { + this.el.replaceChild(newElement.el || newElement, oldElement.el || oldElement); + + return this; + } + checked(isChecked = false) { + if (arguments.length == 0) { + return !!this.el.checked; + } - }); + this.el.checked = !!isChecked; + + return this; } + click () { + this.el.click(); + return this; + } - emit($1, $2, $3, $4, $5) { - this.sendMessage(this.source, $1, $2, $3, $4, $5); + focus() { + this.el.focus(); + + return this; } - trigger($1, $2, $3, $4, $5) { - this.triggerMessage(this.source, $1, $2, $3, $4, $5); + select() { + this.el.select(); + return this; } - execute($1, $2, $3, $4, $5){ - this.runCommand(this.source, $1, $2, $3, $4, $5); + blur() { + this.el.blur(); + + return this; } -} -// collectProps 에서 제외될 메소드 목록 -const expectMethod = { - "constructor": true, - "initState": true, - "refresh": true, - "updateData": true, - "constructor": true, - "initializeProperty": true, - "created": true, - "getRealEventName": true, - "initializeStoreEvent": true, - "destoryStoreEvent": true, - "destroy": true, - "emit": true, - "trigger": true, - "on": true, - "off": true, - "setState": true, - "_reload": true, - "render": true, - "initialize": true, - "afterRender": true, - "components": true, - "getRef": true, - "parseTemplate": true, - "childrenIds": true, - "exists": true, - "parseProperty": true, - "parseSourceName": true, - "parseComponent": true, - "clean": true, - "refresh": true, - "template": true, - "eachChildren": true, - "initializeEvent": true, - "destroy": true, - "self": true, - "isAltKey": true, - "isCtrlKey": true, - "isShiftKey": true, - "isMetaKey": true, - "preventDefault": true, - "stopPropagation": true, - "bodyMouseMove": true, - "bodyMouseUp": true, - }; + select() { + this.el.select(); -class BaseHandler { - constructor (context, options = {}) { - this.context = context; - this.options = options; - } + return this; + } - // 초기화 설정 - initialize () { + // canvas functions + context(contextType = "2d") { + if (!this._initContext) { + this._initContext = this.el.getContext(contextType); } - // html 을 로드 할 때 - load () { + return this._initContext; + } - } + resize({ width, height }) { + // support hi-dpi for retina display + this._initContext = null; + var ctx = this.context(); + var scale = window.devicePixelRatio || 1; - // 새로고침 할 때 - refresh () { + this.px("width", +width); + this.px("height", +height); - } - - // 화면에 그린 이후에 실행 되는 로직들 - render () { + this.el.width = width * scale; + this.el.height = height * scale; - } + ctx.scale(scale, scale); + } - getRef(id) { - return this.context.getRef(id); - } - - splitMethodByKeyword (arr, keyword) { - var filterKeys = arr.filter(code => code.indexOf(`${keyword}(`) > -1); - var filterMaps = filterKeys.map(code => { - var [target, param] = code - .split(`${keyword}(`)[1] - .split(")")[0] - .trim() - .split(" "); - - return { target, param }; - }); - - return [filterKeys, filterMaps]; - } + toDataURL (type = 'image/png', quality = 1) { + return this.el.toDataURL(type, quality) + } - /** - * property 수집하기 - * 상위 클래스의 모든 property 를 수집해서 리턴한다. - */ - collectProps() { + clear() { + this.context().clearRect(0, 0, this.el.width, this.el.height); + } - var context = this.context; - var p = context.__proto__; - var results = []; - do { - var isObject = p instanceof Object; + update(callback) { + this.clear(); + callback.call(this, this); + } - if (isObject === false) { - break; - } - const names = Object.getOwnPropertyNames(p).filter(name => { - return context && isFunction(context[name]) && !expectMethod[name]; - }); + drawImage (img, dx = 0, dy = 0) { + var ctx = this.context(); + var scale = window.devicePixelRatio || 1; + ctx.drawImage(img, dx, dy, img.width, img.height, 0, 0, this.el.width / scale, this.el.height / scale); + } - results.push(...names); - p = p.__proto__; - } while (p); + drawOption(option = {}) { + var ctx = this.context(); - return results; - } + Object.assign(ctx, option); + } + drawLine(x1, y1, x2, y2) { + var ctx = this.context(); + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + ctx.closePath(); + } - filterProps(pattern) { - return this.collectProps().filter(key => { - return key.match(pattern); - }); - } + drawPath(...path) { + var ctx = this.context(); - run () { + ctx.beginPath(); - } + path.forEach((p, index) => { + if (index == 0) { + ctx.moveTo(p[0], p[1]); + } else { + ctx.lineTo(p[0], p[1]); + } + }); + ctx.stroke(); + ctx.fill(); + ctx.closePath(); + } - destroy() { + drawCircle(cx, cy, r) { + var ctx = this.context(); + ctx.beginPath(); + ctx.arc(cx, cy, r, 0, 2 * Math.PI); + ctx.stroke(); + ctx.fill(); + } + + drawText(x, y, text) { + this.context().fillText(text, x, y); + } + /* utility */ + fullscreen () { + var element = this.el; + + if (element.requestFullscreen) { + element.requestFullscreen(); + } else if (element.wekitRequestFullscreen) { + element.wekitRequestFullscreen(); } + } } const scrollBlockingEvents = { @@ -2689,4 +2689,4 @@ var App = /*#__PURE__*/Object.freeze({ start: start }); -export { AFTER, ALT, ANIMATIONEND, ANIMATIONITERATION, ANIMATIONSTART, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, App, BEFORE, BIND, BIND_CHECK_DEFAULT_FUNCTION, BIND_CHECK_FUNCTION, BIND_SAPARATOR, BLUR, CAPTURE, CHANGE, CHANGEINPUT, CHECKER, CHECK_BIND_PATTERN, CHECK_DOM_EVENT_PATTERN, CHECK_LOAD_PATTERN, CHECK_SAPARATOR, CLICK, CONTEXTMENU, CONTROL, CUSTOM, D1000, DEBOUNCE, DOM_EVENT_SAPARATOR, DOUBLECLICK, DRAG, DRAGEND, DRAGENTER, DRAGEXIT, DRAGLEAVE, DRAGOUT, DRAGOVER, DRAGSTART, DROP, END, ENTER, ESCAPE, EVENT, FIT, FOCUS, FOCUSIN, FOCUSOUT, IF, INPUT, KEY, KEYDOWN, KEYPRESS, KEYUP, LOAD, LOAD_SAPARATOR, META, MOUSEDOWN, MOUSEENTER, MOUSELEAVE, MOUSEMOVE, MOUSEOUT, MOUSEOVER, MOUSEUP, MOVE, NAME_SAPARATOR, PASSIVE, PASTE, PIPE, POINTEREND, POINTERMOVE, POINTERSTART, PREVENT, RESIZE, SAPARATOR, SCROLL, SELF, SHIFT, SPACE, STOP, SUBMIT, THROTTLE, TOUCHEND, TOUCHMOVE, TOUCHSTART, TRANSITIONCANCEL, TRANSITIONEND, TRANSITIONRUN, TRANSITIONSTART, UIElement, VDOM, WHEEL, clone, createRef, debounce, getRef, html, isArray, isBoolean, isFunction, isNotString, isNotUndefined, isNumber, isObject, isString, isUndefined, keyEach, keyMap, makeEventChecker, throttle, uuid, uuidShort }; +export { AFTER, ALT, ANIMATIONEND, ANIMATIONITERATION, ANIMATIONSTART, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, App, BEFORE, BIND, BIND_CHECK_DEFAULT_FUNCTION, BIND_CHECK_FUNCTION, BIND_SAPARATOR, BLUR, CAPTURE, CHANGE, CHANGEINPUT, CHECKER, CHECK_BIND_PATTERN, CHECK_DOM_EVENT_PATTERN, CHECK_LOAD_PATTERN, CHECK_SAPARATOR, CLICK, CONTEXTMENU, CONTROL, CUSTOM, D1000, DEBOUNCE, DOM_EVENT_SAPARATOR, DOUBLECLICK, DRAG, DRAGEND, DRAGENTER, DRAGEXIT, DRAGLEAVE, DRAGOUT, DRAGOVER, DRAGSTART, DROP, Dom, END, ENTER, ESCAPE, EVENT, FIT, FOCUS, FOCUSIN, FOCUSOUT, IF, INPUT, KEY, KEYDOWN, KEYPRESS, KEYUP, LOAD, LOAD_SAPARATOR, META, MOUSEDOWN, MOUSEENTER, MOUSELEAVE, MOUSEMOVE, MOUSEOUT, MOUSEOVER, MOUSEUP, MOVE, NAME_SAPARATOR, PASSIVE, PASTE, PIPE, POINTEREND, POINTERMOVE, POINTERSTART, PREVENT, RESIZE, SAPARATOR, SCROLL, SELF, SHIFT, SPACE, STOP, SUBMIT, THROTTLE, TOUCHEND, TOUCHMOVE, TOUCHSTART, TRANSITIONCANCEL, TRANSITIONEND, TRANSITIONRUN, TRANSITIONSTART, UIElement, VDOM, WHEEL, clone, createRef, debounce, getRef, html, isArray, isBoolean, isFunction, isNotString, isNotUndefined, isNumber, isObject, isString, isUndefined, keyEach, keyMap, makeEventChecker, throttle, uuid, uuidShort }; diff --git a/dist/sapa.js b/dist/sapa.js index dc3634e..f6a7f0e 100644 --- a/dist/sapa.js +++ b/dist/sapa.js @@ -1,7 +1,7 @@ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = global || self, factory(global.sapa = {})); + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.sapa = {})); }(this, (function (exports) { 'use strict'; function debounce (callback, delay = 0) { @@ -159,1421 +159,1421 @@ return uuid; } - const setBooleanProp = (el, name, value) => { - if (value) { - el.setAttribute(name, name); - el[name] = value; - } else { - el.removeAttribute(name); - el[name] = value; - } - }; - - const setProp = (el, name, value) => { - if (typeof value === 'boolean') { - setBooleanProp(el, name, value); - } else { - el.setAttribute(name, value); - } - }; - - - const removeBooleanProp = (node, name) => { - node.removeAttribute(name); - node[name] = false; - }; - - const removeUndefinedProp = (node, name) => { - node.removeAttribute(name); - }; - - const removeProp = (node, name, value) => { - if (typeof value === 'boolean') { - removeBooleanProp(node, name); - } else if (name) { - removeUndefinedProp(node, name); - } - }; - - - const updateProp = (node, name, newValue, oldValue) => { - if (!newValue) { - removeProp(node, name, oldValue); - } else if (!oldValue || newValue !== oldValue) { - setProp(node, name, newValue); - } - }; - - - const updateProps = (node, newProps = {}, oldProps = {}) => { - const props = {...newProps,...oldProps}; - - Object.keys(props).forEach((name) => { - updateProp(node, name, newProps[name], oldProps[name]); - }); + const makeEventChecker = (value, split = CHECK_SAPARATOR) => { + return ` ${split} ${value}`; }; - function changed(node1, node2) { - return ( - (node1.nodeType === Node.TEXT_NODE && node1 !== node2) - || node1.nodeName !== node2.nodeName - ) - } - - function getProps (attributes) { - var results = {}; - for(var t of attributes) { - results[t.name] = t.value; - } - - return results; - - } - - function updateElement (parentElement, oldEl, newEl, i) { - if (!oldEl) { - parentElement.appendChild(newEl.cloneNode(true)); - } else if (!newEl) { - parentElement.removeChild(oldEl); - } else if (changed(newEl, oldEl)) { - parentElement.replaceChild(newEl.cloneNode(true), oldEl); - } else if (newEl.nodeType !== Node.TEXT_NODE) { - updateProps(oldEl, getProps(newEl.attributes), getProps(oldEl.attributes)); // added - var oldChildren = children(oldEl); - var newChildren = children(newEl); - var max = Math.max(oldChildren.length, newChildren.length); - - for (var i = 0; i < max; i++) { - updateElement(oldEl, oldChildren[i], newChildren[i], i); - } - } + // event name regular expression + const CHECK_DOM_EVENT_PATTERN = /^dom (.*)/gi; + const CHECK_LOAD_PATTERN = /^load (.*)/gi; + const CHECK_BIND_PATTERN = /^bind (.*)/gi; - } - const children = (el) => { - var element = el.firstChild; - if (!element) { - return [] - } + const NAME_SAPARATOR = ":"; + const CHECK_SAPARATOR = "|"; + const DOM_EVENT_SAPARATOR = "dom "; + const LOAD_SAPARATOR = "load "; + const BIND_SAPARATOR = "bind "; - var results = []; + const SAPARATOR = ' '; - do { - results.push(element); - element = element.nextSibling; - } while (element); + const refManager = {}; - return results; + const DOM_EVENT_MAKE = (...keys) => { + var key = keys.join(NAME_SAPARATOR); + return (...args) => { + return DOM_EVENT_SAPARATOR + [key, ...args].join(SAPARATOR); + }; }; + const CUSTOM = DOM_EVENT_MAKE; + const CLICK = DOM_EVENT_MAKE("click"); + const DOUBLECLICK = DOM_EVENT_MAKE("dblclick"); + const MOUSEDOWN = DOM_EVENT_MAKE("mousedown"); + const MOUSEUP = DOM_EVENT_MAKE("mouseup"); + const MOUSEMOVE = DOM_EVENT_MAKE("mousemove"); + const MOUSEOVER = DOM_EVENT_MAKE("mouseover"); + const MOUSEOUT = DOM_EVENT_MAKE("mouseout"); + const MOUSEENTER = DOM_EVENT_MAKE("mouseenter"); + const MOUSELEAVE = DOM_EVENT_MAKE("mouseleave"); + const TOUCHSTART = DOM_EVENT_MAKE("touchstart"); + const TOUCHMOVE = DOM_EVENT_MAKE("touchmove"); + const TOUCHEND = DOM_EVENT_MAKE("touchend"); + const KEYDOWN = DOM_EVENT_MAKE("keydown"); + const KEYUP = DOM_EVENT_MAKE("keyup"); + const KEYPRESS = DOM_EVENT_MAKE("keypress"); + const DRAG = DOM_EVENT_MAKE("drag"); + const DRAGSTART = DOM_EVENT_MAKE("dragstart"); + const DROP = DOM_EVENT_MAKE("drop"); + const DRAGOVER = DOM_EVENT_MAKE("dragover"); + const DRAGENTER = DOM_EVENT_MAKE("dragenter"); + const DRAGLEAVE = DOM_EVENT_MAKE("dragleave"); + const DRAGEXIT = DOM_EVENT_MAKE("dragexit"); + const DRAGOUT = DOM_EVENT_MAKE("dragout"); + const DRAGEND = DOM_EVENT_MAKE("dragend"); + const CONTEXTMENU = DOM_EVENT_MAKE("contextmenu"); + const CHANGE = DOM_EVENT_MAKE("change"); + const INPUT = DOM_EVENT_MAKE("input"); + const FOCUS = DOM_EVENT_MAKE("focus"); + const FOCUSIN = DOM_EVENT_MAKE("focusin"); + const FOCUSOUT = DOM_EVENT_MAKE("focusout"); + const BLUR = DOM_EVENT_MAKE("blur"); + const PASTE = DOM_EVENT_MAKE("paste"); + const RESIZE = DOM_EVENT_MAKE("resize"); + const SCROLL = DOM_EVENT_MAKE("scroll"); + const SUBMIT = DOM_EVENT_MAKE("submit"); + const POINTERSTART = CUSTOM("pointerdown"); + const POINTERMOVE = CUSTOM("pointermove"); + const POINTEREND = CUSTOM("pointerup"); + const CHANGEINPUT = CUSTOM("change", "input"); + const WHEEL = CUSTOM("wheel", "mousewheel", "DOMMouseScroll"); + const ANIMATIONSTART = DOM_EVENT_MAKE('animationstart'); + const ANIMATIONEND = DOM_EVENT_MAKE('animationend'); + const ANIMATIONITERATION = DOM_EVENT_MAKE('animationiteration'); + const TRANSITIONSTART = DOM_EVENT_MAKE('transitionstart'); + const TRANSITIONEND = DOM_EVENT_MAKE('transitionend'); + const TRANSITIONRUN = DOM_EVENT_MAKE('transitionrun'); + const TRANSITIONCANCEL = DOM_EVENT_MAKE('transitioncancel'); - function DomDiff (A, B) { - - A = A.el || A; - B = B.el || B; - - var childrenA = children(A); - var childrenB = children(B); - - var len = Math.max(childrenA.length, childrenB.length); - for (var i = 0; i < len; i++) { - updateElement(A, childrenA[i], childrenB[i], i); - } - } - - class Dom { - constructor(tag, className, attr) { - if (isNotString(tag)) { - this.el = tag; - } else { - var el = document.createElement(tag); - - if (className) { - el.className = className; - } - - attr = attr || {}; - - for (var k in attr) { - el.setAttribute(k, attr[k]); - } - - this.el = el; - } - } - - static create (tag, className, attr) { - return new Dom(tag, className, attr); - } - - static createByHTML (htmlString) { - var div = Dom.create('div'); - var list = div.html(htmlString).children(); + // Predefined CHECKER + const CHECKER = (value, split = CHECK_SAPARATOR) => { + return makeEventChecker(value, split); + }; - if (list.length) { - return Dom.create(list[0].el); - } + const AFTER = (value, split = CHECK_SAPARATOR) => { + return makeEventChecker(`after(${value})`, split); + }; - return null; - } + const BEFORE = (value, split = CHECK_SAPARATOR) => { + return makeEventChecker(`before(${value})`, split); + }; - static createFragment (htmlString) { - var div = Dom.create('div'); - return Dom.create(div.html(htmlString).createChildrenFragment()) - } + const IF = CHECKER; + const KEY = CHECKER; - static getScrollTop() { - return Math.max( - window.pageYOffset, - document.documentElement.scrollTop, - document.body.scrollTop - ); - } + const ARROW_UP = CHECKER('ArrowUp'); + const ARROW_DOWN = CHECKER('ArrowDown'); + const ARROW_LEFT = CHECKER('ArrowLeft'); + const ARROW_RIGHT = CHECKER('ArrowRight'); + const ENTER = CHECKER('Enter'); + const SPACE = CHECKER('Space'); + const ESCAPE = CHECKER('Escape'); - static getScrollLeft() { - return Math.max( - window.pageXOffset, - document.documentElement.scrollLeft, - document.body.scrollLeft - ); - } + const ALT = CHECKER("isAltKey"); + const SHIFT = CHECKER("isShiftKey"); + const META = CHECKER("isMetaKey"); + const CONTROL = CHECKER("isCtrlKey"); + const SELF = CHECKER("self"); - static parse(html) { - var parser = DOMParser(); - return parser.parseFromString(html, "text/htmll"); - } + const FIT = CHECKER("fit"); + const PASSIVE = CHECKER("passive"); + const VDOM = CHECKER('vdom'); - static body () { - return Dom.create(document.body) - } + // event config method + const DEBOUNCE = (t = 100) => { + return CHECKER(`debounce(${t})`); + }; - isFragment () { - return this.el.nodeType === 11; - } + const D1000 = DEBOUNCE(1000); - setAttr (obj) { - if (this.isFragment()) return; - Object.keys(obj).forEach(key => { - this.attr(key, obj[key]); - }); - return this; - } + const THROTTLE = (t = 100) => { + return CHECKER(`throttle(${t})`); + }; - attr(key, value) { - if (this.isFragment()) return; - if (arguments.length == 1) { - if (this.el.getAttribute) { - return this.el.getAttribute(key); - } else { - return undefined; - } + const CAPTURE = CHECKER("capture()"); + // event config method - } + // before method - this.el.setAttribute(key, value); + // after method + const MOVE = (method = "move") => { + return AFTER(`bodyMouseMove ${method}`); + }; + const END = (method = "end") => { + return AFTER(`bodyMouseUp ${method}`); + }; - return this; - } + const PREVENT = AFTER(`preventDefault`); + const STOP = AFTER(`stopPropagation`); - attrKeyValue(keyField) { - if (this.isFragment()) return {}; - return { - [this.el.getAttribute(keyField)]: this.val() - } - } + // Predefined LOADER + const LOAD = (value = "$el") => { + return LOAD_SAPARATOR + value; + }; - attrs(...args) { - if (this.isFragment()) return []; - return args.map(key => { - return this.el.getAttribute(key); - }); - } + const createRef = value => { + if (value === '') return ''; - styles(...args) { - return args.map(key => { - return this.el.style[key]; - }); - } + var id = uuid(); + refManager[id] = value; - removeAttr(key) { - this.el.removeAttribute(key); + return id; + }; - return this; - } + const getRef = id => { + return refManager[id] || ''; + }; - removeStyle(key) { - this.el.style.removeProperty(key); - return this; - } + const BIND_CHECK_FUNCTION = field => { + return function() { + return this.prevState[field] != this.state[field]; + }; + }; - is(checkElement) { - return this.el === (checkElement.el || checkElement); - } + const BIND_CHECK_DEFAULT_FUNCTION = () => { + return true; + }; - isTag(tag) { - return this.el.tagName.toLowerCase() === tag.toLowerCase() - } + const BIND = (value = "$el", checkFieldOrCallback = '') => { + return ( + BIND_SAPARATOR + value + ( + checkFieldOrCallback ? CHECK_SAPARATOR + createRef(checkFieldOrCallback) : '' + ) + ); + }; - closest(cls) { - var temp = this; - var checkCls = false; + var Event = { + addEvent(dom, eventName, callback, useCapture = false) { + if (dom) { + dom.addEventListener(eventName, callback, useCapture); + } + }, - while (!(checkCls = temp.hasClass(cls))) { - if (temp.el.parentNode) { - temp = Dom.create(temp.el.parentNode); - } else { - return null; - } + removeEvent(dom, eventName, callback) { + if (dom) { + dom.removeEventListener(eventName, callback); } + }, - if (checkCls) { - return temp; + pos(e) { + if (e.touches && e.touches[0]) { + return e.touches[0]; } - return null; + return e; + }, + + posXY(e) { + var pos = this.pos(e); + return { + x: pos.pageX, + y: pos.pageY + }; } + }; - parent() { - return Dom.create(this.el.parentNode); + class BaseStore { + constructor(opt = {}) { + this.cachedCallback = {}; + this.callbacks = {}; + this.commandes = []; } - hasParent () { - return !!this.el.parentNode + getCallbacks(event) { + if (!this.callbacks[event]) { + this.callbacks[event] = []; + } + + return this.callbacks[event] } - removeClass(...args) { - this.el.classList.remove(...args); - return this; + setCallbacks(event, list = []) { + this.callbacks[event] = list; } - hasClass(cls) { - if (!this.el.classList) return false; - return this.el.classList.contains(cls); + on(event, originalCallback, context, delay = 0) { + var callback = delay > 0 ? debounce(originalCallback, delay) : originalCallback; + + this.getCallbacks(event).push({ event, callback, context, originalCallback }); } - addClass(...args) { - this.el.classList.add(...args); + off(event, originalCallback) { - return this; + if (arguments.length == 1) { + this.setCallbacks(event); + } else if (arguments.length == 2) { + this.setCallbacks(event, this.getCallbacks(event).filter(f => { + return f.originalCallback !== originalCallback + })); + } } - onlyOneClass(cls) { - var parent = this.parent(); + offAll (context) { - parent.children().forEach(it => { - it.removeClass(cls); + Object.keys(this.callbacks).forEach(event => { + this.setCallbacks(event, this.getCallbacks(event).filter(f => { + return f.context !== context; + })); }); + } - this.addClass(cls); + getCachedCallbacks (event) { + return this.getCallbacks(event); } - toggleClass(cls, isForce) { - this.el.classList.toggle(cls, isForce); + sendMessage(source, event, $2, $3, $4, $5) { + Promise.resolve().then(() => { + var list = this.getCachedCallbacks(event); + if (list) { + list + .filter(f => f.originalCallback.source !== source) + .forEach(f => { + f.callback($2, $3, $4, $5); + }); + } + + }); } - html(html) { - if (isUndefined(html)) { - return this.el.innerHTML; - } + triggerMessage(source, event, $2, $3, $4, $5) { + Promise.resolve().then(() => { + var list = this.getCachedCallbacks(event); + if (list) { + list + .filter(f => f.originalCallback.source === source) + .forEach(f => { + f.callback($2, $3, $4, $5); + }); + } else { + console.warn(event, ' is not valid event'); + } - if (isString(html)) { - this.el.innerHTML = html; - } else { - this.empty().append(html); - } - return this; + }); } - htmlDiff(fragment) { - DomDiff(this, fragment); - } - updateDiff (html, rootElement = 'div') { - DomDiff(this, Dom.create(rootElement).html(html)); - } - updateSVGDiff (html, rootElement = 'div') { - DomDiff(this, Dom.create(rootElement).html(`${html}`).firstChild); - } - find(selector) { - return this.el.querySelector(selector); + emit($1, $2, $3, $4, $5) { + this.sendMessage(this.source, $1, $2, $3, $4, $5); } - $(selector) { - var node = this.find(selector); - return node ? Dom.create(node) : null; + trigger($1, $2, $3, $4, $5) { + this.triggerMessage(this.source, $1, $2, $3, $4, $5); } - findAll(selector) { - return this.el.querySelectorAll(selector); + execute($1, $2, $3, $4, $5){ + this.runCommand(this.source, $1, $2, $3, $4, $5); } + } + + // collectProps 에서 제외될 메소드 목록 + const expectMethod = { + "constructor": true, + "initState": true, + "refresh": true, + "updateData": true, + "constructor": true, + "initializeProperty": true, + "created": true, + "getRealEventName": true, + "initializeStoreEvent": true, + "destoryStoreEvent": true, + "destroy": true, + "emit": true, + "trigger": true, + "on": true, + "off": true, + "setState": true, + "_reload": true, + "render": true, + "initialize": true, + "afterRender": true, + "components": true, + "getRef": true, + "parseTemplate": true, + "childrenIds": true, + "exists": true, + "parseProperty": true, + "parseSourceName": true, + "parseComponent": true, + "clean": true, + "refresh": true, + "template": true, + "eachChildren": true, + "initializeEvent": true, + "destroy": true, + "self": true, + "isAltKey": true, + "isCtrlKey": true, + "isShiftKey": true, + "isMetaKey": true, + "preventDefault": true, + "stopPropagation": true, + "bodyMouseMove": true, + "bodyMouseUp": true, + }; - $$(selector) { - var arr = this.findAll(selector); - return Object.keys(arr).map(key => { - return Dom.create(arr[key]); - }); - } + class BaseHandler { + constructor (context, options = {}) { + this.context = context; + this.options = options; + } - empty() { - while (this.el.firstChild) this.el.removeChild(this.el.firstChild); - return this; - } + // 초기화 설정 + initialize () { - append(el) { - if (isString(el)) { - this.el.appendChild(document.createTextNode(el)); - } else { - this.el.appendChild(el.el || el); } - return this; - } + // html 을 로드 할 때 + load () { - prepend(el) { - if (isString(el)) { - this.el.prepend(document.createTextNode(el)); - } else { - this.el.prepend(el.el || el); } - return this; - } + // 새로고침 할 때 + refresh () { - prependHTML(html) { - var $dom = Dom.create("div").html(html); + } + + // 화면에 그린 이후에 실행 되는 로직들 + render () { - this.prepend($dom.createChildrenFragment()); + } - return $dom; - } + getRef(id) { + return this.context.getRef(id); + } + + splitMethodByKeyword (arr, keyword) { + var filterKeys = arr.filter(code => code.indexOf(`${keyword}(`) > -1); + var filterMaps = filterKeys.map(code => { + var [target, param] = code + .split(`${keyword}(`)[1] + .split(")")[0] + .trim() + .split(" "); + + return { target, param }; + }); + + return [filterKeys, filterMaps]; + } - appendHTML(html) { - var $dom = Dom.create("div").html(html); + /** + * property 수집하기 + * 상위 클래스의 모든 property 를 수집해서 리턴한다. + */ + collectProps() { - this.append($dom.createChildrenFragment()); + var context = this.context; + var p = context.__proto__; + var results = []; + do { + var isObject = p instanceof Object; - return $dom; - } + if (isObject === false) { + break; + } + const names = Object.getOwnPropertyNames(p).filter(name => { + return context && isFunction(context[name]) && !expectMethod[name]; + }); - /** - * create document fragment with children dom - */ - createChildrenFragment() { - const list = this.children(); + results.push(...names); + p = p.__proto__; + } while (p); - var fragment = document.createDocumentFragment(); - list.forEach($el => fragment.appendChild($el.el)); + return results; + } - return fragment; - } - appendTo(target) { - var t = target.el ? target.el : target; - t.appendChild(this.el); + filterProps(pattern) { + return this.collectProps().filter(key => { + return key.match(pattern); + }); + } - return this; - } + run () { - remove() { - if (this.el.parentNode) { - this.el.parentNode.removeChild(this.el); } - return this; - } + destroy() { - removeChild(el) { - this.el.removeChild(el.el || el); - return this; - } + } + } - text(value) { - if (isUndefined(value)) { - return this.el.textContent; + const setBooleanProp = (el, name, value) => { + if (value) { + el.setAttribute(name, name); + el[name] = value; } else { - var tempText = value; - - if (value instanceof Dom) { - tempText = value.text(); - } - - this.el.textContent = tempText; - return this; + el.removeAttribute(name); + el[name] = value; } - } + }; + + const setProp = (el, name, value) => { + if (typeof value === 'boolean') { + setBooleanProp(el, name, value); + } else { + el.setAttribute(name, value); + } + }; - /** - * - * $el.css` - * border-color: yellow; - * ` - * - * @param {*} key - * @param {*} value - */ - css(key, value) { - if (isNotUndefined(key) && isNotUndefined(value)) { - Object.assign(this.el.style, {[key]: value}); - } else if (isNotUndefined(key)) { - if (isString(key)) { - return getComputedStyle(this.el)[key]; - } else { - Object.assign(this.el.style, key); - } + const removeBooleanProp = (node, name) => { + node.removeAttribute(name); + node[name] = false; + }; + + const removeUndefinedProp = (node, name) => { + node.removeAttribute(name); + }; + + const removeProp = (node, name, value) => { + if (typeof value === 'boolean') { + removeBooleanProp(node, name); + } else if (name) { + removeUndefinedProp(node, name); } + }; + - return this; - } + const updateProp = (node, name, newValue, oldValue) => { + if (!newValue) { + removeProp(node, name, oldValue); + } else if (!oldValue || newValue !== oldValue) { + setProp(node, name, newValue); + } + }; - getComputedStyle (...list) { - var css = getComputedStyle(this.el); - var obj = {}; - list.forEach(it => { - obj[it] = css[it]; + const updateProps = (node, newProps = {}, oldProps = {}) => { + const props = {...newProps,...oldProps}; + + Object.keys(props).forEach((name) => { + updateProp(node, name, newProps[name], oldProps[name]); }); + }; - return obj; - } - - getStyleList(...list) { - var style = {}; - - var len = this.el.style.length; - for (var i = 0; i < len; i++) { - var key = this.el.style[i]; + function changed(node1, node2) { + return ( + (node1.nodeType === Node.TEXT_NODE && node1 !== node2) + || node1.nodeName !== node2.nodeName + ) + } - style[key] = this.el.style[key]; + function getProps (attributes) { + var results = {}; + for(var t of attributes) { + results[t.name] = t.value; } - list.forEach(key => { - style[key] = this.css(key); - }); - - return style; - } + return results; + + } - cssText(value) { - if (isUndefined(value)) { - return this.el.style.cssText; - } + function updateElement (parentElement, oldEl, newEl, i) { + if (!oldEl) { + parentElement.appendChild(newEl.cloneNode(true)); + } else if (!newEl) { + parentElement.removeChild(oldEl); + } else if (changed(newEl, oldEl)) { + parentElement.replaceChild(newEl.cloneNode(true), oldEl); + } else if (newEl.nodeType !== Node.TEXT_NODE) { + updateProps(oldEl, getProps(newEl.attributes), getProps(oldEl.attributes)); // added + var oldChildren = children(oldEl); + var newChildren = children(newEl); + var max = Math.max(oldChildren.length, newChildren.length); - if (value != this.el.tempCssText) { - this.el.style.cssText = value; - this.el.tempCssText = value; - } + for (var i = 0; i < max; i++) { + updateElement(oldEl, oldChildren[i], newChildren[i], i); + } + } - return this; - } + } - cssArray(arr) { - if (arr[0]) this.el.style[arr[0]] = arr[1]; - if (arr[2]) this.el.style[arr[2]] = arr[3]; - if (arr[4]) this.el.style[arr[4]] = arr[5]; - if (arr[6]) this.el.style[arr[6]] = arr[7]; - if (arr[8]) this.el.style[arr[8]] = arr[9]; + const children = (el) => { + var element = el.firstChild; - return this; - } + if (!element) { + return [] + } - cssFloat(key) { - return parseFloat(this.css(key)); - } + var results = []; - cssInt(key) { - return parseInt(this.css(key)); - } + do { + results.push(element); + element = element.nextSibling; + } while (element); - px(key, value) { - return this.css(key, value + 'px'); - } + return results; + }; - rect() { - return this.el.getBoundingClientRect(); - } - isSVG () { - return this.el.tagName.toUpperCase() === 'SVG'; - } + function DomDiff (A, B) { - offsetRect() { + A = A.el || A; + B = B.el || B; - if (this.isSVG()) { - const parentBox = this.parent().rect(); - const box = this.rect(); + var childrenA = children(A); + var childrenB = children(B); - return { - x: box.x - parentBox.x, - y: box.y - parentBox.y, - top: box.x - parentBox.x, - left: box.y - parentBox.y, - width: box.width, - height: box.height - } + var len = Math.max(childrenA.length, childrenB.length); + for (var i = 0; i < len; i++) { + updateElement(A, childrenA[i], childrenB[i], i); } + } - return { - x: this.el.offsetLeft, - y: this.el.offsetTop, - top: this.el.offsetTop, - left: this.el.offsetLeft, - width: this.el.offsetWidth, - height: this.el.offsetHeight - }; - } + class Dom { + constructor(tag, className, attr) { + if (isNotString(tag)) { + this.el = tag; + } else { + var el = document.createElement(tag); - offset() { - var rect = this.rect(); + if (className) { + el.className = className; + } - var scrollTop = Dom.getScrollTop(); - var scrollLeft = Dom.getScrollLeft(); + attr = attr || {}; - return { - top: rect.top + scrollTop, - left: rect.left + scrollLeft - }; - } + for (var k in attr) { + el.setAttribute(k, attr[k]); + } - offsetLeft() { - return this.offset().left; + this.el = el; + } } - offsetTop() { - return this.offset().top; + static create (tag, className, attr) { + return new Dom(tag, className, attr); } + + static createByHTML (htmlString) { + var div = Dom.create('div'); + var list = div.html(htmlString).children(); - position() { - if (this.el.style.top) { - return { - top: parseFloat(this.css("top")), - left: parseFloat(this.css("left")) - }; - } else { - return this.rect(); + if (list.length) { + return Dom.create(list[0].el); } + + return null; } - size() { - return [this.width(), this.height()]; + static createFragment (htmlString) { + var div = Dom.create('div'); + return Dom.create(div.html(htmlString).createChildrenFragment()) } - width() { - return this.el.offsetWidth || this.rect().width; + static getScrollTop() { + return Math.max( + window.pageYOffset, + document.documentElement.scrollTop, + document.body.scrollTop + ); } - contentWidth() { - return ( - this.width() - - this.cssFloat("padding-left") - - this.cssFloat("padding-right") + static getScrollLeft() { + return Math.max( + window.pageXOffset, + document.documentElement.scrollLeft, + document.body.scrollLeft ); } - height() { - return this.el.offsetHeight || this.rect().height; + static parse(html) { + var parser = DOMParser(); + return parser.parseFromString(html, "text/htmll"); } - contentHeight() { - return ( - this.height() - - this.cssFloat("padding-top") - - this.cssFloat("padding-bottom") - ); + static body () { + return Dom.create(document.body) } - val(value) { - if (isUndefined(value)) { - return this.el.value; - } else if (isNotUndefined(value)) { - var tempValue = value; + isFragment () { + return this.el.nodeType === 11; + } - if (value instanceof Dom) { - tempValue = value.val(); + setAttr (obj) { + if (this.isFragment()) return; + Object.keys(obj).forEach(key => { + this.attr(key, obj[key]); + }); + return this; + } + + attr(key, value) { + if (this.isFragment()) return; + if (arguments.length == 1) { + if (this.el.getAttribute) { + return this.el.getAttribute(key); + } else { + return undefined; } - this.el.value = tempValue; } + this.el.setAttribute(key, value); + return this; } - matches (selector) { - if (this.el) { - - if (!this.el.matches) return null; - - if (this.el.matches(selector)) { - return this; - } - return this.parent().matches(selector); + attrKeyValue(keyField) { + if (this.isFragment()) return {}; + return { + [this.el.getAttribute(keyField)]: this.val() } + } - return null; - } + attrs(...args) { + if (this.isFragment()) return []; + return args.map(key => { + return this.el.getAttribute(key); + }); + } + + styles(...args) { + return args.map(key => { + return this.el.style[key]; + }); + } + removeAttr(key) { + this.el.removeAttribute(key); - get value() { - return this.el.value; + return this; } - get naturalWidth () { - return this.el.naturalWidth + removeStyle(key) { + this.el.style.removeProperty(key); + return this; } - get naturalHeight () { - return this.el.naturalHeight - } + is(checkElement) { + return this.el === (checkElement.el || checkElement); + } - get files() { - return this.el.files ? [...this.el.files] : []; + isTag(tag) { + return this.el.tagName.toLowerCase() === tag.toLowerCase() } - realVal() { - switch (this.el.nodeType) { - case "INPUT": - var type = this.attr("type"); - if (type == "checkbox" || type == "radio") { - return this.checked(); - } - case "SELECT": - case "TEXTAREA": - return this.el.value; + closest(cls) { + var temp = this; + var checkCls = false; + + while (!(checkCls = temp.hasClass(cls))) { + if (temp.el.parentNode) { + temp = Dom.create(temp.el.parentNode); + } else { + return null; + } } - return ""; + if (checkCls) { + return temp; + } + + return null; } - show(displayType = "block") { - return this.css("display", displayType != "none" ? displayType : "block"); + parent() { + return Dom.create(this.el.parentNode); } - hide() { - return this.css("display", "none"); + hasParent () { + return !!this.el.parentNode } - isHide () { - return this.css("display") == "none" + removeClass(...args) { + this.el.classList.remove(...args); + return this; } - isShow () { - return !this.isHide(); + hasClass(cls) { + if (!this.el.classList) return false; + return this.el.classList.contains(cls); } - toggle(isForce) { - var currentHide = this.isHide(); + addClass(...args) { + this.el.classList.add(...args); - if (arguments.length == 1) { - if (isForce) { - return this.show(); - } else { - return this.hide(); - } - } else { - if (currentHide) { - return this.show(); - } else { - return this.hide(); - } - } + return this; } - get totalLength () { - return this.el.getTotalLength() - } + onlyOneClass(cls) { + var parent = this.parent(); - scrollIntoView () { - this.el.scrollIntoView(); + parent.children().forEach(it => { + it.removeClass(cls); + }); + + this.addClass(cls); } - addScrollLeft (dt) { - this.el.scrollLeft += dt; - return this; + toggleClass(cls, isForce) { + this.el.classList.toggle(cls, isForce); } - addScrollTop (dt) { - this.el.scrollTop += dt; - return this; - } + html(html) { + if (isUndefined(html)) { + return this.el.innerHTML; + } - setScrollTop(scrollTop) { - this.el.scrollTop = scrollTop; - return this; - } + if (isString(html)) { + this.el.innerHTML = html; + } else { + this.empty().append(html); + } - setScrollLeft(scrollLeft) { - this.el.scrollLeft = scrollLeft; return this; } - scrollTop() { - if (this.el === document.body) { - return Dom.getScrollTop(); - } - - return this.el.scrollTop; + htmlDiff(fragment) { + DomDiff(this, fragment); + } + updateDiff (html, rootElement = 'div') { + DomDiff(this, Dom.create(rootElement).html(html)); } - scrollLeft() { - if (this.el === document.body) { - return Dom.getScrollLeft(); - } + updateSVGDiff (html, rootElement = 'div') { - return this.el.scrollLeft; + DomDiff(this, Dom.create(rootElement).html(`${html}`).firstChild); + } + + find(selector) { + return this.el.querySelector(selector); } - scrollHeight() { - return this.el.scrollHeight; + $(selector) { + var node = this.find(selector); + return node ? Dom.create(node) : null; } - scrollWidth() { - return this.el.scrollWidth; - } + findAll(selector) { + return this.el.querySelectorAll(selector); + } - on(eventName, callback, opt1, opt2) { - this.el.addEventListener(eventName, callback, opt1, opt2); + $$(selector) { + var arr = this.findAll(selector); + return Object.keys(arr).map(key => { + return Dom.create(arr[key]); + }); + } + empty() { + while (this.el.firstChild) this.el.removeChild(this.el.firstChild); return this; } - off(eventName, callback) { - this.el.removeEventListener(eventName, callback); + append(el) { + if (isString(el)) { + this.el.appendChild(document.createTextNode(el)); + } else { + this.el.appendChild(el.el || el); + } return this; } - getElement() { - return this.el; + prepend(el) { + if (isString(el)) { + this.el.prepend(document.createTextNode(el)); + } else { + this.el.prepend(el.el || el); + } + + return this; } - createChild(tag, className = '', attrs = {}, css = {}) { - let $element = Dom.create(tag, className, attrs); - $element.css(css); + prependHTML(html) { + var $dom = Dom.create("div").html(html); - this.append($element); + this.prepend($dom.createChildrenFragment()); - return $element; + return $dom; } - get firstChild() { - return Dom.create(this.el.firstElementChild); - } + appendHTML(html) { + var $dom = Dom.create("div").html(html); - children() { - var element = this.el.firstElementChild; + this.append($dom.createChildrenFragment()); - if (!element) { - return []; - } + return $dom; + } - var results = []; + /** + * create document fragment with children dom + */ + createChildrenFragment() { + const list = this.children(); - do { - results.push(Dom.create(element)); - element = element.nextElementSibling; - } while (element); + var fragment = document.createDocumentFragment(); + list.forEach($el => fragment.appendChild($el.el)); - return results; + return fragment; } - childLength() { - return this.el.children.length; - } + appendTo(target) { + var t = target.el ? target.el : target; - replace(newElement) { + t.appendChild(this.el); + return this; + } + + remove() { if (this.el.parentNode) { - this.el.parentNode.replaceChild(newElement.el || newElement, this.el); + this.el.parentNode.removeChild(this.el); } return this; } - replaceChild(oldElement, newElement) { - this.el.replaceChild(newElement.el || newElement, oldElement.el || oldElement); - - return this; - } + removeChild(el) { + this.el.removeChild(el.el || el); + return this; + } - checked(isChecked = false) { - if (arguments.length == 0) { - return !!this.el.checked; - } + text(value) { + if (isUndefined(value)) { + return this.el.textContent; + } else { + var tempText = value; - this.el.checked = !!isChecked; + if (value instanceof Dom) { + tempText = value.text(); + } - return this; + this.el.textContent = tempText; + return this; + } } + /** + * + * $el.css` + * border-color: yellow; + * ` + * + * @param {*} key + * @param {*} value + */ - click () { - this.el.click(); - - return this; - } - - focus() { - this.el.focus(); + css(key, value) { + if (isNotUndefined(key) && isNotUndefined(value)) { + Object.assign(this.el.style, {[key]: value}); + } else if (isNotUndefined(key)) { + if (isString(key)) { + return getComputedStyle(this.el)[key]; + } else { + Object.assign(this.el.style, key); + } + } return this; } - select() { - this.el.select(); - return this; - } + getComputedStyle (...list) { + var css = getComputedStyle(this.el); - blur() { - this.el.blur(); + var obj = {}; + list.forEach(it => { + obj[it] = css[it]; + }); - return this; + return obj; } - select() { - this.el.select(); + getStyleList(...list) { + var style = {}; + + var len = this.el.style.length; + for (var i = 0; i < len; i++) { + var key = this.el.style[i]; + + style[key] = this.el.style[key]; + } - return this; - } + list.forEach(key => { + style[key] = this.css(key); + }); - // canvas functions + return style; + } - context(contextType = "2d") { - if (!this._initContext) { - this._initContext = this.el.getContext(contextType); + cssText(value) { + if (isUndefined(value)) { + return this.el.style.cssText; } - return this._initContext; - } + if (value != this.el.tempCssText) { + this.el.style.cssText = value; + this.el.tempCssText = value; + } - resize({ width, height }) { - // support hi-dpi for retina display - this._initContext = null; - var ctx = this.context(); - var scale = window.devicePixelRatio || 1; + return this; + } - this.px("width", +width); - this.px("height", +height); + cssArray(arr) { + if (arr[0]) this.el.style[arr[0]] = arr[1]; + if (arr[2]) this.el.style[arr[2]] = arr[3]; + if (arr[4]) this.el.style[arr[4]] = arr[5]; + if (arr[6]) this.el.style[arr[6]] = arr[7]; + if (arr[8]) this.el.style[arr[8]] = arr[9]; - this.el.width = width * scale; - this.el.height = height * scale; + return this; + } - ctx.scale(scale, scale); + cssFloat(key) { + return parseFloat(this.css(key)); } - toDataURL (type = 'image/png', quality = 1) { - return this.el.toDataURL(type, quality) + cssInt(key) { + return parseInt(this.css(key)); } - clear() { - this.context().clearRect(0, 0, this.el.width, this.el.height); + px(key, value) { + return this.css(key, value + 'px'); } - update(callback) { - this.clear(); - callback.call(this, this); + rect() { + return this.el.getBoundingClientRect(); } - drawImage (img, dx = 0, dy = 0) { - var ctx = this.context(); - var scale = window.devicePixelRatio || 1; - ctx.drawImage(img, dx, dy, img.width, img.height, 0, 0, this.el.width / scale, this.el.height / scale); + isSVG () { + return this.el.tagName.toUpperCase() === 'SVG'; } - drawOption(option = {}) { - var ctx = this.context(); + offsetRect() { - Object.assign(ctx, option); - } + if (this.isSVG()) { + const parentBox = this.parent().rect(); + const box = this.rect(); - drawLine(x1, y1, x2, y2) { - var ctx = this.context(); + return { + x: box.x - parentBox.x, + y: box.y - parentBox.y, + top: box.x - parentBox.x, + left: box.y - parentBox.y, + width: box.width, + height: box.height + } + } - ctx.beginPath(); - ctx.moveTo(x1, y1); - ctx.lineTo(x2, y2); - ctx.stroke(); - ctx.closePath(); + return { + x: this.el.offsetLeft, + y: this.el.offsetTop, + top: this.el.offsetTop, + left: this.el.offsetLeft, + width: this.el.offsetWidth, + height: this.el.offsetHeight + }; } - drawPath(...path) { - var ctx = this.context(); + offset() { + var rect = this.rect(); - ctx.beginPath(); + var scrollTop = Dom.getScrollTop(); + var scrollLeft = Dom.getScrollLeft(); - path.forEach((p, index) => { - if (index == 0) { - ctx.moveTo(p[0], p[1]); - } else { - ctx.lineTo(p[0], p[1]); - } - }); - ctx.stroke(); - ctx.fill(); - ctx.closePath(); + return { + top: rect.top + scrollTop, + left: rect.left + scrollLeft + }; } - drawCircle(cx, cy, r) { - var ctx = this.context(); - ctx.beginPath(); - ctx.arc(cx, cy, r, 0, 2 * Math.PI); - ctx.stroke(); - ctx.fill(); + offsetLeft() { + return this.offset().left; } - drawText(x, y, text) { - this.context().fillText(text, x, y); + offsetTop() { + return this.offset().top; } - /* utility */ - fullscreen () { - var element = this.el; - - if (element.requestFullscreen) { - element.requestFullscreen(); - } else if (element.wekitRequestFullscreen) { - element.wekitRequestFullscreen(); + position() { + if (this.el.style.top) { + return { + top: parseFloat(this.css("top")), + left: parseFloat(this.css("left")) + }; + } else { + return this.rect(); } } - } - const makeEventChecker = (value, split = CHECK_SAPARATOR) => { - return ` ${split} ${value}`; - }; + size() { + return [this.width(), this.height()]; + } - // event name regular expression - const CHECK_DOM_EVENT_PATTERN = /^dom (.*)/gi; - const CHECK_LOAD_PATTERN = /^load (.*)/gi; - const CHECK_BIND_PATTERN = /^bind (.*)/gi; + width() { + return this.el.offsetWidth || this.rect().width; + } + contentWidth() { + return ( + this.width() - + this.cssFloat("padding-left") - + this.cssFloat("padding-right") + ); + } + height() { + return this.el.offsetHeight || this.rect().height; + } - const NAME_SAPARATOR = ":"; - const CHECK_SAPARATOR = "|"; - const DOM_EVENT_SAPARATOR = "dom "; - const LOAD_SAPARATOR = "load "; - const BIND_SAPARATOR = "bind "; + contentHeight() { + return ( + this.height() - + this.cssFloat("padding-top") - + this.cssFloat("padding-bottom") + ); + } - const SAPARATOR = ' '; + val(value) { + if (isUndefined(value)) { + return this.el.value; + } else if (isNotUndefined(value)) { + var tempValue = value; - const refManager = {}; + if (value instanceof Dom) { + tempValue = value.val(); + } - const DOM_EVENT_MAKE = (...keys) => { - var key = keys.join(NAME_SAPARATOR); - return (...args) => { - return DOM_EVENT_SAPARATOR + [key, ...args].join(SAPARATOR); - }; - }; + this.el.value = tempValue; + } - const CUSTOM = DOM_EVENT_MAKE; - const CLICK = DOM_EVENT_MAKE("click"); - const DOUBLECLICK = DOM_EVENT_MAKE("dblclick"); - const MOUSEDOWN = DOM_EVENT_MAKE("mousedown"); - const MOUSEUP = DOM_EVENT_MAKE("mouseup"); - const MOUSEMOVE = DOM_EVENT_MAKE("mousemove"); - const MOUSEOVER = DOM_EVENT_MAKE("mouseover"); - const MOUSEOUT = DOM_EVENT_MAKE("mouseout"); - const MOUSEENTER = DOM_EVENT_MAKE("mouseenter"); - const MOUSELEAVE = DOM_EVENT_MAKE("mouseleave"); - const TOUCHSTART = DOM_EVENT_MAKE("touchstart"); - const TOUCHMOVE = DOM_EVENT_MAKE("touchmove"); - const TOUCHEND = DOM_EVENT_MAKE("touchend"); - const KEYDOWN = DOM_EVENT_MAKE("keydown"); - const KEYUP = DOM_EVENT_MAKE("keyup"); - const KEYPRESS = DOM_EVENT_MAKE("keypress"); - const DRAG = DOM_EVENT_MAKE("drag"); - const DRAGSTART = DOM_EVENT_MAKE("dragstart"); - const DROP = DOM_EVENT_MAKE("drop"); - const DRAGOVER = DOM_EVENT_MAKE("dragover"); - const DRAGENTER = DOM_EVENT_MAKE("dragenter"); - const DRAGLEAVE = DOM_EVENT_MAKE("dragleave"); - const DRAGEXIT = DOM_EVENT_MAKE("dragexit"); - const DRAGOUT = DOM_EVENT_MAKE("dragout"); - const DRAGEND = DOM_EVENT_MAKE("dragend"); - const CONTEXTMENU = DOM_EVENT_MAKE("contextmenu"); - const CHANGE = DOM_EVENT_MAKE("change"); - const INPUT = DOM_EVENT_MAKE("input"); - const FOCUS = DOM_EVENT_MAKE("focus"); - const FOCUSIN = DOM_EVENT_MAKE("focusin"); - const FOCUSOUT = DOM_EVENT_MAKE("focusout"); - const BLUR = DOM_EVENT_MAKE("blur"); - const PASTE = DOM_EVENT_MAKE("paste"); - const RESIZE = DOM_EVENT_MAKE("resize"); - const SCROLL = DOM_EVENT_MAKE("scroll"); - const SUBMIT = DOM_EVENT_MAKE("submit"); - const POINTERSTART = CUSTOM("pointerdown"); - const POINTERMOVE = CUSTOM("pointermove"); - const POINTEREND = CUSTOM("pointerup"); - const CHANGEINPUT = CUSTOM("change", "input"); - const WHEEL = CUSTOM("wheel", "mousewheel", "DOMMouseScroll"); - const ANIMATIONSTART = DOM_EVENT_MAKE('animationstart'); - const ANIMATIONEND = DOM_EVENT_MAKE('animationend'); - const ANIMATIONITERATION = DOM_EVENT_MAKE('animationiteration'); - const TRANSITIONSTART = DOM_EVENT_MAKE('transitionstart'); - const TRANSITIONEND = DOM_EVENT_MAKE('transitionend'); - const TRANSITIONRUN = DOM_EVENT_MAKE('transitionrun'); - const TRANSITIONCANCEL = DOM_EVENT_MAKE('transitioncancel'); + return this; + } - // Predefined CHECKER - const CHECKER = (value, split = CHECK_SAPARATOR) => { - return makeEventChecker(value, split); - }; + matches (selector) { + if (this.el) { - const AFTER = (value, split = CHECK_SAPARATOR) => { - return makeEventChecker(`after(${value})`, split); - }; + if (!this.el.matches) return null; - const BEFORE = (value, split = CHECK_SAPARATOR) => { - return makeEventChecker(`before(${value})`, split); - }; + if (this.el.matches(selector)) { + return this; + } + return this.parent().matches(selector); + } - const IF = CHECKER; - const KEY = CHECKER; + return null; + } - const ARROW_UP = CHECKER('ArrowUp'); - const ARROW_DOWN = CHECKER('ArrowDown'); - const ARROW_LEFT = CHECKER('ArrowLeft'); - const ARROW_RIGHT = CHECKER('ArrowRight'); - const ENTER = CHECKER('Enter'); - const SPACE = CHECKER('Space'); - const ESCAPE = CHECKER('Escape'); - const ALT = CHECKER("isAltKey"); - const SHIFT = CHECKER("isShiftKey"); - const META = CHECKER("isMetaKey"); - const CONTROL = CHECKER("isCtrlKey"); - const SELF = CHECKER("self"); + get value() { + return this.el.value; + } - const FIT = CHECKER("fit"); - const PASSIVE = CHECKER("passive"); - const VDOM = CHECKER('vdom'); + get naturalWidth () { + return this.el.naturalWidth + } - // event config method - const DEBOUNCE = (t = 100) => { - return CHECKER(`debounce(${t})`); - }; + get naturalHeight () { + return this.el.naturalHeight + } - const D1000 = DEBOUNCE(1000); + get files() { + return this.el.files ? [...this.el.files] : []; + } - const THROTTLE = (t = 100) => { - return CHECKER(`throttle(${t})`); - }; + realVal() { + switch (this.el.nodeType) { + case "INPUT": + var type = this.attr("type"); + if (type == "checkbox" || type == "radio") { + return this.checked(); + } + case "SELECT": + case "TEXTAREA": + return this.el.value; + } - const CAPTURE = CHECKER("capture()"); - // event config method + return ""; + } - // before method + show(displayType = "block") { + return this.css("display", displayType != "none" ? displayType : "block"); + } - // after method - const MOVE = (method = "move") => { - return AFTER(`bodyMouseMove ${method}`); - }; - const END = (method = "end") => { - return AFTER(`bodyMouseUp ${method}`); - }; + hide() { + return this.css("display", "none"); + } - const PREVENT = AFTER(`preventDefault`); - const STOP = AFTER(`stopPropagation`); + isHide () { + return this.css("display") == "none" + } - // Predefined LOADER - const LOAD = (value = "$el") => { - return LOAD_SAPARATOR + value; - }; + isShow () { + return !this.isHide(); + } - const createRef = value => { - if (value === '') return ''; + toggle(isForce) { + var currentHide = this.isHide(); - var id = uuid(); - refManager[id] = value; + if (arguments.length == 1) { + if (isForce) { + return this.show(); + } else { + return this.hide(); + } + } else { + if (currentHide) { + return this.show(); + } else { + return this.hide(); + } + } + } - return id; - }; + get totalLength () { + return this.el.getTotalLength() + } - const getRef = id => { - return refManager[id] || ''; - }; + scrollIntoView () { + this.el.scrollIntoView(); + } - const BIND_CHECK_FUNCTION = field => { - return function() { - return this.prevState[field] != this.state[field]; - }; - }; + addScrollLeft (dt) { + this.el.scrollLeft += dt; + return this; + } - const BIND_CHECK_DEFAULT_FUNCTION = () => { - return true; - }; + addScrollTop (dt) { + this.el.scrollTop += dt; + return this; + } - const BIND = (value = "$el", checkFieldOrCallback = '') => { - return ( - BIND_SAPARATOR + value + ( - checkFieldOrCallback ? CHECK_SAPARATOR + createRef(checkFieldOrCallback) : '' - ) - ); - }; + setScrollTop(scrollTop) { + this.el.scrollTop = scrollTop; + return this; + } - var Event = { - addEvent(dom, eventName, callback, useCapture = false) { - if (dom) { - dom.addEventListener(eventName, callback, useCapture); - } - }, + setScrollLeft(scrollLeft) { + this.el.scrollLeft = scrollLeft; + return this; + } - removeEvent(dom, eventName, callback) { - if (dom) { - dom.removeEventListener(eventName, callback); + scrollTop() { + if (this.el === document.body) { + return Dom.getScrollTop(); } - }, - pos(e) { - if (e.touches && e.touches[0]) { - return e.touches[0]; + return this.el.scrollTop; + } + + scrollLeft() { + if (this.el === document.body) { + return Dom.getScrollLeft(); } - return e; - }, + return this.el.scrollLeft; + } - posXY(e) { - var pos = this.pos(e); - return { - x: pos.pageX, - y: pos.pageY - }; + scrollHeight() { + return this.el.scrollHeight; } - }; - class BaseStore { - constructor(opt = {}) { - this.cachedCallback = {}; - this.callbacks = {}; - this.commandes = []; + scrollWidth() { + return this.el.scrollWidth; + } + + on(eventName, callback, opt1, opt2) { + this.el.addEventListener(eventName, callback, opt1, opt2); + + return this; } - getCallbacks(event) { - if (!this.callbacks[event]) { - this.callbacks[event] = []; - } + off(eventName, callback) { + this.el.removeEventListener(eventName, callback); - return this.callbacks[event] + return this; } - setCallbacks(event, list = []) { - this.callbacks[event] = list; + getElement() { + return this.el; } - on(event, originalCallback, context, delay = 0) { - var callback = delay > 0 ? debounce(originalCallback, delay) : originalCallback; + createChild(tag, className = '', attrs = {}, css = {}) { + let $element = Dom.create(tag, className, attrs); + $element.css(css); - this.getCallbacks(event).push({ event, callback, context, originalCallback }); + this.append($element); + + return $element; } - off(event, originalCallback) { + get firstChild() { + return Dom.create(this.el.firstElementChild); + } - if (arguments.length == 1) { - this.setCallbacks(event); - } else if (arguments.length == 2) { - this.setCallbacks(event, this.getCallbacks(event).filter(f => { - return f.originalCallback !== originalCallback - })); + children() { + var element = this.el.firstElementChild; + + if (!element) { + return []; } - } - offAll (context) { + var results = []; - Object.keys(this.callbacks).forEach(event => { - this.setCallbacks(event, this.getCallbacks(event).filter(f => { - return f.context !== context; - })); - }); + do { + results.push(Dom.create(element)); + element = element.nextElementSibling; + } while (element); + + return results; } - getCachedCallbacks (event) { - return this.getCallbacks(event); + childLength() { + return this.el.children.length; } - sendMessage(source, event, $2, $3, $4, $5) { - Promise.resolve().then(() => { - var list = this.getCachedCallbacks(event); - if (list) { - list - .filter(f => f.originalCallback.source !== source) - .forEach(f => { - f.callback($2, $3, $4, $5); - }); - } + replace(newElement) { - }); + if (this.el.parentNode) { + this.el.parentNode.replaceChild(newElement.el || newElement, this.el); + } + + return this; } - triggerMessage(source, event, $2, $3, $4, $5) { - Promise.resolve().then(() => { - var list = this.getCachedCallbacks(event); - if (list) { - list - .filter(f => f.originalCallback.source === source) - .forEach(f => { - f.callback($2, $3, $4, $5); - }); - } else { - console.warn(event, ' is not valid event'); - } + replaceChild(oldElement, newElement) { + this.el.replaceChild(newElement.el || newElement, oldElement.el || oldElement); + + return this; + } + checked(isChecked = false) { + if (arguments.length == 0) { + return !!this.el.checked; + } - }); + this.el.checked = !!isChecked; + + return this; } + click () { + this.el.click(); + return this; + } - emit($1, $2, $3, $4, $5) { - this.sendMessage(this.source, $1, $2, $3, $4, $5); + focus() { + this.el.focus(); + + return this; } - trigger($1, $2, $3, $4, $5) { - this.triggerMessage(this.source, $1, $2, $3, $4, $5); + select() { + this.el.select(); + return this; } - execute($1, $2, $3, $4, $5){ - this.runCommand(this.source, $1, $2, $3, $4, $5); + blur() { + this.el.blur(); + + return this; } - } - // collectProps 에서 제외될 메소드 목록 - const expectMethod = { - "constructor": true, - "initState": true, - "refresh": true, - "updateData": true, - "constructor": true, - "initializeProperty": true, - "created": true, - "getRealEventName": true, - "initializeStoreEvent": true, - "destoryStoreEvent": true, - "destroy": true, - "emit": true, - "trigger": true, - "on": true, - "off": true, - "setState": true, - "_reload": true, - "render": true, - "initialize": true, - "afterRender": true, - "components": true, - "getRef": true, - "parseTemplate": true, - "childrenIds": true, - "exists": true, - "parseProperty": true, - "parseSourceName": true, - "parseComponent": true, - "clean": true, - "refresh": true, - "template": true, - "eachChildren": true, - "initializeEvent": true, - "destroy": true, - "self": true, - "isAltKey": true, - "isCtrlKey": true, - "isShiftKey": true, - "isMetaKey": true, - "preventDefault": true, - "stopPropagation": true, - "bodyMouseMove": true, - "bodyMouseUp": true, - }; + select() { + this.el.select(); - class BaseHandler { - constructor (context, options = {}) { - this.context = context; - this.options = options; - } + return this; + } - // 초기화 설정 - initialize () { + // canvas functions + context(contextType = "2d") { + if (!this._initContext) { + this._initContext = this.el.getContext(contextType); } - // html 을 로드 할 때 - load () { + return this._initContext; + } - } + resize({ width, height }) { + // support hi-dpi for retina display + this._initContext = null; + var ctx = this.context(); + var scale = window.devicePixelRatio || 1; - // 새로고침 할 때 - refresh () { + this.px("width", +width); + this.px("height", +height); - } - - // 화면에 그린 이후에 실행 되는 로직들 - render () { + this.el.width = width * scale; + this.el.height = height * scale; - } + ctx.scale(scale, scale); + } - getRef(id) { - return this.context.getRef(id); - } - - splitMethodByKeyword (arr, keyword) { - var filterKeys = arr.filter(code => code.indexOf(`${keyword}(`) > -1); - var filterMaps = filterKeys.map(code => { - var [target, param] = code - .split(`${keyword}(`)[1] - .split(")")[0] - .trim() - .split(" "); - - return { target, param }; - }); - - return [filterKeys, filterMaps]; - } + toDataURL (type = 'image/png', quality = 1) { + return this.el.toDataURL(type, quality) + } - /** - * property 수집하기 - * 상위 클래스의 모든 property 를 수집해서 리턴한다. - */ - collectProps() { + clear() { + this.context().clearRect(0, 0, this.el.width, this.el.height); + } - var context = this.context; - var p = context.__proto__; - var results = []; - do { - var isObject = p instanceof Object; + update(callback) { + this.clear(); + callback.call(this, this); + } - if (isObject === false) { - break; - } - const names = Object.getOwnPropertyNames(p).filter(name => { - return context && isFunction(context[name]) && !expectMethod[name]; - }); + drawImage (img, dx = 0, dy = 0) { + var ctx = this.context(); + var scale = window.devicePixelRatio || 1; + ctx.drawImage(img, dx, dy, img.width, img.height, 0, 0, this.el.width / scale, this.el.height / scale); + } - results.push(...names); - p = p.__proto__; - } while (p); + drawOption(option = {}) { + var ctx = this.context(); - return results; - } + Object.assign(ctx, option); + } + drawLine(x1, y1, x2, y2) { + var ctx = this.context(); + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + ctx.closePath(); + } - filterProps(pattern) { - return this.collectProps().filter(key => { - return key.match(pattern); - }); - } + drawPath(...path) { + var ctx = this.context(); - run () { + ctx.beginPath(); - } + path.forEach((p, index) => { + if (index == 0) { + ctx.moveTo(p[0], p[1]); + } else { + ctx.lineTo(p[0], p[1]); + } + }); + ctx.stroke(); + ctx.fill(); + ctx.closePath(); + } - destroy() { + drawCircle(cx, cy, r) { + var ctx = this.context(); + ctx.beginPath(); + ctx.arc(cx, cy, r, 0, 2 * Math.PI); + ctx.stroke(); + ctx.fill(); + } + + drawText(x, y, text) { + this.context().fillText(text, x, y); + } + /* utility */ + fullscreen () { + var element = this.el; + + if (element.requestFullscreen) { + element.requestFullscreen(); + } else if (element.wekitRequestFullscreen) { + element.wekitRequestFullscreen(); } + } } const scrollBlockingEvents = { @@ -2736,6 +2736,7 @@ exports.DRAGOVER = DRAGOVER; exports.DRAGSTART = DRAGSTART; exports.DROP = DROP; + exports.Dom = Dom; exports.END = END; exports.ENTER = ENTER; exports.ESCAPE = ESCAPE; diff --git a/package-lock.json b/package-lock.json index c4588c0..011de66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10443,6 +10443,24 @@ "inherits": "^2.0.1" } }, + "rollup": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.23.0.tgz", + "integrity": "sha512-vLNmZFUGVwrnqNAJ/BvuLk1MtWzu4IuoqsH9UWK5AIdO3rt8/CSiJNvPvCIvfzrbNsqKbNzPAG1V2O4eTe2XZg==", + "dev": true, + "requires": { + "fsevents": "~2.1.2" + }, + "dependencies": { + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + } + } + }, "rollup-plugin-serve": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rollup-plugin-serve/-/rollup-plugin-serve-1.0.1.tgz", diff --git a/package.json b/package.json index d6e8c2c..dc7511e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@easylogic/sapa", - "version": "0.0.15", + "version": "0.1.0", "description": "Simple JS Application Library", "main": "dist/sapa.js", "module": "dist/sapa.esm.js", @@ -27,6 +27,7 @@ "html-webpack-plugin": "^3.2.0", "mini-css-extract-plugin": "^0.6.0", "postcss-loader": "^3.0.0", + "rollup": "^2.23.0", "rollup-plugin-serve": "^1.0.1", "sass": "^1.19.0", "sass-loader": "^7.1.0", diff --git a/src/util/App.js b/src/util/App.js index deb0bae..198d3f9 100644 --- a/src/util/App.js +++ b/src/util/App.js @@ -1,9 +1,10 @@ -import Dom from "./Dom"; + import { POINTERMOVE, POINTEREND, DEBOUNCE, RESIZE } from "./Event"; import BaseStore from "./BaseStore"; import { UIElement, EVENT } from "./UIElement"; import { debounce } from "./functions/func"; import { ADD_BODY_MOUSEMOVE, ADD_BODY_MOUSEUP } from "../types/constants"; +import { Dom } from "./Dom"; const EMPTY_POS = { x: 0, y: 0 }; const DEFAULT_POS = { x: Number.MAX_SAFE_INTEGER, y: Number.MAX_SAFE_INTEGER }; diff --git a/src/util/Dom.js b/src/util/Dom.js index 316110a..8a22dc0 100644 --- a/src/util/Dom.js +++ b/src/util/Dom.js @@ -7,7 +7,7 @@ import { } from "./functions/func"; import { DomDiff } from "./DomDiff"; -export default class Dom { +export class Dom { constructor(tag, className, attr) { if (isNotString(tag)) { this.el = tag; diff --git a/src/util/EventMachine.js b/src/util/EventMachine.js index e50541d..684ae7d 100644 --- a/src/util/EventMachine.js +++ b/src/util/EventMachine.js @@ -5,7 +5,6 @@ import { LOAD, VDOM, } from "./Event"; -import Dom from "./Dom"; import { isFunction, isArray, @@ -17,6 +16,7 @@ import { import DomEventHandler from "./handler/DomEventHandler"; import BindHandler from "./handler/BindHandler"; import { ADD_BODY_MOUSEMOVE, ADD_BODY_MOUSEUP } from "../types/constants"; +import { Dom } from "./Dom"; const REFERENCE_PROPERTY = "ref"; const TEMP_DIV = Dom.create("div"); diff --git a/src/util/handler/DomEventHandler.js b/src/util/handler/DomEventHandler.js index fd822d9..45ca29c 100644 --- a/src/util/handler/DomEventHandler.js +++ b/src/util/handler/DomEventHandler.js @@ -1,8 +1,7 @@ import BaseHandler from "./BaseHandler"; import Event, { CHECK_SAPARATOR, DOM_EVENT_SAPARATOR, SAPARATOR, NAME_SAPARATOR, CHECK_DOM_EVENT_PATTERN } from "../Event"; import { debounce, throttle, isNotUndefined, isFunction } from "../functions/func"; -import Dom from "../Dom"; - +import { Dom } from "../Dom"; const scrollBlockingEvents = { 'touchstart': true, diff --git a/src/util/index.js b/src/util/index.js index b0d2cb3..953ccca 100644 --- a/src/util/index.js +++ b/src/util/index.js @@ -1,4 +1,3 @@ - export * as App from "./App"; export * from "./Dom"; export * from "./Event";