Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / file_manager / audio_player / elements / track_list.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 (function() {
6   'use strict';
7
8   Polymer('track-list', {
9     /**
10      * Initializes an element. This method is called automatically when the
11      * element is ready.
12      */
13     ready: function() {
14       this.tracksObserver_ = new ArrayObserver(
15           this.tracks,
16           this.tracksValueChanged_.bind(this));
17     },
18
19     /**
20      * Registers handlers for changing of external variables
21      */
22     observe: {
23       'model.shuffle': 'onShuffleChanged',
24     },
25
26     /**
27      * Model object of the Audio Player.
28      * @type {AudioPlayerModel}
29      */
30     model: null,
31
32     /**
33      * List of tracks.
34      * @type {Array.<AudioPlayer.TrackInfo>}
35      */
36     tracks: [],
37
38     /**
39      * Play order of the tracks. Each value is the index of 'this.tracks'.
40      * @type {Array.<number>}
41      */
42     playOrder: [],
43
44     /**
45      * Track index of the current track.
46      * If the tracks propertye is empty, it should be -1. Otherwise, be a valid
47      * track number.
48      *
49      * @type {number}
50      */
51     currentTrackIndex: -1,
52
53     /**
54      * Invoked when 'shuffle' property is changed.
55      * @param {boolean} oldValue Old value.
56      * @param {boolean} newValue New value.
57      */
58     onShuffleChanged: function(oldValue, newValue) {
59       this.generatePlayOrder(true /* keep the current track */);
60     },
61
62     /**
63      * Invoked when the current track index is changed.
64      * @param {number} oldValue old value.
65      * @param {number} newValue new value.
66      */
67     currentTrackIndexChanged: function(oldValue, newValue) {
68       if (oldValue === newValue)
69         return;
70
71       if (!isNaN(oldValue) && oldValue !== -1)
72         this.tracks[oldValue].active = false;
73
74       if (0 <= newValue && newValue < this.tracks.length) {
75         var currentPlayOrder = this.playOrder.indexOf(newValue);
76         if (currentPlayOrder !== -1) {
77           // Success
78           this.tracks[newValue].active = true;
79
80           var trackSelector = '.track[index="' + newValue + '"]';
81           var trackElement = this.impl.querySelector(trackSelector);
82           if (trackElement) {
83             this.scrollTop = Math.max(
84                 0,
85                 (trackElement.offsetTop + trackElement.offsetHeight -
86                  this.clientHeight));
87           }
88           return;
89         }
90       }
91
92       // Invalid index
93       if (this.tracks.length === 0)
94         this.currentTrackIndex = -1;
95       else
96         this.generatePlayOrder(false /* no need to keep the current track */);
97     },
98
99     /**
100      * Invoked when 'tracks' property is clicked.
101      * @param {Event} event Click event.
102      */
103     tracksChanged: function(oldValue, newValue) {
104       // Note: Sometimes both oldValue and newValue are null though the actual
105       // values are not null. Maybe it's a bug of Polymer.
106
107       // Re-register the observer of 'this.tracks'.
108       this.tracksObserver_.close();
109       this.tracksObserver_ = new ArrayObserver(
110           this.tracks,
111           this.tracksValueChanged_.bind(this));
112
113       // Reset play order and current index.
114       if (this.tracks.length !== 0)
115         this.generatePlayOrder(false /* no need to keep the current track */);
116
117       if (this.tracks.length === 0) {
118         this.playOrder = [];
119         this.currentTrackIndex = -1;
120       }
121     },
122
123     /**
124      * Invoked when the value in the 'tracks' is changed.
125      * @param {Array.<Object>} splices The detail of the change.
126      */
127     tracksValueChanged_: function(splices) {
128       if (this.tracks.length === 0)
129         this.currentTrackIndex = -1;
130       else
131         this.tracks[this.currentTrackIndex].active = true;
132     },
133
134     /**
135      * Invoked when the track element is clicked.
136      * @param {Event} event Click event.
137      */
138     trackClicked: function(event) {
139       var track = event.target.templateInstance.model;
140       this.selectTrack(track);
141     },
142
143     /**
144      * Invoked when the track element is clicked.
145      * @param {boolean} keepCurrentTrack Keep the current track or not.
146      */
147     generatePlayOrder: function(keepCurrentTrack) {
148       console.assert((keepCurrentTrack !== undefined),
149                      'The argument "forward" is undefined');
150
151       if (this.tracks.length === 0) {
152         this.playOrder = [];
153         return;
154       }
155
156       // Creates sequenced array.
157       this.playOrder =
158           this.tracks.
159           map(function(unused, index) { return index; });
160
161       if (this.model && this.model.shuffle) {
162         // Randomizes the play order array (Schwarzian-transform algorithm).
163         this.playOrder =
164             this.playOrder.
165             map(function(a) {
166               return {weight: Math.random(), index: a};
167             }).
168             sort(function(a, b) { return a.weight - b.weight }).
169             map(function(a) { return a.index });
170
171         if (keepCurrentTrack) {
172           // Puts the current track at the beginning of the play order.
173           this.playOrder =
174               this.playOrder.filter(function(value) {
175                 return this.currentTrackIndex !== value;
176               }, this);
177           this.playOrder.splice(0, 0, this.currentTrackIndex);
178         }
179       }
180
181       if (!keepCurrentTrack)
182         this.currentTrackIndex = this.playOrder[0];
183     },
184
185     /**
186      * Sets the current track.
187      * @param {AudioPlayer.TrackInfo} track TrackInfo to be set as the current
188      *     track.
189      */
190     selectTrack: function(track) {
191       var index = -1;
192       for (var i = 0; i < this.tracks.length; i++) {
193         if (this.tracks[i].url === track.url) {
194           index = i;
195           break;
196         }
197       }
198       if (index >= 0)
199         this.currentTrackIndex = index;
200     },
201
202     /**
203      * Returns the current track.
204      * @param {AudioPlayer.TrackInfo} track TrackInfo of the current track.
205      */
206     getCurrentTrack: function() {
207       if (this.tracks.length === 0)
208         return null;
209
210       return this.tracks[this.currentTrackIndex];
211     },
212
213     /**
214      * Returns the next (or previous) track in the track list. If there is no
215      * next track, returns -1.
216      *
217      * @param {boolean} forward Specify direction: forward or previous mode.
218      *     True: forward mode, false: previous mode.
219      * @param {boolean} cyclic Specify if cyclically or not: It true, the first
220      *     track is succeeding to the last track, otherwise no track after the
221      *     last.
222      * @return {number} The next track index.
223      */
224     getNextTrackIndex: function(forward, cyclic)  {
225       if (this.tracks.length === 0)
226         return -1;
227
228       var defaultTrackIndex =
229           forward ? this.playOrder[0] : this.playOrder[this.tracks.length - 1];
230
231       var currentPlayOrder = this.playOrder.indexOf(this.currentTrackIndex);
232       console.assert(
233           (0 <= currentPlayOrder && currentPlayOrder < this.tracks.length),
234           'Insufficient TrackList.playOrder. The current track is not on the ' +
235             'track list.');
236
237       var newPlayOrder = currentPlayOrder + (forward ? +1 : -1);
238       if (newPlayOrder === -1 || newPlayOrder === this.tracks.length)
239         return cyclic ? defaultTrackIndex : -1;
240
241       var newTrackIndex = this.playOrder[newPlayOrder];
242       console.assert(
243           (0 <= newTrackIndex && newTrackIndex < this.tracks.length),
244           'Insufficient TrackList.playOrder. New Play Order: ' + newPlayOrder);
245
246       return newTrackIndex;
247     },
248   });  // Polymer('track-list') block
249 })();  // Anonymous closure