Skip to content

Commit d75ab85

Browse files
committedSep 9, 2017
remove requestAnimationFrame stuff, convert time ranges to {start, end} objects
1 parent 35637d8 commit d75ab85

File tree

5 files changed

+511
-47
lines changed

5 files changed

+511
-47
lines changed
 

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

+41-47
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ import { State } from '../../interfaces';
88
import getObject from '../../../../utils/getObject';
99
import getTailSnippet from '../../../../utils/getTailSnippet';
1010

11+
const readOnlyMediaAttributes = new Set([
12+
'duration',
13+
'buffered',
14+
'seekable',
15+
'played'
16+
]);
17+
1118
export default function visitBinding(
1219
generator: DomGenerator,
1320
block: Block,
@@ -25,9 +32,9 @@ export default function visitBinding(
2532
state.allUsedContexts.push(context);
2633
});
2734

28-
const eventName = getBindingEventName(node, attribute);
35+
const eventNames = getBindingEventName(node, attribute);
2936
const handler = block.getUniqueName(
30-
`${state.parentNode}_${eventName}_handler`
37+
`${state.parentNode}_${eventNames.join('_')}_handler`
3138
);
3239
const isMultipleSelect =
3340
node.name === 'select' &&
@@ -38,24 +45,28 @@ export default function visitBinding(
3845
const bindingGroup = attribute.name === 'group'
3946
? getBindingGroup(generator, attribute.value)
4047
: null;
48+
49+
const isMediaElement = node.name === 'audio' || node.name === 'video';
50+
const isReadOnly = isMediaElement && readOnlyMediaAttributes.has(attribute.name)
51+
4152
const value = getBindingValue(
4253
generator,
4354
block,
4455
state,
4556
node,
4657
attribute,
4758
isMultipleSelect,
59+
isMediaElement,
4860
bindingGroup,
4961
type
5062
);
5163

5264
let setter = getSetter(block, name, snippet, state.parentNode, attribute, dependencies, value);
5365
let updateElement = `${state.parentNode}.${attribute.name} = ${snippet};`;
5466

55-
const needsLock = node.name !== 'input' || !/radio|checkbox|range|color/.test(type); // TODO others?
67+
const needsLock = !isReadOnly && node.name !== 'input' || !/radio|checkbox|range|color/.test(type); // TODO others?
5668
const lock = `#${state.parentNode}_updating`;
5769
let updateConditions = needsLock ? [`!${lock}`] : [];
58-
let readOnly = false;
5970

6071
if (needsLock) block.addVariable(lock, 'false');
6172

@@ -115,7 +126,7 @@ export default function visitBinding(
115126
);
116127

117128
updateElement = `${state.parentNode}.checked = ${condition};`;
118-
} else if (node.name === 'audio' || node.name === 'video') {
129+
} else if (isMediaElement) {
119130
generator.hasComplexBindings = true;
120131
block.builders.hydrate.addBlock(`#component._root._beforecreate.push(${handler});`);
121132

@@ -129,37 +140,13 @@ export default function visitBinding(
129140
`;
130141

131142
updateConditions.push(`!isNaN(${snippet})`);
132-
} else if (attribute.name === 'duration') {
133-
readOnly = true;
134143
} else if (attribute.name === 'paused') {
135144
// this is necessary to prevent the audio restarting by itself
136145
const last = block.getUniqueName(`${state.parentNode}_paused_value`);
137146
block.addVariable(last, 'true');
138147

139148
updateConditions = [`${last} !== (${last} = ${snippet})`];
140149
updateElement = `${state.parentNode}[${last} ? "pause" : "play"]();`;
141-
} else if (attribute.name === 'buffered') {
142-
const frame = block.getUniqueName(`${state.parentNode}_animationframe`);
143-
block.addVariable(frame);
144-
setter = deindent`
145-
cancelAnimationFrame(${frame});
146-
${frame} = requestAnimationFrame(${handler});
147-
${setter}
148-
`;
149-
150-
updateConditions.push(`${snippet}.start`);
151-
readOnly = true;
152-
} else if (attribute.name === 'seekable' || attribute.name === 'played') {
153-
const frame = block.getUniqueName(`${state.parentNode}_animationframe`);
154-
block.addVariable(frame);
155-
setter = deindent`
156-
cancelAnimationFrame(${frame});
157-
if (!${state.parentNode}.paused) ${frame} = requestAnimationFrame(${handler});
158-
${setter}
159-
`;
160-
161-
updateConditions.push(`${snippet}.start`);
162-
readOnly = true;
163150
}
164151
}
165152

@@ -183,21 +170,23 @@ export default function visitBinding(
183170
@removeListener(${state.parentNode}, "change", ${handler});
184171
`);
185172
} else {
186-
block.builders.hydrate.addLine(
187-
`@addListener(${state.parentNode}, "${eventName}", ${handler});`
188-
);
189-
190-
block.builders.destroy.addLine(
191-
`@removeListener(${state.parentNode}, "${eventName}", ${handler});`
192-
);
173+
eventNames.forEach(eventName => {
174+
block.builders.hydrate.addLine(
175+
`@addListener(${state.parentNode}, "${eventName}", ${handler});`
176+
);
177+
178+
block.builders.destroy.addLine(
179+
`@removeListener(${state.parentNode}, "${eventName}", ${handler});`
180+
);
181+
});
193182
}
194183

