Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(81)

Side by Side Diff: ui/file_manager/audio_player/elements/audio_player.js

Issue 1176483002: AudioPlayer.app: Migrate to Polymer 1.0. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove unnecessary line break. Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @constructor 6 * @constructor
7 * @extends {PolymerElement} 7 * @extends {PolymerElement}
8 */ 8 */
9 var AudioPlayerElement = function() {}; 9 var AudioPlayerElement = function() {};
10 10
11 AudioPlayerElement.prototype = { 11 AudioPlayerElement.prototype = {
12 // Child Elements 12 is: 'audio-player',
13 audioController: null,
14 audioElement: null,
15 trackList: null,
16 13
17 // Published values 14 properties: {
18 playing: true, 15 /**
19 currenttrackurl: '', 16 * Flag whether the audio is playing or paused. True if playing, or false
20 playcount: 0, 17 * paused.
18 */
19 playing: {
20 type: Boolean,
21 observer: 'playingChanged',
22 reflectToAttribute: true
23 },
21 24
22 // Attributes of the element (lower characters only). 25 /**
23 // These values must be used only to data binding and shouldn't be assigned 26 * Current elapsed time in the current music in millisecond.
24 // any value nowhere except in the handler. 27 */
25 publish: { 28 time: {
26 playing: { 29 type: Number,
27 value: true, 30 observer: 'timeChanged'
28 reflect: true
29 }, 31 },
32
33 /**
34 * Whether the shuffle button is ON.
35 */
36 shuffle: {
37 type: Boolean,
38 observer: 'shuffleChanged'
39 },
40
41 /**
42 * Whether the repeat button is ON.
43 */
44 repeat: {
45 type: Boolean,
46 observer: 'repeatChanged'
47 },
48
49 /**
50 * The audio volume. 0 is silent, and 100 is maximum loud.
51 */
52 volume: {
53 type: Number,
54 observer: 'volumeChanged'
55 },
56
57 /**
58 * Whether the expanded button is ON.
59 */
60 expanded: {
61 type: Boolean,
62 observer: 'expandedChanged'
63 },
64
65 /**
66 * Track index of the current track.
67 */
68 currentTrackIndex: {
69 type: Number,
70 observer: 'currentTrackIndexChanged'
71 },
72
73 /**
74 * Model object of the Audio Player.
75 * @type {AudioPlayerModel}
76 */
77 model: {
78 type: Object,
79 value: null,
80 observer: 'modelChanged'
81 },
82
83 /**
84 * URL of the current track. (exposed publicly for tests)
85 */
30 currenttrackurl: { 86 currenttrackurl: {
87 type: String,
31 value: '', 88 value: '',
32 reflect: true 89 reflectToAttribute: true
33 }, 90 },
91
92 /**
93 * The number of played tracks. (exposed publicly for tests)
94 */
34 playcount: { 95 playcount: {
96 type: Number,
35 value: 0, 97 value: 0,
36 reflect: true 98 reflectToAttribute: true
37 } 99 }
38 }, 100 },
39 101
40 /** 102 /**
41 * Model object of the Audio Player. 103 * Handles change event for shuffle mode.
42 * @type {AudioPlayerModel} 104 * @param {boolean} shuffle
43 */ 105 */
44 model: null, 106 shuffleChanged: function(shuffle) {
107 if (this.model)
108 this.model.shuffle = shuffle;
109 },
110
111 /**
112 * Handles change event for repeat mode.
113 * @param {boolean} repeat
114 */
115 repeatChanged: function(repeat) {
116 if (this.model)
117 this.model.repeat = repeat;
118 },
119
120 /**
121 * Handles change event for audio volume.
122 * @param {boolean} volume
123 */
124 volumeChanged: function(volume) {
125 if (this.model)
126 this.model.volume = volume;
127 },
128
129 /**
130 * Handles change event for expanded state of track list.
131 */
132 expandedChanged: function(expanded) {
133 if (this.model)
134 this.model.expanded = expanded;
135 },
45 136
46 /** 137 /**
47 * Initializes an element. This method is called automatically when the 138 * Initializes an element. This method is called automatically when the
48 * element is ready. 139 * element is ready.
49 */ 140 */
50 ready: function() { 141 ready: function() {
51 this.audioController = this.$.audioController;
52 this.audioElement = this.$.audio;
53 this.trackList = this.$.trackList;
54
55 this.addEventListener('keydown', this.onKeyDown_.bind(this)); 142 this.addEventListener('keydown', this.onKeyDown_.bind(this));
56 143
57 this.audioElement.volume = 0; // Temporary initial volume. 144 this.$.audio.volume = 0; // Temporary initial volume.
58 this.audioElement.addEventListener('ended', this.onAudioEnded.bind(this)); 145 this.$.audio.addEventListener('ended', this.onAudioEnded.bind(this));
59 this.audioElement.addEventListener('error', this.onAudioError.bind(this)); 146 this.$.audio.addEventListener('error', this.onAudioError.bind(this));
60 147
61 var onAudioStatusUpdatedBound = this.onAudioStatusUpdate_.bind(this); 148 var onAudioStatusUpdatedBound = this.onAudioStatusUpdate_.bind(this);
62 this.audioElement.addEventListener('timeupdate', onAudioStatusUpdatedBound); 149 this.$.audio.addEventListener('timeupdate', onAudioStatusUpdatedBound);
63 this.audioElement.addEventListener('ended', onAudioStatusUpdatedBound); 150 this.$.audio.addEventListener('ended', onAudioStatusUpdatedBound);
64 this.audioElement.addEventListener('play', onAudioStatusUpdatedBound); 151 this.$.audio.addEventListener('play', onAudioStatusUpdatedBound);
65 this.audioElement.addEventListener('pause', onAudioStatusUpdatedBound); 152 this.$.audio.addEventListener('pause', onAudioStatusUpdatedBound);
66 this.audioElement.addEventListener('suspend', onAudioStatusUpdatedBound); 153 this.$.audio.addEventListener('suspend', onAudioStatusUpdatedBound);
67 this.audioElement.addEventListener('abort', onAudioStatusUpdatedBound); 154 this.$.audio.addEventListener('abort', onAudioStatusUpdatedBound);
68 this.audioElement.addEventListener('error', onAudioStatusUpdatedBound); 155 this.$.audio.addEventListener('error', onAudioStatusUpdatedBound);
69 this.audioElement.addEventListener('emptied', onAudioStatusUpdatedBound); 156 this.$.audio.addEventListener('emptied', onAudioStatusUpdatedBound);
70 this.audioElement.addEventListener('stalled', onAudioStatusUpdatedBound); 157 this.$.audio.addEventListener('stalled', onAudioStatusUpdatedBound);
71 },
72
73 /**
74 * Registers handlers for changing of external variables
75 */
76 observe: {
77 'trackList.currentTrackIndex': 'onCurrentTrackIndexChanged',
78 'audioController.playing': 'onControllerPlayingChanged',
79 'audioController.time': 'onControllerTimeChanged',
80 'model.volume': 'onVolumeChanged',
81 }, 158 },
82 159
83 /** 160 /**
84 * Invoked when trackList.currentTrackIndex is changed. 161 * Invoked when trackList.currentTrackIndex is changed.
162 * @param {number} newValue new value.
85 * @param {number} oldValue old value. 163 * @param {number} oldValue old value.
86 * @param {number} newValue new value.
87 */ 164 */
88 onCurrentTrackIndexChanged: function(oldValue, newValue) { 165 currentTrackIndexChanged: function(newValue, oldValue) {
89 var currentTrackUrl = ''; 166 var currentTrackUrl = '';
90 167
91 if (oldValue != newValue) { 168 if (oldValue != newValue) {
92 var currentTrack = this.trackList.getCurrentTrack(); 169 var currentTrack = this.$.trackList.getCurrentTrack();
93 if (currentTrack && currentTrack.url != this.audioElement.src) { 170 if (currentTrack && currentTrack.url != this.$.audio.src) {
94 this.audioElement.src = currentTrack.url; 171 this.$.audio.src = currentTrack.url;
95 currentTrackUrl = this.audioElement.src; 172 currentTrackUrl = this.$.audio.src;
96 if (this.audioController.playing) 173 if (this.playing)
97 this.audioElement.play(); 174 this.$.audio.play();
98 } 175 }
99 } 176 }
100 177
101 // The attributes may be being watched, so we change it at the last. 178 // The attributes may be being watched, so we change it at the last.
102 this.currenttrackurl = currentTrackUrl; 179 this.currenttrackurl = currentTrackUrl;
103 }, 180 },
104 181
105 /** 182 /**
106 * Invoked when audioController.playing is changed. 183 * Invoked when playing is changed.
184 * @param {boolean} newValue new value.
107 * @param {boolean} oldValue old value. 185 * @param {boolean} oldValue old value.
108 * @param {boolean} newValue new value.
109 */ 186 */
110 onControllerPlayingChanged: function(oldValue, newValue) { 187 playingChanged: function(newValue, oldValue) {
111 this.playing = newValue;
112
113 if (newValue) { 188 if (newValue) {
114 if (!this.audioElement.src) { 189 if (!this.$.audio.src) {
115 var currentTrack = this.trackList.getCurrentTrack(); 190 var currentTrack = this.$.trackList.getCurrentTrack();
116 if (currentTrack && currentTrack.url != this.audioElement.src) { 191 if (currentTrack && currentTrack.url != this.$.audio.src) {
117 this.audioElement.src = currentTrack.url; 192 this.$.audio.src = currentTrack.url;
118 } 193 }
119 } 194 }
120 195
121 if (this.audioElement.src) { 196 if (this.$.audio.src) {
122 this.currenttrackurl = this.audioElement.src; 197 this.currenttrackurl = this.$.audio.src;
123 this.audioElement.play(); 198 this.$.audio.play();
124 return; 199 return;
125 } 200 }
126 } 201 }
127 202
128 // When the new status is "stopped". 203 // When the new status is "stopped".
129 this.cancelAutoAdvance_(); 204 this.cancelAutoAdvance_();
130 this.audioElement.pause(); 205 this.$.audio.pause();
131 this.currenttrackurl = ''; 206 this.currenttrackurl = '';
132 this.lastAudioUpdateTime_ = null; 207 this.lastAudioUpdateTime_ = null;
133 }, 208 },
134 209
135 /** 210 /**
136 * Invoked when audioController.volume is changed. 211 * Invoked when the model changed.
137 * @param {number} oldValue old value. 212 * @param {AudioPlayerModel} newModel New model.
138 * @param {number} newValue new value. 213 * @param {AudioPlayerModel} oldModel Old model.
139 */ 214 */
140 onVolumeChanged: function(oldValue, newValue) { 215 modelChanged: function(newModel, oldModel) {
141 this.audioElement.volume = newValue / 100; 216 // Setting up the UI
217 if (newModel !== oldModel && newModel) {
218 this.shuffle = newModel.shuffle;
219 this.repeat = newModel.repeat;
220 this.volume = newModel.volume;
221 this.expanded = newModel.expanded;
222 }
142 }, 223 },
143 224
144 /** 225 /**
145 * Invoked when the model changed. 226 * Invoked when time is changed.
146 * @param {AudioPlayerModel} oldValue Old Value. 227 * @param {number} newValue new time (in ms).
147 * @param {AudioPlayerModel} newValue New Value. 228 * @param {number} oldValue old time (in ms).
148 */ 229 */
149 modelChanged: function(oldValue, newValue) { 230 timeChanged: function(newValue, oldValue) {
150 this.trackList.model = newValue;
151 this.audioController.model = newValue;
152
153 // Invoke the handler manually.
154 this.onVolumeChanged(0, newValue.volume);
155 },
156
157 /**
158 * Invoked when audioController.time is changed.
159 * @param {number} oldValue old time (in ms).
160 * @param {number} newValue new time (in ms).
161 */
162 onControllerTimeChanged: function(oldValue, newValue) {
163 // Ignores updates from the audio element. 231 // Ignores updates from the audio element.
164 if (this.lastAudioUpdateTime_ === newValue) 232 if (this.lastAudioUpdateTime_ === newValue)
165 return; 233 return;
166 234
167 if (this.audioElement.readyState !== 0) 235 if (this.$.audio.readyState !== 0)
168 this.audioElement.currentTime = this.audioController.time / 1000; 236 this.$.audio.currentTime = this.time / 1000;
169 }, 237 },
170 238
171 /** 239 /**
172 * Invoked when the next button in the controller is clicked. 240 * Invoked when the next button in the controller is clicked.
173 * This handler is registered in the 'on-click' attribute of the element. 241 * This handler is registered in the 'on-click' attribute of the element.
174 */ 242 */
175 onControllerNextClicked: function() { 243 onControllerNextClicked: function() {
176 this.advance_(true /* forward */, true /* repeat */); 244 this.advance_(true /* forward */, true /* repeat */);
177 }, 245 },
178 246
179 /** 247 /**
180 * Invoked when the previous button in the controller is clicked. 248 * Invoked when the previous button in the controller is clicked.
181 * This handler is registered in the 'on-click' attribute of the element. 249 * This handler is registered in the 'on-click' attribute of the element.
182 */ 250 */
183 onControllerPreviousClicked: function() { 251 onControllerPreviousClicked: function() {
184 this.advance_(false /* forward */, true /* repeat */); 252 this.advance_(false /* forward */, true /* repeat */);
185 }, 253 },
186 254
187 /** 255 /**
188 * Invoked when the playback in the audio element is ended. 256 * Invoked when the playback in the audio element is ended.
189 * This handler is registered in this.ready(). 257 * This handler is registered in this.ready().
190 */ 258 */
191 onAudioEnded: function() { 259 onAudioEnded: function() {
192 this.playcount++; 260 this.playcount++;
193 this.advance_(true /* forward */, this.model.repeat); 261 this.advance_(true /* forward */, this.repeat);
194 }, 262 },
195 263
196 /** 264 /**
197 * Invoked when the playback in the audio element gets error. 265 * Invoked when the playback in the audio element gets error.
198 * This handler is registered in this.ready(). 266 * This handler is registered in this.ready().
199 */ 267 */
200 onAudioError: function() { 268 onAudioError: function() {
201 this.scheduleAutoAdvance_(true /* forward */, this.model.repeat); 269 this.scheduleAutoAdvance_(true /* forward */, this.repeat);
202 }, 270 },
203 271
204 /** 272 /**
205 * Invoked when the time of playback in the audio element is updated. 273 * Invoked when the time of playback in the audio element is updated.
206 * This handler is registered in this.ready(). 274 * This handler is registered in this.ready().
207 * @private 275 * @private
208 */ 276 */
209 onAudioStatusUpdate_: function() { 277 onAudioStatusUpdate_: function() {
210 this.audioController.time = 278 this.time = (this.lastAudioUpdateTime_ = this.$.audio.currentTime * 1000);
211 (this.lastAudioUpdateTime_ = this.audioElement.currentTime * 1000); 279 this.duration = this.$.audio.duration * 1000;
212 this.audioController.duration = this.audioElement.duration * 1000; 280 this.playing = !this.$.audio.paused;
213 this.audioController.playing = !this.audioElement.paused;
214 }, 281 },
215 282
216 /** 283 /**
217 * Invoked when receiving a request to replay the current music from the track 284 * Invoked when receiving a request to replay the current music from the track
218 * list element. 285 * list element.
219 */ 286 */
220 onReplayCurrentTrack: function() { 287 onReplayCurrentTrack: function() {
221 // Changes the current time back to the beginning, regardless of the current 288 // Changes the current time back to the beginning, regardless of the current
222 // status (playing or paused). 289 // status (playing or paused).
223 this.audioElement.currentTime = 0; 290 this.$.audio.currentTime = 0;
224 this.audioController.time = 0; 291 this.time = 0;
225 }, 292 },
226 293
227 /** 294 /**
228 * Goes to the previous or the next track. 295 * Goes to the previous or the next track.
229 * @param {boolean} forward True if next, false if previous. 296 * @param {boolean} forward True if next, false if previous.
230 * @param {boolean} repeat True if repeat-mode is enabled. False otherwise. 297 * @param {boolean} repeat True if repeat-mode is enabled. False otherwise.
231 * @private 298 * @private
232 */ 299 */
233 advance_: function(forward, repeat) { 300 advance_: function(forward, repeat) {
234 this.cancelAutoAdvance_(); 301 this.cancelAutoAdvance_();
235 302
236 var nextTrackIndex = this.trackList.getNextTrackIndex(forward, true); 303 var nextTrackIndex = this.$.trackList.getNextTrackIndex(forward, true);
237 var isNextTrackAvailable = 304 var isNextTrackAvailable =
238 (this.trackList.getNextTrackIndex(forward, repeat) !== -1); 305 (this.$.trackList.getNextTrackIndex(forward, repeat) !== -1);
239 306
240 this.audioController.playing = isNextTrackAvailable; 307 this.playing = isNextTrackAvailable;
241 308
242 // If there is only a single file in the list, 'currentTrackInde' is not 309 // If there is only a single file in the list, 'currentTrackInde' is not
243 // changed and the handler is not invoked. Instead, plays here. 310 // changed and the handler is not invoked. Instead, plays here.
244 // TODO(yoshiki): clean up the code around here. 311 // TODO(yoshiki): clean up the code around here.
245 if (isNextTrackAvailable && 312 if (isNextTrackAvailable &&
246 this.trackList.currentTrackIndex == nextTrackIndex) { 313 this.$.trackList.currentTrackIndex == nextTrackIndex) {
247 this.audioElement.play(); 314 this.$.audio.play();
248 } 315 }
249 316
250 this.trackList.currentTrackIndex = nextTrackIndex; 317 this.$.trackList.currentTrackIndex = nextTrackIndex;
251 }, 318 },
252 319
253 /** 320 /**
254 * Timeout ID of auto advance. Used internally in scheduleAutoAdvance_() and 321 * Timeout ID of auto advance. Used internally in scheduleAutoAdvance_() and
255 * cancelAutoAdvance_(). 322 * cancelAutoAdvance_().
256 * @type {number?} 323 * @type {number?}
257 * @private 324 * @private
258 */ 325 */
259 autoAdvanceTimer_: null, 326 autoAdvanceTimer_: null,
260 327
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
296 * @private 363 * @private
297 */ 364 */
298 cancelAutoAdvance_: function() { 365 cancelAutoAdvance_: function() {
299 if (this.autoAdvanceTimer_) { 366 if (this.autoAdvanceTimer_) {
300 clearTimeout(this.autoAdvanceTimer_); 367 clearTimeout(this.autoAdvanceTimer_);
301 this.autoAdvanceTimer_ = null; 368 this.autoAdvanceTimer_ = null;
302 } 369 }
303 }, 370 },
304 371
305 /** 372 /**
306 * The index of the current track.
307 * If the list has no tracks, the value must be -1.
308 *
309 * @type {number}
310 */
311 get currentTrackIndex() {
312 return this.trackList.currentTrackIndex;
313 },
314 set currentTrackIndex(value) {
315 this.trackList.currentTrackIndex = value;
316 },
317
318 /**
319 * The list of the tracks in the playlist. 373 * The list of the tracks in the playlist.
320 * 374 *
321 * When it changed, current operation including playback is stopped and 375 * When it changed, current operation including playback is stopped and
322 * restarts playback with new tracks if necessary. 376 * restarts playback with new tracks if necessary.
323 * 377 *
324 * @type {Array<AudioPlayer.TrackInfo>} 378 * @type {Array<AudioPlayer.TrackInfo>}
325 */ 379 */
326 get tracks() { 380 get tracks() {
327 return this.trackList ? this.trackList.tracks : null; 381 return this.$.trackList ? this.$.trackList.tracks : null;
328 }, 382 },
329 set tracks(tracks) { 383 set tracks(tracks) {
330 if (this.trackList.tracks === tracks) 384 if (this.$.trackList.tracks === tracks)
331 return; 385 return;
332 386
333 this.cancelAutoAdvance_(); 387 this.cancelAutoAdvance_();
334 388
335 this.trackList.tracks = tracks; 389 this.$.trackList.tracks = tracks;
336 var currentTrack = this.trackList.getCurrentTrack(); 390 var currentTrack = this.$.trackList.getCurrentTrack();
337 if (currentTrack && currentTrack.url != this.audioElement.src) { 391 if (currentTrack && currentTrack.url != this.$.audio.src) {
338 this.audioElement.src = currentTrack.url; 392 this.$.audio.src = currentTrack.url;
339 this.audioElement.play(); 393 this.$.audio.play();
340 } 394 }
341 }, 395 },
342 396
343 /** 397 /**
344 * Invoked when the audio player is being unloaded. 398 * Invoked when the audio player is being unloaded.
345 */ 399 */
346 onPageUnload: function() { 400 onPageUnload: function() {
347 this.audioElement.src = ''; // Hack to prevent crashing. 401 this.$.audio.src = ''; // Hack to prevent crashing.
348 }, 402 },
349 403
350 /** 404 /**
351 * Invoked when the 'keydown' event is fired. 405 * Invoked when the 'keydown' event is fired.
352 * @param {Event} event The event object. 406 * @param {Event} event The event object.
353 */ 407 */
354 onKeyDown_: function(event) { 408 onKeyDown_: function(event) {
355 switch (event.keyIdentifier) { 409 switch (event.keyIdentifier) {
356 case 'Up': 410 case 'Up':
357 if (this.audioController.volumeSliderShown && this.model.volume < 100) 411 if (this.$.audioController.volumeSliderShown && this.model.volume < 100)
358 this.model.volume += 1; 412 this.model.volume += 1;
359 break; 413 break;
360 case 'Down': 414 case 'Down':
361 if (this.audioController.volumeSliderShown && this.model.volume > 0) 415 if (this.$.audioController.volumeSliderShown && this.model.volume > 0)
362 this.model.volume -= 1; 416 this.model.volume -= 1;
363 break; 417 break;
364 case 'PageUp': 418 case 'PageUp':
365 if (this.audioController.volumeSliderShown && this.model.volume < 91) 419 if (this.$.audioController.volumeSliderShown && this.model.volume < 91)
366 this.model.volume += 10; 420 this.model.volume += 10;
367 break; 421 break;
368 case 'PageDown': 422 case 'PageDown':
369 if (this.audioController.volumeSliderShown && this.model.volume > 9) 423 if (this.$.audioController.volumeSliderShown && this.model.volume > 9)
370 this.model.volume -= 10; 424 this.model.volume -= 10;
371 break; 425 break;
372 case 'MediaNextTrack': 426 case 'MediaNextTrack':
373 this.onControllerNextClicked(); 427 this.onControllerNextClicked();
374 break; 428 break;
375 case 'MediaPlayPause': 429 case 'MediaPlayPause':
376 var playing = this.audioController.playing; 430 this.playing = !this.playing;
377 this.onControllerPlayingChanged(playing, !playing);
378 break; 431 break;
379 case 'MediaPreviousTrack': 432 case 'MediaPreviousTrack':
380 this.onControllerPreviousClicked(); 433 this.onControllerPreviousClicked();
381 break; 434 break;
382 case 'MediaStop': 435 case 'MediaStop':
383 // TODO: Define "Stop" behavior. 436 // TODO: Define "Stop" behavior.
384 break; 437 break;
385 } 438 }
386 }, 439 },
440
441 /**
442 * Computes volume value for audio element. (should be in [0.0, 1.0])
443 * @param {number} volume Volume which is set in the UI. ([0, 100])
444 * @return {number}
445 */
446 computeAudioVolume_: function(volume) {
447 return volume / 100;
448 }
387 }; 449 };
388 450
389 Polymer('audio-player', AudioPlayerElement.prototype); 451 Polymer(AudioPlayerElement.prototype);
OLDNEW
« no previous file with comments | « ui/file_manager/audio_player/elements/audio_player.html ('k') | ui/file_manager/audio_player/elements/control_panel.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698