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

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

Issue 641283002: Separate the audio player app from Files.app Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Clean up Created 6 years, 2 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
(Empty)
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
3 // found in the LICENSE file.
4
5 'use strict';
6
7 Polymer('audio-player', {
8 /**
9 * Child Elements
10 */
11 audioController: null,
12 audioElement: null,
13 trackList: null,
14
15 // Attributes of the element (lower characters only).
16 // These values must be used only to data binding and shouldn't be assigned
17 // any value nowhere except in the handler.
18 publish: {
19 playing: {
20 value: true,
21 reflect: true
22 },
23 currenttrackurl: {
24 value: '',
25 reflect: true
26 },
27 playcount: {
28 value: 0,
29 reflect: true
30 }
31 },
32
33 /**
34 * Model object of the Audio Player.
35 * @type {AudioPlayerModel}
36 */
37 model: null,
38
39 /**
40 * Initializes an element. This method is called automatically when the
41 * element is ready.
42 */
43 ready: function() {
44 this.audioController = this.$.audioController;
45 this.audioElement = this.$.audio;
46 this.trackList = this.$.trackList;
47
48 this.addEventListener('keydown', this.onKeyDown_.bind(this));
49
50 this.audioElement.volume = 0; // Temporary initial volume.
51 this.audioElement.addEventListener('ended', this.onAudioEnded.bind(this));
52 this.audioElement.addEventListener('error', this.onAudioError.bind(this));
53
54 var onAudioStatusUpdatedBound = this.onAudioStatusUpdate_.bind(this);
55 this.audioElement.addEventListener('timeupdate', onAudioStatusUpdatedBound);
56 this.audioElement.addEventListener('ended', onAudioStatusUpdatedBound);
57 this.audioElement.addEventListener('play', onAudioStatusUpdatedBound);
58 this.audioElement.addEventListener('pause', onAudioStatusUpdatedBound);
59 this.audioElement.addEventListener('suspend', onAudioStatusUpdatedBound);
60 this.audioElement.addEventListener('abort', onAudioStatusUpdatedBound);
61 this.audioElement.addEventListener('error', onAudioStatusUpdatedBound);
62 this.audioElement.addEventListener('emptied', onAudioStatusUpdatedBound);
63 this.audioElement.addEventListener('stalled', onAudioStatusUpdatedBound);
64 },
65
66 /**
67 * Registers handlers for changing of external variables
68 */
69 observe: {
70 'trackList.currentTrackIndex': 'onCurrentTrackIndexChanged',
71 'audioController.playing': 'onControllerPlayingChanged',
72 'audioController.time': 'onControllerTimeChanged',
73 'model.volume': 'onVolumeChanged',
74 },
75
76 /**
77 * Invoked when trackList.currentTrackIndex is changed.
78 * @param {number} oldValue old value.
79 * @param {number} newValue new value.
80 */
81 onCurrentTrackIndexChanged: function(oldValue, newValue) {
82 var currentTrackUrl = '';
83
84 if (oldValue != newValue) {
85 var currentTrack = this.trackList.getCurrentTrack();
86 if (currentTrack && currentTrack.url != this.audioElement.src) {
87 this.audioElement.src = currentTrack.url;
88 currentTrackUrl = this.audioElement.src;
89 if (this.audioController.playing)
90 this.audioElement.play();
91 }
92 }
93
94 // The attributes may be being watched, so we change it at the last.
95 this.currenttrackurl = currentTrackUrl;
96 },
97
98 /**
99 * Invoked when audioController.playing is changed.
100 * @param {boolean} oldValue old value.
101 * @param {boolean} newValue new value.
102 */
103 onControllerPlayingChanged: function(oldValue, newValue) {
104 this.playing = newValue;
105
106 if (newValue) {
107 if (!this.audioElement.src) {
108 var currentTrack = this.trackList.getCurrentTrack();
109 if (currentTrack && currentTrack.url != this.audioElement.src) {
110 this.audioElement.src = currentTrack.url;
111 }
112 }
113
114 if (this.audioElement.src) {
115 this.currenttrackurl = this.audioElement.src;
116 this.audioElement.play();
117 return;
118 }
119 }
120
121 // When the new status is "stopped".
122 this.cancelAutoAdvance_();
123 this.audioElement.pause();
124 this.currenttrackurl = '';
125 this.lastAudioUpdateTime_ = null;
126 },
127
128 /**
129 * Invoked when audioController.volume is changed.
130 * @param {number} oldValue old value.
131 * @param {number} newValue new value.
132 */
133 onVolumeChanged: function(oldValue, newValue) {
134 this.audioElement.volume = newValue / 100;
135 },
136
137 /**
138 * Invoked when the model changed.
139 * @param {AudioPlayerModel} oldValue Old Value.
140 * @param {AudioPlayerModel} newValue New Value.
141 */
142 modelChanged: function(oldValue, newValue) {
143 this.trackList.model = newValue;
144 this.audioController.model = newValue;
145
146 // Invoke the handler manually.
147 this.onVolumeChanged(0, newValue.volume);
148 },
149
150 /**
151 * Invoked when audioController.time is changed.
152 * @param {number} oldValue old time (in ms).
153 * @param {number} newValue new time (in ms).
154 */
155 onControllerTimeChanged: function(oldValue, newValue) {
156 // Ignores updates from the audio element.
157 if (this.lastAudioUpdateTime_ === newValue)
158 return;
159
160 if (this.audioElement.readyState !== 0)
161 this.audioElement.currentTime = this.audioController.time / 1000;
162 },
163
164 /**
165 * Invoked when the next button in the controller is clicked.
166 * This handler is registered in the 'on-click' attribute of the element.
167 */
168 onControllerNextClicked: function() {
169 this.advance_(true /* forward */, true /* repeat */);
170 },
171
172 /**
173 * Invoked when the previous button in the controller is clicked.
174 * This handler is registered in the 'on-click' attribute of the element.
175 */
176 onControllerPreviousClicked: function() {
177 this.advance_(false /* forward */, true /* repeat */);
178 },
179
180 /**
181 * Invoked when the playback in the audio element is ended.
182 * This handler is registered in this.ready().
183 */
184 onAudioEnded: function() {
185 this.playcount++;
186 this.advance_(true /* forward */, this.model.repeat);
187 },
188
189 /**
190 * Invoked when the playback in the audio element gets error.
191 * This handler is registered in this.ready().
192 */
193 onAudioError: function() {
194 this.scheduleAutoAdvance_(true /* forward */, this.model.repeat);
195 },
196
197 /**
198 * Invoked when the time of playback in the audio element is updated.
199 * This handler is registered in this.ready().
200 * @private
201 */
202 onAudioStatusUpdate_: function() {
203 this.audioController.time =
204 (this.lastAudioUpdateTime_ = this.audioElement.currentTime * 1000);
205 this.audioController.duration = this.audioElement.duration * 1000;
206 this.audioController.playing = !this.audioElement.paused;
207 },
208
209 /**
210 * Invoked when receiving a request to replay the current music from the track
211 * list element.
212 */
213 onReplayCurrentTrack: function() {
214 // Changes the current time back to the beginning, regardless of the current
215 // status (playing or paused).
216 this.audioElement.currentTime = 0;
217 this.audioController.time = 0;
218 },
219
220 /**
221 * Goes to the previous or the next track.
222 * @param {boolean} forward True if next, false if previous.
223 * @param {boolean} repeat True if repeat-mode is enabled. False otherwise.
224 * @private
225 */
226 advance_: function(forward, repeat) {
227 this.cancelAutoAdvance_();
228
229 var nextTrackIndex = this.trackList.getNextTrackIndex(forward, true);
230 var isNextTrackAvailable =
231 (this.trackList.getNextTrackIndex(forward, repeat) !== -1);
232
233 this.audioController.playing = isNextTrackAvailable;
234
235 // If there is only a single file in the list, 'currentTrackInde' is not
236 // changed and the handler is not invoked. Instead, plays here.
237 // TODO(yoshiki): clean up the code around here.
238 if (isNextTrackAvailable &&
239 this.trackList.currentTrackIndex == nextTrackIndex) {
240 this.audioElement.play();
241 }
242
243 this.trackList.currentTrackIndex = nextTrackIndex;
244
245 Platform.performMicrotaskCheckpoint();
246 },
247
248 /**
249 * Timeout ID of auto advance. Used internally in scheduleAutoAdvance_() and
250 * cancelAutoAdvance_().
251 * @type {number}
252 * @private
253 */
254 autoAdvanceTimer_: null,
255
256 /**
257 * Schedules automatic advance to the next track after a timeout.
258 * @param {boolean} forward True if next, false if previous.
259 * @param {boolean} repeat True if repeat-mode is enabled. False otherwise.
260 * @private
261 */
262 scheduleAutoAdvance_: function(forward, repeat) {
263 this.cancelAutoAdvance_();
264 var currentTrackIndex = this.currentTrackIndex;
265
266 var timerId = setTimeout(
267 function() {
268 // If the other timer is scheduled, do nothing.
269 if (this.autoAdvanceTimer_ !== timerId)
270 return;
271
272 this.autoAdvanceTimer_ = null;
273
274 // If the track has been changed since the advance was scheduled, do
275 // nothing.
276 if (this.currentTrackIndex !== currentTrackIndex)
277 return;
278
279 // We are advancing only if the next track is not known to be invalid.
280 // This prevents an endless auto-advancing in the case when all tracks
281 // are invalid (we will only visit each track once).
282 this.advance_(forward, repeat, true /* only if valid */);
283 }.bind(this),
284 3000);
285
286 this.autoAdvanceTimer_ = timerId;
287 },
288
289 /**
290 * Cancels the scheduled auto advance.
291 * @private
292 */
293 cancelAutoAdvance_: function() {
294 if (this.autoAdvanceTimer_) {
295 clearTimeout(this.autoAdvanceTimer_);
296 this.autoAdvanceTimer_ = null;
297 }
298 },
299
300 /**
301 * The index of the current track.
302 * If the list has no tracks, the value must be -1.
303 *
304 * @type {number}
305 */
306 get currentTrackIndex() {
307 return this.trackList.currentTrackIndex;
308 },
309 set currentTrackIndex(value) {
310 this.trackList.currentTrackIndex = value;
311 },
312
313 /**
314 * The list of the tracks in the playlist.
315 *
316 * When it changed, current operation including playback is stopped and
317 * restarts playback with new tracks if necessary.
318 *
319 * @type {Array.<AudioPlayer.TrackInfo>}
320 */
321 get tracks() {
322 return this.trackList ? this.trackList.tracks : null;
323 },
324 set tracks(tracks) {
325 if (this.trackList.tracks === tracks)
326 return;
327
328 this.cancelAutoAdvance_();
329
330 this.trackList.tracks = tracks;
331 var currentTrack = this.trackList.getCurrentTrack();
332 if (currentTrack && currentTrack.url != this.audioElement.src) {
333 this.audioElement.src = currentTrack.url;
334 this.audioElement.play();
335 }
336 },
337
338 /**
339 * Invoked when the audio player is being unloaded.
340 */
341 onPageUnload: function() {
342 this.audioElement.src = ''; // Hack to prevent crashing.
343 },
344
345 /**
346 * Invoked when the 'keydown' event is fired.
347 * @param {Event} event The event object.
348 */
349 onKeyDown_: function(event) {
350 switch (event.keyIdentifier) {
351 case 'Up':
352 if (this.audioController.volumeSliderShown && this.model.volume < 100)
353 this.model.volume += 1;
354 break;
355 case 'Down':
356 if (this.audioController.volumeSliderShown && this.model.volume > 0)
357 this.model.volume -= 1;
358 break;
359 case 'PageUp':
360 if (this.audioController.volumeSliderShown && this.model.volume < 91)
361 this.model.volume += 10;
362 break;
363 case 'PageDown':
364 if (this.audioController.volumeSliderShown && this.model.volume > 9)
365 this.model.volume -= 10;
366 break;
367 case 'MediaNextTrack':
368 this.onControllerNextClicked();
369 break;
370 case 'MediaPlayPause':
371 var playing = this.audioController.playing;
372 this.onControllerPlayingChanged(playing, !playing);
373 break;
374 case 'MediaPreviousTrack':
375 this.onControllerPreviousClicked();
376 break;
377 case 'MediaStop':
378 // TODO: Define "Stop" behavior.
379 break;
380 }
381 },
382 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698