1 | var isObservable = require('../is-observable')
|
2 | var Set = require('../set')
|
3 | var watch = require('../watch')
|
4 |
|
5 | module.exports = applyProperties
|
6 |
|
7 | function applyProperties (target, properties, data) {
|
8 | var classList = Set()
|
9 | if (target.classList && target.classList.value) {
|
10 | classList.add(target.classList.value)
|
11 | }
|
12 |
|
13 | for (var key in properties) {
|
14 | var valueOrObs = properties[key]
|
15 |
|
16 | if (key === 'style') {
|
17 |
|
18 | var value = resolve(valueOrObs)
|
19 | for (var k in value) {
|
20 | var styleObs = isObservable(value[k]) ? value[k] : null
|
21 | if (styleObs) {
|
22 | data.bindings.push(new Binding(bindStyle, target, styleObs, k))
|
23 | } else {
|
24 | target.style.setProperty(k, value[k])
|
25 | }
|
26 | }
|
27 | } else if (key === 'hooks') {
|
28 | var value = resolve(valueOrObs)
|
29 | if (Array.isArray(value)) {
|
30 | value.forEach(function (v) {
|
31 | data.bindings.push(new HookBinding(v, target))
|
32 | })
|
33 | }
|
34 | } else if (key === 'attributes') {
|
35 | var value = resolve(valueOrObs)
|
36 | for (var k in value) {
|
37 | var attrObs = isObservable(value[k]) ? value[k] : null
|
38 | if (attrObs) {
|
39 | data.bindings.push(new Binding(bindAttr, target, attrObs, k))
|
40 | } else {
|
41 | target.setAttribute(k, value[k])
|
42 | }
|
43 | }
|
44 | } else if (key === 'events') {
|
45 | for (var name in valueOrObs) {
|
46 | target.addEventListener(name, valueOrObs[name], false)
|
47 | }
|
48 | } else if (key.slice(0, 3) === 'ev-') {
|
49 | target.addEventListener(key.slice(3), valueOrObs, false)
|
50 | } else if (key === 'className' || key === 'classList') {
|
51 | if (Array.isArray(valueOrObs)) {
|
52 | valueOrObs.forEach(function (v) {
|
53 | classList.add(v)
|
54 | })
|
55 | } else {
|
56 | classList.add(valueOrObs)
|
57 | }
|
58 | } else {
|
59 | target[key] = resolve(valueOrObs)
|
60 | var obs = isObservable(valueOrObs) ? valueOrObs : null
|
61 | if (obs) {
|
62 | data.bindings.push(new Binding(bind, target, obs, key))
|
63 | }
|
64 | }
|
65 | }
|
66 |
|
67 | if (containsObservables(classList)) {
|
68 | data.bindings.push(new Binding(bindClassList, target.classList, classList, 'value'))
|
69 | } else {
|
70 |
|
71 | target.classList.value = classList().join(' ')
|
72 | }
|
73 | }
|
74 |
|
75 | function containsObservables (obs) {
|
76 | for (var i = 0, len = obs.getLength(); i < len; i++) {
|
77 | if (isObservable(obs.get(i))) {
|
78 | return true
|
79 | }
|
80 | }
|
81 | }
|
82 |
|
83 | function bindClassList (target, obs, key) {
|
84 | return watch(obs, function boundClassList (value) {
|
85 | value = [].concat.apply([], value).filter(present).join(' ')
|
86 | if (value || target[key]) {
|
87 | target[key] = value
|
88 | }
|
89 | })
|
90 | }
|
91 |
|
92 | function bindStyle (target, styleObs, key) {
|
93 | return watch(styleObs, function boundStyle (value) {
|
94 | target.style.setProperty(key, value)
|
95 | })
|
96 | }
|
97 |
|
98 | function bindAttr (target, attrObs, key) {
|
99 | return watch(attrObs, function boundAttr (value) {
|
100 | if (value == null) {
|
101 | target.removeAttribute(key)
|
102 | } else {
|
103 | target.setAttribute(key, value)
|
104 | }
|
105 | })
|
106 | }
|
107 |
|
108 | function bind (target, obs, key) {
|
109 | return watch(obs, function bound (toValue) {
|
110 | var fromValue = target[key]
|
111 | if (fromValue !== toValue) {
|
112 | target[key] = toValue
|
113 | }
|
114 | })
|
115 | }
|
116 |
|
117 | function present (val) {
|
118 | return val != null
|
119 | }
|
120 |
|
121 | function resolve (source) {
|
122 | return typeof source === 'function' ? source() : source
|
123 | }
|
124 |
|
125 | function Binding (fn, element, source, key) {
|
126 | this.element = element
|
127 | this.source = source
|
128 | this.key = key
|
129 | this.fn = fn
|
130 | this.bound = false
|
131 | }
|
132 |
|
133 | Binding.prototype = {
|
134 | bind: function () {
|
135 | if (!this.bound) {
|
136 | this._release = this.fn(this.element, this.source, this.key)
|
137 | this.bound = true
|
138 | }
|
139 | },
|
140 | unbind: function () {
|
141 | if (this.bound) {
|
142 | this._release()
|
143 | this._release = null
|
144 | this.bound = false
|
145 | }
|
146 | }
|
147 | }
|
148 |
|
149 | function HookBinding (fn, element) {
|
150 | this.element = element
|
151 | this.fn = fn
|
152 | this.bound = false
|
153 | }
|
154 |
|
155 | HookBinding.prototype = {
|
156 | bind: function () {
|
157 | if (!this.bound) {
|
158 | this._release = this.fn(this.element)
|
159 | this.bound = true
|
160 | }
|
161 | },
|
162 | unbind: function () {
|
163 | if (this.bound && typeof this._release === 'function') {
|
164 | this._release()
|
165 | this._release = null
|
166 | this.bound = false
|
167 | }
|
168 | }
|
169 | }
|