195-
if (node.name !== 'audio' && node.name !== 'video') {
184+
if (!isMediaElement) {
196185
node.initialUpdate = updateElement;
197186
node.initialUpdateNeedsStateObject = !block.contexts.has(name);
198187
}
199188

200-
if (!readOnly) { // audio/video duration is read-only, it never updates
189+
if (!isReadOnly) { // audio/video duration is read-only, it never updates
201190
if (updateConditions.length) {
202191
block.builders.update.addBlock(deindent`
203192
if (${updateConditions.join(' && ')}) {
@@ -228,18 +217,18 @@ function getBindingEventName(node: Node, attribute: Node) {
228217
);
229218
const type = typeAttribute ? typeAttribute.value[0].data : 'text'; // TODO in validation, should throw if type attribute is not static
230219

231-
return type === 'checkbox' || type === 'radio' ? 'change' : 'input';
220+
return [type === 'checkbox' || type === 'radio' ? 'change' : 'input'];
232221
}
233222

234-
if (node.name === 'textarea') return 'input';
235-
if (attribute.name === 'currentTime') return 'timeupdate';
236-
if (attribute.name === 'duration') return 'durationchange';
237-
if (attribute.name === 'paused') return 'pause';
238-
if (attribute.name === 'buffered') return 'progress';
239-
if (attribute.name === 'seekable') return 'timeupdate';
240-
if (attribute.name === 'played') return 'timeupdate';
223+
if (node.name === 'textarea') return ['input'];
224+
if (attribute.name === 'currentTime') return ['timeupdate'];
225+
if (attribute.name === 'duration') return ['durationchange'];
226+
if (attribute.name === 'paused') return ['pause'];
227+
if (attribute.name === 'buffered') return ['progress', 'loadedmetadata'];
228+
if (attribute.name === 'seekable') return ['loadedmetadata'];
229+
if (attribute.name === 'played') return ['timeupdate'];
241230

242-
return 'change';
231+
return ['change'];
243232
}
244233

245234
function getBindingValue(
@@ -249,6 +238,7 @@ function getBindingValue(
249238
node: Node,
250239
attribute: Node,
251240
isMultipleSelect: boolean,
241+
isMediaElement: boolean,
252242
bindingGroup: number,
253243
type: string
254244
) {
@@ -276,6 +266,10 @@ function getBindingValue(
276266
return `@toNumber(${state.parentNode}.${attribute.name})`;
277267
}
278268

269+
if (isMediaElement && attribute.name === 'buffered' || attribute.name === 'seekable' || attribute.name === 'played') {
270+
return `@timeRangesToArray(${state.parentNode}.${attribute.name})`
271+
}
272+
279273
// everything else
280274
return `${state.parentNode}.${attribute.name}`;
281275
}

‎src/shared/dom.js

+8
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ export function toNumber(value) {
102102
return value === '' ? undefined : +value;
103103
}
104104

105+
export function timeRangesToArray(ranges) {
106+
var array = [];
107+
for (let i = 0; i < ranges.length; i += 1) {
108+
array.push({ start: ranges.start(i), end: ranges.end(i) });
109+
}
110+
return array;
111+
}
112+
105113
export function children (element) {
106114
return Array.from(element.childNodes);
107115
}

0 commit comments

Comments
 (0)