├── .gitignore
├── .jshintrc
├── .travis.yml
├── LICENSE
├── README.md
├── bower.json
├── dist
├── zect.js
└── zect.min.js
├── gulpfile.js
├── index.js
├── lib
├── compile.js
├── compiler.js
├── conf.js
├── directives.js
├── dm.js
├── elements.js
├── execute.js
├── expression.js
├── is.js
├── scope.js
├── util.js
└── zect.js
├── package.json
└── test
├── array.html
├── debug.html
├── index.html
├── runner.html
├── spec.directive.if.js
├── spec.directive.js
├── spec.directive.repeat.js
├── spec.js
└── tools.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /test/node_modules
3 | .DS_Store
4 | Thumbs.db
5 | *.bak
6 | *.tmp
7 | *.log
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "bitwise": false,
3 | "strict": true,
4 | "browser": true,
5 | "node": true,
6 | "strict": true,
7 | "expr": true,
8 | "globals": {
9 | "require": true,
10 | "module": true,
11 | "Zect": true,
12 | "it": true,
13 | "describe": true,
14 | "assert": true,
15 | "expect": true
16 | },
17 | "asi": true //makes it an error to leave a trailing whitespace in your code
18 | }
19 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - '0.12'
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 guankaishe
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | Zect , component & mvvm
3 | ====
4 | [](https://travis-ci.org/switer/Zect)
5 | [](https://badge.fury.io/js/zect)
6 |
7 | A lightweight Web components and MVVM framework.
8 | **Zect**'s state observer is power by [muxjs](https://github.com/switer/muxjs)
9 |
10 | ## Examples
11 |
12 | * Todo MVC: http://zectjs.github.io/zect-todo
13 | * Production:
14 | - http://corner.ucweb.com
15 | - http://m.v.qq.com/gift/bigbang/
16 |
17 | ## Downloads
18 | - [zect.js](https://raw.githubusercontent.com/switer/zect/master/dist/zect.js)
19 | - [zect.min.js](https://raw.githubusercontent.com/switer/zect/master/dist/zect.min.js)
20 |
21 | ## Usage
22 |
23 | ```html
24 |
25 |
26 |
27 | {title}
28 |
29 |
30 | ```
31 | Define and instance
32 |
33 | ```js
34 | var app = new Zect({
35 | el: '#app',
36 | data: function () {
37 | return {
38 | title: 'Hello, Zect'
39 | }
40 | }
41 | })
42 | ```
43 |
44 | ## API Reference
45 | - Global API
46 | * [Zect()](https://github.com/switer/Zect/wiki/Global-API#zectoptions)
47 | * [Zect.extend()](https://github.com/switer/Zect/wiki/Global-API#zectextendoptions)
48 | * [Zect.component()](https://github.com/switer/Zect/wiki/Global-API#zectcomponentid-definition)
49 | * [Zect.namespace(namespace)](https://github.com/switer/Zect/wiki/Global-API#zectnamespacenamespace)
50 | * [Zect.directive(id, definition)](https://github.com/switer/Zect/wiki/Global-API#zectdirectiveid-definition)
51 |
52 | - Instance Options
53 | * [el](https://github.com/switer/Zect/wiki/Instance-Options#el)
54 | * [data](https://github.com/switer/Zect/wiki/Instance-Options#data)
55 | * [mixins](https://github.com/switer/Zect/wiki/Instance-Options#mixins)
56 | * [replace](https://github.com/switer/Zect/wiki/Instance-Options#replace)
57 | * [methods](https://github.com/switer/Zect/wiki/Instance-Options#methods)
58 | * [template](https://github.com/switer/Zect/wiki/Instance-Options#template)
59 | * [computed](https://github.com/switer/Zect/wiki/Instance-Options#computed)
60 | * [directives](https://github.com/switer/Zect/wiki/Instance-Options#directives)
61 | * [components](https://github.com/switer/Zect/wiki/Instance-Options#components)
62 |
63 | - Lifecyle Methods
64 | * [created](https://github.com/switer/Zect/wiki/Lifecyle-Methods#created)
65 | * [ready](https://github.com/switer/Zect/wiki/Lifecyle-Methods#ready)
66 | * [destroy](https://github.com/switer/Zect/wiki/Lifecyle-Methods#destroy)
67 |
68 | - Instance Methods
69 | * [$set](https://github.com/switer/Zect/wiki/Instance-Methods#setkeypath-value)
70 | * [$get](https://github.com/switer/Zect/wiki/Instance-Methods#getkeypath)
71 | * [$watch](https://github.com/switer/Zect/wiki/Instance-Methods#watchkeypath-callback)
72 | * [$unwatch](https://github.com/switer/Zect/wiki/Instance-Methods#unwatchcallback)
73 | * [$compile](https://github.com/switer/Zect/wiki/Instance-Methods#compileel-scope)
74 | * [$component](https://github.com/switer/Zect/wiki/Instance-Methods#componentid)
75 | * [$destroy](https://github.com/switer/Zect/wiki/Instance-Methods#destroy)
76 |
77 | - Instance Properties
78 | * [$el](https://github.com/switer/Zect/wiki/Instance-Properties#el)
79 | * [$refs](https://github.com/switer/Zect/wiki/Instance-Properties#refs)
80 | * [$methods](https://github.com/switer/Zect/wiki/Instance-Properties#methods)
81 | * [$destroyed](https://github.com/switer/Zect/wiki/Instance-Properties#destroyed)
82 |
83 | - Template Syntax
84 | * [if](https://github.com/switer/Zect/wiki/Template-Syntax#if)
85 | * [repeat](https://github.com/switer/Zect/wiki/Template-Syntax#repeat)
86 | * [template](https://github.com/switer/Zect/wiki/Template-Syntax#template)
87 | * [{expression}](https://github.com/switer/Zect/wiki/Template-Syntax#expression)
88 | * [{- expression}](https://github.com/switer/Zect/wiki/Template-Syntax#--expression)
89 |
90 | - Direcitves
91 | * [on](https://github.com/switer/Zect/wiki/Directives#z-on)
92 | * [show](https://github.com/switer/Zect/wiki/Directives#z-show)
93 | * [html](https://github.com/switer/Zect/wiki/Directives#z-html)
94 | * [attr](https://github.com/switer/Zect/wiki/Directives#z-attr)
95 | * [class](https://github.com/switer/Zect/wiki/Directives#z-class)
96 | * [style](https://github.com/switer/Zect/wiki/Directives#z-style)
97 | * [component](https://github.com/switer/Zect/wiki/Directives#z-component)
98 | * [static](https://github.com/switer/Zect/wiki/Directives#z-static)
99 |
100 | ## Guide
101 | ### Custom directive
102 |
103 | Options's Methods:
104 | * **bind** Call only once when directive is binding.
105 | * **update** Call every time when expression's value has been changed.
106 | * **unbind** Call only once when directive is unbinded.
107 |
108 | Directive instance properties:
109 | * **$vm** Mounted VM of the directive
110 | * **$el** Mounted target Node of the directive
111 | * **$id** Current directive instance id
112 | * **$scope** Repeat directive will create a scope for each item when compiling,
113 | so your can access "$index", "$value" through "$scope".
114 |
115 | **Example below:**
116 |
117 | ```html
118 |
119 | ```
120 |
121 | ```js
122 | Zect.directive('tap', {
123 | bind: function (result, expression) {
124 | // do something when init
125 | },
126 | update: function (result) {
127 | // do something when state change or after init
128 | },
129 | unbind: function () {
130 | // do something before destroy the directive instance
131 | }
132 | })
133 | ```
134 |
135 | ### Two way binding
136 |
137 | ```html
138 |
139 |
140 |
141 |
142 | ```
143 |
144 | ```js
145 | new Zect({
146 | el: '#con',
147 | data: {
148 | search: 'Please input'
149 | },
150 | methods: {
151 | onSubmit: function () {
152 | this.$data.search // input-value
153 | }
154 | }
155 | })
156 | ```
157 |
158 | ### Filter Expression
159 | Filters actually are function call using in template's expression.
160 |
161 | ```html
162 |
163 |
164 | {$value}
165 |
166 |
167 | ```
168 |
169 | ```js
170 | new Zect({
171 | el: '#con',
172 | data: function () {
173 | return [1,2,3,4,5]
174 | },
175 | methods: {
176 | lessThanFour: function (items) {
177 | return items.filter(function (item) {
178 | if (item < 4) return true
179 | })
180 | }
181 | }
182 | })
183 | ```
184 |
185 | * **Render result:**
186 |
187 | ```html
188 |
189 | 1
190 | 2
191 | 3
192 |
193 | ```
194 |
195 | ### Template syntax
196 |
197 | * **Content Render:**
198 |
199 | ```html
200 |
201 | {title}
202 |
203 |
204 | {- title}
205 | ```
206 |
207 | * **Javascript Syntax In Expression:**
208 |
209 | ```html
210 |
211 | {'Current time is: ' + new Date()}
212 |
213 |
214 | {- 'Current Page: ' + page}
215 | ```
216 |
217 | * **Condition Statements:**
218 | `"is"` is a keyword-attribute for the "if" directive.
219 | If value is truly, the template that is included by "if" directive element will be compiled and insert into to parent's DOM tree.
220 | Otherwise template will be removed from parent's DOM tree.
221 |
222 | ```html
223 |
224 |
225 | {title}
226 |
227 | ```
228 |
229 | * **Array Iterator:**
230 | `"items"` is a keyword-attribute for the "repeat" directive.
231 | The value of items's expression should be an Array object.
232 |
233 | ```html
234 |
235 |
236 | {- $value}
237 |
238 | ```
239 |
240 | ### Reusable Component
241 |
242 | Zect support reusable component that are conceptually similar to Web Components.
243 |
244 | * **define:**
245 |
246 | ```html
247 |
252 | ```
253 |
254 | ```js
255 | Zect.component('c-header', {
256 | template: document.querySelector('#tpl-header').innerHTML,
257 | data: {
258 | title: 'index'
259 | },
260 | ready: function () {
261 |
262 | }
263 | })
264 | ```
265 | * **use:**
266 |
267 | ```html
268 |
269 |
273 |
278 |
279 | ```
280 |
281 | * **render result:**
282 |
283 | ```html
284 |
285 |
288 |
291 |
292 | ```
293 |
294 | ## Component Template
295 |
296 | Zect will copy all attributes for "template" element to instance component element.
297 |
298 | Component's HTML template:
299 |
300 | ```html
301 |
307 | ```
308 |
309 | Define component:
310 | ```javascript
311 | Zect.component('c-header', {
312 | template: document.querySelector('#tpl-header').innerHTML
313 | })
314 | ```
315 |
316 |
317 | ## Component Attributes
318 |
319 | * **data**
320 | "data" property is used to declare binding data from the parent ViewModel.
321 | Just like your instance a component and pass data option. When those binding variables of expression change,
322 | `Zect` will be re-excute the expression and call component instance's "$set" method automatically for updating child component.
323 |
324 | > Notice: r-data has multiple keys must using ';' as separator, otherwise can't create binding for each keys.
325 |
326 | ```html
327 |
328 |
334 |
335 |
336 | ```
337 |
338 | * **methods**
339 | Just like your instance a component and pass method option. Methods only set once, so when those binding variables of expression change, it will do nothing.
340 |
341 | ```html
342 |
343 |
349 |
350 | ```
351 |
352 | * **ref**
353 | This option is used to save ref to parent ViewModel, so that access it's instance with the specified name by "$refs".
354 |
355 | ```html
356 |
357 |
358 |
359 | ```
360 |
361 | ```js
362 | this.$refs.header // access child component instance.
363 | ```
364 |
365 | * **replace**
366 | This option is uesed to reduce one level document structure. if attribute value equal "true",
367 | will replace component's element with it's main child element.
368 |
369 | Component template:
370 | ```html
371 |
374 | ```
375 |
376 | Usage:
377 | ```html
378 |
379 |
380 |
381 | ```
382 |
383 | Render result:
384 | ```html
385 |
386 |
389 |
390 | ```
391 |
392 |
393 | ## Computed Properties
394 | For those complicated logic, you should use computed properties to replace inline expressions.
395 |
396 | ```js
397 | var demo = new Zect({
398 | data: {
399 | host: 'https://github.com',
400 | user: 'switer',
401 | repos: 'zect'
402 | },
403 | computed: {
404 | link: {
405 | // property dependencies of getter
406 | deps: ['host', 'user', 'repos'],
407 | // property getter
408 | get: function () {
409 | var $data = this.$data
410 | return [$data.host, $data.user, $data.repos].join('/') // https://github.com/switer/zect
411 | },
412 | // setter is optional
413 | set: function (link) {
414 | // input: https://github.com/zectjs/zect.github.io
415 | var $data = this.$data
416 | var parts = link.replace(/\/$/, '').split('\/')
417 | $data.repos = parts.pop()
418 | $data.user = parts.pop()
419 | $data.host = parts.join('/')
420 | }
421 | }
422 | }
423 | })
424 |
425 | ```
426 | ## List operate
427 |
428 | - **Display List**
429 |
430 | Use `z-repeat` block element to repeat display template.
431 |
432 | ```html
433 |
434 |
435 |
436 | {$value}
437 |
438 |
439 |
440 | ```
441 | ```js
442 | new Zect({
443 | data: {
444 | items: ["Switer", "Zect", "Xiaokai"]
445 | }
446 | })
447 | ```
448 | Result:
449 | ```
450 | * Switer
451 | * Zect
452 | * Xiaokai
453 | ```
454 |
455 | - **Append More**
456 |
457 | ```js
458 | vm.$data.items.$concat(['Web Component'])
459 | ```
460 | Will delta update:
461 | ```
462 | * Switer
463 | * Zect
464 | * Xiaokai
465 | + Web Component
466 | ```
467 |
468 | - **Append Before**
469 |
470 | ```js
471 | vm.$data.items.splice(0, 0, 'Web Component', 'MVVM')
472 | ```
473 | Result:
474 | ```
475 | + Web Component
476 | + MVVM
477 | * Switer
478 | * Zect
479 | * Xiaokai
480 | ```
481 |
482 | - **Remove**
483 |
484 | ```js
485 | vm.$data.items.splice(1, 1)
486 | ```
487 | Result:
488 | ```
489 | + Web Component
490 | - MVVM
491 | * Switer
492 | * Zect
493 | * Xiaokai
494 | ```
495 |
496 | - **Push**
497 |
498 | ```js
499 | vm.$data.items.push('Web Component')
500 | ```
501 | Result:
502 | ```
503 | * Switer
504 | * Zect
505 | * Xiaokai
506 | + Web Component
507 | ```
508 |
509 | and `shift`, `unshift`, `sort`, `reverse`, `pop`. `shift`, `unshift`, `pop` whill be Update in delta (includes `splice` and `concat`).
510 |
511 |
512 | 
513 |
514 | ## License
515 |
516 | MIT
517 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zect",
3 | "main": "index.js",
4 | "version": "0.0.0",
5 | "homepage": "https://github.com/switer/Zect",
6 | "authors": [
7 | "switer "
8 | ],
9 | "keywords": [
10 | "web",
11 | "component",
12 | "mvvm",
13 | "data-binding",
14 | "lightweight",
15 | "directive"
16 | ],
17 | "license": "MIT",
18 | "ignore": [
19 | "**/.*",
20 | "node_modules",
21 | "bower_components",
22 | "test"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/dist/zect.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Zect v1.2.26
3 | * (c) 2015 guankaishe
4 | * Released under the MIT License.
5 | */
6 | !function(a,b){"object"==typeof exports&&"object"==typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):"object"==typeof exports?exports.Zect=b():a.Zect=b()}(this,function(){return function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={exports:{},id:d,loaded:!1};return a[d].call(e.exports,e,e.exports,b),e.loaded=!0,e.exports}var c={};return b.m=a,b.c=c,b.p="",b(0)}([function(a,b,c){a.exports=c(1)},function(a,b,c){"use strict";function d(a){var b=k([a]);return e.call(this,b)}function e(a){function b(){var a={};return G.forEach(function(b){p.objEach(b,function(b,c){a[J+b]=c})}),a}function c(a,b){a&&a.bindings&&a.bindings.push(b)}function d(a,b,c){var d,f=!0;switch(a.nodeType){case 1:if(a.hasAttribute(J+"static")){f=!1;break}if(a=e(a),d=i(a,b,c)){f=!1;break}if(k(a,b),d=j(a,B,b,c)){f=!1;break}break;case 3:a.nodeValue.trim()&&(d=y(a,B,b,c)),f=!1;break;case 11:break;default:f=!1}return{into:!!f,inst:!d&&c?new s(a):d}}function e(a){var b,c,d=J+"repeat",f=J+"if";if(a.hasAttribute(f))c=f,b="is";else{if(!a.hasAttribute(d))return a;c=d,b="items"}var g=a.getAttribute(c),h=document.createElement(c);for(a.removeAttribute(c),a.parentNode&&a.parentNode.replaceChild(h,a),h.appendChild(a),h.setAttribute(b,g);a.hasAttribute(f)||a.hasAttribute(d);)e(a);return h}function h(a){var b,c=a.toLowerCase();return F.some(function(a){return a.hasOwnProperty(c)?(b=a[c],!0):void 0}),b}function i(a,b,d){var e,g=a.tagName;switch(!0){case n.IfElement(g):var h=f(a.children),i=[m(a).attr("is")];return h.forEach(function(a){n.ElseElement(a)&&i.push(m(a).attr(q.namespace+"else")||"")}),e=new x(B,b,a,z["if"],J+"if",i),d||e.$mount(a),H.push(e),c(b,e),e;case n.RepeatElement(g):return e=new x(B,b,a,z.repeat,J+"repeat",m(a).attr("items")),d||e.$mount(a),H.push(e),c(b,e),e}}function j(a,b,d){function e(a){var b;return a=a.replace(/^[^:]+:/,function(a){return b=a.replace(/:$/,"").trim(),""}).trim(),{name:b,expr:a,vars:t.extract(a)}}function g(a){var b=e(a);F[b.name]=b,(b.vars||[]).forEach(function(a){!G[a]&&(G[a]=[]),!~G[a].indexOf(b.name)&&G[a].push(b.name)})}var i=J+"component",j=a.getAttribute(i),k=a.tagName;if(j||"DIV"!=k&&"SPAN"!=k&&"A"!=k&&"IMG"!=k){j=j||k;var l=h(j);if(l){var n=m(a);if(n.removeAttr(i),a!==b.$el){var o=J+"ref",q=J+"data",s=J+"methods",u=J+"replace",v=n.attr(o),w=n.attr(q),x=n.attr(s),y=n.attr(u);n.removeAttr(o).removeAttr(q).removeAttr(s).removeAttr(u);var z,A,B,C=D(w),E=t.execLiteral,F={},G={};z=E(w,b,d),A=E(x,b,d),B=new l({el:a,data:z,methods:A,$parent:b,$childrens:f(a.childNodes),replace:"true"==y});var H=C?t.strip(w):"",K=t.sep;return H&&(H.match(K)?H.replace(new RegExp(K+"\\s*$"),"").split(K).forEach(g):g(H)),C&&b.$data.$watch(function(a){var c;p.objEach(G,function(e,f){0===a.indexOf(e)&&(!c&&(c={}),f.forEach(function(a){c[a]=r(b,d,F[a].expr)}))}),c&&B.$set(c)}),v&&(b.$refs[v]=B),I.push(B),c(d,B),B.$update=function(){C&&B.$set(E(w,b,d))},B}}}}function k(a,d){var e={attrs:{},dires:{}},g=b();f(a.attributes).forEach(function(b){var c=b.name,d=b.value;if(!~K.indexOf(c)){if(D(c))e.attrs[c]=d;else if(0===c.indexOf(J)){var f=g[c];if(!f)return;e.dires[c]={def:f,expr:d}}else{if(!D(d.trim()))return;e.attrs[c]=d}a.removeAttribute(c)}}),p.objEach(e.attrs,function(b,e){var f=new v(B,d,a,b,e);H.push(f),c(d,f)}),p.objEach(e.dires,function(b,e){var f,g=e.def,h=e.expr,i=";";g.multi&&h.match(i)?t.strip(h).split(i).forEach(function(e){e.trim()&&(f=new u(B,d,a,g,b,"{"+e+"}"),H.push(f),c(d,f))}):(f=new u(B,d,a,g,b,h),H.push(f),c(d,f))})}function y(a,b,d){var e,f=a.nodeValue,g=t.veil(f),h=t.exprRegexp,i=g.split(h),j=g.match(h);return j&&j.length&&(e=new w(b,d,a,f,i,j),H.push(e),c(d,e)),e}var B=this,E=a.el,F=[C,a.components||{}],G=A.concat([a.directives||{}]),H=[],I=[],J=q.namespace,K=[J+"component",J+"data",J+"methods",J+"ref",J+"replace"],L=a.$childrens;if(B.$parent=a.$parent||null,"string"==p.type(E)&&(E=document.querySelector(E)),E&&a.template)E.innerHTML=a.template;else if(a.template)E=document.createElement("div"),E.innerHTML=a.template;else if(!n.Element(E))throw new Error("Unmatch el option");if(1==E.children.length&&E.firstElementChild.tagName.toLowerCase()==J+"template"){var M=E.firstElementChild,N=f(M.childNodes),O=f(M.attributes);E.removeChild(M),m(N).appendTo(E),O.forEach(function(a){var b;if("class"==a.name)b=a.value+(E.className?" "+E.className:"");else{if(E.hasAttribute(a.name))return;b=a.value}E.setAttribute(a.name,b)})}var P=f(E.querySelectorAll("content"));if(P){var Q;L&&L.length&&(Q=document.createDocumentFragment(),f(L).forEach(function(a){Q.appendChild(a)})),P.forEach(function(a){if(!L||!L.length)return m(a).remove();var b,c,d=m(a),e=d.attr("select");e&&(b=Q.querySelector(e))&&~(c=L.indexOf(b))?(d.replace(b),L.splice(c,1)):e||(d.replace(Q),L=null)})}if(a.replace)if(1!==E.children.length)console.warn("Can't using '"+J+"replace=true' for a component that has no or multiple child-elements.");else if(E.parentNode){var R=E.firstElementChild;l(R,E),E.parentNode.replaceChild(R,E),E=R}else E=E.firstElementChild;B.$el=E,B.$component=h,B.$refs={};var S={};p.objEach(a.methods,function(a,b){return"function"!==p.type(b)?console.warn(a+" is not a function."):void(B[a]=S[a]=p.bind(b,B))}),B.$methods=S;var T,U={};Object.defineProperty(B,"$data",{enumerable:!0,get:function(){return T||U},set:function(a){return T?(T.$set(a),T):p.merge(U,a)}}),B.$set=function(){T.$set.apply(T,arguments)},B.$get=function(){return T.$get.apply(T,arguments)},B.$watch=function(){return T.$watch.apply(T,arguments)},B.$unwatch=function(){return T.$unwatch.apply(T,arguments)};var V=a.created;if(a.$data)T=a.$data,V&&V.call(B);else{p.merge(U,g(a,"data")),V&&V.call(B);var W={props:U,computed:a.computed,computedContext:B};T=new o(W)}B.$compile=function(a,b){var c;return p.walk(a,function(e){var f=e===a,g=d(e,b,f);return f&&(c=g.inst),g.into}),c};var X=a.destroy;B.$destroy=function(){B.$destroyed||(X&&X.call(B),[I,H].forEach(function(a){a.forEach(function(a){a.$destroy()})}),T.$destroy(),B.$el=null,B.$get=null,B.$set=null,B.$refs=null,B.$watch=null,B.$unwatch=null,B.$compile=null,B.$component=null,G=null,F=null,H=null,I=null,B.$destroyed=!0)},B.$compiler=B.$compile(E),a.ready&&a.ready.call(B)}function f(a){return a?[].slice.call(a):[]}function g(a,b){var c=a[b];return"function"==p.type(c)?c.call(a):c}function h(a){return p.extend.apply(p,a)}function i(a,b){var c=a.prototype;return a.prototype=Object.create(b.prototype),c}function j(a){var b={};return h([b].concat(a)),["data","methods","directives","components"].forEach(function(c){b[c]=h([{}].concat(a.map(function(a){return g(a,c)})))}),b}function k(a){var b=[];a.forEach(function(a){a&&(b.push(a),a.mixins&&(b=b.concat(a.mixins)))});var c=j(b),d=c.methods=c.methods||{};return a.forEach(function(a){var b=a&&a.methods&&a.methods.mixins;b&&b.forEach(function(a){p.objEach(a,function(a,b){"mixins"!==a&&(d[a]=b)})})}),delete c.methods.mixins,c}function l(a,b){var c=m(b);return f(a.attributes).forEach(function(a){"class"==a.name?c.addClass(a.value):c.attr(a.name,a.value)}),b}var m=c(2),n=c(3),o=c(11),p=c(4),q=c(5),r=c(6),s=c(7),t=c(8),u=s.Directive,v=s.Attribute,w=s.Text,x=s.Element,y=c(9)(d),z=c(10)(d),A=[y,{}],B=A[1],C={},D=t.isExpr;d.create=d.extend=function(a){function b(b){var c=k([a,b]);return e.call(this,c)}return i(b,d),b},d.component=function(a,b){var c=d.extend(b);return C[a.toLowerCase()]=c,c},d.directive=function(a,b){B[a]=b},d.namespace=function(a){q.namespace=a},d.$=m,i(d,s),a.exports=d},function(a,b,c){"use strict";function d(a){if("string"==j.type(a))return e(j.copyArray(document.querySelectorAll(a)));if("array"==j.type(a))return e(a);if(a instanceof e)return a;if(k.DOM(a))return e(new f(a));throw new Error("Unexpect selector !")}function e(a){if(a instanceof e)return a;var b=new f;return a.forEach(function(a){b.push(a)}),b}function f(){this.push=function(){Array.prototype.push.apply(this,arguments)},this.forEach=function(){Array.prototype.forEach.apply(this,arguments)},this.push.apply(this,arguments)}function g(a){return a&&a.parentNode}function h(){return document.createDocumentFragment()}function i(a,b){return a.appendChild(b)}var j=c(4),k=c(3);f.prototype=Object.create(e.prototype);var l=e.prototype;l.find=function(a){var b=[];return this.forEach(function(c){b=b.concat(j.copyArray(c.querySelectorAll(a)))}),e(b)},l.attr=function(a,b){var c=arguments.length,d=this[0];if(c>1)d.setAttribute(a,b);else if(1==c)return(d.getAttribute(a)||"").toString();return this},l.removeAttr=function(a){return this.forEach(function(b){b.removeAttribute(a)}),this},l.addClass=function(a){return this.forEach(function(b){var c=b.className.split(" ");~c.indexOf(a)||c.push(a),b.className=c.join(" ")}),this},l.removeClass=function(a){return this.forEach(function(b){var c=b.className.split(" "),d=c.indexOf(a);~d&&c.splice(d,1),b.className=c.join(" ")}),this},l.each=function(a){return this.forEach(a),this},l.on=function(a,b,c){return this.forEach(function(d){d.addEventListener(a,b,c)}),this},l.off=function(a,b){return this.forEach(function(c){c.removeEventListener(a,b)}),this},l.html=function(a){var b=arguments.length;if(b>=1)this.forEach(function(b){b.innerHTML=a});else if(this.length)return this[0].innerHTML;return this},l.parent=function(){return this.length?e([g(this[0])]):null},l.remove=function(){return this.forEach(function(a){var b=g(a);b&&b.removeChild(a)}),this},l.insertBefore=function(a){var b;return this.length?(1==this.length?b=this[0]:(b=h(),this.forEach(function(a){i(b,a)})),g(a).insertBefore(b,a),this):this},l.insertAfter=function(a){var b;return this.length?(1==this.length?b=this[0]:(b=h(),this.forEach(function(a){i(b,a)})),g(a).insertBefore(b,a.nextSibling),this):this},l.get=function(a){return this[a]},l.append=function(a){return this.length&&i(this[0],a),this},l.appendTo=function(a){if(1==this.length)i(a,this[0]);else if(this.length>1){var b=h();this.forEach(function(a){i(b,a)}),i(a,b)}},l.replace=function(a){var b=this[0],c=g(b);return c&&c.replaceChild(a,b),this},a.exports=d},function(a,b,c){"use strict";var d=c(5);a.exports={Element:function(a){return 1==a.nodeType||11==a.nodeType},DOM:function(a){return this.Element(a)||8==a.nodeType},IfElement:function(a){return a==(d.namespace+"if").toUpperCase()},ElseElement:function(a){return a.hasAttribute&&a.hasAttribute(d.namespace+"else")},RepeatElement:function(a){return a==(d.namespace+"repeat").toUpperCase()}}},function(a,b,c){"use strict";function d(a){return Object.keys(a)}function e(a,b){for(var c=a.length||0,d=0;c>d&&!b(a[d],d);d++);}function f(a){return a?[].slice.call(a):[]}var g=c(11),h=g.utils,i=g.keyPath.normalize,j=g.keyPath.digest,k={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},l=new RegExp(d(k).join("|"),"g");a.exports={type:h.type,diff:h.diff,merge:h.merge,objEach:h.objEach,copyArray:h.copyArray,copyObject:h.copyObject,extend:function(a){if("object"!=this.type(a))return a;for(var b,c,d=1,e=arguments.length;e>d;d++){b=arguments[d];for(c in b)a[c]=b[c]}return a},valueDiff:function(a,b){return a!==b||a instanceof Object},walk:function(a,b){var c=b(a)!==!1,d=this;c&&f(a.childNodes).forEach(function(a){d.walk(a,b)})},domRange:function(a,b,c){for(var d=[],e=a.childNodes,f=!1,g=0;g]+?>/),d=b.match(/<\/[^<]+?>$/);return[c?c[0]:"",d?d[0]:""]},relative:function(a,b){if(a=i(a),b=i(b),a==b)return!0;var c=0===a.indexOf(b),d=a.replace(b,"").match(/^[\.\[]/);return c&&d},escape:function(a){return"string"==!this.type(a)?a:a.replace(l,function(a){return k[a]})},isUndef:function(a){return void 0===a},isNon:function(a){var b=this.type(a);return"undefined"===b||"null"===b},bind:function(a,b){var c=function(){return a.apply(b,arguments)};return c.toString=function(){return a.toString.apply(a,arguments)},c},forEach:e,normalize:i,digest:j}},function(a,b,c){"use strict";var d="z-";a.exports={set namespace(a){d=a+"-"},get namespace(){return d}}},function(a,b,c){function d(a,b){b&&b.$parent&&(b.data.$parent=b.$parent.data);var c=arguments[2],d=g[c];b=b||{},b=e.extend({},a.$methods,a.$data,b.methods,b.data);try{return d||(d=g[c]=f(c)),e.immutable(d(b))}catch(h){switch(c=/^\{/.test(c)?". "+c:". {"+c+"}",h.name){case"ReferenceError":console.warn(h.message+c);break;default:console.error(arguments[3]?"'"+arguments[3]+"': ":"",h.message+c,arguments[4]||"")}return""}}var e=c(4),f=c(12),g={};a.exports=d},function(a,b,c){"use strict";function d(){}function e(a,b,c){function e(a){f.some(function(b){return r(a,b)?!0:void 0})&&c.apply(null,arguments)}var f=[];return b&&b.length?(b.forEach(function(a){if(!~u.indexOf(a))for(;a;)~f.indexOf(a)||f.push(a),a=o.digest(a)}),f.length?a.$watch(e):d):d}function f(a){this.$el=a}function g(a,b){for(var c=b.nextSibling;c;){if(c===a)return!0;{if(3!=c.nodeType||!/^\s*$/m.test(c.nodeValue))break;c=c.nextSibling}}return!1}function h(a,b){return a.appendChild(b)}function i(a){return document.createComment(a)}function j(a,b,c){return a.insertBefore(b,c)}function k(a){return a&&a.parentNode}function l(a){return a&&a.nextSibling}function m(a,b){return b&&b.parentNode===a}var n=c(2),o=c(4),p=c(8),q=c(6),r=o.relative,s=p.isExpr,t=p.extract,u=["$index","$value","$parent","$vm","$scope"],v=p.strip,w=f.prototype;f.inherit=function(a){return a.prototype=Object.create(f.prototype),a},w.$bundle=function(){return this.$el},w.$floor=function(){return this.$el},w.$ceil=function(){return this.$el},w.$mount=function(a){return n(a).replace(this.$bundle()),this},w.$remove=function(){var a=this.$bundle();return k(a)&&n(a).remove(),this},w.$appendTo=function(a){return h(a,this.$bundle()),this},w.$insertBefore=function(a){return j(k(a),this.$bundle(),a),this},w.$insertAfter=function(a){return j(k(a),this.$bundle(),l(a)),this},w.$destroy=function(){return this.$el=null,this},w.$nextTo=function(a){return a=a instanceof f?a.$ceil():a,g(a,this.$floor())},w.$preTo=function(a){return a=a instanceof f?a.$floor():a,g(this.$ceil(),a)},w.$update=d;var x=0;f.Directive=f.inherit(function(a,b,c,d,f,g){function h(c){return q(a,b,c,f)}function i(a){if(!j.$destroyed){var b=h(g);if(o.diff(b,n)){var c=n;n=b,w&&w.call(j,b,c,a)}}}var j=this,k=[],l=!!s(g);if(j.$expr=g,l&&(g=v(g)),d.multi){var m;g=g.replace(/^[^:]+:/,function(a){return m=a.replace(/:$/,"").replace(/(^\s*['"]?|['"]?\s*$)/g,""),""}).trim(),k.push(m)}j.$id="d"+x++,j.$name=f,j.$el=c,j.$vm=a,j.$scope=b||null;var n,p,r=d.bind,u=d.unbind,w=d.update;o.objEach(d,function(a,b){j[a]=b}),n=l?h(g):g,k.push(n),k.push(g),d.watch!==!1&&l&&(p=e(a,t(g),i)),j.$destroy=function(){u&&u.call(j),p&&p(),j.$el=null,j.$vm=null,j.$scope=null,j.$destroyed=!0},j.$update=i,r&&r.apply(j,k,g),w&&w.call(j,n)});var y=0;f.Element=f.inherit(function(a,b,c,d,f,g){function l(a,b,c,d,e,f){if(!w.$destroyed){var h;if(D){var i;h=g.map(function(a,b){return p[b][0]?i&&E?!1:i=n(p[b][1]):a})}else h=n(g);var j;if(B&&(j=B.call(w,h,r,a)))return C&&C.call(w,h,r,a,j);if(o.diff(h,r)){var k=r;r=h,A&&A.call(w,h,k,a,d,e,f)}}}function n(c){return q(a,b,c,f)}var p,r,u,w=this,x=d.bind,z=d.unbind,A=d.update,B=d.delta,C=d.deltaUpdate,D=d.multiExpr&&"array"==o.type(g),E="exclusion"==d.multiExpr;w.$expr=g,D&&(p=g.map(function(a){var b=s(a);return[!!b,b?v(a):a]})),w.$id="e"+y++,w.$name=f,w.$vm=a,w.$el=c,w.$scope=b;var F=o.tagHTML(c);if(w.$before=i(F[0]),w.$after=i(F[1]),w.$container=document.createDocumentFragment(),h(w.$container,w.$before),h(w.$container,w.$after),o.objEach(d,function(a,b){w[a]=b}),w.$bundle=function(){var a=this.$ceil(),b=this.$floor(),c=this.$container,d=this;return m(c,a)||(o.domRange(k(a),a,b).forEach(function(a){h(d.$container,a)}),j(c,a,c.firstChild),h(c,b)),c},w.$floor=function(){return this.$after},w.$ceil=function(){return this.$before},w.$destroy=function(){z&&z.call(w),u&&u(),w.$el=null,w.$vm=null,w.$scope=null,w.$destroyed=!0},w.$update=l,D){var G,H=[];r=g.map(function(a,b){return p[b][0]?(a=p[b][1],H=H.concat(t(a)),G&&E?!1:G=n(a)):a}),H.length&&(u=e(a,H,l))}else{var I=!!s(g);I&&(g=v(g)),r=I?n(g):g,d.watch!==!1&&I&&(u=e(a,t(g),l))}x&&x.call(w,r,g),A&&A.call(w,r)});var z=0;f.Text=f.inherit(function(a,b,c,d,f,g){function m(c){return q(a,b,c,null)}function n(){var a=[];f.forEach(function(b,c){a.push(b),c1&&!this.$expr[c]&&(d=c,e=!0),this._lIndex=d,b!=d&&~b&&this._unmount(b),~d&&(e?this.compileds[d]?this._mount(d):(this.compileds[d]=!0,this.$vm.$compile(this._tmpCons[d],this.$scope),this._mount(d)):this._unmount(d))},unbind:function(){this.$update=this._mount=this._unmount=i,this.compileds=this._tmpCons=null}},repeat:{bind:function(a,b){var c=this.$el.getAttribute("ref"),d=this;return c&&(this.$vm.$refs[c]=this),this.$items=function(){return this.$vms},this.$itemBindings=function(a){if(!d.$vms||!d.$vms.length)return[];var b=d.$vms[a];return b?b.$scope.bindings:[]},this.child=this.$el.firstElementChild,this.expr=b,this.child?void(this._noArrayFilter=o.notFunctionCall(b)):console.warn('"'+k.namespace+"repeat\"'s childNode must has a HTMLElement node. {"+b+"}")},unbind:function(){this.$vms&&this.$vms.forEach(function(a){h(a)}),this.child=this.$vms=this._lastItems=null,this.$items=this.$itemBindings=i},delta:function(a,b,c){if(!c)return!1;var d,e,f=l.normalize(o.strip(this.$expr).trim()),g=c.replace(f,"");if("$value"==f&&(e=g.match(/^\d+\.(\d+)(\.|$)/)))g=g.replace(/\d+\.?/,"");else{if(!(e=g.match(/^\.(\d+)(\.|$)/)))return!1;g=g.replace(/^\./,"")}return d=Number(e[1]),this.$vms&&dc?c:a,i.length>2){var d=[].slice.call(i,2).map(function(b,c){return e.call(m,b,a+c)});this.$vms.splice.apply(this.$vms,[a,b].concat(d)),j(d.map(function(a){return a.$compiler.$bundle()})).insertAfter(0===a?q:this.$vms[a-1].$compiler.$bundle());var f=a+d.length;this.$vms.forEach(function(a,b){b>=f&&g(a,b)})}else this.$vms.splice.apply(this.$vms,i).forEach(function(a){h(a)}),this.$vms.forEach(function(b,c){c>=a&&g(b,c)})},push:function(){var b=a.length-1,c=e.call(m,a[b],b);this.$vms.push(c),c.$compiler.$insertBefore(p)},pop:function(){var a=this.$vms.pop();h(a)},shift:function(){var a=this.$vms.shift();h(a),this.$vms.forEach(function(a,b){g(a,b)})},unshift:function(){var b=e.call(m,a[0],0);this.$vms.unshift(b),b.$compiler.$insertAfter(q),this.$vms.forEach(function(a,b){0!==b&&g(a,b)})},$concat:function(){var b=this.$vms.length;j(a.slice(b).map(function(a,c){var d=e.call(m,a,c+b);return m.$vms.push(d),d.$compiler.$bundle()})).insertBefore(p)}},s=r[d];if(n&&this._noArrayFilter&&s)return s.call(this),void(this._lastItems=l.copyArray(a));var t=this._lastItems?this._lastItems.map(function(a){return{data:a}}):null,u=a.map(function(a,b){var c={data:a};if(t){var d,e=-1;t.some(function(c,f){if(!c.used){var g=l.diff(c.data,a);if(!g){if(e=f,b===e)return d=!0;~e||(e=f)}}}),~e&&d?(t[e].used=!0,c.status="reused"):~e?(t[e].used=!0,c.status="moved",c.from=e):(t.some(function(a,c){return a.used||b!=c?void 0:(e=c,!0)}),~e?(t[e].used=!0,c.status="updated",c.from=e):c.status="created")}else c.status="created";return c}),v=(t||[]).reduce(function(a,b,c){return b.used||a.push(c),a},[]);u.some(function(a){return v.length?void("created"==a.status&&(a.from=v.pop(),a.status="recycled")):!0}),v.forEach(function(a){h(m.$vms[a])});var w=q;this.$vms=u.map(function(a,b){var d;switch(a.status){case"created":d=e.call(m,a.data,b);break;case"updated":d=m.$vms[b],f(d,a.data,b,c);break;case"moved":d=m.$vms[a.from],g(d,b);break;case"reused":d=m.$vms[b];break;case"recycled":d=m.$vms[a.from],f(d,a.data,a.from,c)}var h=d.$compiler;return h.$preTo(w)||d.$compiler.$insertAfter(w),w=h.$floor(),d}),this._lastItems=l.copyArray(a)}}}}}},function(a,b,c){"use strict";a.exports=c(14)},function(a,b,c){a.exports=function(a){return/^[_$][\w$]*$/.test(a)?function(b){return b[a]}:new Function("$scope","with($scope){return ("+a+")}")}},function(a,b,c){"use strict";function d(a,b){this.data=a,this.bindings=[],this.children=[],this.$parent=b||null}d.prototype.$update=function(){var a=arguments;this.bindings.forEach(function(b){b.$update.apply(b,a)}),this.children.forEach(function(b){b.$update.apply(b,a)})},d.prototype.$removeChild=function(a){var b=this.children.indexOf(a);return~b&&(a.$parent=null,this.children.splice(b,1)),this},d.prototype.$addChild=function(a){return~this.children.indexOf(a)||this.children.push(a),this},a.exports=d},function(a,b,c){"use strict";function d(){return A++}function e(a){a=a||{},g.call(this,a)}function f(a){function b(b){g.call(this,a,b)}return b.prototype=Object.create(e.prototype),b}function g(a,b){function c(){return N||""}function f(a,b){j(b,Function)&&(b=b.bind(J)),R[a]=b,o.def(J,a,{enumerable:!1,value:b})}function g(){return u("Instance already has bean destroyed"),I}function n(a){var b=arguments,d=p(q(c(),a));b[0]=z+":"+d,L.emit(z,d),K.emit.apply(K,b),b=o.copyArray(b),b[0]=d,b.unshift("*"),K.emit.apply(K,b)}function A(a,b){o.patch(Y,b,[]);var c=Y[b];s(c,a)||c.push(a)}function B(a,b,c){var d,f=a.__mux__;return f&&f.__kp__===c&&f.__root__===O?(d=a,d._$emitter(K),d._$_emitter(L)):d=new e({props:b,emitter:K,_emitter:L,__kp__:c}),d.__root__||o.def(d,"__root__",{enumerable:!1,value:O}),d}function C(a,b,d){var f=r(b),g=d?d:q(c(),a);switch(f==w&&m(b,function(b,c,d,e){var f=o.copyArray(b),h=d.apply(b,e);return _[a]=C(a,b,g),"splice"==c?n(g,b,f,c,e):n(g,b,f,c),h}),f){case x:var h={},i=b;return j(b,e)&&(i=b.$props()),o.objEach(i,function(a,b){h[a]=C(a,b,q(g,a))}),B(b,h,g);case w:return b.forEach(function(a,c){b[c]=C(c,a,q(g,c))}),b;default:return b}}function D(a,b,d){var f=p(a).split("."),g=f[0];if(s(X,g))return void(J[g]=b);if(!s($,g))return void u('Property "'+g+'" has not been observed');var h,i=l.get(_,a),k=j(b,Object),m=f.join("."),r=f.pop(),v=f.join("."),w=l.get(_,v),x=j(w,e);return x?t(w,r)?h=w._$set(r,b,d):(w._$add(r,b,d),h=[l.join(c(),a),b]):(l.set(_,a,k?C(r,b,q(c(),m)):b),o.diff(b,i)&&(d?h=[l.join(c(),a),b,i]:n(a,b,i))),h}function E(a,b,c){return I?g():D(a,b,c)}function F(a){if(I)return g();if(a&&r(a)==x){var b=[];o.objEach(a,function(a,c){var d=E(a,c,!0);d&&b.push(d)}),b.forEach(function(a){n.apply(null,a)})}}function G(a,b,c){if(a.match(/[\.\[\]]/))throw new Error('Propname shoudn\'t contains "." or "[" or "]"');return s($,a)?arguments.length>1?!0:!1:(_[a]=C(a,o.copyValue(b)),$.push(a),o.def(J,a,{enumerable:!0,get:function(){return _[a]},set:function(b){E(a,b)}}),c?{kp:a,vl:b}:void n(a,b))}function H(a,b,c,d,e){if(!s(X,a)){X.push(a),W[a]={deps:b,get:c,set:d},(b||[]).forEach(function(b){for(;b;)A(a,b),b=l.digest(b)}),o.patch(Z,a,{});var f=Z[a];f.cur=c?c.call(M,J):void 0,o.def(J,a,{enumerable:void 0===e?!0:!!e,get:function(){return f.cur},set:function(){d&&d.apply(M,arguments)}}),n(a,f.cur)}}var I,J=this,K=a.emitter||new k(J),L=a._emitter||new k(J),M=t(a,"computedContext")?a.computedContext:J,N=l.normalize(a.__kp__||""),O=d(),P=!!a.emitter,Q=!!a._emitter,R={};f("__muxid__",O),f("__kp__",N);var S=a.props,T={},U=r(S);U==y?T=S():U==x&&(T=S),S=null;var V=a.computed,W={},X=[],Y={},Z={},$=[],_={};o.objEach(T,function(a,b){G(a,b,!0)}),T=null,o.objEach(V,function(a,b){H(a,b.deps,b.get,b.set,b["enum"])}),V=null,L.on(z,function(a){var b=[],c=[];if(Object.keys(Y).length){for(;a;)Y[a]&&(c=c.concat(Y[a])),a=l.digest(a);c.length&&(c.reduce(function(a,b){return s(a,b)||a.push(b),a},b),b.forEach(function(a){o.patch(Z,a,{});var b=Z[a],c=b.pre=b.cur,d=b.cur=(W[a].get||i).call(M,J);o.diff(d,c)&&n(a,d,c)}))}},O),f("$add",function(){var a,b,c=arguments,d=c[0];switch(r(d)){case v:a=d,c.length>1?(b=c[1],G(a,b)&&E(a,b)):G(a);break;case w:d.forEach(function(a){G(a)});break;case x:var e;o.objEach(d,function(a,b){G(a,b)&&(!e&&(e={}),e[a]=b)}),e&&F(e);break;default:u("Unexpect params")}return this}),f("_$add",function(a,b,c){var d=G(a,b,!!c);return d===!0?E(a,b,!!c):d}),f("$computed",function(a){return r(a)==v?H.apply(null,arguments):r(a)==x?o.objEach(arguments[0],function(a,b){H(a,b.deps,b.get,b.set,b["enum"])}):u('$computed params show be "(String, Array, Function, Function)" or "(Object)"'),this}),f("$set",function(){var a=arguments,b=a.length;return b>=2||1==b&&r(a[0])==v?E(a[0],a[1]):1==b&&r(a[0])==x?F(a[0]):void u("Unexpect $set params")}),f("_$set",function(a,b,c){return E(a,b,!!c)}),f("$get",function(a){if(s($,a))return _[a];if(s(X,a))return(W[a].get||i).call(M,J);var b=p(a),c=b.split(".");return s($,c[0])?l.get(_,b):void 0}),f("$watch",function(){var a,b,d=arguments,e=d.length,f=d[0];if(e>=2)a=z+":"+p(q(c(),f)),b=d[1];else{if(1!=e||r(f)!=y)return u("Unexpect $watch params"),i;a="*",b=f}K.on(a,b,O);var g=this;return function(){g.$unwatch.apply(g,d)}}),f("$unwatch",function(){var a,b,d=arguments,e=d.length,f=d[0];switch(!0){case e>=2:a=[d[1]];case 1==e&&r(f)==v:!a&&(a=[]),
7 | b=z+":"+p(q(c(),f)),a.unshift(b);break;case 1==e&&r(f)==y:a=["*",f];break;case 0===e:a=[];break;default:u("Unexpect param type of "+f)}return a&&(a.push(O),K.off.apply(K,a)),this}),f("$props",function(){return o.copyObject(_)}),f("$emitter",function(a,b){return 0===arguments.length?K:(K=a,h(this.$props(),a,b),this)}),f("_$emitter",function(a){K=a}),f("_$_emitter",function(a){j(a,k)&&(L=a)}),f("$destroy",function(){o.objEach(R,function(a,b){r(b)==y&&"$destroyed"!=a&&(R[a]=g)}),P?K.off(O):K.off(),Q?L.off(O):L.off(),K=null,L=null,W=null,X=null,Y=null,Z=null,$=null,_=null,I=!0}),f("$destroyed",function(){return I}),F(b)}function h(a,b,c){if(r(a)==x){var d=a;j(a,e)&&(a._$emitter(b,c),d=a.$props()),o.objEach(d,function(a,d){h(d,b,c)})}else r(a)==w&&a.forEach(function(a){h(a,b,c)})}function i(){}function j(a,b){return a instanceof b}var k=c(15),l=c(16),m=c(17),n=c(18),o=c(19),p=l.normalize,q=l.join,r=o.type,s=o.indexOf,t=o.hasOwn,u=n.warn,v="string",w="array",x="object",y="function",z="change",A=0;e.extend=function(a){return f(a||{})},e.config=function(a){a.warn===!1?n.disable():n.enable()},e.emitter=function(a){return new k(a)},e.keyPath=l,e.utils=o,a.exports=e},function(a,b,c){"use strict";function d(a){this._obs={},this._context=a}var e=c(19),f=e.patch,g=e.type,h="__default_scope__",i=d.prototype;i.on=function(a,b,c){c=c||h,f(this._obs,a,[]),this._obs[a].push({cb:b,scope:c})},i.off=function(){var a,b,c,d=arguments,e=d.length;if(e>=3)a=[d[0]],b=d[1],c=d[2];else if(2==e&&"function"==g(d[0]))a=Object.keys(this._obs),b=d[0],c=d[1];else if(2==e)a=[d[0]],c=d[1];else{if(1!=e)return this._obs=[],this;a=Object.keys(this._obs),c=d[0]}c=c||h;var f=this;return a.forEach(function(a){var d=f._obs[a];if(d){var e=[];b?d.forEach(function(a){(a.cb!==b||a.scope!==c)&&e.push(a)}):d.forEach(function(a){a.scope!==c&&e.push(a)}),f._obs[a]=e}}),this},i.emit=function(a){var b=this._obs[a];if(b){var c=[].slice.call(arguments);c.shift();var d=this;b.forEach(function(a){a.cb&&a.cb.apply(d._context||null,c)})}},a.exports=d},function(a,b,c){"use strict";function d(a){return new String(a).replace(/\[([^\[\]]+)\]/g,function(a,b){return"."+b.replace(/^["']|["']$/g,"")})}function e(a,b,c,e){var f=d(b).split("."),g=f.pop(),h=a;return f.forEach(function(a){h=h[a]}),e?e(h,g,c):h[g]=c,a}function f(){return void 0}function g(a){return a===f()||null===a}function h(a,b){var c=d(b).split("."),e=a;return c.forEach(function(a){return g(e)?!(e=f()):void(e=e[a])}),e}function i(a,b){var c=!!a;return c||(a=""),/^\[.*\]$/.exec(b)?a+b:"number"==typeof b?a+"["+b+"]":c?a+"."+b:b}function j(a){var b=/(\.[^\.]+|\[([^\[\]])+\])$/;return b.exec(a)?a.replace(b,""):""}a.exports={normalize:d,set:e,get:h,join:i,digest:j}},function(a,b,c){"use strict";var d=c(19),e=["splice","push","pop","shift","unshift","reverse","sort","$concat"],f=Array.prototype.push,g=Array.prototype.slice,h={$concat:function(){var a=g.call(arguments),b=this;return a.forEach(function(a){"array"==d.type(a)?a.forEach(function(a){f.call(b,a)}):f.call(b,a)}),b}},i="__hook__";a.exports=function(a,b){e.forEach(function(c){if(a[c]&&a[c][i])return void a[c][i](b);var e=a[c]||h[c];d.def(a,c,{enumerable:!1,value:function(){return b(a,c,e,arguments)}}),d.def(a[c],i,{enumerable:!1,value:function(a){b=a}})})}},function(a,b,c){"use strict";var d=!0;a.exports={enable:function(){d=!0},disable:function(){d=!1},warn:function(a){return d?console.warn?console.warn(a):void console.log(a):void 0}}},function(a,b,c){"use strict";function d(a,b){return a&&a.hasOwnProperty(b)}var e=void 0;a.exports={type:function(a){if(null===a)return"null";if(a===e)return"undefined";var b=/\[object (\w+)\]/.exec(Object.prototype.toString.call(a));return b?b[1].toLowerCase():""},objEach:function(a,b){if(a)for(var c in a)if(d(a,c)&&b(c,a[c])===!1)break},patch:function(a,b,c){!a[b]&&(a[b]=c)},diff:function(a,b,c){var d=this;if(c=void 0===c?4:c,0>=c)return a!==b;if("array"==this.type(a)&&"array"==this.type(b))return a.length!==b.length?!0:a.some(function(a,e){return d.diff(a,b[e],c-1)});if("object"==this.type(a)&&"object"==this.type(b)){var e=Object.keys(a),f=Object.keys(b);return e.length!=f.length?!0:e.some(function(e){return!~f.indexOf(e)||d.diff(a[e],b[e],c-1)})}return a!==b},copyArray:function(a){for(var b=a.length,c=new Array(b);b--;)c[b]=a[b];return c},copyObject:function(a){var b={};return this.objEach(a,function(a,c){b[a]=c}),b},copyValue:function(a){var b=this.type(a);switch(b){case"object":return this.copyObject(a);case"array":return this.copyArray(a);default:return a}},def:function(){return Object.defineProperty.apply(Object,arguments)},indexOf:function(a,b){return~a.indexOf(b)},merge:function(a,b){return b?(this.objEach(b,function(b,c){a[b]=c}),a):a},hasOwn:d}}])});
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp')
2 | var webpack = require('gulp-webpack')
3 | var uglify = require('gulp-uglifyjs')
4 | var header = require('gulp-header')
5 | var meta = require('./package.json')
6 | var watch = require('gulp-watch')
7 |
8 | var banner = ['/**',
9 | '* Zect v${version}',
10 | '* (c) 2015 ${author}',
11 | '* Released under the ${license} License.',
12 | '*/',
13 | ''].join('\n')
14 | var bannerVars = {
15 | version : meta.version,
16 | author: 'guankaishe',
17 | license: 'MIT'
18 | }
19 |
20 | gulp.task('watch', function () {
21 | watch(['lib/*.js', './package.json'], function () {
22 | gulp.start('default')
23 | })
24 | });
25 |
26 | gulp.task('default', function() {
27 | return gulp.src('index.js')
28 | .pipe(webpack({
29 | output: {
30 | library: 'Zect',
31 | libraryTarget: 'umd',
32 | filename: 'zect.js'
33 | }
34 | }))
35 | .pipe(header(banner, bannerVars))
36 | .pipe(gulp.dest('dist/'))
37 | .pipe(uglify('zect.min.js', {
38 | mangle: true,
39 | compress: true
40 | }))
41 | .pipe(header(banner, bannerVars))
42 | .pipe(gulp.dest('dist/'))
43 | });
44 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/zect')
--------------------------------------------------------------------------------
/lib/compile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (__$expr__) {
2 | if (/^[_$][\w$]*$/.test(__$expr__)) {
3 | // access property if begin with _ or $
4 | return function ($scope) {
5 | return $scope[__$expr__]
6 | }
7 | } else {
8 | return new Function('$scope', 'with($scope){return (' + __$expr__ + ')}')
9 | }
10 | }
--------------------------------------------------------------------------------
/lib/compiler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var $ = require('./dm')
4 | var util = require('./util')
5 | var Expression = require('./expression')
6 | var _execute = require('./execute')
7 | var _relative = util.relative
8 | /**
9 | * Whether a text is with express syntax
10 | */
11 | var _isExpr = Expression.isExpr
12 | /**
13 | * Get varibales of expression
14 | */
15 | var _extractVars = Expression.extract
16 |
17 | function noop () {}
18 |
19 | var keywords = ['$index', '$value', '$parent', '$vm', '$scope']
20 | /**
21 | * watch changes of variable-name of keypath
22 | * @return unwatch
23 | */
24 | function _watch(vm, vars, update) {
25 | var watchKeys = []
26 | function _handler (kp) {
27 | if (watchKeys.some(function(key) {
28 | if (_relative(kp, key)) {
29 | return true
30 | }
31 | })) update.apply(null, arguments)
32 | }
33 |
34 | if (vars && vars.length) {
35 | vars.forEach(function (k) {
36 | if (~keywords.indexOf(k)) return
37 | while (k) {
38 | if (!~watchKeys.indexOf(k)) watchKeys.push(k)
39 | k = util.digest(k)
40 | }
41 | })
42 | if (!watchKeys.length) return noop
43 | return vm.$watch(_handler)
44 | }
45 | return noop
46 | }
47 |
48 |
49 | var _strip = Expression.strip
50 |
51 | /**
52 | * Compoiler constructor for wrapping node with consistent API
53 | * @node
54 | */
55 | function Compiler (node) {
56 | this.$el = node
57 | }
58 |
59 | var cproto = Compiler.prototype
60 |
61 | Compiler.inherit = function (Ctor) {
62 | Ctor.prototype = Object.create(Compiler.prototype)
63 | return Ctor
64 | }
65 | cproto.$bundle = function () {
66 | return this.$el
67 | }
68 | cproto.$floor = function () {
69 | return this.$el
70 | }
71 | cproto.$ceil = function () {
72 | return this.$el
73 | }
74 | cproto.$mount = function (pos) {
75 | $(pos).replace(this.$bundle())
76 | return this
77 | }
78 | cproto.$remove = function () {
79 | var $el = this.$bundle()
80 | _parentNode($el) && $($el).remove()
81 | return this
82 | }
83 | cproto.$appendTo = function (pos) {
84 | _appendChild(pos, this.$bundle())
85 | return this
86 | }
87 | cproto.$insertBefore = function (pos) {
88 | _insertBefore(_parentNode(pos), this.$bundle(), pos)
89 | return this
90 | }
91 | cproto.$insertAfter = function (pos) {
92 | _insertBefore(_parentNode(pos), this.$bundle(), _nextSibling(pos))
93 | return this
94 | }
95 | cproto.$destroy = function () {
96 | this.$el = null
97 | return this
98 | }
99 | function _nextTo (src, tar) {
100 | var next = tar.nextSibling
101 | while(next) {
102 | if (next === src) return true
103 | else if (next.nodeType == 3 && /^\s*$/m.test(next.nodeValue)) {
104 | next = next.nextSibling
105 | continue
106 | } else {
107 | break
108 | }
109 | }
110 | return false
111 | }
112 | cproto.$nextTo = function (tar) {
113 | // Compiler of Node
114 | tar = tar instanceof Compiler ? tar.$ceil() : tar
115 | return _nextTo(tar, this.$floor())
116 | }
117 | cproto.$preTo = function (tar) {
118 | tar = tar instanceof Compiler ? tar.$floor() : tar
119 | return _nextTo(this.$ceil(), tar)
120 | }
121 | /**
122 | * Can be overwrited
123 | * @type {[type]}
124 | */
125 | cproto.$update = noop
126 | /**
127 | * Standard directive
128 | */
129 | var _did = 0
130 | Compiler.Directive = Compiler.inherit(function Directive (vm, scope, tar, def, name, expr) {
131 | var d = this
132 | var bindParams = []
133 | var isExpr = !!_isExpr(expr)
134 |
135 | d.$expr = expr
136 |
137 | isExpr && (expr = _strip(expr))
138 |
139 | if (def.multi) {
140 | // extract key and expr from "key: expression" format
141 | var key
142 | expr = expr.replace(/^[^:]+:/, function (m) {
143 | key = m.replace(/:$/, '').replace(/(^\s*['"]?|['"]?\s*$)/g, '')
144 | return ''
145 | }).trim()
146 | bindParams.push(key)
147 | }
148 |
149 | d.$id = 'd' + _did++
150 | d.$name = name
151 | d.$el = tar
152 | d.$vm = vm
153 | d.$scope = scope || null
154 |
155 | var bind = def.bind
156 | var unbind = def.unbind
157 | var upda = def.update
158 | var prev
159 | var unwatch
160 |
161 |
162 | // set properties
163 | util.objEach(def, function (k, v) {
164 | d[k] = v
165 | })
166 |
167 | /**
168 | * execute wrap with directive name
169 | */
170 | function _exec(expr) {
171 | return _execute(vm, scope, expr, name)
172 | }
173 |
174 | /**
175 | * update handler
176 | */
177 | function _update(kp) {
178 | if (d.$destroyed) return
179 | var nexv = _exec(expr)
180 |
181 | if (util.diff(nexv, prev)) {
182 | var p = prev
183 | prev = nexv
184 | upda && upda.call(d, nexv, p, kp)
185 | }
186 | }
187 |
188 | /**
189 | * If expression is a string iteral, use it as value
190 | */
191 | prev = isExpr ? _exec(expr):expr
192 | bindParams.push(prev)
193 | bindParams.push(expr)
194 | // watch variable changes of expression
195 | if (def.watch !== false && isExpr) {
196 | unwatch = _watch(vm, _extractVars(expr), _update)
197 | }
198 |
199 | d.$destroy = function () {
200 | unbind && unbind.call(d)
201 | unwatch && unwatch()
202 | d.$el = null
203 | d.$vm = null
204 | d.$scope = null
205 | d.$destroyed = true
206 | }
207 | d.$update = _update
208 |
209 | // ([property-name], expression-value, expression)
210 | bind && bind.apply(d, bindParams, expr)
211 | upda && upda.call(d, prev)
212 |
213 | })
214 |
215 |
216 | var _eid = 0
217 | Compiler.Element = Compiler.inherit(function ZElement(vm, scope, tar, def, name, expr) {
218 | var d = this
219 | var bind = def.bind
220 | var unbind = def.unbind
221 | var upda = def.update
222 | var delta = def.delta
223 | var deltaUpdate = def.deltaUpdate
224 | var isMultiExpr = def.multiExpr && util.type(expr) == 'array'
225 | var isExclusion = def.multiExpr == 'exclusion'
226 | var multiExprMetas
227 | var prev
228 | var unwatch
229 |
230 |
231 | d.$expr = expr
232 | if (isMultiExpr) {
233 | multiExprMetas = expr.map(function (exp) {
234 | var isExpr = _isExpr(exp)
235 | return [!!isExpr, isExpr ? _strip(exp) : exp]
236 | })
237 | }
238 | d.$id = 'e' + _eid ++
239 | d.$name = name
240 | d.$vm = vm
241 | d.$el = tar
242 | d.$scope = scope // save the scope reference
243 |
244 | var tagHTML = util.tagHTML(tar)
245 | d.$before = _createComment(tagHTML[0])
246 | d.$after = _createComment(tagHTML[1])
247 | d.$container = document.createDocumentFragment()
248 |
249 | _appendChild(d.$container, d.$before)
250 | _appendChild(d.$container, d.$after)
251 |
252 | // set properties
253 | util.objEach(def, function (k, v) {
254 | d[k] = v
255 | })
256 |
257 | d.$bundle = function () {
258 | var $ceil = this.$ceil()
259 | var $floor = this.$floor()
260 | var $con = this.$container
261 | var that = this
262 |
263 | if (!_contains($con, $ceil)) {
264 | util.domRange(_parentNode($ceil), $ceil, $floor)
265 | .forEach(function(n) {
266 | _appendChild(that.$container, n)
267 | })
268 | _insertBefore($con, $ceil, $con.firstChild)
269 | _appendChild($con, $floor)
270 | }
271 | return $con
272 | }
273 | d.$floor = function () {
274 | return this.$after
275 | }
276 | d.$ceil = function () {
277 | return this.$before
278 | }
279 |
280 | d.$destroy = function () {
281 | unbind && unbind.call(d)
282 | unwatch && unwatch()
283 | d.$el = null
284 | d.$vm = null
285 | d.$scope = null
286 | d.$destroyed = true
287 | }
288 | /**
289 | * update handler
290 | */
291 | function _update(kp, nv, pv, method, ind, len) {
292 | if (d.$destroyed) return
293 |
294 | var nexv
295 | if (isMultiExpr) {
296 | var lastV
297 | nexv = expr.map(function (exp, i) {
298 | if (multiExprMetas[i][0]) {
299 | if (lastV && isExclusion) return false
300 | return (lastV = _exec(multiExprMetas[i][1]))
301 | } else {
302 | return exp
303 | }
304 | })
305 | } else {
306 | nexv = _exec(expr)
307 | }
308 | var deltaResult
309 | if ( delta && (deltaResult = delta.call(d, nexv, prev, kp)) ) {
310 | return deltaUpdate && deltaUpdate.call(d, nexv, prev, kp, deltaResult)
311 | }
312 | if (util.diff(nexv, prev)) {
313 | var p = prev
314 | prev = nexv
315 | upda && upda.call(d, nexv, p, kp, method, ind, len)
316 | }
317 | }
318 |
319 | d.$update = _update
320 |
321 | /**
322 | * execute wrap with directive name
323 | */
324 | function _exec(expr) {
325 | return _execute(vm, scope, expr, name)
326 | }
327 |
328 | if (isMultiExpr) {
329 | var watchedKeys = []
330 | // result exclusion
331 | var lastV
332 | prev = expr.map(function (exp, i) {
333 | if (multiExprMetas[i][0]) {
334 | exp = multiExprMetas[i][1]
335 | watchedKeys = watchedKeys.concat(_extractVars(exp))
336 | if (lastV && isExclusion) {
337 | return false
338 | } else {
339 | return (lastV = _exec(exp))
340 | }
341 | } else {
342 | return exp
343 | }
344 | })
345 | if (watchedKeys.length) {
346 | unwatch = _watch(vm, watchedKeys, _update)
347 | }
348 | } else {
349 | var isExpr = !!_isExpr(expr)
350 | isExpr && (expr = _strip(expr))
351 | prev = isExpr ? _exec(expr) : expr
352 | if (def.watch !== false && isExpr) {
353 | unwatch = _watch(vm, _extractVars(expr), _update)
354 | }
355 | }
356 | bind && bind.call(d, prev, expr)
357 | upda && upda.call(d, prev)
358 | })
359 |
360 | var _tid = 0
361 | Compiler.Text = Compiler.inherit(function ZText(vm, scope, tar, originExpr, parts, exprs) {
362 | var d = this
363 | d.$expr = originExpr
364 | d.$id = 't' + _tid ++
365 |
366 | function _exec (expr) {
367 | return _execute(vm, scope, expr, null)
368 | }
369 | var cache = new Array(exprs.length)
370 | var isUnescape = exprs.some(function (expr) {
371 | return Expression.isUnescape(expr)
372 | })
373 | var unwatches = []
374 |
375 | exprs.forEach(function(exp, index) {
376 | // watch change
377 | exp = _strip(exp)
378 | var vars = _extractVars(exp)
379 |
380 | function _update() {
381 | if (d.$destroyed) return
382 |
383 | var pv = cache[index]
384 | var nv = _exec(exp)
385 |
386 | if (util.diff(nv, pv)) {
387 | // re-render
388 | cache[index] = nv
389 | render()
390 | }
391 | }
392 | // initial value
393 | cache[index] = _exec(exp)
394 |
395 | unwatches.push(_watch(vm, vars, _update))
396 | })
397 |
398 | if (isUnescape) {
399 | var $tmp = document.createElement('div')
400 | var $con = document.createDocumentFragment()
401 | var $before = _createComment('{' + _strip(originExpr))
402 | var $after = _createComment('}')
403 |
404 | var pn = _parentNode(tar)
405 | _insertBefore(pn, $before, tar)
406 | _insertBefore(pn, $after, _nextSibling(tar))
407 | }
408 |
409 | function render() {
410 | var frags = []
411 | parts.forEach(function(item, index) {
412 | frags.push(item)
413 | if (index < exprs.length) {
414 | frags.push(cache[index])
415 | }
416 | })
417 |
418 | var value = Expression.unveil(frags.join(''))
419 |
420 | if (isUnescape) {
421 | var cursor = _nextSibling($before)
422 | while(cursor && cursor !== $after) {
423 | var next = _nextSibling(cursor)
424 | _parentNode(cursor).removeChild(cursor)
425 | cursor = next
426 | }
427 | $tmp.innerHTML = value
428 | ;[].slice.call($tmp.childNodes).forEach(function (n) {
429 | _appendChild($con, n)
430 | })
431 | _insertBefore(_parentNode($after), $con, $after)
432 | } else {
433 | tar.nodeValue = value
434 | }
435 | }
436 |
437 | this.$destroy = function () {
438 | d.$destroyed = true
439 | unwatches.forEach(function (f) {
440 | f()
441 | })
442 | }
443 |
444 | this.$update = function () {
445 | if (d.$destroyed) return
446 |
447 | var hasDiff
448 | exprs.forEach(function(exp, index) {
449 | exp = _strip(exp)
450 | var pv = cache[index]
451 | var nv = _exec(exp)
452 |
453 | if (!hasDiff && util.diff(nv, pv)) {
454 | hasDiff = true
455 | }
456 | cache[index] = nv
457 | })
458 | hasDiff && render()
459 | }
460 |
461 | /**
462 | * initial render
463 | */
464 | render()
465 | })
466 |
467 | var _aid = 0
468 | Compiler.Attribute = function ZAttribute (vm, scope, tar, name, value) {
469 | var d = this
470 | d.$name = name
471 | d.$expr = value
472 | d.$id = 'a' + _aid ++
473 |
474 | var isNameExpr = _isExpr(name)
475 | var isValueExpr = _isExpr(value)
476 |
477 | var nexpr = isNameExpr ? _strip(name) : null
478 | var vexpr = isValueExpr ? _strip(value) : null
479 |
480 | var unwatches = []
481 |
482 | function _exec(expr) {
483 | return _execute(vm, scope, expr, name + '=' + value)
484 | }
485 | // validate atrribute name, from: http://www.w3.org/TR/REC-xml/#NT-NameChar
486 | // /^(:|[a-zA-Z0-9]|_|-|[\uC0-\uD6]|[\uD8-\uF6]|[\uF8-\u2FF]|[\u370-\u37D]|[\u37F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]|[\u10000-\uEFFFF])+$/
487 |
488 | // cache last name/value
489 | var preName = isNameExpr ? _exec(nexpr) : name
490 | var preValue = isValueExpr ? _exec(vexpr) : value
491 | var $tar = $(tar)
492 | function _emptyUndef(v) {
493 | return util.isUndef(v) ? '' : v
494 | }
495 | $tar.attr(preName, _emptyUndef(preValue))
496 |
497 | function _updateName() {
498 | if (d.$destroyed) return
499 |
500 | var next = _exec(nexpr)
501 |
502 | if (util.diff(next, preName)) {
503 | $tar.removeAttr(preName)
504 | .attr(next, _emptyUndef(preValue))
505 | preValue = next
506 | }
507 | }
508 | function _updateValue() {
509 | if (d.$destroyed) return
510 |
511 | var next = _exec(vexpr)
512 | if (util.diff(next, preValue)) {
513 | $tar.attr(preName, _emptyUndef(next))
514 | preValue = next
515 | }
516 | }
517 |
518 |
519 | this.$destroy = function () {
520 | unwatches.forEach(function (f) {
521 | f()
522 | })
523 | d.$destroyed = true
524 | }
525 |
526 | this.$update = function () {
527 | if (d.$destroyed) return
528 |
529 | isNameExpr && _updateName()
530 | isValueExpr && _updateValue()
531 | }
532 | /**
533 | * watch attribute name expression variable changes
534 | */
535 | if (isNameExpr) {
536 | unwatches.push(_watch(vm, _extractVars(name), _updateName))
537 | }
538 | /**
539 | * watch attribute value expression variable changes
540 | */
541 | if (isValueExpr) {
542 | unwatches.push(_watch(vm, _extractVars(value), _updateValue))
543 | }
544 |
545 | }
546 |
547 | function _appendChild (con, child) {
548 | return con.appendChild(child)
549 | }
550 | function _createComment (ns) {
551 | return document.createComment(ns)
552 | }
553 | function _insertBefore (con, child, pos) {
554 | return con.insertBefore(child, pos)
555 | }
556 | function _parentNode (tar) {
557 | return tar && tar.parentNode
558 | }
559 | function _nextSibling (tar) {
560 | return tar && tar.nextSibling
561 | }
562 | function _contains (con, tar) {
563 | return tar && tar.parentNode === con
564 | }
565 |
566 | module.exports = Compiler
567 |
--------------------------------------------------------------------------------
/lib/conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _ns = 'z-' // default namespace is z that means "Zect"
4 |
5 | module.exports = {
6 | set namespace (n) {
7 | _ns = n + '-'
8 | },
9 | get namespace () {
10 | return _ns
11 | }
12 | }
--------------------------------------------------------------------------------
/lib/directives.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Build-in Global Directives
3 | */
4 |
5 | 'use strict';
6 |
7 | var $ = require('./dm')
8 | var conf = require('./conf')
9 | var util = require('./util')
10 |
11 |
12 | module.exports = function() {
13 | return {
14 | 'attr': {
15 | multi: true,
16 | bind: function(attname) {
17 | this.attrs = attname ? attname.trim().split(/\s+/) : []
18 | if (this.attrs.length) {
19 | this._$el = $(this.$el)
20 | }
21 | },
22 | update: function(next) {
23 | var that = this
24 | this.attrs.forEach(function (attname) {
25 | if (util.isUndef(next)) {
26 | that._$el.removeAttr(attname)
27 | } else {
28 | that._$el.attr(attname, next)
29 | }
30 | })
31 | },
32 | unbind: function () {
33 | this._$el = this.attname = null
34 | }
35 | },
36 | 'class': {
37 | multi: true,
38 | bind: function(className) {
39 | this.classes = className ? className.trim().split(/\s+/) : []
40 | if (this.classes.length) {
41 | this._$el = $(this.$el)
42 | }
43 | },
44 | update: function(isUseClass) {
45 | var that = this
46 | this.classes.forEach(function (className) {
47 | if (isUseClass) that._$el.addClass(className)
48 | else that._$el.removeClass(className)
49 | })
50 | },
51 | unbind: function () {
52 | this._$el = this.className = null
53 | }
54 | },
55 | 'html': {
56 | update: function (nextHTML) {
57 | this.$el.innerHTML = nextHTML
58 | }
59 | },
60 | 'model': {
61 | bind: function () {
62 | var tagName = this.$el.tagName
63 | var type = tagName.toLowerCase()
64 | var $el = this._$el = $(this.$el)
65 |
66 | // pick input element type spec
67 | type = type == 'input' ? $el.attr('type') || 'text' : type
68 |
69 | switch (type) {
70 | case 'tel':
71 | case 'url':
72 | case 'text':
73 | case 'search':
74 | case 'password':
75 | case 'textarea':
76 | this.evtType = 'input'
77 | break
78 |
79 | case 'date':
80 | case 'week':
81 | case 'time':
82 | case 'month':
83 | case 'datetime':
84 | case 'datetime-local':
85 | case 'color':
86 | case 'range':
87 | case 'number':
88 | case 'select':
89 | case 'checkbox':
90 | this.evtType = 'change'
91 | break
92 | default:
93 | console.warn('"' + conf.namespace + 'model" only support input,textarea,select')
94 | return
95 | }
96 |
97 | var vm = this.$vm
98 | var _update = this.$update
99 | var vType = type == 'checkbox' ? 'checked':'value'
100 | var that = this
101 |
102 | /**
103 | * DOM input 2 state
104 | */
105 | this._requestChange = function () {
106 | vm.$set(that._prop, that.$el[vType])
107 | }
108 | /**
109 | * State 2 DOM input
110 | */
111 | this._update = function () {
112 | var nv = vm.$get(that._prop)
113 | nv = util.isUndef(nv) ? '' : nv
114 | if (that.$el[vType] !== nv) {
115 | that.$el[vType] = nv
116 | }
117 | }
118 | this.$update = function () {
119 | that._update()
120 | _update && _update.apply(this, arguments)
121 | }
122 | $el.on(this.evtType, this._requestChange)
123 | },
124 | watch: function (prop) {
125 | if (this._watches) {
126 | this._watches.forEach(function (fn) {
127 | fn()
128 | })
129 | this._watches = []
130 | }
131 | if (!prop) return
132 | var watches = this._watches = []
133 | var wKeypath = util.normalize(prop)
134 | while (wKeypath) {
135 | watches.push(this.$vm.$watch(wKeypath, this._update))
136 | wKeypath = util.digest(wKeypath)
137 | }
138 | },
139 | update: function (prop) {
140 | this._prop = prop
141 | this.watch(prop)
142 | this._update()
143 | },
144 | unbind: function () {
145 | this._$el.off(this.evtType, this._requestChange)
146 | this._watches.forEach(function (f) {
147 | f()
148 | })
149 | this._$el = null
150 | this._requestChange = this._update = noop
151 | }
152 | },
153 | 'on': {
154 | multi: true,
155 | watch: false,
156 | bind: function(evtType, handler, expression ) {
157 | this._expr = expression
158 | this.type = evtType
159 | this._$el = $(this.$el)
160 | },
161 | update: function (handler) {
162 | this.off()
163 | var fn = handler
164 | if (util.type(fn) !== 'function')
165 | return console.warn('"' + conf.namespace + 'on" only accept function. {' + this._expr + '}')
166 |
167 | this.fn = fn.bind(this.$vm)
168 | this._$el && this._$el.on(this.type, this.fn, false)
169 |
170 | },
171 | off: function () {
172 | if (this.fn) {
173 | this._$el && this._$el.off(this.type, this.fn)
174 | this.fn = null
175 | }
176 | },
177 | unbind: function() {
178 | this.off()
179 | this._$el = this.type = null
180 | }
181 | },
182 | 'show': {
183 | update: function(next) {
184 | this.$el.style.display = next ? '' : 'none'
185 | }
186 | },
187 | 'style': {
188 | multi: true,
189 | bind: function (sheet) {
190 | this.sheet = sheet
191 | },
192 | update: function (next) {
193 | this.$el.style && (this.$el.style[this.sheet] = next)
194 | },
195 | unbind: function () {
196 | this.sheet = null
197 | }
198 | },
199 | 'src': {
200 | bind: function () {
201 | this._$el = $(this.$el)
202 | },
203 | update: function (src) {
204 | if (util.isNon(src)) {
205 | this._$el.removeAttr('src')
206 | } else {
207 | this._$el.attr('src', src)
208 | }
209 | },
210 | unbind: function () {
211 | this._$el = null
212 | }
213 | }
214 | }
215 | }
216 | function noop () {}
--------------------------------------------------------------------------------
/lib/dm.js:
--------------------------------------------------------------------------------
1 | /**
2 | * DOM manipulations
3 | */
4 |
5 | 'use strict';
6 | var util = require('./util')
7 | var is = require('./is')
8 |
9 | function Selector(sel) {
10 | if (util.type(sel) == 'string') {
11 | return Shell(util.copyArray(document.querySelectorAll(sel)))
12 | }
13 | else if (util.type(sel) == 'array') {
14 | return Shell(sel)
15 | }
16 | else if (sel instanceof Shell) return sel
17 | else if (is.DOM(sel)) {
18 | return Shell(new ElementArray(sel))
19 | }
20 | else {
21 | throw new Error('Unexpect selector !')
22 | }
23 | }
24 |
25 | function Shell(nodes) {
26 | if (nodes instanceof Shell) return nodes
27 | var $items = new ElementArray()
28 | nodes.forEach(function (item) {
29 | $items.push(item)
30 | })
31 | return $items
32 | }
33 |
34 | function ElementArray () {
35 | this.push = function () {
36 | Array.prototype.push.apply(this, arguments)
37 | }
38 | this.forEach = function () {
39 | Array.prototype.forEach.apply(this, arguments)
40 | }
41 | this.push.apply(this, arguments)
42 | }
43 |
44 | ElementArray.prototype = Object.create(Shell.prototype)
45 |
46 | var proto = Shell.prototype
47 | proto.find = function(sel) {
48 | var subs = []
49 | this.forEach(function(n) {
50 | subs = subs.concat(util.copyArray(n.querySelectorAll(sel)))
51 | })
52 | return Shell(subs)
53 | }
54 | proto.attr = function(attname, attvalue) {
55 | var len = arguments.length
56 | var el = this[0]
57 |
58 | if (len > 1) {
59 | el.setAttribute(attname, attvalue)
60 | } else if (len == 1) {
61 | return (el.getAttribute(attname) || '').toString()
62 | }
63 | return this
64 | }
65 | proto.removeAttr = function(attname) {
66 | this.forEach(function(el) {
67 | el.removeAttribute(attname)
68 | })
69 | return this
70 | }
71 | proto.addClass = function(clazz) {
72 | this.forEach(function(el) {
73 |
74 | // IE9 below not support classList
75 | // el.classList.add(clazz)
76 |
77 | var classList = el.className.split(' ')
78 | if (!~classList.indexOf(clazz)) classList.push(clazz)
79 | el.className = classList.join(' ')
80 | })
81 | return this
82 | }
83 | proto.removeClass = function(clazz) {
84 | this.forEach(function(el) {
85 |
86 | // IE9 below not support classList
87 | // el.classList.remove(clazz)
88 |
89 | var classList = el.className.split(' ')
90 | var index = classList.indexOf(clazz)
91 | if (~index) classList.splice(index, 1)
92 | el.className = classList.join(' ')
93 | })
94 | return this
95 | }
96 | proto.each = function(fn) {
97 | this.forEach(fn)
98 | return this
99 | }
100 | proto.on = function(type, listener, capture) {
101 | this.forEach(function(el) {
102 | el.addEventListener(type, listener, capture)
103 | })
104 | return this
105 | }
106 | proto.off = function(type, listener) {
107 | this.forEach(function(el) {
108 | el.removeEventListener(type, listener)
109 | })
110 | return this
111 | }
112 | proto.html = function(html) {
113 | var len = arguments.length
114 | if (len >= 1) {
115 | this.forEach(function(el) {
116 | el.innerHTML = html
117 | })
118 | } else if (this.length) {
119 | return this[0].innerHTML
120 | }
121 | return this
122 | }
123 | proto.parent = function() {
124 | if (!this.length) return null
125 | return Shell([_parentNode(this[0])])
126 | }
127 | proto.remove = function() {
128 | this.forEach(function(el) {
129 | var parent = _parentNode(el)
130 | parent && parent.removeChild(el)
131 | })
132 | return this
133 | }
134 | proto.insertBefore = function (pos) {
135 | var tar
136 | if (!this.length) return this
137 | else if (this.length == 1) {
138 | tar = this[0]
139 | } else {
140 | tar = _createDocumentFragment()
141 | this.forEach(function (el) {
142 | _appendChild(tar, el)
143 | })
144 | }
145 | _parentNode(pos).insertBefore(tar, pos)
146 | return this
147 | }
148 | proto.insertAfter = function (pos) {
149 | var tar
150 | if (!this.length) return this
151 | else if (this.length == 1) {
152 | tar = this[0]
153 | } else {
154 | tar = _createDocumentFragment()
155 | this.forEach(function (el) {
156 | _appendChild(tar, el)
157 | })
158 | }
159 | _parentNode(pos).insertBefore(tar, pos.nextSibling)
160 | return this
161 | }
162 | // return element by index
163 | proto.get = function(i) {
164 | return this[i]
165 | }
166 | proto.append = function(n) {
167 | if (this.length) _appendChild(this[0], n)
168 | return this
169 | }
170 | proto.appendTo = function (p) {
171 | if (this.length == 1) _appendChild(p, this[0])
172 | else if (this.length > 1) {
173 | var f = _createDocumentFragment()
174 | this.forEach(function (n) {
175 | _appendChild(f, n)
176 | })
177 | _appendChild(p, f)
178 | }
179 | }
180 | proto.replace = function(n) {
181 | var tar = this[0]
182 | var pn = _parentNode(tar)
183 | pn && pn.replaceChild(n, tar)
184 | return this
185 | }
186 |
187 | function _parentNode (e) {
188 | return e && e.parentNode
189 | }
190 |
191 | function _createDocumentFragment () {
192 | return document.createDocumentFragment()
193 | }
194 |
195 | function _appendChild (p, c) {
196 | return p.appendChild(c)
197 | }
198 | module.exports = Selector
199 |
--------------------------------------------------------------------------------
/lib/elements.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Build-in Global Custom-Elements
3 | */
4 |
5 | 'use strict';
6 |
7 | var $ = require('./dm')
8 | var conf = require('./conf')
9 | var util = require('./util')
10 | var is = require('./is')
11 | var Scope = require('./scope')
12 | var Expression = require('./expression')
13 |
14 | module.exports = function() {
15 | return {
16 | 'if': {
17 | multiExpr: 'exclusion',
18 | bind: function(cnd, exprs) {
19 | this._tmpCons = exprs.map(function () {
20 | return document.createDocumentFragment()
21 | })
22 |
23 | /**
24 | * Initial unmount childNodes
25 | */
26 | var cursor = 0
27 | ;[].slice
28 | .call(this.$el.childNodes)
29 | .forEach(function(e) {
30 | if (is.ElseElement(e)) {
31 | cursor ++
32 | } else {
33 | this._tmpCons[cursor].appendChild(e)
34 | }
35 | }.bind(this))
36 |
37 | /**
38 | * Instance method
39 | */
40 | var mounteds = {}
41 | this._mount = function (index) {
42 | if (mounteds[index]) return
43 | mounteds[index] = true
44 | var $floor = this.$floor()
45 | $floor.parentNode.insertBefore(this._tmpCons[index], $floor)
46 | }
47 | this._unmount = function (index) {
48 | if (!mounteds) return
49 | mounteds[index] = false
50 | var $ceil = this.$ceil()
51 | var $floor = this.$floor()
52 |
53 | var that = this
54 | util.domRange($ceil.parentNode, $ceil, $floor)
55 | .forEach(function(n) {
56 | that._tmpCons[index].appendChild(n)
57 | })
58 | }
59 | this.compileds = {}
60 | this._lIndex = -1
61 | },
62 | update: function(next) {
63 | var lIndex = this._lIndex
64 | var rearIndex = next.length - 1
65 | var tIndex = -1
66 | var cnd = false
67 | next.some(function (v, i) {
68 | if (v) tIndex = i
69 | return !!v
70 | })
71 |
72 | cnd = next[tIndex]
73 | // is last else without condition
74 | if (!~tIndex && next.length > 1 && !this.$expr[rearIndex]) {
75 | tIndex = rearIndex
76 | cnd = true
77 | }
78 |
79 | this._lIndex = tIndex
80 | if (lIndex != tIndex && ~lIndex) {
81 | this._unmount(lIndex)
82 | }
83 |
84 | // not else and all conditions is false
85 | if (!~tIndex) return
86 |
87 | if (!cnd) {
88 | this._unmount(tIndex)
89 | } else if (this.compileds[tIndex]) {
90 | this._mount(tIndex)
91 | } else {
92 | this.compileds[tIndex] = true
93 | this.$vm.$compile(this._tmpCons[tIndex], this.$scope)
94 | this._mount(tIndex)
95 | }
96 | },
97 | unbind: function () {
98 | this.$update = this._mount = this._unmount = noop
99 | this.compileds = this._tmpCons = null
100 | }
101 | },
102 | 'repeat': {
103 | bind: function(items, expr) {
104 | var name = this.$el.getAttribute('ref')
105 | var that = this
106 | if (name) {
107 | this.$vm.$refs[name] = this
108 | }
109 | this.$items = function() {
110 | return this.$vms
111 | },
112 | this.$itemBindings = function(index) {
113 | if (!that.$vms || !that.$vms.length) return []
114 | var target = that.$vms[index]
115 | if (!target) return []
116 | return target.$scope.bindings
117 | }
118 | this.child = this.$el.firstElementChild
119 | this.expr = expr
120 | if (!this.child) {
121 | return console.warn('"' + conf.namespace + 'repeat"\'s childNode must has a HTMLElement node. {' + expr + '}')
122 | }
123 | // if use filter, Zect can't patch array by array-method
124 | this._noArrayFilter = Expression.notFunctionCall(expr)
125 | },
126 | unbind: function () {
127 | this.$vms && this.$vms.forEach(function (vm) {
128 | destroyVM(vm)
129 | })
130 | this.child = this.$vms = this._lastItems = null
131 | this.$items = this.$itemBindings = noop
132 | },
133 | delta: function (nv, pv, kp) {
134 | if (!kp) return false
135 | var exprProp = util.normalize(Expression.strip(this.$expr).trim())
136 | var path = kp.replace(exprProp, '')
137 | var index
138 | var matches
139 | /**
140 | * 1. mount.0
141 | * 2. mount.0.0.prop
142 | * 3. $value.prop
143 | */
144 | if (exprProp == '$value' && (matches = path.match(/^\d+\.(\d+)(\.|$)/))) {
145 | path = path.replace(/\d+\.?/, '')
146 | } else if (matches = path.match(/^\.(\d+)(\.|$)/)) {
147 | path = path.replace(/^\./, '')
148 | } else {
149 | return false
150 | }
151 | index = Number(matches[1])
152 | // can be delta update
153 | if (this.$vms && index < this.$vms.length) return {
154 | index: index,
155 | path: path,
156 | mount: exprProp
157 | }
158 | else return false
159 | },
160 | deltaUpdate: function (nextItems, preItems, kp, payload) {
161 | var index = payload.index
162 | var nv = nextItems[index]
163 | // delta update
164 | this._lastItems[index] = nv
165 |
166 | var $vm = this.$vms[index]
167 | updateVMData($vm, nv, index, payload.path)
168 | },
169 | update: function(items, preItems, kp, method, args) {
170 | if (!items || !items.forEach) {
171 | return console.warn('"' + conf.namespace + 'repeat" only accept Array data. {' + this.expr + '}')
172 | }
173 | var that = this
174 | var valueOnly = Expression.variableOnly(this.$expr)
175 |
176 | // it's not modify
177 | if (valueOnly && method == 'splice' && args.length == 2 && (!args[1] || args[1] < 0)) return
178 |
179 | var $floor = this.$floor()
180 | var $ceil = this.$ceil()
181 | var arrayPatcher = {
182 | splice: function () {
183 | var ind = Number(args[0] || 0)
184 | var len = Number(args[1] || 0)
185 | var max = this.$vms.length
186 | ind = ind > max ? max : ind
187 | if (args.length > 2) {
188 | /**
189 | * Insert
190 | */
191 | // create vms for each inserted item
192 | var insertVms = [].slice.call(args, 2).map(function (item, index) {
193 | return createSubVM.call(that, item, ind + index)
194 | })
195 | // insert items into current $vms
196 | this.$vms.splice.apply(this.$vms, [ind, len].concat(insertVms))
197 |
198 | // element bound for inserted item vm element
199 | $(insertVms.map(function (vm) {
200 | return vm.$compiler.$bundle()
201 | })).insertAfter(
202 | ind === 0
203 | ? $ceil
204 | : this.$vms[ind - 1].$compiler.$bundle()
205 | )
206 | // get last update index
207 | var start = ind + insertVms.length
208 | this.$vms.forEach(function (vm, i) {
209 | if (i >= start) {
210 | updateVMIndex(vm, i)
211 | }
212 | })
213 |
214 | } else {
215 | /**
216 | * remove
217 | */
218 | this.$vms.splice
219 | .apply(this.$vms, args)
220 | .forEach(function (vm) {
221 | destroyVM(vm)
222 | })
223 |
224 | this.$vms.forEach(function (vm, i) {
225 | if (i >= ind) {
226 | updateVMIndex(vm, i)
227 | }
228 | })
229 | }
230 | },
231 | push: function () {
232 | var index = items.length - 1
233 | var vm = createSubVM.call(that, items[index], index)
234 | this.$vms.push(vm)
235 | vm.$compiler.$insertBefore($floor)
236 | },
237 | pop: function () {
238 | var vm = this.$vms.pop()
239 | destroyVM(vm)
240 | },
241 | shift: function () {
242 | var vm = this.$vms.shift()
243 | destroyVM(vm)
244 | this.$vms.forEach(function (v, i) {
245 | updateVMIndex(v, i)
246 | })
247 | },
248 | unshift: function () {
249 | var vm = createSubVM.call(that, items[0], 0)
250 | this.$vms.unshift(vm)
251 | vm.$compiler.$insertAfter($ceil)
252 | this.$vms.forEach(function (v, i) {
253 | if (i !== 0) {
254 | updateVMIndex(v, i)
255 | }
256 | })
257 | },
258 | $concat: function () {
259 | var len = this.$vms.length
260 | $(items.slice(len).map(function (item, i) {
261 | var vm = createSubVM.call(that, item, i + len)
262 | that.$vms.push(vm)
263 | return vm.$compiler.$bundle()
264 | })).insertBefore($floor)
265 | }
266 | }
267 |
268 | var patch = arrayPatcher[method]
269 | if (valueOnly && this._noArrayFilter && patch) {
270 | patch.call(this)
271 | this._lastItems = util.copyArray(items)
272 | return
273 | }
274 | /**
275 | * vms diff
276 | */
277 | var source = this._lastItems
278 | ? this._lastItems.map(function (item) {
279 | return {
280 | data: item
281 | }
282 | })
283 | : null
284 | var diffItems = items.map(function (item, index) {
285 | var data = {
286 | data: item
287 | }
288 | if (!source) {
289 | data.status = 'created'
290 | } else {
291 | var i = -1
292 | var dontUpdated
293 | source.some(function (s, k) {
294 | if (s.used) return
295 |
296 | var hasDiff = util.diff(s.data, item)
297 | if (!hasDiff) {
298 | i = k
299 | // and reuse the pos
300 | if (index === i) return (dontUpdated = true)
301 | else if (!~i) i = k
302 | }
303 | })
304 | if (~i && dontUpdated) {
305 | source[i].used = true
306 | data.status = 'reused'
307 | } else if (~i) {
308 | source[i].used = true
309 | data.status = 'moved'
310 | data.from = i
311 | } else {
312 | source.some(function (s, k) {
313 | if (!s.used && index == k) {
314 | i = k
315 | return true
316 | }
317 | })
318 | if (~i) {
319 | source[i].used = true
320 | data.status = 'updated'
321 | data.from = i
322 | } else {
323 | data.status = 'created'
324 | }
325 | }
326 | }
327 | return data
328 | })
329 |
330 |
331 | /**
332 | * reuse those instance that data changed and index unmatch
333 | * state from "created" to "recycled"
334 | */
335 | var reusables = (source || []).reduce(function (collects, item, index) {
336 | if (!item.used) {
337 | collects.push(index)
338 | }
339 | return collects
340 | }, [])
341 |
342 | diffItems.some(function (item) {
343 | if (!reusables.length) return true
344 |
345 | if (item.status == 'created') {
346 | item.from = reusables.pop()
347 | item.status = 'recycled'
348 | }
349 | })
350 | /**
351 | * destroy
352 | */
353 | reusables.forEach(function (i) {
354 | destroyVM(that.$vms[i])
355 | })
356 | /**
357 | * Patch
358 | */
359 | var floor = $ceil
360 | this.$vms = diffItems.map(function (item, index) {
361 | var vm
362 | switch (item.status) {
363 | case 'created':
364 | vm = createSubVM.call(that, item.data, index)
365 | break
366 | case 'updated':
367 | vm = that.$vms[index]
368 | updateVMData(vm, item.data, index, kp)
369 | break
370 | case 'moved':
371 | vm = that.$vms[item.from]
372 | updateVMIndex(vm, index)
373 | break
374 | case 'reused':
375 | vm = that.$vms[index]
376 | break
377 | case 'recycled':
378 | vm = that.$vms[item.from]
379 | updateVMData(vm, item.data, item.from, kp)
380 | break
381 | }
382 | var $compiler = vm.$compiler
383 | if (!$compiler.$preTo(floor)) {
384 | vm.$compiler.$insertAfter(floor)
385 | }
386 | floor = $compiler.$floor()
387 | return vm
388 | })
389 | // prevent data source changed
390 | this._lastItems = util.copyArray(items)
391 | }
392 | }
393 | }
394 | }
395 | function shallowClone (data) {
396 | return util.type(data) == 'object' ? util.copyObject(data) : {}
397 | }
398 | // function RepeatViewModel (el, data, index, parentVM, parentScope) {
399 | // data = shallowClone(data)
400 | // data.$index = index
401 | // data.$value = data
402 | // var $scope = new Scope(data, parentScope)
403 | // // on the top of current scope
404 | // parentScope && parentScope.children.push($scope)
405 | // this.$index = index
406 | // this.$value = data
407 | // this.$compiler = parentVM.$compile(el, $scope)
408 | // this.$scope = $scope
409 | // }
410 | /**
411 | * create a sub-vm for array item with specified index
412 | */
413 | function createSubVM (item, index) {
414 | var subEl = this.child.cloneNode(true)
415 | var data = shallowClone(item)
416 |
417 | data.$index = index
418 | data.$value = item
419 |
420 | var $scope = new Scope(data, this.$scope)
421 | // this.$scope is a parent scope,
422 | // on the top of current scope
423 | if(this.$scope) {
424 | this.$scope.children.push($scope)
425 | // data.$parent = this.$scope.data
426 | }
427 | return {
428 | $index: index,
429 | $value: item,
430 | $compiler: this.$vm.$compile(subEl, $scope),
431 | $scope: $scope
432 | }
433 | }
434 | function updateVMData (vm, data, index, mounted) {
435 | // next time will add props to data object
436 | var $data = vm.$scope.data = shallowClone(data)
437 | $data.$index = index
438 | $data.$value = data
439 | // $data.$parent = vm.$scope.$parent ? vm.$scope.$parent.data : null
440 |
441 | vm.$value = data
442 | vm.$index = index
443 | vm.$scope.$update(mounted || '')
444 | }
445 | function updateVMIndex (vm, index) {
446 | vm.$index = index
447 | var $data = vm.$scope.data
448 | $data.$index = index
449 | vm.$scope.$update()
450 | }
451 | function destroyVM (vm) {
452 | var $parent = vm.$scope.$parent
453 | $parent && $parent.$removeChild(vm.$scope)
454 | // $compiler be inclued in $scope.bindings probably
455 | vm.$compiler.$remove().$destroy()
456 | vm.$scope.bindings.forEach(function (bd) {
457 | bd.$destroy()
458 | })
459 | }
460 | function noop () {}
461 |
--------------------------------------------------------------------------------
/lib/execute.js:
--------------------------------------------------------------------------------
1 | /**
2 | * execute expression from template with specified Scope and ViewModel
3 | */
4 |
5 | var util = require('./util')
6 | var __$compile__ = require('./compile')
7 | var __$compiledExprs___ = {}
8 | /**
9 | * Calc expression value
10 | */
11 | function _execute($vm, $scope/*, expression, [label], [target]*/) {
12 | /**
13 | * $scope is passed when call instance method $compile,
14 | * Each "scope" object maybe include "$parent, data, methods" properties
15 | */
16 | // var $parent = $scope && $scope.$parent ? util.extend({}, $scope.$parent.methods, $scope.$parent.data) : {}
17 | if ($scope && $scope.$parent) {
18 | $scope.data.$parent = $scope.$parent.data
19 | }
20 | var __$expression__ = arguments[2]
21 | var __$fn__ = __$compiledExprs___[__$expression__]
22 | $scope = $scope || {}
23 | $scope = util.extend({}, $vm.$methods, $vm.$data, $scope.methods, $scope.data)
24 | try {
25 | if (!__$fn__) {
26 | __$fn__ = __$compiledExprs___[__$expression__] = __$compile__(__$expression__)
27 | }
28 | return util.immutable(__$fn__($scope))
29 | } catch (e) {
30 | __$expression__ = /^\{/.test(__$expression__)
31 | ? '. ' + __$expression__
32 | : '. {' + __$expression__ + '}' // expr
33 | // arguments[3] // label
34 | // arguments[4] // target
35 | switch (e.name) {
36 | case 'ReferenceError':
37 | console.warn(e.message + __$expression__)
38 | break
39 | default:
40 | console.error(
41 | (arguments[3] ? '\'' + arguments[3] + '\': ' : ''),
42 | e.message + __$expression__,
43 | arguments[4] || ''
44 | )
45 | }
46 | return ''
47 | }
48 | }
49 | module.exports = _execute
--------------------------------------------------------------------------------
/lib/expression.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var execute = require('./execute')
4 | var util = require('./util')
5 | var _sep = ';'
6 | var _sepRegexp = new RegExp(_sep, 'g')
7 | var _literalSep = ','
8 | var _exprRegexp = /\{[\s\S]*?\}/g
9 | var _varsRegexp = /("|').+?[^\\]\1|\.\w*|\$\w*|\w*:|\b(?:this|true|false|null|undefined|new|typeof|Number|String|Object|Array|Math|Date|JSON)\b|([a-z_]\w*)\(|([a-z_]\w*)/gi
10 | /**
11 | * Whether a text is with express syntax
12 | */
13 | function _isExpr(c) {
14 | return c ? !!c.trim().match(/^\{[\s\S]*?\}$/m) : false
15 | }
16 | module.exports = {
17 | sep: _sep,
18 | literalSep: _literalSep,
19 |
20 | sepRegexp: _sepRegexp,
21 | exprRegexp: _exprRegexp,
22 |
23 | isExpr: _isExpr,
24 | isUnescape: function(expr) {
25 | return !!expr.match(/^\{\- /)
26 | },
27 | execLiteral: function (expr, vm, scope) {
28 | if (!_isExpr(expr)) return {}
29 | return execute(vm, scope, expr.replace(_sepRegexp, _literalSep))
30 | },
31 | veil: function (expr) {
32 | return expr.replace(/\\{/g, '\uFFF0')
33 | .replace(/\\}/g, '\uFFF1')
34 | },
35 | unveil: function (expr) {
36 | return expr.replace(/\uFFF0/g, '\\{')
37 | .replace(/\uFFF1/g, '\\}')
38 | },
39 | strip: function (expr) {
40 | // -\d*\.?\d* TBD
41 | var m = expr.trim().match(/^\{([\s\S]*)\}$/m)
42 | return m && m[1] ? m[1].replace(/^- /, '') : ''
43 | },
44 | extract: function(expr) {
45 | if (!expr) return null
46 | var vars = expr.match(_varsRegexp)
47 | vars = !vars ? [] : vars.filter(function(i) {
48 | if (!i.match(/^[\."'\]\[]/) && !i.match(/\($/)) {
49 | return i
50 | }
51 | })
52 | return vars
53 | },
54 | /**
55 | * abc
56 | * abc.0
57 | * abc[0]
58 | * abc[0].abc
59 | * abc["xx-xx"] || abc['xx-xx']
60 | */
61 | variableOnly: function (expr) {
62 | return !util.normalize(expr || '').split('.').some(function (k) {
63 | return !k.match(/^([a-zA-Z_$][\w$]*|\d+|".*"|'.*')$/)
64 | })
65 | },
66 | notFunctionCall: function (expr) {
67 | return !/[()]/.test(expr)
68 | }
69 | }
--------------------------------------------------------------------------------
/lib/is.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var conf = require('./conf')
3 | module.exports = {
4 | Element: function(el) {
5 | // 1: ELEMENT_NODE, 11: DOCUMENT_FRAGMENT_NODE
6 | return el.nodeType == 1 || el.nodeType == 11
7 | },
8 | DOM: function (el) {
9 | // 8: COMMENT_NODE
10 | return this.Element(el) || el.nodeType == 8
11 | },
12 | IfElement: function(tn) {
13 | return tn == (conf.namespace + 'if').toUpperCase()
14 | },
15 | ElseElement: function(node) {
16 | return node.hasAttribute && node.hasAttribute(conf.namespace + 'else')
17 | },
18 | RepeatElement: function(tn) {
19 | return tn == (conf.namespace + 'repeat').toUpperCase()
20 | }
21 | }
--------------------------------------------------------------------------------
/lib/scope.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Scope abstraction is a colletor when compiler child template with scope
3 | */
4 |
5 | 'use strict';
6 |
7 | function Scope (data, parent) {
8 | this.data = data
9 | this.bindings = []
10 | this.children = []
11 | this.$parent = parent || null
12 | }
13 |
14 | Scope.prototype.$update = function () {
15 | var args = arguments
16 | this.bindings.forEach(function (bd) {
17 | bd.$update.apply(bd, args)
18 | })
19 | this.children.forEach(function (child) {
20 | child.$update.apply(child, args)
21 | })
22 | }
23 | Scope.prototype.$removeChild = function (scope) {
24 | var i = this.children.indexOf(scope)
25 | if (~i) {
26 | scope.$parent = null
27 | this.children.splice(i, 1)
28 | }
29 | return this
30 | }
31 | Scope.prototype.$addChild = function (scope) {
32 | if (!~this.children.indexOf(scope)) this.children.push(scope)
33 | return this
34 | }
35 |
36 | module.exports = Scope
--------------------------------------------------------------------------------
/lib/util.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Mux = require('muxjs')
4 | var mUtils = Mux.utils
5 | var _normalize = Mux.keyPath.normalize
6 | var _digest = Mux.keyPath.digest
7 |
8 | function _keys(o) {
9 | return Object.keys(o)
10 | }
11 | function _forEach (items, fn) {
12 | var len = items.length || 0
13 | for (var i = 0; i < len; i ++) {
14 | if(fn(items[i], i)) break
15 | }
16 | }
17 | function _slice (obj) {
18 | if (!obj) return []
19 | return [].slice.call(obj)
20 | }
21 | var escapeCharMap = {
22 | '&': '&',
23 | '<': '<',
24 | '>': '>',
25 | '\"': '"',
26 | '\'': ''',
27 | '/': '/'
28 | }
29 | var escapeRex = new RegExp(_keys(escapeCharMap).join('|'), 'g')
30 | module.exports = {
31 | type: mUtils.type,
32 | diff: mUtils.diff,
33 | merge: mUtils.merge,
34 | objEach: mUtils.objEach,
35 | copyArray: mUtils.copyArray,
36 | copyObject: mUtils.copyObject,
37 |
38 | extend: function(obj) {
39 | if (this.type(obj) != 'object') return obj;
40 | var source, prop;
41 | for (var i = 1, length = arguments.length; i < length; i++) {
42 | source = arguments[i];
43 | for (prop in source) {
44 | obj[prop] = source[prop];
45 | }
46 | }
47 | return obj;
48 | },
49 | valueDiff: function(next, pre) {
50 | return next !== pre || next instanceof Object
51 | },
52 | walk: function(node, fn) {
53 | var into = fn(node) !== false
54 | var that = this
55 | if (into) {
56 | _slice(node.childNodes).forEach(function (node) {
57 | that.walk(node, fn)
58 | })
59 | }
60 | },
61 | domRange: function (tar, before, after) {
62 | var children = []
63 | var nodes = tar.childNodes
64 | var start = false
65 | for (var i = 0; i < nodes.length; i++) {
66 | var item = nodes[i]
67 | if (item === after) break
68 | else if (start) {
69 | children.push(item)
70 | } else if (item == before) {
71 | start = true
72 | }
73 | }
74 | return children
75 | },
76 | immutable: function (obj) {
77 | var that = this
78 | var _t = this.type(obj)
79 | var n
80 |
81 | if (_t == 'array') {
82 | n = obj.map(function (item) {
83 | return that.immutable(item)
84 | })
85 | } else if (_t == 'object') {
86 | n = {}
87 | this.objEach(obj, function (k, v) {
88 | n[k] = that.immutable(v)
89 | })
90 | } else {
91 | n = obj
92 | }
93 | return n
94 | },
95 | tagHTML: function (tag) {
96 | var h = tag.outerHTML
97 | var open = h.match(/^<[^>]+?>/)
98 | var close = h.match(/<\/[^<]+?>$/)
99 |
100 | return [open ? open[0]:'', close ? close[0]:'']
101 | },
102 | relative: function (src, dest) {
103 | src = _normalize(src)
104 | dest = _normalize(dest)
105 |
106 | if (src == dest) return true
107 | else {
108 | var start = src.indexOf(dest) === 0
109 | var subkp = src.replace(dest, '').match(/^[\.\[]/)
110 | return start && subkp
111 | }
112 | },
113 | escape: function (str) {
114 | if (!this.type(str) == 'string') return str
115 | return str.replace(escapeRex, function (m) {
116 | return escapeCharMap[m]
117 | })
118 | },
119 | isUndef: function (o) {
120 | return o === void(0)
121 | },
122 | isNon: function (o) {
123 | var t = this.type(o)
124 | return t === 'undefined' || t === 'null'
125 | },
126 | bind: function (fn, ctx) {
127 | var dummy = function () {
128 | return fn.apply(ctx, arguments)
129 | }
130 | dummy.toString = function () {
131 | return fn.toString.apply(fn, arguments)
132 | }
133 | return dummy
134 | },
135 | forEach: _forEach,
136 | normalize: _normalize,
137 | digest: _digest
138 | }
139 |
--------------------------------------------------------------------------------
/lib/zect.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var $ = require('./dm')
4 | var is = require('./is')
5 | var Mux = require('muxjs')
6 | var util = require('./util')
7 | var conf = require('./conf')
8 | var execute = require('./execute')
9 | var Compiler = require('./compiler')
10 | var Expression = require('./expression')
11 |
12 | var Directive = Compiler.Directive
13 | var AttributeDirective = Compiler.Attribute
14 | var TextDirective = Compiler.Text
15 | var ElementDirective = Compiler.Element
16 |
17 |
18 | /**
19 | * private vars
20 | */
21 | var buildInDirts = require('./directives')(Zect) // preset directives getter
22 | var elements = require('./elements')(Zect) // preset directives getter
23 | var allDirectives = [buildInDirts, {}] // [preset, global]
24 | var gdirs = allDirectives[1]
25 | var gcomps = {} // global define components
26 |
27 | var _isExpr = Expression.isExpr
28 | /**
29 | * Global API
30 | */
31 | function Zect(options) {
32 | var insOpt = _mergeMethodMixins([options])
33 | return ViewModel.call(this, insOpt)
34 | }
35 | Zect.create = Zect.extend = function(options) {
36 | function Class(opt) {
37 | var insOpt = _mergeMethodMixins([options, opt])
38 | /**
39 | * Prototype inherit
40 | */
41 | return ViewModel.call(this, insOpt)
42 | }
43 | _inherit(Class, Zect)
44 | return Class
45 | }
46 | Zect.component = function(id, definition) {
47 | var Comp = Zect.extend(definition)
48 | gcomps[id.toLowerCase()] = Comp
49 | return Comp
50 | }
51 | Zect.directive = function(id, definition) {
52 | gdirs[id] = definition
53 | }
54 | Zect.namespace = function(ns) {
55 | conf.namespace = ns
56 | }
57 | Zect.$ = $
58 | _inherit(Zect, Compiler)
59 |
60 | /*******************************
61 | ViewModel Constructor
62 | *******************************/
63 | function ViewModel(options) {
64 | // inherit Compiler
65 |
66 | var vm = this
67 | var el = options.el
68 | var components = [gcomps, options.components || {}]
69 | var directives = allDirectives.concat([options.directives || {}])
70 |
71 | var _directives = [] // local refs for all directives instance of the vm
72 | var _components = [] // local refs for all components
73 | var NS = conf.namespace
74 | var componentProps = [NS + 'component', NS + 'data', NS + 'methods', NS + 'ref', NS + 'replace']
75 | var $childrens = options.$childrens
76 |
77 | // set $parent ref
78 | vm.$parent = options.$parent || null
79 |
80 | /**
81 | * Mounted element detect
82 | */
83 | if (util.type(el) == 'string') {
84 | el = document.querySelector(el)
85 | }
86 | if (el && options.template) {
87 | el.innerHTML = options.template
88 | } else if (options.template) {
89 | el = document.createElement('div')
90 | el.innerHTML = options.template
91 | } else if (!is.Element(el)) {
92 | throw new Error('Unmatch el option')
93 | }
94 |
95 | // replace "$NS-template" of actual instance's DOM
96 | if (el.children.length == 1 && el.firstElementChild.tagName.toLowerCase() == (NS + 'template')) {
97 | var $holder = el.firstElementChild
98 | var childNodes = _slice($holder.childNodes)
99 | var attributes = _slice($holder.attributes)
100 |
101 | el.removeChild($holder)
102 | /**
103 | * Migrate childNodes
104 | */
105 | $(childNodes).appendTo(el)
106 | /**
107 | * Merge attributes
108 | */
109 | attributes.forEach(function (att) {
110 | var nv
111 | if (att.name == 'class') {
112 | nv = att.value + (el.className
113 | ? ' ' + el.className
114 | : '')
115 |
116 | } else if (!el.hasAttribute(att.name)) {
117 | nv = att.value
118 | } else {
119 | return
120 | }
121 | el.setAttribute(att.name, nv)
122 | })
123 | }
124 | // content insertion
125 | var points = _slice(el.querySelectorAll('content'))
126 | if (points) {
127 | var $con
128 | if ($childrens && $childrens.length) {
129 | $con = document.createDocumentFragment()
130 | _slice($childrens).forEach(function (n) {
131 | $con.appendChild(n)
132 | })
133 | }
134 | points.forEach(function (p) {
135 | if (!$childrens || !$childrens.length) {
136 | return $(p).remove()
137 | }
138 | var $p = $(p)
139 | var select = $p.attr('select')
140 | var tar
141 | var ind
142 |
143 | if (select
144 | && (tar = $con.querySelector(select))
145 | && ~(ind = $childrens.indexOf(tar)) ) {
146 |
147 | $p.replace(tar)
148 | $childrens.splice(ind, 1)
149 | } else if (!select) {
150 | $p.replace($con)
151 | $childrens = null
152 | }
153 | })
154 | }
155 |
156 | /**
157 | * Replace external component element holder with internal child element
158 | */
159 | if (options.replace) {
160 | if (el.children.length !== 1) {
161 | console.warn('Can\'t using \'' + NS + 'replace=true\' for a component that has no or multiple child-elements.')
162 | } else if (el.parentNode) {
163 | var replacedEl = el.firstElementChild
164 | _cloneArributes(replacedEl, el)
165 | el.parentNode.replaceChild(replacedEl, el)
166 | el = replacedEl
167 | } else {
168 | el = el.firstElementChild
169 | }
170 | }
171 |
172 | vm.$el = el
173 |
174 | /**
175 | * get component define by tagName
176 | */
177 | vm.$component = getComponent
178 |
179 | /**
180 | * Component instance refs
181 | */
182 | vm.$refs = {}
183 |
184 | /**
185 | * assign methods
186 | */
187 | var methods = {}
188 | util.objEach(options.methods, function(k, v) {
189 | if (util.type(v) !== 'function') return console.warn(k + ' is not a function.')
190 | vm[k] = methods[k] = util.bind(v, vm)
191 | })
192 | vm.$methods = methods
193 |
194 | var $data
195 | var dataOpt = {}
196 |
197 | Object.defineProperty(vm, '$data', {
198 | enumerable: true,
199 | get: function() {
200 | // $data will be undefined in created calling, so using dataOpt as temporary
201 | return $data || dataOpt
202 | },
203 | set: function(v) {
204 | if (!$data) return util.merge(dataOpt, v)
205 |
206 | $data.$set(v)
207 | return $data
208 | }
209 | })
210 | vm.$set = function () {
211 | $data.$set.apply($data, arguments)
212 | }
213 | vm.$get = function () {
214 | return $data.$get.apply($data, arguments)
215 | }
216 | vm.$watch = function (/*[ keypath ], fn*/) {
217 | return $data.$watch.apply($data, arguments)
218 | }
219 | vm.$unwatch = function (/*[ keypath ], fn*/) {
220 | return $data.$unwatch.apply($data, arguments)
221 | }
222 |
223 | var created = options.created
224 | if (options.$data) {
225 | $data = options.$data
226 | // if state model instance passsing, call after set
227 | created && created.call(vm)
228 | } else {
229 | util.merge(dataOpt, _funcOrObject(options, 'data'))
230 | // Call before vm-$data instance
231 | created && created.call(vm)
232 | // Instance observable state model
233 | var mopts = {
234 | props: dataOpt,
235 | computed: options.computed,
236 | computedContext: vm
237 | }
238 | $data = new Mux(mopts)
239 | }
240 |
241 | /**
242 | * DOM Compile
243 | * @TODO the unique interface for a compiled node($el, $remove)
244 | */
245 | vm.$compile = function (el, scope) {
246 | var compiler
247 |
248 | util.walk(el, function (node) {
249 | var isRoot = node === el
250 | var result = compile(node, scope, isRoot)
251 | if (isRoot) compiler = result.inst
252 | return result.into
253 | })
254 | return compiler
255 | }
256 |
257 | var beforeDestroy = options.destroy
258 | vm.$destroy = function () {
259 | if (vm.$destroyed) return
260 | beforeDestroy && beforeDestroy.call(vm)
261 |
262 | ;[_components, _directives].forEach(function (items) {
263 | items.forEach(function (inst) {
264 | inst.$destroy()
265 | })
266 | })
267 |
268 | $data.$destroy()
269 |
270 | // instance methods/properties
271 | vm.$el = null
272 | vm.$get = null
273 | vm.$set = null
274 | vm.$refs = null
275 | vm.$watch = null
276 | vm.$unwatch = null
277 | vm.$compile = null
278 | vm.$component = null
279 |
280 | // private vars
281 | directives = null
282 | components = null
283 | _directives = null
284 | _components = null
285 |
286 | // marked
287 | vm.$destroyed = true
288 | }
289 | vm.$compiler = vm.$compile(el)
290 |
291 | /**
292 | * Call ready after compile
293 | */
294 | options.ready && options.ready.call(vm)
295 |
296 | function _getAllDirts () {
297 | var _dirts = {}
298 | directives.forEach(function(group) {
299 | util.objEach(group, function(id, def) {
300 | _dirts[NS + id] = def
301 | })
302 | })
303 | return _dirts
304 | }
305 | function _setBindings2Scope (scope, ref) {
306 | scope && scope.bindings && (scope.bindings.push(ref))
307 | }
308 | function compile (node, scope, isRoot) {
309 | /**
310 | * 1. ELEMENT_NODE;
311 | * 2. ATTRIBUTE_NODE;
312 | * 3. TEXT_NODE;
313 | * 8. COMMENT_NODE;
314 | * 9. DOCUMENT_NODE;
315 | * 11. DOCUMENT_FRAGMENT;
316 | */
317 | var into = true
318 | var inst
319 | switch (node.nodeType) {
320 | case 1:
321 | /**
322 | * static block no parsing
323 | */
324 | if (node.hasAttribute(NS + 'static')) {
325 | into = false
326 | break
327 | }
328 | /**
329 | * convert those $ns-if, $ns-repeat attribute to block element
330 | */
331 | node = compilePseudoDirectiveElement(node)
332 | /**
333 | * scope syntax
334 | */
335 | if (inst = compileElement(node, scope, isRoot)) {
336 | into = false
337 | break
338 | }
339 | /**
340 | * Attribute directive
341 | */
342 | compileDirective(node, scope)
343 | /**
344 | * Compile custom-element
345 | */
346 | if (inst = compileComponent(node, vm, scope, isRoot)) {
347 | into = false
348 | break
349 | }
350 | break
351 | case 3:
352 | // ignore whitespace
353 | if (node.nodeValue.trim()) inst = compileText(node, vm, scope, isRoot)
354 | into = false
355 | break
356 | case 11:
357 | // document fragment
358 | break
359 | default:
360 | into = false
361 | }
362 | return {
363 | into: !!into,
364 | inst: !inst && isRoot ? new Compiler(node) : inst
365 | }
366 | }
367 |
368 | function compilePseudoDirectiveElement (node) {
369 | var repeatAttName = NS + 'repeat'
370 | var ifAttName = NS + 'if'
371 | var valueAttName
372 | var matchedAttName
373 |
374 | if (node.hasAttribute(ifAttName)) {
375 | matchedAttName = ifAttName
376 | valueAttName = 'is'
377 | } else if (node.hasAttribute(repeatAttName)) {
378 | matchedAttName = repeatAttName
379 | valueAttName = 'items'
380 | } else {
381 | return node
382 | }
383 | var attValue = node.getAttribute(matchedAttName)
384 | var blockNode = document.createElement(matchedAttName)
385 | node.removeAttribute(matchedAttName)
386 | node.parentNode && node.parentNode.replaceChild(blockNode, node)
387 | blockNode.appendChild(node)
388 | blockNode.setAttribute(valueAttName, attValue)
389 |
390 | while(node.hasAttribute(ifAttName) || node.hasAttribute(repeatAttName)) {
391 | compilePseudoDirectiveElement(node)
392 | }
393 | return blockNode
394 | }
395 |
396 | /**
397 | * Reverse component Constructor by tagName
398 | */
399 | function getComponent(tn) {
400 | var cid = tn.toLowerCase()
401 | var compDef
402 | components.some(function (comp) {
403 | if (comp.hasOwnProperty(cid)) {
404 | compDef = comp[cid]
405 | return true
406 | }
407 | })
408 | return compDef
409 | }
410 | /**
411 | * Compile element for block syntax handling
412 | */
413 | function compileElement(node, scope, isRoot) {
414 | var tagName = node.tagName
415 | var inst
416 | switch(true) {
417 | /**
418 | * <*-if>*-if>
419 | */
420 | case is.IfElement(tagName):
421 | var children = _slice(node.children)
422 | var exprs = [$(node).attr('is')]
423 | children.forEach(function(c) {
424 | if (is.ElseElement(c)) {
425 | exprs.push($(c).attr(conf.namespace + 'else') || '')
426 | }
427 | })
428 | inst = new ElementDirective(
429 | vm,
430 | scope,
431 | node,
432 | elements['if'],
433 | NS + 'if',
434 | exprs
435 | )
436 | if (!isRoot) {
437 | inst.$mount(node)
438 | }
439 | // save elements refs
440 | _directives.push(inst)
441 | // save bindins to scope
442 | _setBindings2Scope(scope, inst)
443 | return inst
444 | /**
445 | * <*-repeat>*-repeat>
446 | */
447 | case is.RepeatElement(tagName):
448 | inst = new ElementDirective(
449 | vm,
450 | scope,
451 | node,
452 | elements.repeat,
453 | NS + 'repeat',
454 | $(node).attr('items')
455 | )
456 | if (!isRoot) {
457 | inst.$mount(node)
458 | }
459 | _directives.push(inst)
460 | _setBindings2Scope(scope, inst)
461 | return inst
462 | }
463 | }
464 |
465 | /**
466 | * comment
467 | */
468 | function compileComponent (node, parentVM, scope) {
469 | var cAttName = NS + 'component'
470 | var CompName = node.getAttribute(cAttName)
471 | var tagName = node.tagName
472 | // filtrate most no-compoment element
473 | if (!CompName && (tagName == 'DIV' || tagName == 'SPAN' || tagName == 'A' || tagName == 'IMG')) return
474 | CompName = CompName || tagName
475 | var Comp = getComponent(CompName)
476 |
477 | /**
478 | * Tag is not a custom component element
479 | */
480 | if (!Comp) return
481 | var $node = $(node)
482 | $node.removeAttr(cAttName)
483 |
484 | // don't need deep into self
485 | if (node === parentVM.$el) return
486 | // suport expression, TBD
487 | var refName = NS + 'ref'
488 | var dAttName = NS + 'data'
489 | var mAttName = NS + 'methods'
490 | var rAttName = NS + 'replace'
491 |
492 | var ref = $node.attr(refName)
493 | var dataExpr = $node.attr(dAttName)
494 | var methods = $node.attr(mAttName)
495 | var replace = $node.attr(rAttName)
496 |
497 | $node.removeAttr(refName)
498 | .removeAttr(dAttName)
499 | .removeAttr(mAttName)
500 | .removeAttr(rAttName)
501 |
502 | var _isDataExpr = _isExpr(dataExpr)
503 | var bindingData
504 | var bindingMethods
505 | /**
506 | * Watch
507 | */
508 | var execLiteral = Expression.execLiteral
509 | var ast = {}
510 | var revealAst = {}
511 | var compVM
512 |
513 | function _parseExpr (exp) {
514 | var name
515 | exp = exp.replace(/^[^:]+:/, function (m) {
516 | name = m.replace(/:$/, '').trim()
517 | return ''
518 | }).trim()
519 | return {
520 | name: name,
521 | expr: exp,
522 | vars: Expression.extract(exp)
523 | }
524 | }
525 | function _setBindingObj (expr) {
526 | var r = _parseExpr(expr)
527 | ast[r.name] = r
528 | ;(r.vars || []).forEach(function (v) {
529 | !revealAst[v] && (revealAst[v] = []);
530 | !~revealAst[v].indexOf(r.name) && revealAst[v].push(r.name)
531 | })
532 | }
533 |
534 | bindingData = execLiteral(dataExpr, parentVM, scope)
535 | bindingMethods = execLiteral(methods, parentVM, scope) // --> bindingMethods
536 |
537 | compVM = new Comp({
538 | el: node,
539 | data: bindingData,
540 | methods: bindingMethods,
541 | $parent: parentVM,
542 | $childrens: _slice(node.childNodes),
543 | replace: replace == 'true'
544 | })
545 |
546 | var plainDataExpr = _isDataExpr ? Expression.strip(dataExpr) : ''
547 | var sep = Expression.sep
548 |
549 | if (plainDataExpr) {
550 | if (plainDataExpr.match(sep)) {
551 | plainDataExpr.replace(new RegExp(sep + '\\s*$'), '') // trim last seperator
552 | .split(sep)
553 | .forEach(_setBindingObj)
554 | } else {
555 | _setBindingObj(plainDataExpr)
556 | }
557 | }
558 | // watch and binding
559 | if (_isDataExpr) {
560 | parentVM.$data.$watch(function (keyPath) {
561 | var nextState
562 | util.objEach(revealAst, function (varName, bindingNames) {
563 | if (keyPath.indexOf(varName) === 0) {
564 | ;!nextState && (nextState = {})
565 | bindingNames.forEach(function (n) {
566 | nextState[n] = execute(parentVM, scope, ast[n].expr)
567 | })
568 | }
569 | })
570 | nextState && compVM.$set(nextState)
571 | })
572 | }
573 | // set ref to parentVM
574 | ref && (parentVM.$refs[ref] = compVM)
575 |
576 | _components.push(compVM)
577 | _setBindings2Scope(scope, compVM)
578 | // TBM -- to be modify, instance method should not be attached here
579 | compVM.$update = function () {
580 | _isDataExpr && compVM.$set(execLiteral(dataExpr, parentVM, scope))
581 | }
582 | return compVM
583 | }
584 | /**
585 | * Compile attributes to directive
586 | */
587 | function compileDirective (node, scope) {
588 | var ast = {
589 | attrs: {},
590 | dires: {}
591 | }
592 | var dirtDefs = _getAllDirts()
593 | /**
594 | * attributes walk
595 | */
596 | _slice(node.attributes).forEach(function(att) {
597 | var aname = att.name
598 | var v = att.value
599 | // parse att
600 | if (~componentProps.indexOf(aname)) {
601 | return
602 | } else if (_isExpr(aname)) {
603 | // variable attribute name
604 | ast.attrs[aname] = v
605 | } else if (aname.indexOf(NS) === 0) {
606 | var def = dirtDefs[aname]
607 | if (def) {
608 | // directive
609 | ast.dires[aname] = {
610 | def: def,
611 | expr: v
612 | }
613 | } else {
614 | return
615 | }
616 | } else if (_isExpr(v.trim())) {
617 | // named attribute with expression
618 | ast.attrs[aname] = v
619 | } else {
620 | return
621 | }
622 | node.removeAttribute(aname)
623 | })
624 |
625 | /**
626 | * Attributes binding
627 | */
628 | util.objEach(ast.attrs, function(name, value) {
629 | var attd = new AttributeDirective(vm, scope, node, name, value)
630 | _directives.push(attd)
631 | _setBindings2Scope(scope, attd)
632 | })
633 |
634 | /**
635 | * Directives binding
636 | */
637 | util.objEach(ast.dires, function(dname, spec) {
638 | var def = spec.def
639 | var expr = spec.expr
640 | var sep = ';'
641 | var d
642 | // multiple defines expression parse
643 | if (def.multi && expr.match(sep)) {
644 | Expression.strip(expr)
645 | .split(sep)
646 | .forEach(function(item) {
647 | // discard empty expression
648 | if (!item.trim()) return
649 |
650 | d = new Directive(vm, scope, node, def, dname, '{' + item + '}')
651 | _directives.push(d)
652 | _setBindings2Scope(scope, d)
653 | })
654 | } else {
655 | d = new Directive(vm, scope, node, def, dname, expr)
656 | _directives.push(d)
657 | _setBindings2Scope(scope, d)
658 | }
659 | })
660 | }
661 |
662 | function compileText (node, vm, scope) {
663 | var originExpr = node.nodeValue
664 | var v = Expression.veil(originExpr)
665 | var exprReg = Expression.exprRegexp
666 |
667 | var parts = v.split(exprReg)
668 | var exprs = v.match(exprReg)
669 |
670 | var inst
671 | // expression match or not
672 | if (exprs && exprs.length) {
673 | inst = new TextDirective(vm, scope, node, originExpr, parts, exprs)
674 | _directives.push(inst)
675 | _setBindings2Scope(scope, inst)
676 | }
677 | return inst
678 | }
679 | }
680 |
681 | /**
682 | * private functions
683 | */
684 | function _slice (obj) {
685 | if (!obj) return []
686 | return [].slice.call(obj)
687 | }
688 | function _funcOrObject(obj, prop) {
689 | var tar = obj[prop]
690 | return util.type(tar) == 'function' ? tar.call(obj):tar
691 | }
692 | function _extend (args) {
693 | return util.extend.apply(util, args)
694 | }
695 | function _inherit (Ctor, Parent) {
696 | var proto = Ctor.prototype
697 | Ctor.prototype = Object.create(Parent.prototype)
698 | return proto
699 | }
700 | function _mergeOptions (opts) {
701 | var dest = {}
702 | _extend([dest].concat(opts))
703 | ;['data', 'methods', 'directives', 'components'].forEach(function (prop) {
704 | dest[prop] = _extend([{}].concat(opts.map(function (opt) {
705 | return _funcOrObject(opt, prop)
706 | })))
707 | })
708 | return dest
709 | }
710 | function _mergeMethodMixins (optMixins) {
711 | var mixins = []
712 | /**
713 | * Merge option mixins
714 | */
715 | optMixins.forEach(function (o) {
716 | if (o) {
717 | mixins.push(o)
718 | o.mixins && (mixins = mixins.concat(o.mixins))
719 | }
720 | })
721 | var insOpt = _mergeOptions(mixins)
722 | /**
723 | * Merge method mixins
724 | */
725 | var m = insOpt.methods = insOpt.methods || {}
726 | optMixins.forEach(function (o) {
727 | var mxs = o && o.methods && o.methods.mixins
728 | if (mxs) {
729 | mxs.forEach(function (mx) {
730 | util.objEach(mx, function (k, v) {
731 | if (k !== 'mixins') m[k] = v
732 | })
733 | })
734 | }
735 | })
736 | delete insOpt.methods.mixins
737 | return insOpt
738 | }
739 | function _cloneArributes(el, target) {
740 | var $tar = $(target)
741 | _slice(el.attributes).forEach(function (att) {
742 | if (att.name == 'class') $tar.addClass(att.value)
743 | else $tar.attr(att.name, att.value)
744 | })
745 | return target
746 | }
747 |
748 | module.exports = Zect
749 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zect",
3 | "version": "1.2.26",
4 | "description": "A lightweight Web components and MVVM framework.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "./node_modules/.bin/gulp && npm start",
8 | "start": "./node_modules/.bin/mocha-phantomjs ./test/runner.html"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/switer/Zect.git"
13 | },
14 | "keywords": [
15 | "mvvm",
16 | "zect",
17 | "web components",
18 | "data-binding"
19 | ],
20 | "author": "switer",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/switer/Zect/issues"
24 | },
25 | "devDependencies": {
26 | "gulp": "^3.8.10",
27 | "gulp-header": "^1.2.2",
28 | "gulp-uglifyjs": "^0.5.0",
29 | "gulp-watch": "^4.1.0",
30 | "gulp-webpack": "^1.1.2",
31 | "chai": "^3.2.0",
32 | "mocha": "^2.3.2",
33 | "mocha-phantomjs": "^3.6.0",
34 | "phantomjs": "1.9.7-15"
35 | },
36 | "dependencies": {
37 | "muxjs": "^2.4.18"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/test/array.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | + item
11 | ++ item
12 | insert items
13 | shift item
14 | unshift item
15 | - item
16 | Change
17 | reverse items
18 |
19 |
20 |
21 |
22 |
28 |
29 |
30 |
31 |
32 |
33 |
159 |
160 |
--------------------------------------------------------------------------------
/test/debug.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
12 |
13 | 1
14 |
15 | 2
16 |
17 | 3
18 |
19 |
20 |
21 |
42 |
43 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
24 |
25 |
26 | switer
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Please Input
51 | hello
52 | world
53 | renew
54 |
55 |
is check: {inputs.check ? 'yes':'no'}
56 |
{inputs.date}
57 |
{inputs.range}
58 |
{inputs.color}
59 |
{inputs.tel}
60 |
{inputs.url}
61 |
{inputs.search}
62 |
63 |
{part(title)}
68 |
69 |
{- contents.text}
70 |
71 |
72 |
73 |
74 |
177 |
178 |
--------------------------------------------------------------------------------
/test/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Testing
7 |
8 |
9 |
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/spec.directive.if.js:
--------------------------------------------------------------------------------
1 | describe('#Directives/if', function () {
2 | it('without else', function () {
3 | var app = new Zect({
4 | data: function () {
5 | return {
6 | status: 0
7 | }
8 | },
9 | template: tools.template(function () {/*
10 |
11 |
12 | */})
13 | })
14 | assert(!!app.$el.querySelector('.cnd1'))
15 | assert(!app.$el.querySelector('.cnd2'))
16 | app.$data.status = 1
17 | assert(!app.$el.querySelector('.cnd1'))
18 | assert(!!app.$el.querySelector('.cnd2'))
19 | })
20 |
21 | it('with else-if and without else', function () {
22 | var app = new Zect({
23 | data: function () {
24 | return {
25 | status: 0
26 | }
27 | },
28 | template: tools.template(function () {/*
29 |
30 |
31 |
32 |
33 |
34 | */})
35 | })
36 | assert(!!app.$el.querySelector('.cnd1'))
37 | assert(!app.$el.querySelector('.cnd2'))
38 | app.$data.status = 1
39 | assert(!app.$el.querySelector('.cnd1'))
40 | assert(!!app.$el.querySelector('.cnd2'))
41 | app.$data.status = 2
42 | assert(!app.$el.querySelector('.cnd1'))
43 | assert(!app.$el.querySelector('.cnd2'))
44 | app.$data.status = 0
45 | assert(!!app.$el.querySelector('.cnd1'))
46 | assert(!app.$el.querySelector('.cnd2'))
47 | app.$data.status = 1
48 | assert(!app.$el.querySelector('.cnd1'))
49 | assert(!!app.$el.querySelector('.cnd2'))
50 | })
51 |
52 | it('with else and without else-if', function () {
53 | var app = new Zect({
54 | data: function () {
55 | return {
56 | status: 0
57 | }
58 | },
59 | template: tools.template(function () {/*
60 |
61 |
62 |
63 |
64 |
65 | */})
66 | })
67 | assert(!!app.$el.querySelector('.cnd1'))
68 | assert(!app.$el.querySelector('.cnd2'))
69 | app.$data.status = 10
70 | assert(!app.$el.querySelector('.cnd1'))
71 | assert(!!app.$el.querySelector('.cnd2'))
72 | })
73 | it('with else and with else-if', function () {
74 | var app = new Zect({
75 | data: function () {
76 | return {
77 | status: 0
78 | }
79 | },
80 | template: tools.template(function () {/*
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | */})
89 | })
90 | assert(!!app.$el.querySelector('.cnd1'))
91 | assert(!app.$el.querySelector('.cnd2'))
92 | assert(!app.$el.querySelector('.cnd3'))
93 | app.$data.status = 1
94 | assert(!app.$el.querySelector('.cnd1'))
95 | assert(!!app.$el.querySelector('.cnd2'))
96 | assert(!app.$el.querySelector('.cnd3'))
97 | app.$data.status = 2
98 | assert(!app.$el.querySelector('.cnd1'))
99 | assert(!app.$el.querySelector('.cnd2'))
100 | assert(!!app.$el.querySelector('.cnd3'))
101 | })
102 | })
--------------------------------------------------------------------------------
/test/spec.directive.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('#Directive', function () {
4 | it('attr', function () {
5 | var app = new Zect({
6 | template: ' ',
7 | data: function () {
8 | return {
9 | attValue: ''
10 | }
11 | }
12 | })
13 | assert(app.$el.hasAttribute('attName'))
14 | assert.equal(app.$el.getAttribute('attName'), '')
15 | app.$data.attValue = 'v'
16 | assert.equal(app.$el.getAttribute('attName'), 'v')
17 | app.$data.attValue = null
18 | assert.equal(app.$el.getAttribute('attName'), 'null')
19 | })
20 | it('attr:multiple', function () {
21 | var app = new Zect({
22 | template: ' ',
23 | data: function () {
24 | return {
25 | attValue: ''
26 | }
27 | }
28 | })
29 | assert(app.$el.hasAttribute('attName1'))
30 | assert(app.$el.hasAttribute('attName2'))
31 | assert.equal(app.$el.getAttribute('attName1'), '')
32 | assert.equal(app.$el.getAttribute('attName2'), '')
33 | app.$data.attValue = 'v'
34 | assert.equal(app.$el.getAttribute('attName1'), 'v')
35 | assert.equal(app.$el.getAttribute('attName2'), 'v')
36 | app.$data.attValue = null
37 | assert.equal(app.$el.getAttribute('attName1'), 'null')
38 | assert.equal(app.$el.getAttribute('attName2'), 'null')
39 | })
40 | it('class:multiple', function () {
41 | var app = new Zect({
42 | template: ' ',
43 | data: function () {
44 | return {
45 | addClass: false
46 | }
47 | }
48 | })
49 | assert.equal(app.$el.className, 'att')
50 | app.$data.addClass = true
51 | assert.equal(app.$el.className, 'att class1 class2')
52 | })
53 | })
--------------------------------------------------------------------------------
/test/spec.directive.repeat.js:
--------------------------------------------------------------------------------
1 | describe('#Directives/repeat', function () {
2 | it('repeat', function () {
3 | var app = new Zect({
4 | data: function () {
5 | return {
6 | items: [{id: 1, name: '1'}, {id: 2, name: '2'}]
7 | }
8 | },
9 | template: tools.template(function () {/*
10 |
11 | {name}
12 |
13 | */})
14 | })
15 | var $items = app.$el.querySelectorAll('[data-id]')
16 | expect($items.length).to.equal(2)
17 | /**
18 | * delta update
19 | */
20 | app.$data.items[0].id = 3
21 | app.$data.items[1].id = 4
22 | var $nextItems = app.$el.querySelectorAll('[data-id]')
23 | expect($items[0]).to.equal($nextItems[0])
24 | expect($items[1]).to.equal($nextItems[1])
25 | expect($items[0].dataset.id).to.equal('3')
26 | expect($items[1].dataset.id).to.equal('4')
27 | })
28 | it('repeat:x2', function () {
29 | var app = new Zect({
30 | data: function () {
31 | return {
32 | items: [[{id: 1, name: '1'}, {id: 2, name: '2'}]]
33 | }
34 | },
35 | template: tools.template(function () {/*
36 |
37 |
38 |
39 | {name}
40 |
41 |
42 |
43 | */})
44 | })
45 | var $uls = [].slice.call(app.$el.querySelectorAll('ul'))
46 | var $lis = [].slice.call(app.$el.querySelectorAll('ul li'))
47 | expect($uls.length).to.equal(1)
48 | expect($lis.length).to.equal(2)
49 | /**
50 | * delta update
51 | */
52 | // app.$data.items[0][0].id = 3
53 | app.$data.items[0][1].id = 4
54 | var $nextUls = app.$el.querySelectorAll('ul')
55 | var $nextLis = app.$el.querySelectorAll('ul li')
56 | expect($uls[0]).to.equal($nextUls[0])
57 | expect($lis[0]).to.equal($nextLis[0])
58 | expect($lis[1]).to.equal($nextLis[1])
59 | expect($nextLis[1].dataset.id).to.equal('4')
60 |
61 | /**
62 | * delta update item
63 | */
64 | app.$data.$set('items[0][1]', {id: 5, name: '5'})
65 | $nextLis = app.$el.querySelectorAll('ul li')
66 | expect($lis[0]).to.equal($nextLis[0])
67 | expect($lis[1]).to.equal($nextLis[1])
68 | expect($nextLis[1].dataset.id).to.equal('5')
69 |
70 | /**
71 | * array.shift()
72 | */
73 | app.$data.items[0].shift()
74 | $nextLis = app.$el.querySelectorAll('ul li')
75 | assert.notEqual($lis[0], $nextLis[0])
76 | assert.equal($nextLis.length, 1)
77 | assert.equal($nextLis[0].dataset.id, '5')
78 |
79 | /**
80 | * array.splice()
81 | */
82 | app.$data.items[0].splice(0, 0, {id: 2, name: '2'})
83 | $nextLis = app.$el.querySelectorAll('ul li')
84 | assert.equal($nextLis.length, 2)
85 | assert.equal($nextLis[0].dataset.id, '2')
86 | })
87 | it('repeat:diff', function () {
88 | var app = new Zect({
89 | data: function () {
90 | return {
91 | items: [[{id: 0, name: '0'}, {id: 1, name: '1'}, {id: 2, name: '2'}]]
92 | }
93 | },
94 | template: tools.template(function () {/*
95 |
96 |
97 |
98 | {name}
99 |
100 |
101 |
102 | */})
103 | })
104 | var $uls = [].slice.call(app.$el.querySelectorAll('ul'))
105 | var $lis = [].slice.call(app.$el.querySelectorAll('ul li'))
106 | assert.equal($uls.length, 1)
107 | assert.equal($lis.length, 3)
108 | // diff update
109 | app.$data.$set('items[0]', [{
110 | id: 1,
111 | name: '1'
112 | }, {
113 | id: 2,
114 | name: '2'
115 | }])
116 | var $nextLis = [].slice.call(app.$el.querySelectorAll('ul li'))
117 | assert.equal($nextLis.length, 2)
118 | assert.equal($nextLis[0], $lis[1])
119 | assert.equal($nextLis[1], $lis[2])
120 | })
121 | it('repeat:reused', function () {
122 | /**
123 | * and test recycled
124 | */
125 | var app = new Zect({
126 | data: function () {
127 | return {
128 | items: [1,2,3,4]
129 | }
130 | },
131 | template: tools.template(function () {/*
132 |
133 |
134 | {$value}
135 |
136 |
137 | */})
138 | })
139 |
140 | var $lis = [].slice.call(app.$el.querySelectorAll('ul li'))
141 | app.$data.items = [4,5,3,6]
142 | var $nextLis = [].slice.call(app.$el.querySelectorAll('ul li'))
143 |
144 | assert.equal($nextLis[0], $lis[3]) // moved
145 | assert.equal($nextLis[1], $lis[1]) // updated
146 | assert.equal($nextLis[2], $lis[2]) // reused
147 | assert.equal($nextLis[3], $lis[0]) // recycled
148 |
149 | assert.equal($nextLis[0].innerHTML, 4)
150 | assert.equal($nextLis[1].innerHTML, 5)
151 | assert.equal($nextLis[2].innerHTML, 3)
152 | assert.equal($nextLis[3].innerHTML, 6)
153 | })
154 | it('repeat:destroy', function () {
155 | /**
156 | * and test recycled
157 | */
158 | var app = new Zect({
159 | data: function () {
160 | return {
161 | items: [1,2,3,4]
162 | }
163 | },
164 | template: tools.template(function () {/*
165 |
166 |
167 |
168 | {$value}
169 |
170 |
171 |
172 | */}),
173 | methods: {
174 | to2d: function (list) {
175 | var next = []
176 | list = list.slice(0)
177 | while(list.length) {
178 | next.push(list.splice(0, 2))
179 | }
180 | return next
181 | }
182 | }
183 | })
184 | app.$data.items = []
185 | assert.equal(app.$el.querySelectorAll('ul li').length, 0) // moved
186 | })
187 | it('repeat:ref', function () {
188 | var app = new Zect({
189 | data: function () {
190 | return {
191 | items: [1,2,3,4]
192 | }
193 | },
194 | template: tools.template(function () {/*
195 |
196 | {$value}
197 |
198 | */})
199 | })
200 | assert.equal(app.$refs.repeat.$itemBindings(0)[0].$expr, '{$value}') // moved
201 | })
202 | it('repeat:nested-variables', function () {
203 | var app = new Zect({
204 | template: tools.template(function () {/*
205 |
206 |
207 |
208 | {$parent.$parent.$index}.{$parent.$index}.{$value}
209 |
210 |
211 |
212 | */}),
213 | data: function () {
214 | return {
215 | list: [{_items: [{_values: [1]}]}]
216 | }
217 | }
218 | })
219 | assert.equal(app.$el.children[0].innerHTML, '0.0.1')
220 | })
221 | })
--------------------------------------------------------------------------------
/test/spec.js:
--------------------------------------------------------------------------------
1 | describe('#Global API', function () {
2 | it('Zect()', function () {
3 | var app = new Zect({
4 | el: '#app'
5 | })
6 | tools.checkInstance(app)
7 | })
8 | it('Zect.extend()', function () {
9 | var Comp = Zect.extend({
10 | template: 'template
'
11 | })
12 | var app = new Comp({
13 | el: document.createElement('div')
14 | })
15 | tools.checkInstance(app)
16 | assert(app.$el.querySelector('.tpl'))
17 | })
18 | it('Zect.component()', function () {
19 | var Comp = Zect.component('c-comp', {
20 | template: '
'
21 | })
22 | var app = new Zect({
23 | template: tools.template(function () {/*
24 |
28 | */})
29 | })
30 | tools.checkInstance(app)
31 | tools.checkInstance(new Comp())
32 | assert(app.$el.querySelectorAll('.c-comp').length == 2)
33 | })
34 | it('Zect.namespace()', function () {
35 | Zect.namespace('r')
36 | var app = new Zect({
37 | el: document.createElement('div'),
38 | template: '
'
39 | })
40 | Zect.namespace('z')
41 | tools.checkInstance(app)
42 | assert(app.$el.querySelector('.tpl'))
43 | })
44 | it('Zect.directive()', function () {
45 | /**
46 | * Add data-set attribute
47 | * @multiple
48 | */
49 | Zect.directive('dataset', {
50 | multi: true,
51 | bind: function (key, value, expr) {
52 | expect(key).to.be.a('string')
53 | expect(expr).to.be.a('string')
54 | this.key = key
55 | },
56 | update: function (value) {
57 | this.$el.dataset[this.key] = value
58 | }
59 | })
60 | /**
61 | * Check value is number
62 | * @multiple
63 | */
64 | Zect.directive('number', {
65 | bind: function (value, expr) {
66 | expect(expr).to.be.a('string')
67 | expect(value).to.be.a('number')
68 | },
69 | update: function (value) {
70 | expect(value).to.be.a('number')
71 | }
72 | })
73 | var app = new Zect({
74 | el: document.createElement('div'),
75 | data: function () {
76 | return {
77 | num: 123
78 | }
79 | },
80 | template: ''
81 | })
82 | tools.checkInstance(app)
83 | expect(app.$el.querySelector('.tpl').dataset.tpl).to.equal('test')
84 | })
85 | })
86 |
87 | describe('#Instance Options', function () {
88 | it('el', function () {
89 | var app = new Zect({
90 | el: '#app'
91 | })
92 | tools.checkInstance(app)
93 | assert(app.$el === document.querySelector('#app'))
94 |
95 | var el = document.createElement('div')
96 | app = new Zect({
97 | el: el
98 | })
99 | tools.checkInstance(app)
100 | assert(app.$el === el)
101 |
102 | app = new Zect({
103 | el: '#app',
104 | template: '
'
105 | })
106 | tools.checkInstance(app)
107 | assert(app.$el.querySelector('.tpl'))
108 | })
109 | })
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/test/tools.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | window.tools = {
4 | checkInstance: function (inst) {
5 | // element
6 | assert(inst.$el.nodeType == 1 || inst.$el.nodeType == 11)
7 | // instance properties
8 | expect(inst.$refs).to.be.an('object')
9 | expect(inst.$methods).to.be.an('object')
10 | // instance methods
11 | expect(inst.$set).to.be.a('function')
12 | expect(inst.$get).to.be.a('function')
13 | expect(inst.$watch).to.be.a('function')
14 | expect(inst.$unwatch).to.be.a('function')
15 | expect(inst.$compile).to.be.a('function')
16 | expect(inst.$component).to.be.a('function')
17 | expect(inst.$destroy).to.be.a('function')
18 | },
19 | template: function (fn) {
20 | return fn.toString().replace(/^function\s*\(\s*\)\s*\{\s*\/\*|\*\/\s*\}$/g, '')
21 | }
22 | }
--------------------------------------------------------------------------------