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

Side by Side Diff: chrome/browser/resources/file_manager/audio_player/js/audio_player.js

Issue 247123002: Move Files.app files to ui/file_manager (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix the test failure on non-chromeos Created 6 years, 8 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 /**
8 * @param {HTMLElement} container Container element.
9 * @constructor
10 */
11 function AudioPlayer(container) {
12 this.container_ = container;
13 this.volumeManager_ = new VolumeManagerWrapper(
14 VolumeManagerWrapper.DriveEnabledStatus.DRIVE_ENABLED);
15 this.metadataCache_ = MetadataCache.createFull(this.volumeManager_);
16 this.selectedEntry_ = null;
17
18 this.model_ = new AudioPlayerModel();
19 var observer = new PathObserver(this.model_, 'expanded');
20 observer.open(function(newValue, oldValue) {
21 // Inverse arguments intentionally to match the Polymer way.
22 this.onModelExpandedChanged(oldValue, newValue);
23 }.bind(this));
24
25 this.entries_ = [];
26 this.currentTrackIndex_ = -1;
27 this.playlistGeneration_ = 0;
28
29 /**
30 * Whether if the playlist is expanded or not. This value is changed by
31 * this.syncExpanded().
32 * True: expanded, false: collapsed, null: unset.
33 *
34 * @type {?boolean}
35 * @private
36 */
37 this.isExpanded_ = null; // Initial value is null. It'll be set in load().
38
39 this.player_ = document.querySelector('audio-player');
40 // TODO(yoshiki): Move tracks into the model.
41 this.player_.tracks = [];
42 this.player_.model = this.model_;
43 Platform.performMicrotaskCheckpoint();
44
45 this.errorString_ = '';
46 this.offlineString_ = '';
47 chrome.fileBrowserPrivate.getStrings(function(strings) {
48 container.ownerDocument.title = strings['AUDIO_PLAYER_TITLE'];
49 this.errorString_ = strings['AUDIO_ERROR'];
50 this.offlineString_ = strings['AUDIO_OFFLINE'];
51 AudioPlayer.TrackInfo.DEFAULT_ARTIST =
52 strings['AUDIO_PLAYER_DEFAULT_ARTIST'];
53 }.bind(this));
54
55 this.volumeManager_.addEventListener('externally-unmounted',
56 this.onExternallyUnmounted_.bind(this));
57
58 window.addEventListener('resize', this.onResize_.bind(this));
59
60 // Show the window after DOM is processed.
61 var currentWindow = chrome.app.window.current();
62 if (currentWindow)
63 setTimeout(currentWindow.show.bind(currentWindow), 0);
64 }
65
66 /**
67 * Initial load method (static).
68 */
69 AudioPlayer.load = function() {
70 document.ondragstart = function(e) { e.preventDefault() };
71
72 AudioPlayer.instance =
73 new AudioPlayer(document.querySelector('.audio-player'));
74
75 reload();
76 };
77
78 /**
79 * Unloads the player.
80 */
81 function unload() {
82 if (AudioPlayer.instance)
83 AudioPlayer.instance.onUnload();
84 }
85
86 /**
87 * Reloads the player.
88 */
89 function reload() {
90 AudioPlayer.instance.load(window.appState);
91 }
92
93 /**
94 * Loads a new playlist.
95 * @param {Playlist} playlist Playlist object passed via mediaPlayerPrivate.
96 */
97 AudioPlayer.prototype.load = function(playlist) {
98 this.playlistGeneration_++;
99 this.currentTrackIndex_ = -1;
100
101 // Save the app state, in case of restart. Make a copy of the object, so the
102 // playlist member is not changed after entries are resolved.
103 window.appState = JSON.parse(JSON.stringify(playlist)); // cloning
104 util.saveAppState();
105
106 this.isExpanded_ = this.model_.expanded;
107
108 // Resolving entries has to be done after the volume manager is initialized.
109 this.volumeManager_.ensureInitialized(function() {
110 util.URLsToEntries(playlist.items, function(entries) {
111 this.entries_ = entries;
112
113 var position = playlist.position || 0;
114 var time = playlist.time || 0;
115
116 if (this.entries_.length == 0)
117 return;
118
119 var newTracks = [];
120
121 for (var i = 0; i != this.entries_.length; i++) {
122 var entry = this.entries_[i];
123 var onClick = this.select_.bind(this, i, false /* no restore */);
124 newTracks.push(new AudioPlayer.TrackInfo(entry, onClick));
125 }
126
127 this.player_.tracks = newTracks;
128
129 // Makes it sure that the handler of the track list is called, before the
130 // handler of the track index.
131 Platform.performMicrotaskCheckpoint();
132
133 this.select_(position, !!time);
134
135 // Load the selected track metadata first, then load the rest.
136 this.loadMetadata_(position);
137 for (i = 0; i != this.entries_.length; i++) {
138 if (i != position)
139 this.loadMetadata_(i);
140 }
141 }.bind(this));
142 }.bind(this));
143 };
144
145 /**
146 * Loads metadata for a track.
147 * @param {number} track Track number.
148 * @private
149 */
150 AudioPlayer.prototype.loadMetadata_ = function(track) {
151 this.fetchMetadata_(
152 this.entries_[track], this.displayMetadata_.bind(this, track));
153 };
154
155 /**
156 * Displays track's metadata.
157 * @param {number} track Track number.
158 * @param {Object} metadata Metadata object.
159 * @param {string=} opt_error Error message.
160 * @private
161 */
162 AudioPlayer.prototype.displayMetadata_ = function(track, metadata, opt_error) {
163 this.player_.tracks[track].setMetadata(metadata, opt_error);
164 };
165
166 /**
167 * Closes audio player when a volume containing the selected item is unmounted.
168 * @param {Event} event The unmount event.
169 * @private
170 */
171 AudioPlayer.prototype.onExternallyUnmounted_ = function(event) {
172 if (!this.selectedEntry_)
173 return;
174
175 if (this.volumeManager_.getVolumeInfo(this.selectedEntry_) ===
176 event.volumeInfo)
177 window.close();
178 };
179
180 /**
181 * Called on window is being unloaded.
182 */
183 AudioPlayer.prototype.onUnload = function() {
184 if (this.player_)
185 this.player_.onPageUnload();
186
187 if (this.volumeManager_)
188 this.volumeManager_.dispose();
189 };
190
191 /**
192 * Selects a new track to play.
193 * @param {number} newTrack New track number.
194 * @private
195 */
196 AudioPlayer.prototype.select_ = function(newTrack) {
197 if (this.currentTrackIndex_ == newTrack) return;
198
199 this.currentTrackIndex_ = newTrack;
200 this.player_.currentTrackIndex = this.currentTrackIndex_;
201 Platform.performMicrotaskCheckpoint();
202
203 if (!window.appReopen)
204 this.player_.audioElement.play();
205
206 window.appState.position = this.currentTrackIndex_;
207 window.appState.time = 0;
208 util.saveAppState();
209
210 var entry = this.entries_[this.currentTrackIndex_];
211
212 this.fetchMetadata_(entry, function(metadata) {
213 if (this.currentTrackIndex_ != newTrack)
214 return;
215
216 this.selectedEntry_ = entry;
217 }.bind(this));
218 };
219
220 /**
221 * @param {FileEntry} entry Track file entry.
222 * @param {function(object)} callback Callback.
223 * @private
224 */
225 AudioPlayer.prototype.fetchMetadata_ = function(entry, callback) {
226 this.metadataCache_.get(entry, 'thumbnail|media|streaming',
227 function(generation, metadata) {
228 // Do nothing if another load happened since the metadata request.
229 if (this.playlistGeneration_ == generation)
230 callback(metadata);
231 }.bind(this, this.playlistGeneration_));
232 };
233
234 /**
235 * Media error handler.
236 * @private
237 */
238 AudioPlayer.prototype.onError_ = function() {
239 var track = this.currentTrackIndex_;
240
241 this.invalidTracks_[track] = true;
242
243 this.fetchMetadata_(
244 this.entries_[track],
245 function(metadata) {
246 var error = (!navigator.onLine && metadata.streaming) ?
247 this.offlineString_ : this.errorString_;
248 this.displayMetadata_(track, metadata, error);
249 this.scheduleAutoAdvance_();
250 }.bind(this));
251 };
252
253 /**
254 * Toggles the expanded mode when resizing.
255 *
256 * @param {Event} event Resize event.
257 * @private
258 */
259 AudioPlayer.prototype.onResize_ = function(event) {
260 if (!this.isExpanded_ &&
261 window.innerHeight >= AudioPlayer.EXPANDED_MODE_MIN_HEIGHT) {
262 this.isExpanded_ = true;
263 this.model_.expanded = true;
264 } else if (this.isExpanded_ &&
265 window.innerHeight < AudioPlayer.EXPANDED_MODE_MIN_HEIGHT) {
266 this.isExpanded_ = false;
267 this.model_.expanded = false;
268 }
269 };
270
271 /* Keep the below constants in sync with the CSS. */
272
273 /**
274 * Window header size in pixels.
275 * @type {number}
276 * @const
277 */
278 AudioPlayer.HEADER_HEIGHT = 33; // 32px + border 1px
279
280 /**
281 * Track height in pixels.
282 * @type {number}
283 * @const
284 */
285 AudioPlayer.TRACK_HEIGHT = 44;
286
287 /**
288 * Controls bar height in pixels.
289 * @type {number}
290 * @const
291 */
292 AudioPlayer.CONTROLS_HEIGHT = 73; // 72px + border 1px
293
294 /**
295 * Default number of items in the expanded mode.
296 * @type {number}
297 * @const
298 */
299 AudioPlayer.DEFAULT_EXPANDED_ITEMS = 5;
300
301 /**
302 * Minimum size of the window in the expanded mode in pixels.
303 * @type {number}
304 * @const
305 */
306 AudioPlayer.EXPANDED_MODE_MIN_HEIGHT = AudioPlayer.CONTROLS_HEIGHT +
307 AudioPlayer.TRACK_HEIGHT * 2;
308
309 /**
310 * Invoked when the 'expanded' property in the model is changed.
311 * @param {boolean} oldValue Old value.
312 * @param {boolean} newValue New value.
313 */
314 AudioPlayer.prototype.onModelExpandedChanged = function(oldValue, newValue) {
315 if (this.isExpanded_ !== null &&
316 this.isExpanded_ === newValue)
317 return;
318
319 if (this.isExpanded_ && !newValue)
320 this.lastExpandedHeight_ = window.innerHeight;
321
322 if (this.isExpanded_ !== newValue) {
323 this.isExpanded_ = newValue;
324 this.syncHeight_();
325
326 // Saves new state.
327 window.appState.expanded = newValue;
328 util.saveAppState();
329 }
330 };
331
332 /**
333 * @private
334 */
335 AudioPlayer.prototype.syncHeight_ = function() {
336 var targetHeight;
337
338 if (this.model_.expanded) {
339 // Expanded.
340 if (!this.lastExpandedHeight_ ||
341 this.lastExpandedHeight_ < AudioPlayer.EXPANDED_MODE_MIN_HEIGHT) {
342 var expandedListHeight =
343 Math.min(this.entries_.length, AudioPlayer.DEFAULT_EXPANDED_ITEMS) *
344 AudioPlayer.TRACK_HEIGHT;
345 targetHeight = AudioPlayer.CONTROLS_HEIGHT + expandedListHeight;
346 this.lastExpandedHeight_ = targetHeight;
347 } else {
348 targetHeight = this.lastExpandedHeight_;
349 }
350 } else {
351 // Not expanded.
352 targetHeight = AudioPlayer.CONTROLS_HEIGHT + AudioPlayer.TRACK_HEIGHT;
353 }
354
355 window.resizeTo(window.innerWidth, targetHeight + AudioPlayer.HEADER_HEIGHT);
356 };
357
358 /**
359 * Create a TrackInfo object encapsulating the information about one track.
360 *
361 * @param {fileEntry} entry FileEntry to be retrieved the track info from.
362 * @param {function} onClick Click handler.
363 * @constructor
364 */
365 AudioPlayer.TrackInfo = function(entry, onClick) {
366 this.url = entry.toURL();
367 this.title = entry.name;
368 this.artist = this.getDefaultArtist();
369
370 // TODO(yoshiki): implement artwork.
371 this.artwork = null;
372 this.active = false;
373 };
374
375 /**
376 * @return {HTMLDivElement} The wrapper element for the track.
377 */
378 AudioPlayer.TrackInfo.prototype.getBox = function() { return this.box_ };
379
380 /**
381 * @return {string} Default track title (file name extracted from the url).
382 */
383 AudioPlayer.TrackInfo.prototype.getDefaultTitle = function() {
384 var title = this.url.split('/').pop();
385 var dotIndex = title.lastIndexOf('.');
386 if (dotIndex >= 0) title = title.substr(0, dotIndex);
387 title = decodeURIComponent(title);
388 return title;
389 };
390
391 /**
392 * TODO(kaznacheev): Localize.
393 */
394 AudioPlayer.TrackInfo.DEFAULT_ARTIST = 'Unknown Artist';
395
396 /**
397 * @return {string} 'Unknown artist' string.
398 */
399 AudioPlayer.TrackInfo.prototype.getDefaultArtist = function() {
400 return AudioPlayer.TrackInfo.DEFAULT_ARTIST;
401 };
402
403 /**
404 * @param {Object} metadata The metadata object.
405 * @param {string} error Error string.
406 */
407 AudioPlayer.TrackInfo.prototype.setMetadata = function(
408 metadata, error) {
409 if (error) {
410 // TODO(yoshiki): Handle error in better way.
411 this.title = entry.name;
412 this.artist = this.getDefaultArtist();
413 } else if (metadata.thumbnail && metadata.thumbnail.url) {
414 // TODO(yoshiki): implement artwork.
415 }
416 this.title = (metadata.media && metadata.media.title) ||
417 this.getDefaultTitle();
418 this.artist = error ||
419 (metadata.media && metadata.media.artist) || this.getDefaultArtist();
420 };
421
422 // Starts loading the audio player.
423 window.addEventListener('polymer-ready', function(e) {
424 AudioPlayer.load();
425 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698