Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / file_manager / audio_player / elements / audio_player.js
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 (little charactor only).
16   // These value must be used only to data binding and shouldn't be assignred
17   // anu value nowhere except in the handler.
18   playing: false,
19   currenttrackurl: '',
20
21   /**
22    * Model object of the Audio Player.
23    * @type {AudioPlayerModel}
24    */
25   model: null,
26
27   /**
28    * Initializes an element. This method is called automatically when the
29    * element is ready.
30    */
31   ready: function() {
32     this.audioController = this.$.audioController;
33     this.audioElement = this.$.audio;
34     this.trackList = this.$.trackList;
35
36     this.audioElement.volume = 0;  // Temporaly initial volume.
37     this.audioElement.addEventListener('ended', this.onAudioEnded.bind(this));
38     this.audioElement.addEventListener('error', this.onAudioError.bind(this));
39
40     var onAudioStatusUpdatedBound = this.onAudioStatusUpdate_.bind(this);
41     this.audioElement.addEventListener('timeupdate', onAudioStatusUpdatedBound);
42     this.audioElement.addEventListener('ended', onAudioStatusUpdatedBound);
43     this.audioElement.addEventListener('play', onAudioStatusUpdatedBound);
44     this.audioElement.addEventListener('pause', onAudioStatusUpdatedBound);
45     this.audioElement.addEventListener('suspend', onAudioStatusUpdatedBound);
46     this.audioElement.addEventListener('abort', onAudioStatusUpdatedBound);
47     this.audioElement.addEventListener('error', onAudioStatusUpdatedBound);
48     this.audioElement.addEventListener('emptied', onAudioStatusUpdatedBound);
49     this.audioElement.addEventListener('stalled', onAudioStatusUpdatedBound);
50   },
51
52   /**
53    * Registers handlers for changing of external variables
54    */
55   observe: {
56     'trackList.currentTrackIndex': 'onCurrentTrackIndexChanged',
57     'audioController.playing': 'onControllerPlayingChanged',
58     'audioController.time': 'onControllerTimeChanged',
59     'model.volume': 'onVolumeChanged',
60   },
61
62   /**
63    * Invoked when trackList.currentTrackIndex is changed.
64    * @param {number} oldValue old value.
65    * @param {number} newValue new value.
66    */
67   onCurrentTrackIndexChanged: function(oldValue, newValue) {
68     var currentTrackUrl = '';
69
70     if (oldValue != newValue) {
71       var currentTrack = this.trackList.getCurrentTrack();
72       if (currentTrack && currentTrack.url != this.audioElement.src) {
73         this.audioElement.src = currentTrack.url;
74         currentTrackUrl = this.audioElement.src;
75         if (this.audioController.playing)
76           this.audioElement.play();
77       }
78     }
79
80     // The attributes may be being watched, so we change it at the last.
81     this.currenttrackurl = currentTrackUrl;
82   },
83
84   /**
85    * Invoked when audioController.playing is changed.
86    * @param {boolean} oldValue old value.
87    * @param {boolean} newValue new value.
88    */
89   onControllerPlayingChanged: function(oldValue, newValue) {
90     this.playing = newValue;
91
92     if (newValue) {
93       if (!this.audioElement.src) {
94         var currentTrack = this.trackList.getCurrentTrack();
95         if (currentTrack && currentTrack.url != this.audioElement.src) {
96           this.audioElement.src = currentTrack.url;
97         }
98       }
99
100       if (this.audioElement.src) {
101         this.currenttrackurl = this.audioElement.src;
102         this.audioElement.play();
103         return;
104       }
105     }
106
107     // When the new status is "stopped".
108     this.cancelAutoAdvance_();
109     this.audioElement.pause();
110     this.currenttrackurl = '';
111     this.lastAudioUpdateTime_ = null;
112   },
113
114   /**
115    * Invoked when audioController.volume is changed.
116    * @param {number} oldValue old value.
117    * @param {number} newValue new value.
118    */
119   onVolumeChanged: function(oldValue, newValue) {
120     this.audioElement.volume = newValue / 100;
121   },
122
123   /**
124    * Invoked when the model changed.
125    * @param {AudioPlayerModel} oldValue Old Value.
126    * @param {AudioPlayerModel} newValue Nld Value.
127    */
128   modelChanged: function(oldValue, newValue) {
129     this.trackList.model = newValue;
130     this.audioController.model = newValue;
131
132     // Invoke the handler manually.
133     this.onVolumeChanged(0, newValue.volume);
134   },
135
136   /**
137    * Invoked when audioController.time is changed.
138    * @param {number} oldValue old time (in ms).
139    * @param {number} newValue new time (in ms).
140    */
141   onControllerTimeChanged: function(oldValue, newValue) {
142     // Ignores updates from the audio element.
143     if (this.lastAudioUpdateTime_ === newValue)
144       return;
145
146     if (this.audioElement.readyState !== 0)
147       this.audioElement.currentTime = this.audioController.time / 1000;
148   },
149
150   /**
151    * Invoked when the next button in the controller is clicked.
152    * This handler is registered in the 'on-click' attribute of the element.
153    */
154   onControllerNextClicked: function() {
155     this.advance_(true /* forward */, true /* repeat */);
156   },
157
158   /**
159    * Invoked when the previous button in the controller is clicked.
160    * This handler is registered in the 'on-click' attribute of the element.
161    */
162   onControllerPreviousClicked: function() {
163     this.advance_(false /* forward */, true /* repeat */);
164   },
165
166   /**
167    * Invoked when the playback in the audio element is ended.
168    * This handler is registered in this.ready().
169    */
170   onAudioEnded: function() {
171     this.advance_(true /* forward */, this.model.repeat);
172   },
173
174   /**
175    * Invoked when the playback in the audio element gets error.
176    * This handler is registered in this.ready().
177    */
178   onAudioError: function() {
179     this.scheduleAutoAdvance_(true /* forward */, this.model.repeat);
180   },
181
182   /**
183    * Invoked when the time of playback in the audio element is updated.
184    * This handler is registered in this.ready().
185    * @private
186    */
187   onAudioStatusUpdate_: function() {
188     this.audioController.time =
189         (this.lastAudioUpdateTime_ = this.audioElement.currentTime * 1000);
190     this.audioController.duration = this.audioElement.duration * 1000;
191     this.audioController.playing = !this.audioElement.paused;
192   },
193
194   /**
195    * Goes to the previous or the next track.
196    * @param {boolean} forward True if next, false if previous.
197    * @param {boolean} repeat True if repeat-mode is enabled. False otherwise.
198    * @private
199    */
200   advance_: function(forward, repeat) {
201     this.cancelAutoAdvance_();
202
203     var nextTrackIndex = this.trackList.getNextTrackIndex(forward, true);
204     var isNextTrackAvailable =
205         (this.trackList.getNextTrackIndex(forward, repeat) !== -1);
206
207     this.audioController.playing = isNextTrackAvailable;
208     this.trackList.currentTrackIndex = nextTrackIndex;
209
210     Platform.performMicrotaskCheckpoint();
211   },
212
213   /**
214    * Timeout ID of auto advance. Used internally in scheduleAutoAdvance_() and
215    *     cancelAutoAdvance_().
216    * @type {number}
217    * @private
218    */
219   autoAdvanceTimer_: null,
220
221   /**
222    * Schedules automatic advance to the next track after a timeout.
223    * @param {boolean} forward True if next, false if previous.
224    * @param {boolean} repeat True if repeat-mode is enabled. False otherwise.
225    * @private
226    */
227   scheduleAutoAdvance_: function(forward, repeat) {
228     this.cancelAutoAdvance_();
229     this.autoAdvanceTimer_ = setTimeout(
230         function() {
231           this.autoAdvanceTimer_ = null;
232           // We are advancing only if the next track is not known to be invalid.
233           // This prevents an endless auto-advancing in the case when all tracks
234           // are invalid (we will only visit each track once).
235           this.advance_(forward, repeat, true /* only if valid */);
236         }.bind(this),
237         3000);
238   },
239
240   /**
241    * Cancels the scheduled auto advance.
242    * @private
243    */
244   cancelAutoAdvance_: function() {
245     if (this.autoAdvanceTimer_) {
246       clearTimeout(this.autoAdvanceTimer_);
247       this.autoAdvanceTimer_ = null;
248     }
249   },
250
251   /**
252    * The index of the current track.
253    * If the list has no tracks, the value must be -1.
254    *
255    * @type {number}
256    */
257   get currentTrackIndex() {
258     return this.trackList.currentTrackIndex;
259   },
260   set currentTrackIndex(value) {
261     this.trackList.currentTrackIndex = value;
262   },
263
264   /**
265    * The list of the tracks in the playlist.
266    *
267    * When it changed, current operation including playback is stopped and
268    * restarts playback with new tracks if necessary.
269    *
270    * @type {Array.<AudioPlayer.TrackInfo>}
271    */
272   get tracks() {
273     return this.trackList ? this.trackList.tracks : null;
274   },
275   set tracks(tracks) {
276     if (this.trackList.tracks === tracks)
277       return;
278
279     this.cancelAutoAdvance_();
280
281     this.trackList.tracks = tracks;
282     var currentTrack = this.trackList.getCurrentTrack();
283     if (currentTrack && currentTrack.url != this.audioElement.src) {
284       this.audioElement.src = currentTrack.url;
285       this.audioElement.play();
286     }
287   },
288
289   /**
290    * Invoked when the audio player is being unloaded.
291    */
292   onPageUnload: function() {
293     this.audioElement.src = '';  // Hack to prevent crashing.
294   },
295 });