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

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

Powered by Google App Engine
This is Rietveld 408576698