Skip to content

Commit 66c382a

Browse files
committed
Merge branch 'master' of github.com:sveltejs/svelte
2 parents 822f070 + c2352fe commit 66c382a

File tree

4 files changed

+325
-13
lines changed

4 files changed

+325
-13
lines changed

src/generators/dom/visitors/Element/Binding.ts

+21-13
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,13 @@ export default function visitBinding(
5151

5252
let setter = getSetter(block, name, snippet, state.parentNode, attribute, dependencies, value);
5353
let updateElement = `${state.parentNode}.${attribute.name} = ${snippet};`;
54+
55+
const needsLock = node.name !== 'input' || !/radio|checkbox|range|color/.test(type); // TODO others?
5456
const lock = `#${state.parentNode}_updating`;
55-
let updateCondition = `!${lock}`;
57+
let updateConditions = needsLock ? [`!${lock}`] : [];
58+
let readOnly = false;
5659

57-
block.addVariable(lock, 'false');
60+
if (needsLock) block.addVariable(lock, 'false');
5861

5962
// <select> special case
6063
if (node.name === 'select') {
@@ -125,24 +128,24 @@ export default function visitBinding(
125128
${setter}
126129
`;
127130

128-
updateCondition += ` && !isNaN(${snippet})`;
131+
updateConditions.push(`!isNaN(${snippet})`);
129132
} else if (attribute.name === 'duration') {
130-
updateCondition = null;
133+
readOnly = true;
131134
} else if (attribute.name === 'paused') {
132135
// this is necessary to prevent the audio restarting by itself
133136
const last = block.getUniqueName(`${state.parentNode}_paused_value`);
134137
block.addVariable(last, 'true');
135138

136-
updateCondition = `${last} !== (${last} = ${snippet})`;
139+
updateConditions = [`${last} !== (${last} = ${snippet})`];
137140
updateElement = `${state.parentNode}[${last} ? "pause" : "play"]();`;
138141
}
139142
}
140143

141144
block.builders.init.addBlock(deindent`
142145
function ${handler}() {
143-
${lock} = true;
146+
${needsLock && `${lock} = true;`}
144147
${setter}
145-
${lock} = false;
148+
${needsLock && `${lock} = false;`}
146149
}
147150
`);
148151

@@ -172,13 +175,18 @@ export default function visitBinding(
172175
node.initialUpdateNeedsStateObject = !block.contexts.has(name);
173176
}
174177

175-
if (updateCondition !== null) {
176-
// audio/video duration is read-only, it never updates
177-
block.builders.update.addBlock(deindent`
178-
if (${updateCondition}) {
178+
if (!readOnly) { // audio/video duration is read-only, it never updates
179+
if (updateConditions.length) {
180+
block.builders.update.addBlock(deindent`
181+
if (${updateConditions.join(' && ')}) {
182+
${updateElement}
183+
}
184+
`);
185+
} else {
186+
block.builders.update.addBlock(deindent`
179187
${updateElement}
180-
}
181-
`);
188+
`);
189+
}
182190
}
183191

184192
if (attribute.name === 'paused') {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
function noop() {}
2+
3+
function assign(target) {
4+
var k,
5+
source,
6+
i = 1,
7+
len = arguments.length;
8+
for (; i < len; i++) {
9+
source = arguments[i];
10+
for (k in source) target[k] = source[k];
11+
}
12+
13+
return target;
14+
}
15+
16+
function insertNode(node, target, anchor) {
17+
target.insertBefore(node, anchor);
18+
}
19+
20+
function detachNode(node) {
21+
node.parentNode.removeChild(node);
22+
}
23+
24+
function createElement(name) {
25+
return document.createElement(name);
26+
}
27+
28+
function addListener(node, event, handler) {
29+
node.addEventListener(event, handler, false);
30+
}
31+
32+
function removeListener(node, event, handler) {
33+
node.removeEventListener(event, handler, false);
34+
}
35+
36+
function destroy(detach) {
37+
this.destroy = noop;
38+
this.fire('destroy');
39+
this.set = this.get = noop;
40+
41+
if (detach !== false) this._fragment.unmount();
42+
this._fragment.destroy();
43+
this._fragment = this._state = null;
44+
}
45+
46+
function differs(a, b) {
47+
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
48+
}
49+
50+
function dispatchObservers(component, group, changed, newState, oldState) {
51+
for (var key in group) {
52+
if (!changed[key]) continue;
53+
54+
var newValue = newState[key];
55+
var oldValue = oldState[key];
56+
57+
var callbacks = group[key];
58+
if (!callbacks) continue;
59+
60+
for (var i = 0; i < callbacks.length; i += 1) {
61+
var callback = callbacks[i];
62+
if (callback.__calling) continue;
63+
64+
callback.__calling = true;
65+
callback.call(component, newValue, oldValue);
66+
callback.__calling = false;
67+
}
68+
}
69+
}
70+
71+
function get(key) {
72+
return key ? this._state[key] : this._state;
73+
}
74+
75+
function fire(eventName, data) {
76+
var handlers =
77+
eventName in this._handlers && this._handlers[eventName].slice();
78+
if (!handlers) return;
79+
80+
for (var i = 0; i < handlers.length; i += 1) {
81+
handlers[i].call(this, data);
82+
}
83+
}
84+
85+
function observe(key, callback, options) {
86+
var group = options && options.defer
87+
? this._observers.post
88+
: this._observers.pre;
89+
90+
(group[key] || (group[key] = [])).push(callback);
91+
92+
if (!options || options.init !== false) {
93+
callback.__calling = true;
94+
callback.call(this, this._state[key]);
95+
callback.__calling = false;
96+
}
97+
98+
return {
99+
cancel: function() {
100+
var index = group[key].indexOf(callback);
101+
if (~index) group[key].splice(index, 1);
102+
}
103+
};
104+
}
105+
106+
function on(eventName, handler) {
107+
if (eventName === 'teardown') return this.on('destroy', handler);
108+
109+
var handlers = this._handlers[eventName] || (this._handlers[eventName] = []);
110+
handlers.push(handler);
111+
112+
return {
113+
cancel: function() {
114+
var index = handlers.indexOf(handler);
115+
if (~index) handlers.splice(index, 1);
116+
}
117+
};
118+
}
119+
120+
function set(newState) {
121+
this._set(assign({}, newState));
122+
if (this._root._lock) return;
123+
this._root._lock = true;
124+
callAll(this._root._beforecreate);
125+
callAll(this._root._oncreate);
126+
callAll(this._root._aftercreate);
127+
this._root._lock = false;
128+
}
129+
130+
function _set(newState) {
131+
var oldState = this._state,
132+
changed = {},
133+
dirty = false;
134+
135+
for (var key in newState) {
136+
if (differs(newState[key], oldState[key])) changed[key] = dirty = true;
137+
}
138+
if (!dirty) return;
139+
140+
this._state = assign({}, oldState, newState);
141+
this._recompute(changed, this._state, oldState, false);
142+
if (this._bind) this._bind(changed, this._state);
143+
dispatchObservers(this, this._observers.pre, changed, this._state, oldState);
144+
this._fragment.update(changed, this._state);
145+
dispatchObservers(this, this._observers.post, changed, this._state, oldState);
146+
}
147+
148+
function callAll(fns) {
149+
while (fns && fns.length) fns.pop()();
150+
}
151+
152+
function _mount(target, anchor) {
153+
this._fragment.mount(target, anchor);
154+
}
155+
156+
function _unmount() {
157+
this._fragment.unmount();
158+
}
159+
160+
var proto = {
161+
destroy: destroy,
162+
get: get,
163+
fire: fire,
164+
observe: observe,
165+
on: on,
166+
set: set,
167+
teardown: destroy,
168+
_recompute: noop,
169+
_set: _set,
170+
_mount: _mount,
171+
_unmount: _unmount
172+
};
173+
174+
function create_main_fragment(state, component) {
175+
var input;
176+
177+
function input_change_handler() {
178+
component.set({ foo: input.checked });
179+
}
180+
181+
return {
182+
create: function() {
183+
input = createElement( 'input' );
184+
this.hydrate();
185+
},
186+
187+
hydrate: function(nodes) {
188+
input.type = "checkbox";
189+
addListener(input, "change", input_change_handler);
190+
},
191+
192+
mount: function(target, anchor) {
193+
insertNode( input, target, anchor );
194+
195+
input.checked = state.foo;
196+
},
197+
198+
update: function(changed, state) {
199+
input.checked = state.foo;
200+
},
201+
202+
unmount: function() {
203+
detachNode( input );
204+
},
205+
206+
destroy: function() {
207+
removeListener(input, "change", input_change_handler);
208+
}
209+
};
210+
}
211+
212+
function SvelteComponent(options) {
213+
this.options = options;
214+
this._state = options.data || {};
215+
216+
this._observers = {
217+
pre: Object.create(null),
218+
post: Object.create(null)
219+
};
220+
221+
this._handlers = Object.create(null);
222+
223+
this._root = options._root || this;
224+
this._yield = options._yield;
225+
this._bind = options._bind;
226+
227+
this._fragment = create_main_fragment(this._state, this);
228+
229+
if (options.target) {
230+
this._fragment.create();
231+
this._fragment.mount(options.target, options.anchor || null);
232+
}
233+
}
234+
235+
assign(SvelteComponent.prototype, proto );
236+
237+
export default SvelteComponent;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { addListener, assign, createElement, detachNode, insertNode, proto, removeListener } from "svelte/shared.js";
2+
3+
function create_main_fragment(state, component) {
4+
var input;
5+
6+
function input_change_handler() {
7+
component.set({ foo: input.checked });
8+
}
9+
10+
return {
11+
create: function() {
12+
input = createElement( 'input' );
13+
this.hydrate();
14+
},
15+
16+
hydrate: function(nodes) {
17+
input.type = "checkbox";
18+
addListener(input, "change", input_change_handler);
19+
},
20+
21+
mount: function(target, anchor) {
22+
insertNode( input, target, anchor );
23+
24+
input.checked = state.foo;
25+
},
26+
27+
update: function(changed, state) {
28+
input.checked = state.foo;
29+
},
30+
31+
unmount: function() {
32+
detachNode( input );
33+
},
34+
35+
destroy: function() {
36+
removeListener(input, "change", input_change_handler);
37+
}
38+
};
39+
}
40+
41+
function SvelteComponent(options) {
42+
this.options = options;
43+
this._state = options.data || {};
44+
45+
this._observers = {
46+
pre: Object.create(null),
47+
post: Object.create(null)
48+
};
49+
50+
this._handlers = Object.create(null);
51+
52+
this._root = options._root || this;
53+
this._yield = options._yield;
54+
this._bind = options._bind;
55+
56+
this._fragment = create_main_fragment(this._state, this);
57+
58+
if (options.target) {
59+
this._fragment.create();
60+
this._fragment.mount(options.target, options.anchor || null);
61+
}
62+
}
63+
64+
assign(SvelteComponent.prototype, proto );
65+
66+
export default SvelteComponent;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<input type='checkbox' bind:checked='foo'>

0 commit comments

Comments
 (0)