Skip to content

Commit 1842a0d

Browse files
committed
[ Add ] Auto update based on Input events of Form fields
[ Optimize ] Performance of several methods
1 parent 4009aef commit 1842a0d

File tree

17 files changed

+1149
-119
lines changed

17 files changed

+1149
-119
lines changed

.esdoc.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
"option": {
2323
"enable": true
2424
}
25+
},
26+
{
27+
"name": "esdoc-ecmascript-proposal-plugin",
28+
"option": {
29+
"all": true
30+
}
2531
}
2632
]
2733
}

package-lock.json

Lines changed: 831 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
22
"name": "dom-renderer",
3-
"version": "0.7.0",
4-
"description": "Template engine based on HTML 5 & ECMAScript 6",
3+
"version": "0.8.0",
4+
"description": "Template engine based on HTML 5, ECMAScript 6 & MVVM",
55
"keywords": [
66
"template",
77
"html",
88
"ecmascript",
9-
"render"
9+
"render",
10+
"mvvm"
1011
],
1112
"license": "LGPL-3.0",
1213
"author": "shiy2008@gmail.com",
@@ -29,7 +30,8 @@
2930
"format": "prettier --write \"{,!(node_modules|.git|dist|docs)/**/}*.{html,md,css,less,js,json,yml,yaml}\"",
3031
"lint": "eslint source/ test/ --fix",
3132
"pack": "cross-env NODE_ENV=pack amd-bundle source/index dist/dom-renderer -m",
32-
"build": "npm run format && npm run lint && npm run pack",
33+
"patch": "babel source/DOM/polyfill.js -o dist/polyfill.js",
34+
"build": "npm run format && npm run lint && npm run pack && npm run patch",
3335
"debug": "npm run pack && mocha --inspect-brk",
3436
"test": "npm run build && mocha --exit && esdoc",
3537
"prepublishOnly": "npm test",
@@ -44,13 +46,15 @@
4446
"@babel/polyfill": "^7.2.5"
4547
},
4648
"devDependencies": {
49+
"@babel/cli": "^7.2.3",
4750
"@babel/polyfill": "^7.2.5",
4851
"@babel/preset-env": "^7.3.1",
4952
"@babel/register": "^7.0.0",
50-
"amd-bundle": "^1.7.4",
53+
"amd-bundle": "^1.7.5",
5154
"babel-plugin-inline-import": "^3.0.0",
5255
"cross-env": "^5.2.0",
5356
"esdoc": "^1.1.0",
57+
"esdoc-ecmascript-proposal-plugin": "^1.0.0",
5458
"esdoc-external-webapi-plugin": "^1.0.0",
5559
"esdoc-standard-plugin": "^1.0.0",
5660
"eslint": "^5.14.1",

source/DOM/CustomInputEvent.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* A composite Input Event with Input Method & Clipboard supported
3+
*/
4+
export default class CustomInputEvent extends CustomEvent {
5+
/**
6+
* @type {HTMLElement}
7+
*/
8+
get target() {
9+
var node = super.target;
10+
11+
if (this.composed) {
12+
const root = node.getRootNode();
13+
14+
if (root instanceof DocumentFragment) return root.host;
15+
}
16+
17+
return node;
18+
}
19+
}
20+
21+
function customInput(element, detail) {
22+
element.dispatchEvent(
23+
new CustomInputEvent('input', {
24+
bubbles: true,
25+
composed: true,
26+
detail
27+
})
28+
);
29+
}
30+
31+
/**
32+
* @param {HTMLElement} element
33+
*
34+
* @listens {InputEvent} - `input` event
35+
* @listens {CompositionEvent} - `compositionstart` & `compositionend` event
36+
* @listens {ClipboardEvent} - `paste` event
37+
* @listens {ClipboardEvent} - `cut` event
38+
*
39+
* @emits {CustomInputEvent}
40+
*/
41+
export function watchInput(element) {
42+
var IME, clipBoard;
43+
44+
element.addEventListener('compositionstart', () => (IME = true));
45+
46+
element.addEventListener(
47+
'compositionend',
48+
({ target, data }) => ((IME = false), customInput(target, data))
49+
);
50+
51+
element.addEventListener('input', event => {
52+
if (event instanceof CustomInputEvent) return;
53+
54+
if (clipBoard) clipBoard = false;
55+
else if (!IME) customInput(event.target, event.data);
56+
});
57+
58+
element.addEventListener('paste', ({ target, clipboardData }) => {
59+
if (!IME)
60+
(clipBoard = true),
61+
customInput(target, clipboardData.getData('text'));
62+
});
63+
64+
element.addEventListener('cut', ({ target }) => {
65+
if (!IME) (clipBoard = true), customInput(target);
66+
});
67+
}

source/DOM/polyfill.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { JSDOM } from 'jsdom';
2+
3+
export default JSDOM;
4+
5+
const { window } = new JSDOM('', {
6+
url: 'http://test.com/',
7+
pretendToBeVisual: true
8+
});
9+
10+
[
11+
'self',
12+
'document',
13+
'Node',
14+
'HTMLElement',
15+
'DocumentFragment',
16+
'DOMParser',
17+
'XMLSerializer',
18+
'NodeFilter',
19+
'InputEvent',
20+
'CustomEvent'
21+
].forEach(key => (global[key] = window[key]));
22+
23+
/**
24+
* @private
25+
*
26+
* @param {HTMLElement} input
27+
* @param {String} raw
28+
*
29+
* @emits {InputEvent} `input`
30+
*
31+
* @return {Promise}
32+
*/
33+
export function typeIn(input, raw) {
34+
return Promise.all(
35+
Array.from(
36+
raw,
37+
data =>
38+
new Promise(resolve =>
39+
setTimeout(() => {
40+
input.value += data;
41+
42+
input.dispatchEvent(
43+
new InputEvent('input', {
44+
bubbles: true,
45+
composed: true,
46+
data
47+
})
48+
);
49+
50+
resolve();
51+
})
52+
)
53+
)
54+
);
55+
}
Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
export const attributeMap = {
55
class: 'className',
66
for: 'htmlFor',
7-
readonly: 'readOnly',
8-
value: 'defaultValue'
7+
readonly: 'readOnly'
98
};
109

1110
const HTML_page = /<!?(DocType|html|head|body|meta|title|base)[\s\S]*?>/,
@@ -83,31 +82,31 @@ export function* walkDOM(root, filter) {
8382
}
8483

8584
/**
86-
* @param {Node} root
87-
* @param {RegExp} expression
88-
* @param {String} subView - CSS selector
89-
* @param {Object} parser
90-
* @param {function(attr: Attr): void} parser.attribute
91-
* @param {function(text: Text): void} parser.text
92-
* @param {function(node: Element): void} parser.view
85+
* @param {Node} root
86+
* @param {RegExp} expression
87+
* @param {Object} parser
88+
* @param {function(attr: Attr): void} parser.attribute
89+
* @param {function(text: Text): void} parser.text
90+
* @param {... function(node: Element): Boolean} parser.element - Key for CSS selector, Value for Callback
9391
*/
9492
export function scanTemplate(
9593
root,
9694
expression,
97-
subView,
98-
{ attribute, text, view }
95+
{ attribute, text, ...element }
9996
) {
100-
const iterator = walkDOM(root, node =>
101-
node.matches instanceof Function && node.matches(subView)
102-
? (view(node), NodeFilter.FILTER_REJECT)
103-
: NodeFilter.FILTER_ACCEPT
104-
);
97+
const iterator = walkDOM(root, node => {
98+
if (node.matches)
99+
for (let selector in element)
100+
if (node.matches(selector) && element[selector](node) === false)
101+
return NodeFilter.FILTER_REJECT;
102+
103+
return NodeFilter.FILTER_ACCEPT;
104+
});
105105

106106
Array.from(iterator, node => {
107107
switch (node.nodeType) {
108108
case 1:
109-
[].forEach.call(
110-
node.attributes,
109+
Array.from(node.attributes).forEach(
111110
attr => expression.test(attr.value) && attribute(attr)
112111
);
113112
break;
@@ -123,3 +122,34 @@ export function scanTemplate(
123122
export function nextTick() {
124123
return new Promise(resolve => self.requestAnimationFrame(resolve));
125124
}
125+
126+
/**
127+
* @param {HTMLElement} input
128+
*
129+
* @return {String|String[]}
130+
*/
131+
export function valueOf(input) {
132+
switch (input.type || input.tagName.toLowerCase()) {
133+
case 'radio':
134+
return input.checked ? input.value : null;
135+
case 'checkbox':
136+
return Array.from(
137+
input.form
138+
? input.form.elements[input.name]
139+
: input
140+
.getRootNode()
141+
.querySelectorAll(
142+
`input[type="checkbox"][name="${input.name}"]`
143+
),
144+
node => (node.checked ? node.value : null)
145+
);
146+
case 'select':
147+
return input.multiple
148+
? Array.from(input.options, node =>
149+
node.selected ? node.value : null
150+
)
151+
: input.value;
152+
}
153+
154+
return input.value;
155+
}

source/index.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
export * from './utility';
1+
export * from './DOM/utility';
22

3-
export { default as Template } from './Template';
3+
export * from './DOM/CustomInputEvent';
44

5-
export { default as Model } from './Model';
5+
export { default as Template } from './view/Template';
66

7-
export { default } from './View';
7+
export { default as Model } from './view/Model';
8+
9+
export { default } from './view/View';

source/polyfill.js

Lines changed: 0 additions & 21 deletions
This file was deleted.

source/Model.js renamed to source/view/Model.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { nextTick } from './utility';
1+
import { nextTick } from '../DOM/utility';
22

33
const view_data = new WeakMap(),
44
cache_data = Symbol('Cache data'),

0 commit comments

Comments
 (0)