|
60 | 60 | let pattern = /({{[^}]+}})/g;
|
61 | 61 |
|
62 | 62 | export class Fields {
|
63 |
| - attrs: { attr: Attr, origValue: string }[] = []; |
64 |
| - elems: { elem: Node, origText: string }[] = []; |
| 63 | + // elem과 replacedLocalName은 j8-attr-* 속성을 지원하는 용도이다. |
| 64 | + // 이들이 설정되어 있으면 아직 DOM에는 해당 attr이 설정되어 있지 않은 것이다. |
| 65 | + // 일단 attr이 설정된 뒤에는 그 Attr 노드를 직접 고치면 되므로 elem/replacedLocalName은 null이 된다. |
| 66 | + attrs: { attr: Attr, elem: Element, replacedLocalName: string, origValue: string }[] = []; |
| 67 | + nodes: { node: Node, origText: string }[] = []; |
65 | 68 |
|
66 | 69 | set(data: any): void {
|
67 | 70 | for (let a of this.attrs) {
|
68 |
| - var newValue = this.replace(a.origValue, data); |
69 |
| - if (a.attr.value !== newValue) { |
70 |
| - a.attr.value = newValue; |
| 71 | + let newValue = this.replace(a.origValue, data); |
| 72 | + if (a.replacedLocalName) { |
| 73 | + if (a.attr.namespaceURI !== null) { |
| 74 | + a.attr = document.createAttributeNS(a.attr.namespaceURI, a.replacedLocalName); |
| 75 | + a.attr.value = newValue; |
| 76 | + a.elem.setAttributeNodeNS(a.attr); |
| 77 | + } else { |
| 78 | + a.attr = document.createAttribute(a.replacedLocalName); |
| 79 | + a.attr.value = newValue; |
| 80 | + a.elem.setAttributeNode(a.attr); |
| 81 | + } |
| 82 | + a.elem = null; |
| 83 | + a.replacedLocalName = null; |
| 84 | + } else { |
| 85 | + if (a.attr.value !== newValue) { |
| 86 | + a.attr.value = newValue; |
| 87 | + } |
71 | 88 | }
|
72 | 89 | }
|
73 |
| - for (let e of this.elems) { |
| 90 | + |
| 91 | + for (let e of this.nodes) { |
74 | 92 | var newText = this.replace(e.origText, data);
|
75 |
| - if (e.elem.textContent !== newText) { |
76 |
| - e.elem.textContent = newText; |
| 93 | + if (e.node.textContent !== newText) { |
| 94 | + e.node.textContent = newText; |
77 | 95 | }
|
78 | 96 | }
|
79 | 97 | }
|
|
110 | 128 | root.find('[j8-control]').each(
|
111 | 129 | (i, v) => {
|
112 | 130 | let cid = v.getAttribute('j8-control');
|
| 131 | + v.removeAttribute('j8-control'); |
113 | 132 |
|
114 | 133 | if (this.controls[cid]) {
|
115 | 134 | console.error('(Jul8) duplicate control id: [' + cid + ']')
|
|
119 | 138 |
|
120 | 139 | if (scanFields) {
|
121 | 140 | this.fields = new Fields();
|
122 |
| - this.visitElem(root.get(0)); |
| 141 | + this.visitNode(root.get(0)); |
123 | 142 | }
|
124 | 143 | }
|
125 | 144 |
|
|
130 | 149 |
|
131 | 150 | if (elem.hasAttribute('j8-listItem')) {
|
132 | 151 | let itemId = elem.getAttribute('j8-listItem');
|
| 152 | + elem.removeAttribute('j8-listItem'); |
| 153 | + elem.removeAttribute('j8-model'); |
133 | 154 | if (this.lists[itemId]) {
|
134 | 155 | console.error('(Jul8) duplicate listItem id: [' + itemId + ']')
|
135 | 156 | }
|
|
145 | 166 | }
|
146 | 167 | }
|
147 | 168 |
|
148 |
| - private visitElem(elem: Node) { |
149 |
| - let childNodes = elem.childNodes; |
| 169 | + private visitNode(node: Node) { |
| 170 | + let childNodes = node.childNodes; |
150 | 171 | if (childNodes.length > 0) {
|
151 | 172 | for (let i = 0; i < childNodes.length; ++i) {
|
152 |
| - this.visitElem(childNodes[i]) |
| 173 | + this.visitNode(childNodes[i]) |
153 | 174 | }
|
154 | 175 | }
|
155 | 176 | else {
|
156 |
| - if (elem.textContent.search(pattern) >= 0) { |
157 |
| - let e = { elem: elem, origText: elem.textContent }; |
158 |
| - this.fields.elems.push(e); |
| 177 | + if (node.textContent.search(pattern) >= 0) { |
| 178 | + let n = { node: node, origText: node.textContent }; |
| 179 | + this.fields.nodes.push(n); |
159 | 180 | }
|
160 | 181 | }
|
161 | 182 |
|
162 |
| - if (elem.attributes) { |
| 183 | + if (node.attributes) { |
| 184 | + let elem = node as Element; |
| 185 | + let replacedAttrs: { name: string, attr: Attr }[] = []; |
| 186 | + let removedAttrs: Attr[] = []; |
| 187 | + |
163 | 188 | for (let i = 0; i < elem.attributes.length; ++i) {
|
164 | 189 | let attr = elem.attributes[i];
|
| 190 | + let replacedLocalName = null; |
| 191 | + if (attr.localName.substring(0, 8) === 'j8-attr-') { |
| 192 | + replacedLocalName = attr.localName.substring(8); |
| 193 | + } |
165 | 194 | if (attr.value.search(pattern) >= 0) {
|
166 |
| - if (attr.name === 'style') { console.error("(Jul8) can't use {{ ... }} notation in `style` attribute."); } |
167 |
| - if (attr.name === 'class') { console.error("(Jul8) can't use {{ ... }} notation in `class` attribute."); } |
168 |
| - let a = { attr: attr, origValue: attr.value }; |
| 195 | + let localName = replacedLocalName || attr.localName; |
| 196 | + if (localName === 'style') { console.error("(Jul8) can't use {{ ... }} notation in `style` attribute."); } |
| 197 | + if (localName === 'class') { console.error("(Jul8) can't use {{ ... }} notation in `class` attribute."); } |
| 198 | + let a = { elem: elem, attr: attr, replacedLocalName: replacedLocalName, origValue: attr.value }; |
169 | 199 | this.fields.attrs.push(a);
|
| 200 | + if (replacedLocalName) { |
| 201 | + // j8-attr-* 속성은 DOM에는 남아 있지 않고 나중에 재구성한다. |
| 202 | + removedAttrs.push(attr); |
| 203 | + } |
| 204 | + } else if (replacedLocalName) { |
| 205 | + // j8-attr-XXX="YYY" 속성에 {{}}가 아예 안 들어 있을 수도 있다. |
| 206 | + // 이 경우에도 XXX="YYY"로 일괄 처리는 해야 한다. |
| 207 | + replacedAttrs.push({ name: replacedLocalName, attr: attr }); |
170 | 208 | }
|
171 | 209 | }
|
| 210 | + |
| 211 | + for (let { name, attr } of replacedAttrs) { |
| 212 | + elem.removeAttributeNode(attr); |
| 213 | + if (attr.namespaceURI !== null) { |
| 214 | + elem.setAttributeNS(attr.namespaceURI, name, attr.value); |
| 215 | + } else { |
| 216 | + elem.setAttribute(name, attr.value); |
| 217 | + } |
| 218 | + } |
| 219 | + |
| 220 | + for (let attr of removedAttrs) { |
| 221 | + elem.removeAttributeNode(attr); |
| 222 | + } |
172 | 223 | }
|
173 | 224 | }
|
174 | 225 |
|
|
210 | 261 | let j = $(v);
|
211 | 262 | j.detach();
|
212 | 263 | let tid = v.getAttribute('j8-template');
|
| 264 | + v.removeAttribute('j8-template'); |
213 | 265 | this.templates[tid] = j;
|
214 | 266 | });
|
215 | 267 | }
|
|
0 commit comments