cygprofile: increase timeouts to allow showing web contents
[chromium-blink-merge.git] / ui / file_manager / audio_player / elements / audio_player.js
blob3d5fc01410f187a30115251c438f044aed636760
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.
5 Polymer({
6 is: 'audio-player',
8 properties: {
9 /**
10 * Flag whether the audio is playing or paused. True if playing, or false
11 * paused.
13 playing: {
14 type: Boolean,
15 observer: 'playingChanged',
16 reflectToAttribute: true
19 /**
20 * Current elapsed time in the current music in millisecond.
22 time: {
23 type: Number,
24 observer: 'timeChanged'
27 /**
28 * Whether the shuffle button is ON.
30 shuffle: {
31 type: Boolean,
32 observer: 'shuffleChanged'
35 /**
36 * Whether the repeat button is ON.
38 repeat: {
39 type: Boolean,
40 observer: 'repeatChanged'
43 /**
44 * The audio volume. 0 is silent, and 100 is maximum loud.
46 volume: {
47 type: Number,
48 observer: 'volumeChanged'
51 /**
52 * Whether the expanded button is ON.
54 expanded: {
55 type: Boolean,
56 observer: 'expandedChanged'
59 /**
60 * Track index of the current track.
62 currentTrackIndex: {
63 type: Number,
64 observer: 'currentTrackIndexChanged'
67 /**
68 * Model object of the Audio Player.
69 * @type {AudioPlayerModel}
71 model: {
72 type: Object,
73 value: null,
74 observer: 'modelChanged'
77 /**
78 * URL of the current track. (exposed publicly for tests)
80 currenttrackurl: {
81 type: String,
82 value: '',
83 reflectToAttribute: true
86 /**
87 * The number of played tracks. (exposed publicly for tests)
89 playcount: {
90 type: Number,
91 value: 0,
92 reflectToAttribute: true
96 /**
97 * Handles change event for shuffle mode.
98 * @param {boolean} shuffle
100 shuffleChanged: function(shuffle) {
101 if (this.model)
102 this.model.shuffle = shuffle;
106 * Handles change event for repeat mode.
107 * @param {boolean} repeat
109 repeatChanged: function(repeat) {
110 if (this.model)
111 this.model.repeat = repeat;
115 * Handles change event for audio volume.
116 * @param {number} volume
118 volumeChanged: function(volume) {
119 if (this.model)
120 this.model.volume = volume;
124 * Handles change event for expanded state of track list.
126 expandedChanged: function(expanded) {
127 if (this.model)
128 this.model.expanded = expanded;
132 * Initializes an element. This method is called automatically when the
133 * element is ready.
135 ready: function() {
136 this.addEventListener('keydown', this.onKeyDown_.bind(this));
138 this.$.audio.volume = 0; // Temporary initial volume.
139 this.$.audio.addEventListener('ended', this.onAudioEnded.bind(this));
140 this.$.audio.addEventListener('error', this.onAudioError.bind(this));
142 var onAudioStatusUpdatedBound = this.onAudioStatusUpdate_.bind(this);
143 this.$.audio.addEventListener('timeupdate', onAudioStatusUpdatedBound);
144 this.$.audio.addEventListener('ended', onAudioStatusUpdatedBound);
145 this.$.audio.addEventListener('play', onAudioStatusUpdatedBound);
146 this.$.audio.addEventListener('pause', onAudioStatusUpdatedBound);
147 this.$.audio.addEventListener('suspend', onAudioStatusUpdatedBound);
148 this.$.audio.addEventListener('abort', onAudioStatusUpdatedBound);
149 this.$.audio.addEventListener('error', onAudioStatusUpdatedBound);
150 this.$.audio.addEventListener('emptied', onAudioStatusUpdatedBound);
151 this.$.audio.addEventListener('stalled', onAudioStatusUpdatedBound);
155 * Invoked when trackList.currentTrackIndex is changed.
156 * @param {number} newValue new value.
157 * @param {number} oldValue old value.
159 currentTrackIndexChanged: function(newValue, oldValue) {
160 var currentTrackUrl = '';
162 if (oldValue != newValue) {
163 var currentTrack = this.$.trackList.getCurrentTrack();
164 if (currentTrack && currentTrack.url != this.$.audio.src) {
165 this.$.audio.src = currentTrack.url;
166 currentTrackUrl = this.$.audio.src;
167 if (this.playing)
168 this.$.audio.play();
172 // The attributes may be being watched, so we change it at the last.
173 this.currenttrackurl = currentTrackUrl;
177 * Invoked when playing is changed.
178 * @param {boolean} newValue new value.
179 * @param {boolean} oldValue old value.
181 playingChanged: function(newValue, oldValue) {
182 if (newValue) {
183 if (!this.$.audio.src) {
184 var currentTrack = this.$.trackList.getCurrentTrack();
185 if (currentTrack && currentTrack.url != this.$.audio.src) {
186 this.$.audio.src = currentTrack.url;
190 if (this.$.audio.src) {
191 this.currenttrackurl = this.$.audio.src;
192 this.$.audio.play();
193 return;
197 // When the new status is "stopped".
198 this.cancelAutoAdvance_();
199 this.$.audio.pause();
200 this.currenttrackurl = '';
201 this.lastAudioUpdateTime_ = null;
205 * Invoked when the model changed.
206 * @param {AudioPlayerModel} newModel New model.
207 * @param {AudioPlayerModel} oldModel Old model.
209 modelChanged: function(newModel, oldModel) {
210 // Setting up the UI
211 if (newModel !== oldModel && newModel) {
212 this.shuffle = newModel.shuffle;
213 this.repeat = newModel.repeat;
214 this.volume = newModel.volume;
215 this.expanded = newModel.expanded;
220 * Invoked when time is changed.
221 * @param {number} newValue new time (in ms).
222 * @param {number} oldValue old time (in ms).
224 timeChanged: function(newValue, oldValue) {
225 // Ignores updates from the audio element.
226 if (this.lastAudioUpdateTime_ === newValue)
227 return;
229 if (this.$.audio.readyState !== 0)
230 this.$.audio.currentTime = this.time / 1000;
234 * Invoked when the next button in the controller is clicked.
235 * This handler is registered in the 'on-click' attribute of the element.
237 onControllerNextClicked: function() {
238 this.advance_(true /* forward */, true /* repeat */);
242 * Invoked when the previous button in the controller is clicked.
243 * This handler is registered in the 'on-click' attribute of the element.
245 onControllerPreviousClicked: function() {
246 this.advance_(false /* forward */, true /* repeat */);
250 * Invoked when the playback in the audio element is ended.
251 * This handler is registered in this.ready().
253 onAudioEnded: function() {
254 this.playcount++;
255 this.advance_(true /* forward */, this.repeat);
259 * Invoked when the playback in the audio element gets error.
260 * This handler is registered in this.ready().
262 onAudioError: function() {
263 this.scheduleAutoAdvance_(true /* forward */, this.repeat);
267 * Invoked when the time of playback in the audio element is updated.
268 * This handler is registered in this.ready().
269 * @private
271 onAudioStatusUpdate_: function() {
272 this.time = (this.lastAudioUpdateTime_ = this.$.audio.currentTime * 1000);
273 this.duration = this.$.audio.duration * 1000;
274 this.playing = !this.$.audio.paused;
278 * Invoked when receiving a request to replay the current music from the track
279 * list element.
281 onReplayCurrentTrack: function() {
282 // Changes the current time back to the beginning, regardless of the current
283 // status (playing or paused).
284 this.$.audio.currentTime = 0;
285 this.time = 0;
289 * Goes to the previous or the next track.
290 * @param {boolean} forward True if next, false if previous.
291 * @param {boolean} repeat True if repeat-mode is enabled. False otherwise.
292 * @private
294 advance_: function(forward, repeat) {
295 this.cancelAutoAdvance_();
297 var nextTrackIndex = this.$.trackList.getNextTrackIndex(forward, true);
298 var isNextTrackAvailable =
299 (this.$.trackList.getNextTrackIndex(forward, repeat) !== -1);
301 this.playing = isNextTrackAvailable;
303 // If there is only a single file in the list, 'currentTrackInde' is not
304 // changed and the handler is not invoked. Instead, plays here.
305 // TODO(yoshiki): clean up the code around here.
306 if (isNextTrackAvailable &&
307 this.$.trackList.currentTrackIndex == nextTrackIndex) {
308 this.$.audio.play();
311 this.$.trackList.currentTrackIndex = nextTrackIndex;
315 * Timeout ID of auto advance. Used internally in scheduleAutoAdvance_() and
316 * cancelAutoAdvance_().
317 * @type {number?}
318 * @private
320 autoAdvanceTimer_: null,
323 * Schedules automatic advance to the next track after a timeout.
324 * @param {boolean} forward True if next, false if previous.
325 * @param {boolean} repeat True if repeat-mode is enabled. False otherwise.
326 * @private
328 scheduleAutoAdvance_: function(forward, repeat) {
329 this.cancelAutoAdvance_();
330 var currentTrackIndex = this.currentTrackIndex;
332 var timerId = setTimeout(
333 function() {
334 // If the other timer is scheduled, do nothing.
335 if (this.autoAdvanceTimer_ !== timerId)
336 return;
338 this.autoAdvanceTimer_ = null;
340 // If the track has been changed since the advance was scheduled, do
341 // nothing.
342 if (this.currentTrackIndex !== currentTrackIndex)
343 return;
345 // We are advancing only if the next track is not known to be invalid.
346 // This prevents an endless auto-advancing in the case when all tracks
347 // are invalid (we will only visit each track once).
348 this.advance_(forward, repeat);
349 }.bind(this),
350 3000);
352 this.autoAdvanceTimer_ = timerId;
356 * Cancels the scheduled auto advance.
357 * @private
359 cancelAutoAdvance_: function() {
360 if (this.autoAdvanceTimer_) {
361 clearTimeout(this.autoAdvanceTimer_);
362 this.autoAdvanceTimer_ = null;
367 * The list of the tracks in the playlist.
369 * When it changed, current operation including playback is stopped and
370 * restarts playback with new tracks if necessary.
372 * @type {Array<TrackInfo>}
374 get tracks() {
375 return this.$.trackList ? this.$.trackList.tracks : null;
377 set tracks(tracks) {
378 if (this.$.trackList.tracks === tracks)
379 return;
381 this.cancelAutoAdvance_();
383 this.$.trackList.tracks = tracks;
384 var currentTrack = this.$.trackList.getCurrentTrack();
385 if (currentTrack && currentTrack.url != this.$.audio.src) {
386 this.$.audio.src = currentTrack.url;
387 this.$.audio.play();
392 * Invoked when the audio player is being unloaded.
394 onPageUnload: function() {
395 this.$.audio.src = ''; // Hack to prevent crashing.
399 * Invoked when the 'keydown' event is fired.
400 * @param {Event} event The event object.
402 onKeyDown_: function(event) {
403 switch (event.keyIdentifier) {
404 case 'Up':
405 if (this.$.audioController.volumeSliderShown && this.model.volume < 100)
406 this.model.volume += 1;
407 break;
408 case 'Down':
409 if (this.$.audioController.volumeSliderShown && this.model.volume > 0)
410 this.model.volume -= 1;
411 break;
412 case 'PageUp':
413 if (this.$.audioController.volumeSliderShown && this.model.volume < 91)
414 this.model.volume += 10;
415 break;
416 case 'PageDown':
417 if (this.$.audioController.volumeSliderShown && this.model.volume > 9)
418 this.model.volume -= 10;
419 break;
420 case 'MediaNextTrack':
421 this.onControllerNextClicked();
422 break;
423 case 'MediaPlayPause':
424 this.playing = !this.playing;
425 break;
426 case 'MediaPreviousTrack':
427 this.onControllerPreviousClicked();
428 break;
429 case 'MediaStop':
430 // TODO: Define "Stop" behavior.
431 break;
436 * Computes volume value for audio element. (should be in [0.0, 1.0])
437 * @param {number} volume Volume which is set in the UI. ([0, 100])
438 * @return {number}
440 computeAudioVolume_: function(volume) {
441 return volume / 100;