~jonas-drange/online-services-common-js/navbar-autocomplete

« back to all changes in this revision

Viewing changes to src/music/js/music-view.js

  • Committer: Stephen Stewart
  • Date: 2014-02-22 23:57:25 UTC
  • mfrom: (18.1.2 trunk)
  • Revision ID: stephen.stewart@canonical.com-20140222235725-iw6f15t9umws19xd
mergeĀ lp:~stephen-stewart/online-services-common-js/remove-u1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*global GLOBAL_PLAYLIST_LIST:true, soundManager */
2
 
var BOUNDINGBOX = 'boundingBox',
3
 
    PARENTNODE = 'parentNode',
4
 
    CLICK = 'click',
5
 
    EVENTNEXT = 'music:next',
6
 
    EVENTPAUSE = 'music:pause',
7
 
    EVENTPLAY = 'music:play',
8
 
    EVENTPREVIOUS = 'music:previous',
9
 
    EVENTSTARTED = 'music:started',
10
 
    EVENTSTOP = 'music:stop',
11
 
    // Sound object events.
12
 
    EVSOUNDLOADING = 'music:loading',
13
 
    EVSOUNDPLAYING = 'music:playing',
14
 
    EVSOUNDPLAY = 'music:soundplay',
15
 
    EVSOUNDFINISH = 'music:soundfinish',
16
 
    EVSOUNDSTOP = 'music:soundstop',
17
 
    EVSOUNDPAUSE = 'music:soundpause',
18
 
    HISTORYRESET = {
19
 
        album: null,
20
 
        artist: null,
21
 
        playlist: null,
22
 
        search: null,
23
 
        songs: null,
24
 
        view: null
25
 
    },
26
 
    DEFAULT_ALBUM_ART = MEDIA_URL + 'img/music/album.png',
27
 
    DEFAULT_PLAYLIST_ART = MEDIA_URL + 'img/music/playlist.png',
28
 
    ZERO = '0',
29
 
    THIRTYSEVEN = '37',
30
 
    THIRTYNINE = '39',
31
 
    SLIDER = 'slider',
32
 
    PLAYBUTTON = 'playButton',
33
 
    PREVBUTTON = 'prevButton',
34
 
    STOPBUTTON = 'stopButton',
35
 
    NEXTBUTTON = 'nextButton',
36
 
    PROGRESSBARCONT = 'progressBarCont',
37
 
    KEY = 'key',
38
 
    MUTECONTROL = 'muteControl',
39
 
    REPEATCONTROL = 'repeatControl',
40
 
    SHUFFLECONTROL = 'shuffleControl',
41
 
    HISTORY = 'history',
42
 
    VIEWCHANGE = 'viewChange',
43
 
    PLAYLISTCHANGE = 'playlistChange',
44
 
    INDEX = 'index',
45
 
    DISABLED = 'disabled',
46
 
    ENABLED = 'enabled',
47
 
    MED = 'med',
48
 
    LOW = 'low',
49
 
    VIEW = 'view',
50
 
    HIDDEN = 'hidden',
51
 
    SONG = 'song',
52
 
    SONGS = SONG + 's',
53
 
    DATASOURCE = 'datasource',
54
 
    NOWPLAYING = 'nowplaying',
55
 
    CURRENT = 'current',
56
 
    HEIGHT = 'height',
57
 
    PLAYLIST = 'playlist',
58
 
    PLAYLISTS = PLAYLIST + 's',
59
 
    VISIBLE = 'visible',
60
 
    CHECKED = 'checked',
61
 
    TR = 'tr',
62
 
    EMPTY = '',
63
 
    ARTISTS = 'artists',
64
 
    ALBUMS = 'albums',
65
 
    WIDTH = 'width',
66
 
    SELECTEDSONGS = 'selectedSongs',
67
 
    VISIBLECHANGE = 'visibleChange',
68
 
    VALUE = 'value',
69
 
    MYSONGS = 'my-songs',
70
 
    SEARCH = 'search',
71
 
    PROGBARRIGHTARRCOUNT = 'progBarRightArrCount',
72
 
    PROGBARLEFTARRCOUNT = 'progBarLeftArrCount',
73
 
    REPEAT = 'repeat',
74
 
    SHUFFLE = 'shuffle',
75
 
 
76
 
    DOTACTIVESONGSVIEW = '.active-songs-view',
77
 
    DOTLEFT = '.left',
78
 
    DOTRIGHT = '.right',
79
 
 
80
 
    MUSICPLAYSONGATINDEX = 'music:playSongAtIndex',
81
 
    MUSICSETPLAYLIST = 'music:setPlaylist',
82
 
    MUSICUPDATESELECTED = 'music:updateSelected',
83
 
    MUSICADDSONGSTOPLAYLIST = 'music:addSongsToPlaylist',
84
 
    MUSICREMOVEFROMNOWPLAYING = 'music:removeFromNowPlaying',
85
 
    MUSICUPDATECOLPLAYBUTTON = 'music:updateColPlayButton',
86
 
    MUSICUPDATECURRENTTRACKROW = 'music:updateCurrentTrackRow',
87
 
    MUSICCHECKBOXESUPDATED = 'music:checkboxesUpdated',
88
 
    MUSICCHECKBOXCHANGED = 'music:checkboxChanged',
89
 
    MOVIESTARRX = /^aac|mp4|m4a$/,
90
 
    namespace = Y.namespace('Music'),
91
 
    ToggleView,
92
 
    SongView;
93
 
 
94
 
ToggleView = function() {
95
 
    Y.Do.before(this.beforeShow, this, 'show');
96
 
    Y.Do.after(this.afterHide, this, 'hide');
97
 
};
98
 
ToggleView.ATTRS = {events: {value: []}};
99
 
ToggleView.prototype = {
100
 
    afterHide: function() {
101
 
        var events = this.get('events');
102
 
        while (events.length) {
103
 
            events.pop().detach();
104
 
        }
105
 
    },
106
 
    beforeShow: function() {
107
 
        if (this.get("rendered") === true) {
108
 
            this.bindUI();
109
 
        }
110
 
    }
111
 
};
112
 
 
113
 
namespace.PlayerView = Y.Base.create('player', Y.Widget, [], {
114
 
    renderUI: function() {
115
 
        var slider = new Y.Music.Slider({
116
 
            valueChangeHandler: Y.bind(this.onVolumeChange, this),
117
 
            visible: false,
118
 
            zIndex: 5
119
 
        });
120
 
        slider.render();
121
 
        this.set(SLIDER, slider);
122
 
    },
123
 
    bindUI: function() {
124
 
 
125
 
        this.get(PLAYBUTTON).on(CLICK, this.emitPlay, this);
126
 
        this.get(PREVBUTTON).on(CLICK, this.emitPrevious, this);
127
 
        this.get(STOPBUTTON).on(CLICK, this.emitPause, this);
128
 
        this.get(NEXTBUTTON).on(CLICK, this.emitNext, this);
129
 
        this.get(PROGRESSBARCONT).on(CLICK, this.onProgressBarClick, this);
130
 
        this.get(PROGRESSBARCONT).on('blur', this.onProgressBarBlur, this);
131
 
        Y.on(KEY, this.onProgressBarKeyDown, this.get(PROGRESSBARCONT), 'down: 37, 39', this);
132
 
        Y.on(KEY, this.onProgressBarKeyUp, this.get(PROGRESSBARCONT), 'up: 37, 39', this);
133
 
 
134
 
        this.get(MUTECONTROL).on(CLICK, this.onMuteClick, this);
135
 
        this.get('volumeControl').on(CLICK, this.onVolumeClick, this);
136
 
        this.get(REPEATCONTROL).on(CLICK, this.onRepeatClick, this);
137
 
        this.get(SHUFFLECONTROL).on(CLICK, this.onShuffleClick, this);
138
 
        this.get(HISTORY).after(VIEWCHANGE, this.afterViewChange, this);
139
 
 
140
 
        this.after('indexChange', this.afterIndexChange, this);
141
 
        this.after(PLAYLISTCHANGE, this.afterPlaylistChange, this);
142
 
        // This is so nowPlaying is updated as soon as
143
 
        // something is added to the queue.
144
 
        this.after(PLAYLISTCHANGE, this.afterPlay, this);
145
 
        this.after('muteChange', this.afterMuteChange, this);
146
 
        this.after('repeatChange', this.afterRepeatChange, this);
147
 
        this.after('shuffleChange', this.afterShuffleChange, this);
148
 
 
149
 
        Y.Global.on(EVENTSTOP, this.onStop, this);
150
 
        Y.Global.on(EVENTPAUSE, this.onPause, this);
151
 
        Y.Global.on(EVENTSTOP, this.onVuMeterEnd, this);
152
 
        Y.Global.on(EVENTSTOP, this.resetTime, this);
153
 
        Y.Global.on(EVENTPAUSE, this.onVuMeterEnd, this);
154
 
        Y.Global.on(EVENTPLAY, this.onPlay, this);
155
 
        Y.Global.on(MUSICPLAYSONGATINDEX, this.onPlaySongAtIndex, this);
156
 
        Y.Global.on(EVENTNEXT, this.onNext, this);
157
 
        Y.Global.on(EVENTPREVIOUS, this.onPrevious, this);
158
 
        Y.Global.on(EVSOUNDPLAYING, Y.throttle(Y.bind(this.whilePlaying, this), 100), this);
159
 
        Y.Global.on(EVSOUNDLOADING, this.whileLoading, this);
160
 
        Y.Global.on(MUSICSETPLAYLIST, this.setPlaylist, this);
161
 
        Y.Global.on(EVSOUNDFINISH, this.continuePlaylist, this);
162
 
        Y.Global.on(MUSICUPDATESELECTED, this.onUpdateSelected, this);
163
 
        Y.Global.on(MUSICADDSONGSTOPLAYLIST, this.onAddSongsToPlaylist, this);
164
 
        Y.Global.on(MUSICREMOVEFROMNOWPLAYING, this.onRemoveFromNowPlaying, this);
165
 
        Y.Global.on(MUSICUPDATECOLPLAYBUTTON, this.onUpdateColPlayButton, this);
166
 
        Y.Global.on(MUSICUPDATECURRENTTRACKROW, this.updateCurrentTrackRow, this);
167
 
        Y.Global.after(EVENTPLAY, this.afterPlay, this);
168
 
        Y.Unity.onInit(Y.bind(this.bindUnity, this));
169
 
    },
170
 
    afterPlaylistChange: function(e) {
171
 
        var index = this.get(INDEX),
172
 
        playlist = e.newVal,
173
 
        nextButton = this.get(NEXTBUTTON),
174
 
        playButton = this.get(PLAYBUTTON),
175
 
        prevButton = this.get(PREVBUTTON),
176
 
        mp;
177
 
 
178
 
        if (playlist.length === 0) {
179
 
            this.set(INDEX, 0);
180
 
            if (!nextButton.hasClass(DISABLED)) {
181
 
                nextButton.addClass(DISABLED);
182
 
                nextButton.set(DISABLED, DISABLED);
183
 
            }
184
 
            if (!playButton.hasClass(DISABLED)) {
185
 
                playButton.addClass(DISABLED);
186
 
                playButton.set(DISABLED, DISABLED);
187
 
            }
188
 
            if (!prevButton.hasClass(DISABLED)) {
189
 
                prevButton.addClass(DISABLED);
190
 
                prevButton.set(DISABLED, DISABLED);
191
 
            }
192
 
        } else {
193
 
            playButton.removeClass(DISABLED);
194
 
            playButton.removeAttribute(DISABLED);
195
 
        }
196
 
        if (index === 0 || playlist.length === 0) {
197
 
            if (!prevButton.hasClass(DISABLED)) {
198
 
                prevButton.addClass(DISABLED);
199
 
                prevButton.set(DISABLED, DISABLED);
200
 
            }
201
 
        } else {
202
 
            prevButton.removeClass(DISABLED);
203
 
            prevButton.removeAttribute(DISABLED);
204
 
        }
205
 
        if (index + 1 >= playlist.length) {
206
 
            if (!nextButton.hasClass(NEXTBUTTON)) {
207
 
                nextButton.addClass(NEXTBUTTON);
208
 
            }
209
 
        } else {
210
 
            nextButton.removeClass(DISABLED);
211
 
            nextButton.removeAttribute(DISABLED);
212
 
        }
213
 
        if (Y.Unity.ready) {
214
 
            mp = Y.Unity.Unity.MediaPlayer;
215
 
            mp.setCanPlay(playlist.length !== 0);
216
 
            mp.setCanPause(playlist.length !== 0);
217
 
            mp.setCanGoPrevious(index !== 0 && playlist.length !== 0);
218
 
            mp.setCanGoNext(index + 1 < playlist.length);
219
 
        }
220
 
        Y.Global.fire(MUSICCHECKBOXESUPDATED);
221
 
    },
222
 
    onUpdateSelected: function(e) {
223
 
        var song = e.song,
224
 
        index = this.get(INDEX),
225
 
        history = this.get(HISTORY),
226
 
        view = history.get(VIEW),
227
 
        box = Y.one(".active-songs-view"),
228
 
        current;
229
 
 
230
 
        if (song && box) {
231
 
            if (view && view === NOWPLAYING) {
232
 
                current = box.one('[data-u1m-songid='+song.sID+'][data-u1m-npindex='+index+']');
233
 
            } else {
234
 
                current = box.one('[data-u1m-songid="'+song.sID+'"]');
235
 
            }
236
 
            box.all('.current').removeClass(CURRENT);
237
 
 
238
 
            if (current !== null) {
239
 
                current.addClass(CURRENT);
240
 
            }
241
 
            Y.Global.fire(MUSICUPDATECOLPLAYBUTTON, { song: song });
242
 
        }
243
 
    },
244
 
    onVuMeterEnd: function() {
245
 
        var vuMeter = Y.one(".active-songs-view .vu"),
246
 
            vuLeft, vuRight;
247
 
        if (vuMeter) {
248
 
            vuLeft = vuMeter.one(DOTLEFT);
249
 
            vuRight = vuMeter.one(DOTRIGHT);
250
 
            if (vuRight && vuLeft) {
251
 
                vuLeft.transition({
252
 
                    duration: 1,
253
 
                    easing: 'ease-in',
254
 
                    height: 0
255
 
                });
256
 
                vuRight.transition({
257
 
                    duration: 1,
258
 
                    easing: 'ease-in',
259
 
                    height: 0
260
 
                });
261
 
            }
262
 
        }
263
 
    },
264
 
    onUpdateColPlayButton: function(e) {
265
 
        var song = e && e.song || null,
266
 
        oNodeRow = Y.one(".active-songs-view .current");
267
 
        this.updateCurrentTrackRow({"node": oNodeRow, song: song});
268
 
    },
269
 
    updateCurrentTrackRow: function(e) {
270
 
        var song = e.song || Y.Music.lastSong,
271
 
        oNodeRow = e.node,
272
 
        box = Y.one(DOTACTIVESONGSVIEW),
273
 
        currentButton,
274
 
        currentVuMeter,
275
 
        button = null,
276
 
        vuMeter = this.get("vuMeter");
277
 
 
278
 
        if (box) {
279
 
            currentButton = box.one('button.hidden');
280
 
            currentVuMeter = box.one('.vu');
281
 
        }
282
 
 
283
 
        if (currentButton) {
284
 
            currentButton.removeClass(HIDDEN);
285
 
        }
286
 
 
287
 
        if (currentVuMeter) {
288
 
            currentVuMeter.remove();
289
 
        }
290
 
 
291
 
        if (oNodeRow !== null) {
292
 
            button = oNodeRow.one("button.play");
293
 
            button.get(PARENTNODE).appendChild(vuMeter);
294
 
            button.addClass(HIDDEN);
295
 
            oNodeRow.addClass(CURRENT);
296
 
        }
297
 
 
298
 
        if (song && vuMeter) {
299
 
            if (!song.isHTML5 && soundManager.flashVersion >= 9 && song.instanceOptions.usePeakData) {
300
 
                vuMeter.addClass("has-peak-data");
301
 
            } else {
302
 
                vuMeter.removeClass("has-peak-data");
303
 
                vuMeter.one(DOTLEFT).setStyle("height", 0);
304
 
                vuMeter.one(DOTRIGHT).setStyle("height", 0);
305
 
            }
306
 
        }
307
 
    },
308
 
    onProgressBarClick: function(e) {
309
 
        e.preventDefault();
310
 
        var x,
311
 
        target = e.currentTarget,
312
 
        song = Y.Music.lastSong,
313
 
        percOfTrack,
314
 
        newPosition,
315
 
        duration = this.getDuration();
316
 
 
317
 
        if (song){
318
 
            if (e.pageX || e.pageY) {
319
 
                x = e.pageX;
320
 
            }
321
 
            else {
322
 
                x = e.clientX + document.body.scrollLeft +
323
 
                    document.documentElement.scrollLeft;
324
 
            }
325
 
            x -= parseInt(target.getX(), 10);
326
 
            percOfTrack = parseInt((100 / parseInt(target.getComputedStyle(WIDTH), 10)) * x, 10);
327
 
            newPosition = parseInt((duration / 100 * percOfTrack), 10);
328
 
            if (newPosition) {
329
 
                song.setPosition(newPosition);
330
 
            }
331
 
        }
332
 
    },
333
 
    onProgressBarKeyDown: function(e){
334
 
        e.preventDefault();
335
 
        var song = Y.Music.lastSong,
336
 
        keyCode = e.keyCode,
337
 
        leftArrCount = this.get(PROGBARLEFTARRCOUNT),
338
 
        rightArrCount = this.get(PROGBARRIGHTARRCOUNT),
339
 
        leftMultiplier = (leftArrCount / 10),
340
 
        rightMultiplier = (rightArrCount / 10);
341
 
 
342
 
        leftMultiplier = leftMultiplier > 0 ? leftMultiplier : 1;
343
 
        rightMultiplier = rightMultiplier > 0 ? rightMultiplier : 1;
344
 
 
345
 
        if (song && leftArrCount === 0 || rightArrCount === 0) {
346
 
            song.pause();
347
 
        }
348
 
        // Right Arrow Key
349
 
        if (keyCode === THIRTYNINE) {
350
 
            song.setPosition(song.position + (100 * rightMultiplier));
351
 
            this.set(PROGBARRIGHTARRCOUNT, rightArrCount + 1);
352
 
            // Left Arrow key
353
 
        } else if (keyCode === THIRTYSEVEN){
354
 
            song.setPosition(song.position - (100 * leftMultiplier));
355
 
            this.set(PROGBARLEFTARRCOUNT, leftArrCount + 1);
356
 
        }
357
 
        this.whilePlaying();
358
 
    },
359
 
    onProgressBarKeyUp: function(e){
360
 
        e.preventDefault();
361
 
        var song = Y.Music.lastSong,
362
 
        keyCode = e.keyCode;
363
 
 
364
 
        if (song && song.paused) {
365
 
            song.togglePause();
366
 
        }
367
 
        // Right Arrow Key
368
 
        if (keyCode === THIRTYNINE) {
369
 
            this.set(PROGBARRIGHTARRCOUNT, 0);
370
 
            // Left Arrow key
371
 
        } else if (keyCode === THIRTYSEVEN){
372
 
            this.set(PROGBARLEFTARRCOUNT, 0);
373
 
        }
374
 
    },
375
 
    onProgressBarBlur: function() {
376
 
        var song = Y.Music.lastSong,
377
 
        leftArrCount = this.get(PROGBARLEFTARRCOUNT),
378
 
        rightArrCount = this.get(PROGBARRIGHTARRCOUNT);
379
 
 
380
 
        if (leftArrCount > 0 || rightArrCount > 0) {
381
 
            if (song && song.paused) {
382
 
                song.togglePause();
383
 
            }
384
 
            this.set(PROGBARLEFTARRCOUNT, 0);
385
 
            this.set(PROGBARRIGHTARRCOUNT, 0);
386
 
        }
387
 
    },
388
 
    updatePeakData: function () {
389
 
        var vuMeter = Y.one(".vu"),
390
 
        vuLeft, vuRight,
391
 
        song = Y.Music.lastSong;
392
 
 
393
 
        if (vuMeter) {
394
 
            vuLeft = vuMeter.one(DOTLEFT);
395
 
            vuRight = vuMeter.one(DOTRIGHT);
396
 
            if (vuRight && vuLeft) {
397
 
                vuLeft.setStyle(HEIGHT, Math.ceil(17*song.peakData.left)+'px');
398
 
                vuRight.setStyle(HEIGHT, Math.ceil(17*song.peakData.right)+'px');
399
 
            }
400
 
        }
401
 
    },
402
 
    getTime: function(nMSec, bAsString) {
403
 
        // convert milliseconds to mm:ss, return as object literal or string
404
 
        var nSec = Math.floor(nMSec/1000),
405
 
        min = Math.floor(nSec/60),
406
 
        sec = nSec-(min*60);
407
 
        return (bAsString?(min+':'+(sec<10?ZERO+sec:sec)):{'min':min,'sec':sec});
408
 
    },
409
 
    updateTime: function() {
410
 
        var totalTime = Y.one(".time-display .total"),
411
 
        currentTime = Y.one(".time-display .current"),
412
 
        song = Y.Music.lastSong,
413
 
        duration = this.getDuration();
414
 
 
415
 
        if (totalTime && currentTime) {
416
 
            totalTime.setContent(this.getTime(duration, true));
417
 
            currentTime.setContent(this.getTime(song.position, true));
418
 
        }
419
 
    },
420
 
    resetTime: function() {
421
 
        var blankTime = "-:--",
422
 
        totalTime = Y.one(".time-display .total"),
423
 
        currentTime = Y.one(".time-display .current");
424
 
 
425
 
        if (totalTime && currentTime) {
426
 
            totalTime.setContent(blankTime);
427
 
            currentTime.setContent(blankTime);
428
 
        }
429
 
    },
430
 
    onMuteClick: function(e) {
431
 
        e.preventDefault();
432
 
        var newVal = !this.get('mute');
433
 
        this.set('mute', newVal);
434
 
    },
435
 
    afterMuteChange: function(e) {
436
 
        var song = Y.Music.lastSong;
437
 
        if (e.newVal === true) {
438
 
            this.get(MUTECONTROL).addClass(ENABLED);
439
 
            if (song) { song.mute(); }
440
 
        } else {
441
 
            this.get(MUTECONTROL).removeClass(ENABLED);
442
 
            if (song) { song.unmute(); }
443
 
        }
444
 
    },
445
 
    onPlaySongAtIndex: function(e) {
446
 
        var index = e.index;
447
 
        this.set(INDEX, index);
448
 
        Y.Global.fire(EVSOUNDPLAY);
449
 
    },
450
 
    onShuffleClick: function(e) {
451
 
        e.preventDefault();
452
 
        var newVal = !this.get(SHUFFLE);
453
 
        this.set(SHUFFLE, newVal);
454
 
    },
455
 
    afterShuffleChange: function(e) {
456
 
        if (e.newVal === true) {
457
 
            this.get(SHUFFLECONTROL).addClass(ENABLED);
458
 
        } else {
459
 
            this.get(SHUFFLECONTROL).removeClass(ENABLED);
460
 
        }
461
 
        this.setPlaylistContext();
462
 
    },
463
 
    onRepeatClick: function(e) {
464
 
        e.preventDefault();
465
 
        var newVal = !this.get(REPEAT);
466
 
        this.set(REPEAT, newVal);
467
 
    },
468
 
    afterRepeatChange: function(e) {
469
 
        if (e.newVal === true) {
470
 
            this.get(REPEATCONTROL).addClass(ENABLED);
471
 
        } else {
472
 
            this.get(REPEATCONTROL).removeClass(ENABLED);
473
 
        }
474
 
        this.setPlaylistContext();
475
 
    },
476
 
    onVolumeClick: function(e) {
477
 
        var slider = this.get(SLIDER);
478
 
        e.halt();
479
 
        if (slider.get(VISIBLE)) {
480
 
            slider.hide();
481
 
        } else {
482
 
            slider.show();
483
 
            slider.set('align', {
484
 
                node: '.volume',
485
 
                points: [Y.WidgetPositionAlign.BC, Y.WidgetPositionAlign.TC]
486
 
            });
487
 
            Y.one('body').on(CLICK, function(e) {
488
 
                e.halt();
489
 
                if (!slider.get(BOUNDINGBOX).contains(e.target)) {
490
 
                    slider.hide();
491
 
                    e.currentTarget.detach();
492
 
                }
493
 
            });
494
 
        }
495
 
    },
496
 
    onVolumeChange: function(e) {
497
 
        /* The slider is actually reversed, so going up -> 0,
498
 
         * going down -> 100;
499
 
         */
500
 
                        var value = 100 - e.newVal,
501
 
                        volumeControl = this.get(MUTECONTROL),
502
 
                        song = Y.Music.lastSong ;
503
 
 
504
 
                        if (song) {
505
 
                            song.setVolume(value);
506
 
                        }
507
 
                        if (value >= 75) {
508
 
                            volumeControl.removeClass(ENABLED);
509
 
                            volumeControl.removeClass(LOW);
510
 
                            volumeControl.removeClass(MED);
511
 
                        } else if (value === 0) {
512
 
                            volumeControl.removeClass(MED);
513
 
                            volumeControl.removeClass(LOW);
514
 
                            volumeControl.addClass(ENABLED);
515
 
                        } else if (value <= 25) {
516
 
                            volumeControl.removeClass(ENABLED);
517
 
                            volumeControl.removeClass(MED);
518
 
                            volumeControl.addClass(LOW);
519
 
                        } else {
520
 
                            volumeControl.removeClass(ENABLED);
521
 
                            volumeControl.removeClass(LOW);
522
 
                            volumeControl.addClass(MED);
523
 
                        }
524
 
                    },
525
 
                    onRemoveFromNowPlaying: function(e){
526
 
 
527
 
                            var songIds = e.songIds,
528
 
                            length = songIds.length,
529
 
                            quantifier = (length === 1) ? SONG : SONGS,
530
 
                            playlist = this.get(PLAYLIST),
531
 
                            playlistLength = playlist.length,
532
 
                            deletedCurrent = false,
533
 
                            index = this.get(INDEX),
534
 
                            song = null,
535
 
                            message,
536
 
                            wasPlaying = false,
537
 
                            i, k;
538
 
 
539
 
                            if (length === playlistLength) {
540
 
                                Y.Global.fire("music:clearNowPlaying");
541
 
                                return;
542
 
                            }
543
 
 
544
 
                            message = EMPTY;
545
 
                            if (length === 0) {
546
 
                                /* IE is apparently weird about array length of EventFacade
547
 
                                 * items.
548
 
                                 */
549
 
        message = 'Songs removed from queue';
550
 
    } else {
551
 
        message = length + ' ' + quantifier + ' removed from the queue.';
552
 
    }
553
 
 
554
 
    for (i = 0; i < playlistLength; i++) {
555
 
        for (k = 0; k < length; k++) {
556
 
            if (playlist[i].id === songIds[k]) {
557
 
                if (i === this.get(INDEX)){
558
 
                    deletedCurrent = true;
559
 
                }
560
 
                playlist.splice(i, 1);
561
 
                i--;
562
 
                playlistLength = playlist.length;
563
 
 
564
 
                songIds.splice(k, 1);
565
 
                k--;
566
 
                length = songIds.length;
567
 
                break;
568
 
            }
569
 
        }
570
 
    }
571
 
 
572
 
    this.set(PLAYLIST, playlist);
573
 
 
574
 
    if (deletedCurrent === true) {
575
 
        song = Y.Music.lastSong;
576
 
        if (!song.paused) {
577
 
            wasPlaying = true;
578
 
        }
579
 
        Y.Global.fire(EVENTSTOP);
580
 
    }
581
 
 
582
 
    if (index === playlistLength) {
583
 
        this.set(INDEX, index - 1);
584
 
    } else {
585
 
        this.set(INDEX, index);
586
 
    }
587
 
 
588
 
    if (wasPlaying){
589
 
        Y.Global.fire(EVENTPLAY);
590
 
    }
591
 
 
592
 
    Y.Global.fire('music:nowPlayingButtonsUpdated');
593
 
    Y.Global.fire('one:info', {'message': message});
594
 
},
595
 
onAddSongsToPlaylist: function(e) {
596
 
    var songs = e.songs,
597
 
    length = songs.length,
598
 
    quantifier = (length === 1) ? SONG : SONGS,
599
 
    injectSong = e.injectSong || false,
600
 
    playNow = e.playNow || false,
601
 
    playlist = this.get(PLAYLIST),
602
 
    playlistLength = playlist.length,
603
 
    currentIndex,
604
 
    i, message;
605
 
 
606
 
    currentIndex = this.get(INDEX);
607
 
    if (injectSong && playlistLength > 0) {
608
 
        for (i = length-1; i >= 0; i--) {
609
 
            playlist.splice(currentIndex+1, 0, songs[i]);
610
 
        }
611
 
        this.set(PLAYLIST, playlist);
612
 
        this.set(INDEX, currentIndex + 1);
613
 
    } else {
614
 
        this.set(PLAYLIST, playlist.concat(songs));
615
 
    }
616
 
    message = EMPTY;
617
 
    if (length === 0) {
618
 
        /* IE is apparently weird about array length of EventFacade
619
 
         * items.
620
 
         */
621
 
        message = 'Songs added to queue';
622
 
    } else {
623
 
        message = length + ' ' + quantifier + ' added to the queue.';
624
 
    }
625
 
    if (playNow === true) {
626
 
        Y.Global.fire(EVENTPLAY);
627
 
    }
628
 
    Y.Global.fire('one:info', {'message': message});
629
 
},
630
 
 
631
 
setPlaylist: function(e) {
632
 
    Y.Global.fire(EVENTSTOP);
633
 
    this.set(INDEX, 0);
634
 
    this.set(PLAYLIST, e.playlist);
635
 
},
636
 
 
637
 
afterViewChange: function(e) {
638
 
    if (e.newVal === NOWPLAYING) {
639
 
        Y.Global.fire('music:nowPlaying', {
640
 
            songs: this.get(PLAYLIST) });
641
 
    }
642
 
},
643
 
 
644
 
afterPlay: function() {
645
 
               var index = this.get(INDEX),
646
 
               playlist = this.get(PLAYLIST),
647
 
               current = playlist[index],
648
 
               length = playlist.length,
649
 
               albumArtImage = Y.one(".albumart img"),
650
 
               albumArtImageGenerated = false,
651
 
               albumArtContainer = null,
652
 
               mp,
653
 
               trackInfo;
654
 
 
655
 
               // Generate the image elem if it doesn't already
656
 
               // exist.
657
 
               if (!albumArtImage) {
658
 
                   albumArtImage = Y.Node.create("<img>");
659
 
                   albumArtImageGenerated = true;
660
 
               }
661
 
               if (current === undefined) {
662
 
                   return;
663
 
               }
664
 
               Y.all(".player .hidden").removeClass(HIDDEN);
665
 
               Y.one(".player .start-here").addClass(HIDDEN);
666
 
               this.get('album').setContent(Y.Escape.html(current.album));
667
 
               this.get('artist').setContent(Y.Escape.html(current.artist));
668
 
               this.get('title').setContent(Y.Escape.html(current.title));
669
 
               this.setPlaylistContext();
670
 
               this.get(DATASOURCE).getAlbumArt(current, albumArtImage);
671
 
 
672
 
               if (albumArtImageGenerated === true) {
673
 
                   albumArtContainer = Y.one(".albumart");
674
 
                   if (albumArtContainer) {
675
 
                       albumArtContainer.appendChild(albumArtImage);
676
 
                   }
677
 
               }
678
 
 
679
 
               if (Y.Unity.ready) {
680
 
                   mp = Y.Unity.Unity.MediaPlayer;
681
 
                   trackInfo = {
682
 
                       title: current.title,
683
 
                       artist: current.artist,
684
 
                       album: current.album,
685
 
                       artLocation: albumArtImage.get('src')
686
 
                   };
687
 
                   mp.setTrack(trackInfo);
688
 
                   // And show a notification
689
 
                   Y.Unity.Unity.Notification.showNotification(
690
 
                       trackInfo.title,
691
 
                       "by " + trackInfo.artist + " from " + trackInfo.album,
692
 
                       trackInfo.artLocation);
693
 
               }
694
 
 
695
 
               this.get('header').setContent(
696
 
                   'Now playing ' + (index + 1) + ' of ' + length + ' songs');
697
 
},
698
 
setPlaylistContext: function() {
699
 
    var index = this.get(INDEX),
700
 
    repeat = this.get(REPEAT),
701
 
    shuffle = this.get(SHUFFLE),
702
 
    playlist = this.get(PLAYLIST),
703
 
    previous = playlist[index - 1],
704
 
    previousText = previous ? 'Previous: ' + Y.Escape.html(previous.title) + ' - ' + Y.Escape.html(previous.artist) : EMPTY,
705
 
    next = playlist[index + 1],
706
 
    nextText = next ? 'Next: ' + Y.Escape.html(next.title) + ' - ' + Y.Escape.html(next.artist): EMPTY,
707
 
    previousNode,
708
 
    nextNode;
709
 
 
710
 
    if (shuffle === false && repeat === false) {
711
 
        previousNode = this.get('previous');
712
 
        if (previous) {
713
 
            previousNode.setContent(previousText);
714
 
            previousNode.setStyle('display', 'block');
715
 
        } else {
716
 
            previousNode.setStyle('display', 'none');
717
 
        }
718
 
        nextNode = this.get('next');
719
 
        if (next) {
720
 
            nextNode.setContent(nextText);
721
 
            nextNode.setStyle('display', 'block');
722
 
        } else {
723
 
            nextNode.setStyle('display', 'none');
724
 
        }
725
 
    } else {
726
 
        this.get('previous').setContent(null);
727
 
        this.get('next').setContent(null);
728
 
    }
729
 
},
730
 
afterIndexChange: function(e) {
731
 
    var index = e.newVal,
732
 
    playlist = this.get(PLAYLIST),
733
 
    nextButton = this.get(NEXTBUTTON),
734
 
    prevButton = this.get(PREVBUTTON),
735
 
    mp;
736
 
 
737
 
    if (index === 0) {
738
 
        if (!prevButton.hasClass(DISABLED)) {
739
 
            prevButton.addClass(DISABLED);
740
 
            prevButton.set(DISABLED, DISABLED);
741
 
        }
742
 
    } else {
743
 
        prevButton.removeClass(DISABLED);
744
 
        prevButton.removeAttribute(DISABLED);
745
 
    }
746
 
    if (index + 1 >= playlist.length) {
747
 
        if (!nextButton.hasClass(DISABLED)) {
748
 
            nextButton.addClass(DISABLED);
749
 
            nextButton.set(DISABLED, DISABLED);
750
 
        }
751
 
    } else {
752
 
        nextButton.removeClass(DISABLED);
753
 
        nextButton.removeAttribute(DISABLED);
754
 
    }
755
 
    if (Y.Unity.ready) {
756
 
        mp = Y.Unity.Unity.MediaPlayer;
757
 
        mp.setCanGoPrevious(index !== 0);
758
 
        mp.setCanGoNext(index + 1 < playlist.length);
759
 
    }
760
 
 
761
 
    this.get("bytesLoadedBar").setStyle(WIDTH, 0);
762
 
    Y.Global.fire(EVENTPLAY);
763
 
},
764
 
onPause: function() {
765
 
    Y.Music.lastSong.pause();
766
 
    this.get(PLAYBUTTON).show();
767
 
    this.get(STOPBUTTON).hide();
768
 
    if (Y.Unity.ready) {
769
 
        var mp = Y.Unity.Unity.MediaPlayer;
770
 
        mp.setPlaybackState(mp.PlaybackState.PAUSED);
771
 
    }
772
 
},
773
 
onStop: function() {
774
 
    var lastSong = Y.Music.lastSong,
775
 
    mp;
776
 
 
777
 
    this.get("bytesLoadedBar").setStyle(WIDTH, 0);
778
 
    if (lastSong) {
779
 
        lastSong.destruct();
780
 
        Y.Music.lastSong = null;
781
 
    }
782
 
    this.get(PLAYBUTTON).show();
783
 
    this.get(STOPBUTTON).hide();
784
 
    this.get('progressBar').setStyle(WIDTH, ZERO);
785
 
    if (Y.Unity.ready) {
786
 
        mp = Y.Unity.Unity.MediaPlayer;
787
 
        mp.setPlaybackState(mp.PlaybackState.PAUSED);
788
 
    }
789
 
},
790
 
 
791
 
onPlay: function() {
792
 
    var index = this.get(INDEX),
793
 
    playlist = this.get(PLAYLIST),
794
 
    songObj = playlist[index],
795
 
    newSong = soundManager.getSoundById(songObj.id),
796
 
    lastSong = Y.Music.lastSong,
797
 
    songURI = this.get(DATASOURCE).getStreamingURI(songObj),
798
 
    mute = this.get('mute'),
799
 
    volume = 100 - this.get(SLIDER).getValue(),
800
 
    nAPIDurationMilSecs,
801
 
    soundData,
802
 
    mp;
803
 
 
804
 
    if (playlist.length === 0) {
805
 
        Y.Global.fire('music:emptyPlaylist');
806
 
        Y.Music.lastSong = null;
807
 
        return;
808
 
    }
809
 
 
810
 
    if (songURI === null) {
811
 
        return;
812
 
    }
813
 
 
814
 
    if (newSong) {
815
 
        if (newSong && lastSong && newSong === lastSong) {
816
 
            // If readyState not failed/error
817
 
            if (newSong.readyState !== 2) {
818
 
                if (!newSong.paused) {
819
 
                    lastSong.stop();
820
 
                }
821
 
                newSong.play({ 'volume': volume });
822
 
            }
823
 
        } else {
824
 
            // Stop the previous song
825
 
            if (lastSong) {
826
 
                lastSong.stop();
827
 
            }
828
 
            // Start the new one
829
 
            newSong.play();
830
 
        }
831
 
    } else {
832
 
        this.get("bytesLoadedBar").setStyle(WIDTH, 0);
833
 
        if (lastSong) {
834
 
            lastSong.stop();
835
 
        }
836
 
 
837
 
        soundData = {
838
 
            id: songObj.id,
839
 
            url: songURI,
840
 
            onfinish: function() {
841
 
                Y.Global.fire(EVSOUNDFINISH);
842
 
            },
843
 
            onpause: function() {
844
 
                Y.Global.fire(EVSOUNDPAUSE);
845
 
            },
846
 
            onplay: function() {
847
 
                Y.Global.fire(EVSOUNDPLAY);
848
 
            },
849
 
            onstop: function() {
850
 
                Y.Global.fire(EVSOUNDSTOP);
851
 
            },
852
 
            whileplaying: function() {
853
 
                Y.Global.fire(EVSOUNDPLAYING);
854
 
            },
855
 
            whileloading: function() {
856
 
                Y.Global.fire(EVSOUNDLOADING);
857
 
            },
858
 
            usePeakData: true,
859
 
            multiShot: false,
860
 
            isMovieStar: MOVIESTARRX.test(songObj.suffix)
861
 
        };
862
 
 
863
 
        // Inform SM2 about ogg fixes LP: 1002481, 921841
864
 
        if (songObj.suffix === "ogg") {
865
 
            soundData.type = "audio/ogg";
866
 
        }
867
 
 
868
 
        newSong = soundManager.createSound(soundData);
869
 
        newSong.setVolume(volume);
870
 
        newSong.onposition(1000, function() {
871
 
            Y.fire('loaded');
872
 
        });
873
 
        if (mute) {
874
 
            newSong.mute();
875
 
        }
876
 
        newSong.play();
877
 
        newSong.whilePlaying = function() {
878
 
            Y.Global.fire(EVSOUNDPLAYING);
879
 
        };
880
 
        Y.Global.fire(EVENTSTARTED, {song: songObj});
881
 
    }
882
 
 
883
 
    this.get(STOPBUTTON).show();
884
 
    this.get(PLAYBUTTON).hide();
885
 
 
886
 
    nAPIDurationMilSecs = songObj.duration;
887
 
    newSong.nAPIDurationMilSecs = (nAPIDurationMilSecs) ? nAPIDurationMilSecs * 1000 : 0;
888
 
    Y.Music.lastSong = newSong;
889
 
    Y.Global.fire(MUSICUPDATESELECTED, { song: newSong });
890
 
 
891
 
    if (Y.Unity.ready) {
892
 
        mp = Y.Unity.Unity.MediaPlayer;
893
 
        mp.setPlaybackState(mp.PlaybackState.PLAYING);
894
 
    }
895
 
},
896
 
onPrevious: function() {
897
 
    var song = Y.Music.lastSong,
898
 
    index;
899
 
 
900
 
    if (song.position > 5000 && !song.paused) {
901
 
        Y.Global.fire(EVENTSTOP);
902
 
        Y.Global.fire(EVENTPLAY);
903
 
    } else {
904
 
        index = this.get(INDEX);
905
 
        if (index <= 0) { return; }
906
 
        index--;
907
 
        this.set(INDEX, index);
908
 
    }
909
 
},
910
 
onNext: function() {
911
 
    var index = this.get(INDEX),
912
 
    newIndex,
913
 
    shuffle = this.get(SHUFFLE),
914
 
    length = this.get(PLAYLIST).length;
915
 
    if (shuffle === true) {
916
 
        if (length <= 1) { return; }
917
 
        newIndex = null;
918
 
        do {
919
 
            newIndex = Math.floor(Math.random() * length);
920
 
        } while (index === newIndex);
921
 
        index = newIndex;
922
 
    } else if (index+1 >= length) {
923
 
        return;
924
 
    } else {
925
 
        index++;
926
 
    }
927
 
    this.set(INDEX, index);
928
 
},
929
 
continuePlaylist: function() {
930
 
    Y.Global.fire(EVENTSTOP);
931
 
    var index = this.get(INDEX),
932
 
    repeat = this.get(REPEAT),
933
 
    length = this.get(PLAYLIST).length;
934
 
    if (repeat) {
935
 
        Y.Global.fire(EVENTPLAY);
936
 
    } else {
937
 
        Y.Global.fire(EVENTNEXT);
938
 
        if (index+1 < length) {
939
 
            Y.Global.fire(EVENTPLAY);
940
 
        }
941
 
    }
942
 
},
943
 
getDuration: function() {
944
 
    var song = Y.Music.lastSong,
945
 
    duration = song.nAPIDurationMilSecs,
946
 
    songDuration = song.duration,
947
 
    songDurationEstimate = song.durationEstimate,
948
 
    songLoaded = song.loaded;
949
 
 
950
 
    if (!duration && !songLoaded && songDurationEstimate) {
951
 
        duration = songDurationEstimate;
952
 
    }
953
 
 
954
 
    if (songLoaded && songDuration) {
955
 
        duration = songDuration;
956
 
    }
957
 
 
958
 
    return duration;
959
 
},
960
 
whilePlaying: function() {
961
 
    var song = Y.Music.lastSong,
962
 
    progressBar = this.get('progressBar'),
963
 
    duration = this.getDuration(),
964
 
    width = Math.floor((100 * song.position) / duration) + 1;
965
 
 
966
 
    progressBar.setStyle(WIDTH, width + '%');
967
 
    if (soundManager.flashVersion >= 9 && song.instanceOptions.usePeakData) {
968
 
        this.updatePeakData();
969
 
    }
970
 
    this.updateTime();
971
 
},
972
 
whileLoading: function() {
973
 
    var song = Y.Music.lastSong,
974
 
    bytesLoadedBar = this.get('bytesLoadedBar'),
975
 
    maxWidth = Math.ceil((song.bytesLoaded/song.bytesTotal)*100),
976
 
    width = maxWidth > 100 ? 100 : maxWidth;
977
 
    bytesLoadedBar.setStyle(WIDTH, width + '%');
978
 
},
979
 
emitNext: function(e) {
980
 
    e.halt();
981
 
    if (e.target.hasClass(DISABLED)) { return; }
982
 
    Y.Global.fire(EVENTNEXT);
983
 
},
984
 
emitPlay: function(e) {
985
 
    e.halt();
986
 
    if (e.target.hasClass(DISABLED)) { return; }
987
 
    Y.Global.fire(EVENTPLAY);
988
 
},
989
 
emitPrevious: function(e) {
990
 
    e.halt();
991
 
    if (e.target.hasClass(DISABLED)) { return; }
992
 
    Y.Global.fire(EVENTPREVIOUS);
993
 
},
994
 
emitStop: function(e) { e.halt(); Y.Global.fire(EVENTSTOP); },
995
 
emitPause: function(e) { e.halt(); Y.Global.fire(EVENTPAUSE); },
996
 
bindUnity: function() {
997
 
    Y.log("Configuring Unity integration");
998
 
    var mp = Y.Unity.Unity.MediaPlayer;
999
 
    mp.init("Ubuntu One Music");
1000
 
    mp.setCanPlay(true);
1001
 
    mp.setCanPause(true);
1002
 
    mp.setCanGoNext(true);
1003
 
    mp.setCanGoPrevious(true);
1004
 
 
1005
 
    mp.onPlayPause(Y.bind(function() {
1006
 
        Y.log("Sound Menu: play/pause");
1007
 
        if (this.get(PLAYBUTTON).getStyle('display') !== "none") {
1008
 
            Y.Global.fire(EVENTPLAY);
1009
 
        } else {
1010
 
            Y.Global.fire(EVENTPAUSE);
1011
 
        }
1012
 
    }, this));
1013
 
    mp.onNext(function() {
1014
 
        Y.log("Sound Menu: next track");
1015
 
        Y.Global.fire(EVENTNEXT);
1016
 
    });
1017
 
    mp.onPrevious(function() {
1018
 
        Y.log("Sound Menu: previous track");
1019
 
        Y.Global.fire(EVENTPREVIOUS);
1020
 
    });
1021
 
}
1022
 
}, {
1023
 
    ATTRS: {
1024
 
        CONTENT_TEMPLATE: { value: null },
1025
 
        album: { value: null },
1026
 
        albumArt: { value: null },
1027
 
        artist: { value: null },
1028
 
        datasource: { value: null },
1029
 
        header: { value: null },
1030
 
        history: { value: null },
1031
 
        next: { value: null },
1032
 
        previous: { value: null },
1033
 
        title: { value: null },
1034
 
        mute: { value: false },
1035
 
        shuffle: { value: false },
1036
 
        repeat: { value: false },
1037
 
        muteControl: { value: null },
1038
 
        volumeControl: { value: null },
1039
 
        repeatControl: { value: null },
1040
 
        shuffleControl: { value: null },
1041
 
        index: { value: 0 },
1042
 
        nextButton: { value: null },
1043
 
        vuMeter: { value: Y.Node.create(Y.one("#tpl-vu-meter").getContent()) },
1044
 
        playlist: { value: [] },
1045
 
        playButton: { value: null },
1046
 
        prevButton: { value: null },
1047
 
        progressBar: { value: null },
1048
 
        progressBarCont: { value: null },
1049
 
        progBarLeftArrCount: { value: 0 },
1050
 
        progBarRightArrCount: { value: 0 },
1051
 
        bytesLoadedBar: { value: null },
1052
 
        stopButton: { value: null },
1053
 
        timeDisplay: { value: null },
1054
 
        volume: { value: null },
1055
 
        volumeIndicator: { value: null },
1056
 
        volumeNode: { value: null }
1057
 
    },
1058
 
    HTML_PARSER: {
1059
 
        album: '.album',
1060
 
        artist: '.artist',
1061
 
        header: '.header',
1062
 
        next: '.next-song',
1063
 
        previous: '.previous-song',
1064
 
        title: '.title',
1065
 
 
1066
 
        muteControl: '#mute',
1067
 
        volumeControl: '#volume',
1068
 
        repeatControl: '#repeat',
1069
 
        shuffleControl: '#shuffle',
1070
 
        nextButton: '#next',
1071
 
        playButton: '#play',
1072
 
        prevButton: '#prev',
1073
 
        progressBar: '.progress',
1074
 
        progressBarCont: '.progress-bar',
1075
 
        bytesLoadedBar: '.bytes-loaded',
1076
 
        stopButton: '#stop',
1077
 
        timeDisplay: '.time-display span',
1078
 
        volumeIndicator: '.volume-indicator',
1079
 
        volumeNode: '.volume-slider'
1080
 
    }
1081
 
});
1082
 
 
1083
 
namespace.AlbumListView = Y.Base.create('albumView', Y.Widget, [ToggleView], {
1084
 
    initializer: function() {
1085
 
        this.get(HISTORY).after(VIEWCHANGE, this.afterViewChange, this);
1086
 
        this.render();
1087
 
    },
1088
 
    bindUI: function() {
1089
 
        var boundingBox = this.get('inner'),
1090
 
        events = this.get('events');
1091
 
        events.push(boundingBox.delegate(CLICK, this.onAlbumClick, '.album-listing', this));
1092
 
        events.push(boundingBox.delegate(CLICK, this.onPlayAllClick, '.album-listing button', this));
1093
 
 
1094
 
        events.push(this.before(VISIBLECHANGE, this.beforeVisibleChange, this));
1095
 
        events.push(this.after('albumsChange', this.afterAlbumsChange, this));
1096
 
    },
1097
 
    syncUI: function() {
1098
 
        var view = this.get(HISTORY).get(VIEW);
1099
 
        if (view === ALBUMS) {
1100
 
            this.show();
1101
 
        } else {
1102
 
            this.get('inner').all('.album-listing').remove();
1103
 
            this.hide();
1104
 
        }
1105
 
    },
1106
 
    onAlbumClick: function(e) {
1107
 
        e.preventDefault();
1108
 
        if (e.target.get('nodeName').toLowerCase() === 'button' || e.target.ancestor("button") !== null) {
1109
 
            return;
1110
 
        }
1111
 
        var target = e.currentTarget,
1112
 
        albumId = target.one('a').getAttribute('data-u1m-albumid'),
1113
 
        history = this.get(HISTORY);
1114
 
        history.add(Y.mix({
1115
 
            album: albumId,
1116
 
            view: SONGS
1117
 
        }, HISTORYRESET));
1118
 
    },
1119
 
    onPlayAllClick: function(e) {
1120
 
        var albumId = e.target.ancestor(".album-listing").one('a').getAttribute('data-u1m-albumid'),
1121
 
        datasource = this.get(DATASOURCE);
1122
 
        datasource.getAlbum(albumId, function(e) {
1123
 
            if (e.response.json.directory && e.response.json.directory.child) {
1124
 
                var songs = e.response.json.directory.child;
1125
 
                Y.Global.fire(MUSICADDSONGSTOPLAYLIST, {
1126
 
                    songs: songs,
1127
 
                    'injectSong': true,
1128
 
                    'playNow': true
1129
 
                });
1130
 
            }
1131
 
        });
1132
 
    },
1133
 
    afterAlbumsChange: function() {
1134
 
        var albums = this.get(ALBUMS),
1135
 
        album,
1136
 
        count = albums.length,
1137
 
        box = this.get('inner'),
1138
 
        imageGroup = new Y.U1.ImgLoadGroup({
1139
 
            foldDistance: 30,
1140
 
            foldCheckDelay: 500
1141
 
        }),
1142
 
        escapeHtml = function(k,v){
1143
 
            return Y.Escape.html(v);
1144
 
        },
1145
 
        i,
1146
 
        bbViews,
1147
 
        album_art,
1148
 
        albumNode,
1149
 
        temp_vars,
1150
 
        img_id,
1151
 
        placeholder,
1152
 
        link;
1153
 
 
1154
 
        this.set("imageGroup", imageGroup);
1155
 
 
1156
 
        box.setContent(EMPTY);
1157
 
        for (i=0; i<count; i++) {
1158
 
            album = albums[i],
1159
 
            album_art = '/api/music/v2/albums/' + album.u1CoverId + '/art/',
1160
 
            img_id = Y.guid(),
1161
 
            albumNode = Y.Node.create(Y.substitute(Y.one("#tpl-album-listing").getContent(), album, escapeHtml)),
1162
 
            temp_vars = { "img_id": img_id, "default_art_url": DEFAULT_ALBUM_ART },
1163
 
            placeholder = Y.Node.create(Y.substitute(Y.one("#tpl-art").getContent(), temp_vars, escapeHtml)),
1164
 
            link = albumNode.one('[data-u1m-albumid="'+album.id+'"]');
1165
 
 
1166
 
            if (album.has_cover_art === true) {
1167
 
                imageGroup.registerImage({
1168
 
                    domId: img_id,
1169
 
                    srcUrl: album_art
1170
 
                });
1171
 
            }
1172
 
 
1173
 
            albumNode.insertBefore(placeholder, link);
1174
 
            box.appendChild(albumNode);
1175
 
        }
1176
 
        // This forces a redraw in IE8.
1177
 
        if (Y.UA.ie === 8) {
1178
 
            bbViews = Y.one(".views");
1179
 
            if (bbViews) {
1180
 
                bbViews.toggleClass("redraw");
1181
 
            }
1182
 
        }
1183
 
 
1184
 
    },
1185
 
    beforeVisibleChange: function(e) {
1186
 
        if (e.newVal === true) {
1187
 
            var artist = this.get(HISTORY).get('artist');
1188
 
            this.get(DATASOURCE).getAlbums(artist, Y.bind(this.onIOSuccess, this));
1189
 
        }
1190
 
    },
1191
 
    onIOSuccess: function(e) {
1192
 
        var json = e.response.json,
1193
 
        albums = [],
1194
 
        albumList,
1195
 
        albumListLength,
1196
 
        listLength,
1197
 
        i, j,
1198
 
        indexes,
1199
 
        indexesLength;
1200
 
 
1201
 
        if (json.indexes) {
1202
 
            this.get('header').setContent('All albums');
1203
 
            indexes = json.indexes.index,
1204
 
            indexesLength = indexes.length;
1205
 
            for (i=0; i<indexesLength; i++) {
1206
 
                albumList = indexes[i].album;
1207
 
                listLength = albumList.length;
1208
 
                for (j=0; j<listLength; j++) {
1209
 
                    albums.push(albumList[j]);
1210
 
                }
1211
 
            }
1212
 
        } else {
1213
 
            if (json.directory && json.directory.child) {
1214
 
                albumList = json.directory.child;
1215
 
                albumListLength = albumList.length;
1216
 
                this.get('header').setContent(Y.Escape.html(albumList[0].artist) + ' albums');
1217
 
                for (i=0; i<albumListLength; i++) {
1218
 
                    albums.push(albumList[i]);
1219
 
                }
1220
 
            }
1221
 
        }
1222
 
        this.set(ALBUMS, albums);
1223
 
    },
1224
 
    afterViewChange: function(e) {
1225
 
        if (e.newVal === ALBUMS) {
1226
 
            this.show();
1227
 
        } else {
1228
 
            var imageGroup = this.get("imageGroup");
1229
 
            if (imageGroup) {
1230
 
                imageGroup.destroy();
1231
 
            }
1232
 
            this.get('inner').all('.album-listing').remove();
1233
 
            this.hide();
1234
 
        }
1235
 
    }
1236
 
}, {
1237
 
    ATTRS: {
1238
 
        CONTENT_TEMPLATE: { value: null },
1239
 
        albums: { value: [] },
1240
 
        datasource: { value: null },
1241
 
        history: { value: null },
1242
 
        header: { value: null },
1243
 
        inner: { value: null },
1244
 
        visible: { value: false },
1245
 
        imageGroup: { value: false }
1246
 
    },
1247
 
    HTML_PARSER: {
1248
 
        header: '.view-header',
1249
 
        inner: '.view-inner'
1250
 
    }
1251
 
});
1252
 
 
1253
 
namespace.ArtistListView = Y.Base.create('artistsView', Y.Widget, [ToggleView], {
1254
 
    initializer: function() {
1255
 
        this.get(HISTORY).on(VIEWCHANGE, this.onViewChange, this);
1256
 
        this.render();
1257
 
    },
1258
 
    bindUI: function() {
1259
 
        var events = this.get('events');
1260
 
 
1261
 
        events.push(this.get('inner').delegate(CLICK, this.onArtistClick, '.artist-listing', this));
1262
 
        events.push(this.before(VISIBLECHANGE, this.beforeVisibleChange, this));
1263
 
        events.push(this.after('artistsChange', this.afterArtistsChange, this));
1264
 
    },
1265
 
    syncUI: function() {
1266
 
        var view = this.get(HISTORY).get(VIEW);
1267
 
        if (view === ARTISTS) {
1268
 
            this.show();
1269
 
        } else {
1270
 
            this.get('inner').all('.artist-listing').remove();
1271
 
            this.hide();
1272
 
        }
1273
 
    },
1274
 
    beforeVisibleChange: function(e) {
1275
 
        if (e.newVal === true) {
1276
 
            this.get(DATASOURCE).getArtists(Y.bind(this.onIOSuccess, this));
1277
 
        }
1278
 
    },
1279
 
    afterArtistsChange: function() {
1280
 
        var artists = this.get(ARTISTS),
1281
 
        artist,
1282
 
        count = artists.length,
1283
 
        box = this.get('inner'),
1284
 
        imageGroup = new Y.U1.ImgLoadGroup({
1285
 
            foldDistance: 30,
1286
 
            foldCheckDelay: 500
1287
 
        }),
1288
 
        escapeHtml = function(k,v){
1289
 
            return Y.Escape.html(v);
1290
 
        },
1291
 
        i,
1292
 
        bbViews,
1293
 
        artistNode,
1294
 
        temp_vars,
1295
 
        img_id,
1296
 
        placeholder,
1297
 
        link,
1298
 
        artist_art;
1299
 
 
1300
 
        this.set("imageGroup", imageGroup);
1301
 
 
1302
 
        box.setContent(EMPTY);
1303
 
        for (i=0; i<count; i++) {
1304
 
            artist = artists[i],
1305
 
            artist_art = '/api/music/v2/artists/' + artist.u1CoverId + '/art/',
1306
 
            img_id = Y.guid(),
1307
 
            artistNode = Y.Node.create(Y.substitute(Y.one("#tpl-artist-listing").getContent(), artist, escapeHtml)),
1308
 
            temp_vars = { "img_id": img_id, "default_art_url": DEFAULT_ALBUM_ART },
1309
 
            placeholder = Y.Node.create(Y.substitute(Y.one("#tpl-art-artist").getContent(), temp_vars, escapeHtml)),
1310
 
            link = artistNode.one('[data-u1m-artistid="'+artist.id+'"]');
1311
 
 
1312
 
            if (artist.has_cover_art === true) {
1313
 
                imageGroup.registerImage({
1314
 
                    domId: img_id,
1315
 
                    srcUrl: artist_art
1316
 
                });
1317
 
            }
1318
 
 
1319
 
            artistNode.insertBefore(placeholder, link);
1320
 
            box.appendChild(artistNode);
1321
 
        }
1322
 
        // This forces a redraw in IE8.
1323
 
        if (Y.UA.ie === 8) {
1324
 
            bbViews = Y.one(".views");
1325
 
            if (bbViews) {
1326
 
                bbViews.toggleClass("redraw");
1327
 
            }
1328
 
        }
1329
 
    },
1330
 
    onIOSuccess: function(e) {
1331
 
 
1332
 
        var indexes,
1333
 
            indexesLength,
1334
 
            artists = [],
1335
 
            artistList,
1336
 
            listLength,
1337
 
            i, j;
1338
 
 
1339
 
        if (e.response.json && e.response.json.indexes) {
1340
 
            indexes = e.response.json.indexes.index;
1341
 
            indexesLength = indexes.length;
1342
 
        }
1343
 
 
1344
 
        for (i=0; i<indexesLength; i++) {
1345
 
            artistList = indexes[i].artist,
1346
 
            listLength = artistList.length;
1347
 
            for (j=0; j<listLength; j++) {
1348
 
                artists.push(artistList[j]);
1349
 
            }
1350
 
        }
1351
 
        this.set(ARTISTS, artists);
1352
 
    },
1353
 
    onViewChange: function(e) {
1354
 
        var newView = e.newVal,
1355
 
            imageGroup;
1356
 
 
1357
 
        if (newView === ARTISTS) {
1358
 
            this.show();
1359
 
        } else {
1360
 
            imageGroup = this.get("imageGroup");
1361
 
            if (imageGroup) {
1362
 
                imageGroup.destroy();
1363
 
            }
1364
 
            this.get(BOUNDINGBOX).all('.artist-listing').remove();
1365
 
            this.hide();
1366
 
        }
1367
 
    },
1368
 
    onShowArtists: function(e) {
1369
 
        var artists = e.artists,
1370
 
        count = artists.length,
1371
 
        box = this.get(BOUNDINGBOX),
1372
 
        escapeHtml = function(k,v){
1373
 
            return Y.Escape.html(v);
1374
 
        },
1375
 
        i;
1376
 
 
1377
 
        for (i=0; i<count; i++) {
1378
 
            box.appendChild(Y.Node.create(
1379
 
                Y.substitute(Y.one("#tpl-artist-listing").getContent(), artists[i], escapeHtml)));
1380
 
        }
1381
 
    },
1382
 
    onArtistClick: function(e) {
1383
 
        e.preventDefault();
1384
 
        var target = e.currentTarget,
1385
 
        artistId = target.one('a').getAttribute('data-u1m-artistid'),
1386
 
        history = this.get(HISTORY);
1387
 
        history.add(Y.mix({
1388
 
            artist: artistId,
1389
 
            view: ALBUMS
1390
 
        }, HISTORYRESET));
1391
 
    }
1392
 
}, {
1393
 
    ATTRS: {
1394
 
        CONTENT_TEMPLATE: { value: null },
1395
 
        artists: { value: [] },
1396
 
        datasource: { value: null },
1397
 
        history: { value: null },
1398
 
        header: { value: null },
1399
 
        inner: { value: null },
1400
 
        imageGroup: { value: null }
1401
 
    },
1402
 
    HTML_PARSER: {
1403
 
        header: '.view-header',
1404
 
        inner: '.view-inner'
1405
 
    }
1406
 
});
1407
 
 
1408
 
namespace.MenuView = Y.Base.create('menu', Y.Widget, [], {
1409
 
    renderUI: function() {
1410
 
        this.get('playlistsds').getPlaylists(Y.bind(this.onIOSuccess, this));
1411
 
    },
1412
 
    bindUI: function() {
1413
 
        this.get('searchButton').on(CLICK, this.onSearchClick, this);
1414
 
        this.get('searchField').on('keypress', this.onSearchClick, this);
1415
 
        this.get('albumsMenuItem').on(CLICK, this.onAlbumsClick, this);
1416
 
        this.get('artistsMenuItem').on(CLICK, this.onArtistsClick, this);
1417
 
        this.get('songsMenuItem').on(CLICK, this.onSongsClick, this);
1418
 
 
1419
 
        var history = this.get(HISTORY),
1420
 
        playlists;
1421
 
 
1422
 
        history.on(VIEWCHANGE, this.syncUI, this);
1423
 
        history.on(PLAYLISTCHANGE, this.syncUI, this);
1424
 
 
1425
 
        playlists = this.get(PLAYLISTS);
1426
 
        playlists.delegate(CLICK, this.onPlaylistClick, '.playlist-listing', this);
1427
 
        this.get('nowPlaying').on(CLICK, this.onNowPlayingClick, this);
1428
 
 
1429
 
        Y.Global.on('music:playlistsStale', this.renderUI, this);
1430
 
    },
1431
 
    syncUI: function() {
1432
 
        Y.all(".u1-sidebar-menu .active").removeClass("active");
1433
 
        var view = this.get(HISTORY).get(VIEW),
1434
 
            playlist,
1435
 
            playlistNode,
1436
 
            menuItem;
1437
 
 
1438
 
        switch (view) {
1439
 
            case ARTISTS:
1440
 
                this.updateActive(ARTISTS);
1441
 
            break;
1442
 
            case NOWPLAYING:
1443
 
                this.updateActive(NOWPLAYING);
1444
 
            break;
1445
 
            case ALBUMS:
1446
 
                this.updateActive(ALBUMS);
1447
 
            break;
1448
 
            case MYSONGS:
1449
 
                this.updateActive(MYSONGS);
1450
 
            break;
1451
 
            case PLAYLIST:
1452
 
                playlist = this.get(HISTORY).get(PLAYLIST),
1453
 
                playlistNode = Y.one('#menu #' + playlist);
1454
 
                this.updateActive(playlistNode);
1455
 
            break;
1456
 
            case PLAYLISTS:
1457
 
                menuItem = Y.one('#menu .more') || null;
1458
 
                if (menuItem) {
1459
 
                    menuItem.addClass('active');
1460
 
                }
1461
 
            break;
1462
 
            default:
1463
 
                break;
1464
 
        }
1465
 
    },
1466
 
    updateActive: function(classOrNode) {
1467
 
        var menuItem = Y.one("#menu ." + classOrNode) || classOrNode;
1468
 
        if (Y.Lang.isObject(menuItem)) {
1469
 
            menuItem.addClass("active");
1470
 
        }
1471
 
    },
1472
 
    onSearchClick: function(e) {
1473
 
        if (e.keyCode && e.keyCode !== 13) { return; }
1474
 
        e.preventDefault();
1475
 
        var term = this.get('searchField').get(VALUE),
1476
 
        history = this.get(HISTORY);
1477
 
        if (term === "") {
1478
 
            Y.Global.fire('one:error', {
1479
 
                message: 'Please enter a search term'
1480
 
            });
1481
 
            return;
1482
 
        }
1483
 
        history.add(Y.mix({
1484
 
            search: term,
1485
 
            view: SEARCH
1486
 
        }, HISTORYRESET));
1487
 
    },
1488
 
    onIOSuccess: function(e) {
1489
 
        var playlists = e.response.json.playlists.playlist,
1490
 
        playlistsLength = playlists ? playlists.length : 0,
1491
 
        playlistTemplate = Y.one('#playlist-listing').getContent(),
1492
 
        playlistList = Y.one('.playlists ul'),
1493
 
        escapeHtml = function(k,v){
1494
 
            return Y.Escape.html(v);
1495
 
        },
1496
 
        i,
1497
 
        playlist,
1498
 
        node;
1499
 
 
1500
 
        if (playlistList) {
1501
 
            playlistList.all("li:not(#shuffle-all)").remove();
1502
 
        } else {
1503
 
            playlistList = Y.Node.create('<ul></ul>');
1504
 
        }
1505
 
        if (playlistsLength > 0){
1506
 
            GLOBAL_PLAYLIST_LIST = playlists;
1507
 
            if (playlistsLength > 10) {
1508
 
                playlists = playlists.slice(0, 10);
1509
 
                playlistsLength = 10;
1510
 
            }
1511
 
            for (i=0; i<playlistsLength; i++) {
1512
 
                playlist = playlists[i],
1513
 
                node = Y.Node.create(
1514
 
                    Y.substitute(playlistTemplate, playlist, escapeHtml));
1515
 
                    playlistList.append(node);
1516
 
            }
1517
 
            if (playlistsLength >= 1) {
1518
 
                playlistList.append(
1519
 
                    Y.Node.create(Y.one('#more-playlists').getContent()));
1520
 
            }
1521
 
            Y.one('.playlists').append(playlistList);
1522
 
        } else {
1523
 
            GLOBAL_PLAYLIST_LIST = [];
1524
 
        }
1525
 
        this.syncUI();
1526
 
    },
1527
 
    onAlbumsClick: function(e) {
1528
 
        e.preventDefault();
1529
 
        var history = this.get(HISTORY);
1530
 
        history.add(Y.mix({
1531
 
            view: ALBUMS
1532
 
        }, HISTORYRESET));
1533
 
    },
1534
 
    onArtistsClick: function(e) {
1535
 
        e.preventDefault();
1536
 
        this.get(HISTORY).add(Y.mix({
1537
 
            view: ARTISTS
1538
 
        }, HISTORYRESET));
1539
 
    },
1540
 
    onNowPlayingClick: function(e) {
1541
 
        e.preventDefault();
1542
 
        this.get(HISTORY).add(Y.mix({
1543
 
            view: NOWPLAYING
1544
 
        }, HISTORYRESET));
1545
 
    },
1546
 
    onPlaylistClick: function(e) {
1547
 
        e.preventDefault();
1548
 
        var parentNode = e.target.get(PARENTNODE);
1549
 
        if (parentNode.hasClass('more')) {
1550
 
            // 'More' link was clicked
1551
 
            this.get(HISTORY).add(Y.mix({
1552
 
                view: PLAYLISTS
1553
 
            }, HISTORYRESET));
1554
 
        } else {
1555
 
            // Playlist was clicked
1556
 
            this.get(HISTORY).add(Y.mix({
1557
 
                playlist: parentNode.get('id'),
1558
 
                view: PLAYLIST
1559
 
            }, HISTORYRESET));
1560
 
        }
1561
 
    },
1562
 
    onSongsClick: function(e) {
1563
 
        e.preventDefault();
1564
 
        this.get(HISTORY).add(Y.mix({
1565
 
            view: MYSONGS
1566
 
        }, HISTORYRESET));
1567
 
        this.updateActive(MYSONGS);
1568
 
    }
1569
 
}, {
1570
 
    ATTRS: {
1571
 
        CONTENT_TEMPLATE: { value: null },
1572
 
        albumsMenuItem: { value: null },
1573
 
        artistsMenuItem: { value: null },
1574
 
        datasource: { value: null },
1575
 
        playlistsds: { value: null },
1576
 
        history: { value: null },
1577
 
        nowPlaying: { value: null },
1578
 
        playlists: { value: null },
1579
 
        searchField: { value: null },
1580
 
        searchButton: { value: null },
1581
 
        songsMenuItem: { value: null }
1582
 
    },
1583
 
    HTML_PARSER: {
1584
 
        albumsMenuItem: '.albums',
1585
 
        artistsMenuItem: '.artists',
1586
 
        nowPlaying: '.nowplaying',
1587
 
        playlists: '.playlists',
1588
 
        searchField: '.u1-search-input',
1589
 
        searchButton: '.u1-search-button',
1590
 
        songsMenuItem: '.my-songs'
1591
 
    }
1592
 
});
1593
 
 
1594
 
SongView = Y.Base.create('songView', Y.Widget, [ToggleView], {
1595
 
    initializer: function() {
1596
 
        this.after('songsChange', this.afterSongsChange, this);
1597
 
        this.get(HISTORY).after(VIEWCHANGE, this.afterViewChange, this);
1598
 
    },
1599
 
    bindUI: function() {
1600
 
        var events = this.get('events'),
1601
 
        selectAll = this.get('selectAll');
1602
 
        if (selectAll) {
1603
 
            events.push(selectAll.after(CLICK, this.afterSelectAllClick, this));
1604
 
            events.push(selectAll.after('key', this.afterSelectAllClick, 'down: 32', this));
1605
 
        }
1606
 
        events.push(this.before(VISIBLECHANGE, this.beforeVisibleChange, this));
1607
 
        events.push(this.before(VISIBLECHANGE, this.beforeVisibleChangeSongView, this));
1608
 
        events.push(this.after(VISIBLECHANGE, this.afterVisibleChangeSongView, this));
1609
 
        this.trackedSongs = new Y.Array([]);
1610
 
        events.push(this.get(BOUNDINGBOX).delegate(CLICK, this.onRowClick, 'tbody tr', this));
1611
 
        events.push(this.get(BOUNDINGBOX).delegate('key', this.onRowClick, 'tbody input[type=checkbox]', 'down:32', this));
1612
 
        events.push(Y.Global.on(MUSICCHECKBOXCHANGED, this.onCheckboxChange, this));
1613
 
        events.push(Y.Global.on(MUSICCHECKBOXESUPDATED, this.onCheckboxesUpdated, this));
1614
 
    },
1615
 
    syncUI: function() {
1616
 
        if (this.get(HISTORY).get(VIEW) === this.get('viewName')) {
1617
 
            this.show();
1618
 
        } else {
1619
 
            this.hide();
1620
 
        }
1621
 
    },
1622
 
    onCheckboxChange: function(e) {
1623
 
        var input = e.input,
1624
 
        bChecked = input.get("checked"),
1625
 
        index = null,
1626
 
        songData,
1627
 
        oSongFromCache = null,
1628
 
        tr = input.ancestor(TR),
1629
 
        trackedSongsLen = this.trackedSongs.length,
1630
 
        i;
1631
 
 
1632
 
        if (tr) {
1633
 
            if (bChecked === true) {
1634
 
                tr.addClass(CHECKED);
1635
 
            } else {
1636
 
                tr.removeClass(CHECKED);
1637
 
            }
1638
 
            songData = tr.getData("song");
1639
 
        }
1640
 
        if (songData) {
1641
 
            for (i=0; i<trackedSongsLen; i++) {
1642
 
                if (this.trackedSongs[i].id === songData.id) {
1643
 
                    oSongFromCache = this.trackedSongs[i];
1644
 
                    index = i;
1645
 
                    break;
1646
 
                }
1647
 
            }
1648
 
            // Add songData if not already present
1649
 
            if (bChecked === true) {
1650
 
                if (oSongFromCache === null) {
1651
 
                    this.trackedSongs.push(songData);
1652
 
                }
1653
 
                // Remove it
1654
 
            } else if (bChecked === false) {
1655
 
                if (oSongFromCache !== null && index !== null) {
1656
 
                    this.trackedSongs.splice(index, 1);
1657
 
                }
1658
 
            }
1659
 
        }
1660
 
        Y.Global.fire(MUSICCHECKBOXESUPDATED);
1661
 
    },
1662
 
    onRowClick: function(e) {
1663
 
 
1664
 
        var target = e.target,
1665
 
        currentTarget = e.currentTarget,
1666
 
        input = currentTarget.one('input[type=checkbox]'),
1667
 
        button = currentTarget.one('button.play'),
1668
 
        tr,
1669
 
        trs,
1670
 
        index,
1671
 
        view,
1672
 
        song,
1673
 
        history;
1674
 
 
1675
 
        if (target === button || button.contains(target) === true) {
1676
 
            e.halt();
1677
 
            trs = Y.all(".active-songs-view tbody tr");
1678
 
            index = trs.indexOf(currentTarget);
1679
 
            history = this.get(HISTORY);
1680
 
            view = history.get(VIEW);
1681
 
 
1682
 
            if (view === NOWPLAYING || (view === PLAYLIST && this.get("isShufflePlaylist") === true)) {
1683
 
                Y.Global.fire(MUSICPLAYSONGATINDEX, { index: index });
1684
 
            } else {
1685
 
                song = this.get(SONGS)[index];
1686
 
                // Get the song object from data added to the tr
1687
 
                // in the case of infinite scrolled data.
1688
 
                tr = target.ancestor(TR);
1689
 
                if (!song && tr) {
1690
 
                    song = tr.getData(SONG);
1691
 
                }
1692
 
 
1693
 
                Y.Global.fire(MUSICADDSONGSTOPLAYLIST,
1694
 
                              { songs: [song],
1695
 
                                  injectSong: true,
1696
 
                                  playNow: true });
1697
 
            }
1698
 
            return;
1699
 
        }
1700
 
 
1701
 
        if (input && target !== input) {
1702
 
            tr = target.ancestor(TR);
1703
 
            if (tr) {
1704
 
                this.setCheckBox(input, !input.get(CHECKED));
1705
 
            }
1706
 
        } else if (input) {
1707
 
            // Fire for natural checking of the checkbox
1708
 
            Y.Global.fire(MUSICCHECKBOXCHANGED, { input: input });
1709
 
        }
1710
 
 
1711
 
    },
1712
 
    setCheckBox: function(ele, value) {
1713
 
        var curVal = ele.get("checked");
1714
 
        ele.set(CHECKED, value);
1715
 
        Y.Global.fire(MUSICCHECKBOXCHANGED, { input: ele, prevVal: curVal, newVal: value });
1716
 
    },
1717
 
    clearAllCheckboxes: function() {
1718
 
        if (this.trackedSongs) {
1719
 
            this.trackedSongs = new Y.Array([]);
1720
 
        }
1721
 
        var selectAll = this.get("selectAll");
1722
 
        if(selectAll) {
1723
 
            selectAll.set(CHECKED, false);
1724
 
        }
1725
 
        this.get(SELECTEDSONGS).each(function(ele) {
1726
 
            this.setCheckBox(ele, false);
1727
 
        }, this);
1728
 
        Y.Global.fire(MUSICCHECKBOXESUPDATED);
1729
 
    },
1730
 
    resetSelectAll: function() {
1731
 
        var selectAll = this.get('selectAll');
1732
 
        if (selectAll) {
1733
 
            selectAll.set(CHECKED, false);
1734
 
        }
1735
 
    },
1736
 
    afterSelectAllClick: function(e) {
1737
 
        var value = e.target.get(CHECKED);
1738
 
        this.get(BOUNDINGBOX).all('.song-listing input').each(function(ele) {
1739
 
            this.setCheckBox(ele, value);
1740
 
            if (value === true){
1741
 
                ele.ancestor(TR).addClass(CHECKED);
1742
 
            } else {
1743
 
                ele.ancestor(TR).removeClass(CHECKED);
1744
 
            }
1745
 
        }, this);
1746
 
        Y.Global.fire(MUSICCHECKBOXESUPDATED);
1747
 
    },
1748
 
    afterSongsChange: function(e) {
1749
 
        var songs = e.newVal,
1750
 
        playingSong = Y.Music.lastSong,
1751
 
        view = this.get(HISTORY).get(VIEW),
1752
 
        count = songs.length,
1753
 
        tbody = this.get('table').one('.body'),
1754
 
        listing = null,
1755
 
        playButtonTh = Y.one(".active-songs-view th.play-button"),
1756
 
        theadRow,
1757
 
        escapeHtml = function(k,v){
1758
 
            return Y.Escape.html(v);
1759
 
        },
1760
 
        noSongsMsg = tbody.one(".no-songs"),
1761
 
        i,
1762
 
        song,
1763
 
        content,
1764
 
        bbViews;
1765
 
 
1766
 
        this.get(BOUNDINGBOX).all('.song-listing').remove();
1767
 
 
1768
 
        if (count === 0) {
1769
 
            if (noSongsMsg) {
1770
 
                listing = noSongsMsg;
1771
 
            } else {
1772
 
                listing = Y.Node.create(Y.one("#tpl-no-songs").getContent());
1773
 
            }
1774
 
            tbody.appendChild(listing);
1775
 
        } else if (count > 0 && noSongsMsg) {
1776
 
            noSongsMsg.remove();
1777
 
        }
1778
 
 
1779
 
        if (!playButtonTh){
1780
 
            playButtonTh = Y.Node.create(Y.one("#tpl-play-button-th").getContent());
1781
 
            theadRow = Y.one(".active-songs-view thead tr");
1782
 
            if (theadRow) {
1783
 
                theadRow.prepend(playButtonTh);
1784
 
            }
1785
 
        }
1786
 
 
1787
 
        if (view === "playlist" && this.get("isShufflePlaylist") === true) {
1788
 
            view = "shuffle";
1789
 
            this.get(BOUNDINGBOX).addClass("shuffled");
1790
 
        } else {
1791
 
            this.get(BOUNDINGBOX).removeClass("shuffled");
1792
 
        }
1793
 
 
1794
 
        for (i=0; i<count; i++) {
1795
 
            song = songs[i],
1796
 
            content = Y.mix(song, {index: i});
1797
 
 
1798
 
            if (view === 'search') {
1799
 
                view = 'songs';
1800
 
            }
1801
 
            listing = Y.Node.create(Y.substitute(Y.one('#'+view+'-song-listing').getContent(), song, escapeHtml));
1802
 
            // Stash the actual trackid + index on the DOM node using HTML5 custom data attrs.
1803
 
            listing.setAttribute("data-u1m-songid", song.id);
1804
 
            // If we are on the now playing view we need to track the index of the track
1805
 
            // to compare with the the now playing index.
1806
 
            if (view === NOWPLAYING) {
1807
 
                // The concatanation is to co-erce data to a string for IE's benefit.
1808
 
                listing.setAttribute("data-u1m-npindex", EMPTY+i);
1809
 
                // If view !== nowplaying then we currently don't show duplicate tracks.
1810
 
                // Should this change then extra tracking data will be required.
1811
 
            } else if (playingSong !== null && song.id === playingSong.sID) {
1812
 
                listing.addClass(CURRENT);
1813
 
            }
1814
 
            tbody.appendChild(listing);
1815
 
        }
1816
 
        // Now fire the event to update the selected track for the nowplaying view
1817
 
        // using the data + the currently playing track index.
1818
 
        Y.Global.fire(MUSICUPDATESELECTED, { song: playingSong });
1819
 
        // Fire event to update the col playbutton.
1820
 
        Y.Global.fire(MUSICUPDATECOLPLAYBUTTON);
1821
 
        // This forces a redraw in IE8.
1822
 
        if (Y.UA.ie === 8) {
1823
 
            bbViews = Y.one(".views");
1824
 
            if (bbViews) {
1825
 
                bbViews.toggleClass("redraw");
1826
 
            }
1827
 
        }
1828
 
    },
1829
 
    afterVisibleChangeSongView: function() {
1830
 
        var playingSong = Y.Music.lastSong;
1831
 
        Y.Global.fire(MUSICUPDATESELECTED, { song: playingSong });
1832
 
    },
1833
 
    beforeVisibleChangeSongView: function(e) {
1834
 
        if (e.newVal === false ) {
1835
 
            this.resetSelectAll();
1836
 
            this.get(BOUNDINGBOX).all('.song-listing').remove();
1837
 
        } else {
1838
 
            Y.all(DOTACTIVESONGSVIEW).removeClass("active-songs-view");
1839
 
            this.get(BOUNDINGBOX).addClass("active-songs-view");
1840
 
            Y.Global.fire(MUSICCHECKBOXESUPDATED);
1841
 
        }
1842
 
    },
1843
 
    afterViewChange: function(e) {
1844
 
        if (e.newVal === this.get('viewName')) {
1845
 
            if (this.get(VISIBLE) === false) {
1846
 
                this.show();
1847
 
            }
1848
 
        } else {
1849
 
            this.hide();
1850
 
        }
1851
 
    },
1852
 
    onCheckboxesUpdated: function() {
1853
 
        var subMenu = this.get(BOUNDINGBOX).one(".submenu"),
1854
 
        numChecked = this.get(SELECTEDSONGS).size(),
1855
 
        nSongsTemplate = Y.one("#tpl-n-songs").getContent(),
1856
 
        templVars = {"songs": "song", "num": 1 },
1857
 
        numCheckedNodeList = Y.all(".n-songs"),
1858
 
        tmp;
1859
 
 
1860
 
 
1861
 
        // Improve checking size to better take into account the my-songs view which
1862
 
        // doesn't always have rendered checked boxes.
1863
 
        if (numChecked === 0 && this.trackedSongs && this.trackedSongs.length > 0) {
1864
 
            numChecked = this.trackedSongs.length;
1865
 
        }
1866
 
        if (numChecked > 0) {
1867
 
            subMenu.all('.chk[disabled]').each( function(ele){
1868
 
                ele.removeClass(DISABLED);
1869
 
                ele.removeAttribute(DISABLED);
1870
 
            });
1871
 
            numCheckedNodeList.each( function(ele) {
1872
 
                if (numChecked === 1) {
1873
 
                    tmp = Y.substitute(nSongsTemplate, templVars); // safe
1874
 
                } else {
1875
 
                    templVars.songs = "songs";
1876
 
                    templVars.num = numChecked;
1877
 
                    tmp = Y.substitute(nSongsTemplate, templVars); // safe
1878
 
                }
1879
 
                ele.setContent(tmp);
1880
 
 
1881
 
            });
1882
 
        } else {
1883
 
            subMenu.all('.chk:not([disabled=disabled])').each( function(ele){
1884
 
                ele.addClass(DISABLED);
1885
 
                ele.set(DISABLED, DISABLED);
1886
 
            });
1887
 
            numCheckedNodeList.each( function(ele) {
1888
 
                templVars.songs = "songs";
1889
 
                templVars.num = "";
1890
 
                tmp = Y.substitute(nSongsTemplate, templVars); // safe
1891
 
                ele.setContent(tmp);
1892
 
            });
1893
 
        }
1894
 
    },
1895
 
    onPlaylistCreate: function() {
1896
 
        Y.Global.fire('music:playlistsStale');
1897
 
    },
1898
 
    getSelectedTracks: function(){
1899
 
        var songs = this.get(SONGS),
1900
 
        songsLength = songs.length,
1901
 
        selected = [],
1902
 
        selectedSongs = this.get(SELECTEDSONGS),
1903
 
        i, j;
1904
 
 
1905
 
        if (selectedSongs.size() === 0 && this.trackedSongs.length === 0) {
1906
 
            return [];
1907
 
        }
1908
 
        selectedSongs.each(function(ele) {
1909
 
            var id = ele.get(VALUE),
1910
 
            i;
1911
 
            // For when songs is populated
1912
 
            for (i=0; i<songsLength; i++) {
1913
 
                if (songs[i].id === id) {
1914
 
                    selected.push(songs[i]);
1915
 
                }
1916
 
            }
1917
 
        });
1918
 
        // For newer views using the dynamic table widget we use the songs that are tracked
1919
 
        // as checkboxes are checked and unchecked.
1920
 
        if (this.trackedSongs && this.trackedSongs.length > 0) {
1921
 
            selected = [];
1922
 
            for (i=0, j=this.trackedSongs.length; i<j; i++) {
1923
 
                selected.push(this.trackedSongs[i]);
1924
 
            }
1925
 
        }
1926
 
        return selected;
1927
 
    },
1928
 
    onAddSongsClick: function(e) {
1929
 
        e.preventDefault();
1930
 
        var selected = this.getSelectedTracks();
1931
 
        if (selected.length === 0) {
1932
 
            return;
1933
 
        }
1934
 
        Y.Global.fire(MUSICADDSONGSTOPLAYLIST, { songs: selected });
1935
 
        this.clearAllCheckboxes();
1936
 
    },
1937
 
    onAddSongsToNewClick: function(e) {
1938
 
        e.preventDefault();
1939
 
        var songs = [],
1940
 
        selectedSongs = this.get(SELECTEDSONGS),
1941
 
        createFn,
1942
 
        addFn,
1943
 
        dialog;
1944
 
 
1945
 
        if (selectedSongs.size() === 0 && this.trackedSongs.length === 0) {
1946
 
            return;
1947
 
        }
1948
 
 
1949
 
        createFn = function(name) {
1950
 
            if (name === null || name === EMPTY) {
1951
 
                return;
1952
 
            }
1953
 
            this.get(SELECTEDSONGS).each(function(ele) {
1954
 
                songs.push(ele.get(VALUE));
1955
 
            });
1956
 
            if (songs.length === 0) { return; }
1957
 
            this.get(DATASOURCE).createPlaylist(
1958
 
                name, songs, Y.bind(this.onPlaylistCreate, this));
1959
 
                this.clearAllCheckboxes();
1960
 
        },
1961
 
        addFn = function(playlistId) {
1962
 
            var datasource = this.get(DATASOURCE),
1963
 
            self = this,
1964
 
            getCallback = function(e) {
1965
 
                var newSongList = [],
1966
 
                oldSongs = e.response.json.playlist.entry,
1967
 
                oldSongsLength = oldSongs.length,
1968
 
                i;
1969
 
 
1970
 
                for (i=0; i<oldSongsLength; i++) {
1971
 
                    newSongList.push(oldSongs[i].id);
1972
 
                }
1973
 
                selectedSongs.each(function(ele) {
1974
 
                    newSongList.push(ele.get(VALUE));
1975
 
                });
1976
 
                datasource.updatePlaylist(
1977
 
                    playlistId, newSongList, function() {});
1978
 
                    self.clearAllCheckboxes();
1979
 
            };
1980
 
            datasource.getPlaylist(playlistId, getCallback);
1981
 
        },
1982
 
        dialog = new Y.Music.PlaylistDialog({
1983
 
            createFn: Y.bind(createFn, this),
1984
 
            addFn: Y.bind(addFn, this),
1985
 
            focusNode: e.target,
1986
 
            visible: true
1987
 
        });
1988
 
        dialog.render();
1989
 
    },
1990
 
    onPlaySongsClick: function(e) {
1991
 
        e.preventDefault();
1992
 
        var selected = this.getSelectedTracks();
1993
 
        if (selected.length === 0) {
1994
 
            selected = Y.clone(this.get(SONGS), true);
1995
 
        }
1996
 
        Y.Global.fire(MUSICSETPLAYLIST, { playlist: selected });
1997
 
        Y.Global.fire(EVENTPLAY);
1998
 
        this.clearAllCheckboxes();
1999
 
    }
2000
 
}, {
2001
 
    ATTRS: {
2002
 
        datasource: { value: null },
2003
 
        datasourceV2: { value: null },
2004
 
        history: { value: null },
2005
 
        isShufflePlaylist: { value: false },
2006
 
        selectAll: { value: null },
2007
 
        selectedSongs: {
2008
 
            getter: function() {
2009
 
                return Y.all('tbody input:checked');
2010
 
            }
2011
 
        },
2012
 
        songs: { value: [] },
2013
 
        submenu: { value: null },
2014
 
        table: { value: null },
2015
 
        unselectedSongs: {
2016
 
            getter: function() {
2017
 
                var songs = [];
2018
 
                Y.all('tbody input').each(function(ele) {
2019
 
                    if (!ele.get(CHECKED)) {
2020
 
                        songs.push(ele);
2021
 
                    }
2022
 
                });
2023
 
                return songs;
2024
 
            }
2025
 
        },
2026
 
        header: { value: null },
2027
 
        viewName: { value: null }
2028
 
    },
2029
 
    HTML_PARSER: {
2030
 
        selectAll: '.selectall',
2031
 
        submenu: '.submenu',
2032
 
        header: '.view-header',
2033
 
        table: 'table'
2034
 
    }
2035
 
});
2036
 
 
2037
 
namespace.NowPlayingListView = Y.Base.create('nowplayingView', SongView, [], {
2038
 
    initializer: function() {
2039
 
        Y.Global.after('music:nowPlaying', this.afterNowPlaying, this);
2040
 
        this.render();
2041
 
    },
2042
 
    bindUI: function() {
2043
 
        Y.bind(this.constructor.superclass.bindUI, this)();
2044
 
 
2045
 
        var events = this.get('events');
2046
 
        events.push(this.get('clearNowPlayingButton').on(
2047
 
            CLICK, this.onClearNowPlayingClick, this));
2048
 
            events.push(this.get('removeFromNowPlayingButton').on(
2049
 
                CLICK, this.onRemoveFromNowPlayingClick, this));
2050
 
                events.push(this.get('saveNowPlayingButton').on(
2051
 
                    CLICK, this.onSaveNowPlayingClick, this));
2052
 
 
2053
 
    },
2054
 
    onClearNowPlayingClick: function(e) {
2055
 
        e.preventDefault();
2056
 
        Y.Global.fire(MUSICSETPLAYLIST, { playlist: [] });
2057
 
        this.set(SONGS, []);
2058
 
        Y.Global.fire(EVENTSTOP);
2059
 
 
2060
 
        // Clear now playing album art etc
2061
 
        Y.one(".player .current-song").addClass(HIDDEN);
2062
 
        Y.one(".player .next-song").addClass(HIDDEN);
2063
 
        Y.one(".player .previous-song").addClass(HIDDEN);
2064
 
        Y.one(".player .start-here").removeClass(HIDDEN);
2065
 
 
2066
 
        // Update title.
2067
 
        Y.one(".player h2").setContent("Ubuntu One Music Player");
2068
 
        Y.Global.fire('music:nowPlayingButtonsUpdated');
2069
 
    },
2070
 
    onSaveNowPlayingClick: function(e) {
2071
 
        e.preventDefault();
2072
 
        var songlist = this.get(SONGS),
2073
 
        songs = [],
2074
 
        i,
2075
 
        createFn,
2076
 
        dialog;
2077
 
 
2078
 
        if (songlist.length === 0 && this.trackedSongs.length === 0) {
2079
 
            return;
2080
 
        }
2081
 
        for (i=0; i<songlist.length; i++) {
2082
 
            songs.push(songlist[i].id);
2083
 
        }
2084
 
 
2085
 
        createFn = function(name) {
2086
 
            if (name === null || name === EMPTY) { return; }
2087
 
            this.get(DATASOURCE).createPlaylist(
2088
 
                name, songs, Y.bind(this.onPlaylistCreate, this));
2089
 
                this.clearAllCheckboxes();
2090
 
        },
2091
 
        dialog = new Y.Music.PlaylistDialog({
2092
 
            addFn: null,
2093
 
            createFn: Y.bind(createFn, this),
2094
 
            focusNode: e.target,
2095
 
            visible: true
2096
 
        });
2097
 
        dialog.render();
2098
 
    },
2099
 
    onRemoveFromNowPlayingClick: function(e) {
2100
 
        e.preventDefault();
2101
 
        var selected = this.get(SELECTEDSONGS),
2102
 
        length = selected.size(),
2103
 
        songIds = [];
2104
 
 
2105
 
        if (length === 0) {
2106
 
            return;
2107
 
        }
2108
 
        selected.each(function(elm){
2109
 
            songIds.push(elm.get(VALUE));
2110
 
            elm.ancestor(TR).remove();
2111
 
        });
2112
 
        Y.Global.fire(MUSICREMOVEFROMNOWPLAYING, { songIds: songIds });
2113
 
        Y.Global.fire(MUSICCHECKBOXESUPDATED);
2114
 
    },
2115
 
    afterNowPlaying: function(e) {
2116
 
        this.set(SONGS, e.songs);
2117
 
    }
2118
 
}, {
2119
 
    ATTRS: {
2120
 
        clearNowPlayingButton: { value: null },
2121
 
        saveNowPlayingButton: { value: null },
2122
 
        removeFromNowPlayingButton: { value: null },
2123
 
        viewName: { value: NOWPLAYING }
2124
 
    },
2125
 
    HTML_PARSER: {
2126
 
        clearNowPlayingButton: '.submenu .clear',
2127
 
        saveNowPlayingButton: '.submenu .save',
2128
 
        removeFromNowPlayingButton: '.submenu .delete'
2129
 
    }
2130
 
});
2131
 
 
2132
 
namespace.PlaylistListView = Y.Base.create('playlistView', SongView, [], {
2133
 
    // Track the current playListId in flight
2134
 
    currentPlaylistId: null,
2135
 
    initializer: function() {
2136
 
        this.render();
2137
 
    },
2138
 
    bindUI: function() {
2139
 
        Y.bind(this.constructor.superclass.bindUI, this)();
2140
 
 
2141
 
        var events = this.get('events');
2142
 
        events.push(this.get(HISTORY).after(PLAYLISTCHANGE, this.afterPlaylistChange, this));
2143
 
 
2144
 
        events.push(this.get('queuePlaylistButton').on(CLICK, this.onQueuePlaylistClick, this));
2145
 
        events.push(this.get('removeFromPlaylistButton').on(CLICK, this.onRemoveFromPlaylistClick, this));
2146
 
        events.push(this.get('addSongsButton').on(CLICK, this.onAddSongsClick, this));
2147
 
        events.push(this.get('deletePlaylistButton').on(CLICK, this.onDeletePlaylistClick, this));
2148
 
    },
2149
 
    getPlaylist: function(playlistId) {
2150
 
        if (playlistId === "shuffle-all") {
2151
 
            this.set("isShufflePlaylist", true);
2152
 
            this.get(DATASOURCE).getRandomSongs(200, Y.rbind(this.onIOSuccess, this, "random", playlistId));
2153
 
        } else {
2154
 
            this.set("isShufflePlaylist", false);
2155
 
            this.get(DATASOURCE).getPlaylist(playlistId, Y.rbind(this.onIOSuccess, this, "playlist", playlistId));
2156
 
        }
2157
 
    },
2158
 
    afterPlaylistChange: function(e) {
2159
 
        if (this.get(VISIBLE)) {
2160
 
            if (e.newVal !== this.currentPlaylistId) {
2161
 
                this.getPlaylist(e.newVal);
2162
 
                this.currentPlaylistId = e.newVal;
2163
 
            }
2164
 
        }
2165
 
    },
2166
 
    beforeVisibleChange: function(e) {
2167
 
        if (e.newVal === true) {
2168
 
            var playlist = this.get(HISTORY).get(PLAYLIST);
2169
 
            if (e.newVal !== this.currentPlaylistId) {
2170
 
                this.currentPlaylistId = playlist;
2171
 
                this.getPlaylist(playlist);
2172
 
            }
2173
 
        } else {
2174
 
            this.set("isShufflePlaylist", false);
2175
 
        }
2176
 
    },
2177
 
    onIOSuccess: function(e, type) {
2178
 
        type = type || "playlist";
2179
 
        var songs;
2180
 
        if (type === "random") {
2181
 
            this.get('header').setContent("Shuffle All Songs");
2182
 
 
2183
 
            if (e.response.json.randomSongs && e.response.json.randomSongs.song) {
2184
 
                songs = e.response.json.randomSongs.song;
2185
 
                this.set(SONGS, songs);
2186
 
                this.get(BOUNDINGBOX).one(".submenu").hide();
2187
 
                if (songs.length === 0) {
2188
 
                    return;
2189
 
                }
2190
 
                Y.Global.fire(MUSICSETPLAYLIST, { playlist: songs });
2191
 
                Y.Global.fire(EVENTPLAY);
2192
 
            }
2193
 
        } else {
2194
 
            this.get('header').setContent(Y.Escape.html(e.response.json.playlist.name));
2195
 
            this.set(SONGS, e.response.json.playlist.entry);
2196
 
            this.get(BOUNDINGBOX).one(".submenu").show();
2197
 
        }
2198
 
    },
2199
 
    onQueuePlaylistClick: function(e) {
2200
 
        e.preventDefault();
2201
 
        var songs = this.get(SONGS);
2202
 
        if (songs.length === 0) { return; }
2203
 
        Y.Global.fire(MUSICSETPLAYLIST, { playlist: Y.clone(songs, true) });
2204
 
        Y.Global.fire(EVENTPLAY);
2205
 
        this.clearAllCheckboxes();
2206
 
    },
2207
 
    onRemoveFromPlaylistClick: function(e) {
2208
 
        e.preventDefault();
2209
 
        var songs = [],
2210
 
        unselectedSongs = this.get('unselectedSongs'),
2211
 
        length = unselectedSongs.length,
2212
 
        playlistId = this.get(HISTORY).get(PLAYLIST),
2213
 
        i;
2214
 
 
2215
 
        for (i=0; i<length; i++) {
2216
 
            songs.push(unselectedSongs[i].get(VALUE));
2217
 
        }
2218
 
        this.get(DATASOURCE).updatePlaylist(
2219
 
            playlistId, songs, Y.bind(this.onPlaylistUpdate, this));
2220
 
    },
2221
 
    onPlaylistUpdate: function() {
2222
 
        this.get(SELECTEDSONGS).each(function(ele) {
2223
 
            ele.ancestor("tr").remove();
2224
 
        });
2225
 
        Y.Global.fire('music:playlistsStale');
2226
 
    },
2227
 
    onDeletePlaylistClick: function(e) {
2228
 
        e.preventDefault();
2229
 
        var history = this.get(HISTORY),
2230
 
        id = history.get(PLAYLIST),
2231
 
        datasource = this.get(DATASOURCE),
2232
 
        confirmDialog = new Y.Music.YesNoDialog({
2233
 
            message: 'Are you sure you want to delete this playlist?',
2234
 
            yesFn: function() {
2235
 
                datasource.deletePlaylist(id, function() {
2236
 
                    Y.Global.fire('music:playlistsStale');
2237
 
                    history.add(
2238
 
                        Y.mix({ view: ARTISTS }, HISTORYRESET));
2239
 
                        confirmDialog.destroy();
2240
 
                });
2241
 
            },
2242
 
            noFn: function() { confirmDialog.destroy(); }
2243
 
        });
2244
 
        confirmDialog.render();
2245
 
    }
2246
 
}, {
2247
 
    ATTRS: {
2248
 
        queuePlaylistButton: { value: null },
2249
 
        removeFromPlaylistButton: { value: null },
2250
 
        deletePlaylistButton: { value: null },
2251
 
        addSongsButton: { value: null },
2252
 
        viewName: { value: PLAYLIST }
2253
 
    },
2254
 
    HTML_PARSER: {
2255
 
        queuePlaylistButton: '.submenu .play',
2256
 
        removeFromPlaylistButton: '.submenu .delete',
2257
 
        deletePlaylistButton: '.delete-playlist',
2258
 
        addSongsButton: '.submenu .add'
2259
 
    }
2260
 
});
2261
 
 
2262
 
namespace.SongListView = Y.Base.create('songView', SongView, [], {
2263
 
    initializer: function() {
2264
 
        this.render();
2265
 
    },
2266
 
    bindUI: function() {
2267
 
        Y.bind(this.constructor.superclass.bindUI, this)();
2268
 
        var events = this.get('events');
2269
 
 
2270
 
        events.push(this.get(HISTORY).after('searchChange', this.afterSearchChange, this));
2271
 
 
2272
 
        events.push(this.get('addSongsButton').on(CLICK, this.onAddSongsClick, this));
2273
 
        events.push(this.get('addSongsToNewButton').on(CLICK, this.onAddSongsToNewClick, this));
2274
 
        events.push(this.get('playSongsButton').on(CLICK, this.onPlaySongsClick, this));
2275
 
    },
2276
 
    syncUI: function() {
2277
 
        var view = this.get(HISTORY).get(VIEW);
2278
 
        if (view === SONGS || view === SEARCH) {
2279
 
            this.show();
2280
 
        } else {
2281
 
            this.hide();
2282
 
        }
2283
 
    },
2284
 
    afterSearchChange: function(e) {
2285
 
        if (e.newVal === null ) { return; }
2286
 
        if (this.get(VISIBLE)) {
2287
 
            this.get(DATASOURCE).search(e.newVal, Y.bind(this.onIOSuccess, this));
2288
 
        } else {
2289
 
            this.show();
2290
 
        }
2291
 
    },
2292
 
    beforeVisibleChange: function(e) {
2293
 
        var history,
2294
 
        term,
2295
 
        album,
2296
 
        view;
2297
 
 
2298
 
        if (e.newVal === true) {
2299
 
            history = this.get(HISTORY),
2300
 
            view = history.get(VIEW);
2301
 
            if (view === SEARCH) {
2302
 
                term = history.get(SEARCH);
2303
 
                this.get(DATASOURCE).search(term, Y.bind(this.onIOSuccess, this));
2304
 
            } else {
2305
 
                album = history.get('album');
2306
 
                this.get(DATASOURCE).getAlbum(
2307
 
                    album, Y.bind(this.onIOSuccess, this));
2308
 
            }
2309
 
        } else {
2310
 
            this.resetSelectAll();
2311
 
            this.get(BOUNDINGBOX).all('.song-listing').remove();
2312
 
        }
2313
 
    },
2314
 
    onIOSuccess: function(e) {
2315
 
        var view = this.get(HISTORY).get(VIEW),
2316
 
        songs,
2317
 
        searchTerm,
2318
 
        song,
2319
 
        i;
2320
 
 
2321
 
        if (view === SEARCH) {
2322
 
            searchTerm = this.get(HISTORY).get('search');
2323
 
            this.get('header').setContent('Songs matching "' + Y.Escape.html(searchTerm) + '"');
2324
 
            songs = e.response.json.searchResult.match;
2325
 
        } else {
2326
 
            if (e.response.json.directory && e.response.json.directory.child) {
2327
 
                songs = e.response.json.directory.child;
2328
 
                this.get('header').setContent(
2329
 
                    Y.Escape.html(songs[0].album) + ' by ' + Y.Escape.html(songs[0].artist));
2330
 
            }
2331
 
        }
2332
 
        for (i=0; i<songs.length; i++) {
2333
 
            song = songs[i];
2334
 
            if (song.track === ZERO) {
2335
 
                song.track = EMPTY;
2336
 
            }
2337
 
        }
2338
 
        this.set(SONGS, songs);
2339
 
    },
2340
 
    afterViewChange: function(e) {
2341
 
        var view = e.newVal;
2342
 
        if (view === SONGS || view === SEARCH) {
2343
 
            this.show();
2344
 
        } else {
2345
 
            this.resetSelectAll();
2346
 
            this.hide();
2347
 
        }
2348
 
        Y.Global.fire(MUSICCHECKBOXESUPDATED);
2349
 
    }
2350
 
}, {
2351
 
    ATTRS: {
2352
 
        addSongsButton: { value: null },
2353
 
        addSongsToNewButton: { value: null },
2354
 
        playSongsButton: { value: null }
2355
 
    },
2356
 
    HTML_PARSER: {
2357
 
        addSongsButton: '.submenu .add',
2358
 
        addSongsToNewButton: '.submenu .addnew',
2359
 
        playSongsButton: '.submenu .play'
2360
 
    }
2361
 
});
2362
 
 
2363
 
namespace.MySongListView = Y.Base.create('mySongView', SongView, [], {
2364
 
    initializer: function() {
2365
 
        this.render();
2366
 
    },
2367
 
    renderUI: function() {
2368
 
        this.nRowPlayingCacheIndex = null;
2369
 
        var datasourceV2 = this.get("datasourceV2");
2370
 
        this.tableScroll = new Y.u1.TableScroll({
2371
 
            oNodeContainer: "#my-song-view .tablescroll",
2372
 
            oDataSource: datasourceV2,
2373
 
            nRecordLimit: 100,
2374
 
            nRowHeight: 34,
2375
 
            sDataSourceMethod: "getSongs",
2376
 
            fRecordTotalSchema: function(o) {
2377
 
                return o.response.meta.total;
2378
 
            },
2379
 
            fRecordListSchema: function(o) {
2380
 
                return o.response.json.songs;
2381
 
            },
2382
 
            bDebugMode: false,
2383
 
            aColumnSpec: [
2384
 
                // Width is a fixed width in px.
2385
 
                // flexwidth is a percentage based width based on the
2386
 
                // space remaining after the px based widths have been accounted for.
2387
 
                // TODO: name isn't used currently should bin it if it's superfluos.
2388
 
                { "name": "play", "width": 40 },
2389
 
                { "name": "chk", "width": 35 },
2390
 
                { "name": "title", "flexwidth": "33.3", "ellipsis": true },
2391
 
                { "name": "artist", "flexwidth": "33.3", "ellipsis": true },
2392
 
                { "name": "album", "flexwidth": "33.3", "ellipsis": true }
2393
 
            ],
2394
 
            oRowSpec: {
2395
 
                "preDataTemplate": '#song-list-item-predata',
2396
 
                "postDataTemplate": '#song-list-item-postdata'
2397
 
            },
2398
 
            // The rowData is the 1st arg
2399
 
            // The row number is the 2nd arg
2400
 
            // The instance is the 3rd arg
2401
 
            fCustomRowRenderer: Y.rbind(function(rowData, nRow, context){
2402
 
                var lastSong = Y.Music.lastSong,
2403
 
                node = Y.Node.create(Y.substitute(context.get("sRowPostDataTemplate"), rowData, function(k,v){return Y.Escape.html(v);})),
2404
 
                songChecked,
2405
 
                songId = node.getAttribute("data-u1m-songid");
2406
 
 
2407
 
                node.setData(SONG, rowData);
2408
 
                if (lastSong && songId === lastSong.sID) {
2409
 
                    this.nRowPlayingCacheIndex = nRow;
2410
 
                    Y.Global.fire(MUSICUPDATECURRENTTRACKROW, {"node": node, "song": lastSong});
2411
 
                }
2412
 
                // Looked for checked tracks based on the data we are tracking.
2413
 
                if (this.trackedSongs && this.trackedSongs.length > 0) {
2414
 
                    songChecked = Y.Array.find(this.trackedSongs, function(songData) {
2415
 
                        if (songData.id === this.songId) {
2416
 
                            return true;
2417
 
                        }
2418
 
                    }, {"songId": songId});
2419
 
                    if (songChecked !== null) {
2420
 
                        this.setCheckBox(node.one('input[type=checkbox]'), true);
2421
 
                        Y.Global.fire(MUSICCHECKBOXESUPDATED);
2422
 
                    }
2423
 
                }
2424
 
                return node;
2425
 
            }, this)
2426
 
        });
2427
 
        // If the current view is MYSONGS at the point of render
2428
 
        // so we're on this view via history being set
2429
 
        // update the scroll widget which will render if
2430
 
        // it's not already rendered.
2431
 
        if (this.get(HISTORY).get(VIEW) === MYSONGS) {
2432
 
            this.updateScrollView();
2433
 
        }
2434
 
    },
2435
 
    bindUI: function() {
2436
 
        Y.bind(this.constructor.superclass.bindUI, this)();
2437
 
        var events = this.get('events');
2438
 
 
2439
 
        events.push(this.get('addSongsButton').on(CLICK, this.onAddSongsClick, this));
2440
 
        events.push(this.get('addSongsToNewButton').on(CLICK, this.onAddSongsToNewClick, this));
2441
 
        events.push(this.get('playSongsButton').on(CLICK, this.onPlaySongsClick, this));
2442
 
 
2443
 
        events.push(this.after(VISIBLECHANGE, this.afterVisibleChange, this));
2444
 
        events.push(this.tableScroll.after("visibleChange", this.updateScrollView, this));
2445
 
    },
2446
 
    updateScrollView: function() {
2447
 
        if (this.tableScroll.get("rendered") === true) {
2448
 
            // Dump the cache so we re-evaluate what's currently playing.
2449
 
            this.tableScroll.clearDataCache();
2450
 
            this.tableScroll.clearRows();
2451
 
            this.tableScroll.syncUI();
2452
 
        } else {
2453
 
            this.tableScroll.render();
2454
 
        }
2455
 
    },
2456
 
    afterVisibleChange: function(e) {
2457
 
        if (e.newVal === true ) {
2458
 
            this.tableScroll.set("visible", true);
2459
 
        } else {
2460
 
            this.tableScroll.set("visible", false);
2461
 
        }
2462
 
    },
2463
 
    afterViewChange: function(e) {
2464
 
        var view = e.newVal;
2465
 
        if (view === MYSONGS) {
2466
 
            this.show();
2467
 
        } else {
2468
 
            this.clearAllCheckboxes();
2469
 
            this.hide();
2470
 
        }
2471
 
    }
2472
 
}, {
2473
 
    ATTRS: {
2474
 
        addSongsButton: { value: null },
2475
 
        addSongsToNewButton: { value: null },
2476
 
        datasourceV2: { value: null },
2477
 
        playSongsButton: { value: null },
2478
 
        viewName: { value: MYSONGS }
2479
 
    },
2480
 
    HTML_PARSER: {
2481
 
        addSongsButton: '.submenu .add',
2482
 
        addSongsToNewButton: '.submenu .addnew',
2483
 
        playSongsButton: '.submenu .play'
2484
 
    }
2485
 
});
2486
 
 
2487
 
namespace.PlaylistGridView = Y.Base.create('PlaylistGridView', Y.Widget, [ToggleView], {
2488
 
    initializer: function() {
2489
 
        this.get(HISTORY).after(VIEWCHANGE, this.afterViewChange, this);
2490
 
        this.render();
2491
 
    },
2492
 
    bindUI: function() {
2493
 
        var boundingBox = this.get('inner'),
2494
 
        events = this.get('events');
2495
 
        events.push(boundingBox.delegate(CLICK, this.onPlaylistClick, '.playlist-grid-listing', this));
2496
 
        events.push(boundingBox.delegate(CLICK, this.onPlayAllClick, '.playlist-grid-listing button', this));
2497
 
        events.push(this.get(HISTORY).after(VIEWCHANGE, this.afterViewChange, this));
2498
 
 
2499
 
        events.push(this.before(VISIBLECHANGE, this.beforeVisibleChange, this));
2500
 
        events.push(this.after('playlistsChange', this.afterPlaylistsChange, this));
2501
 
    },
2502
 
    syncUI: function() {
2503
 
        var view = this.get(HISTORY).get(VIEW);
2504
 
        if (view === PLAYLISTS) {
2505
 
            this.show();
2506
 
        } else {
2507
 
            this.get('inner').all('.playlist-listing').remove();
2508
 
            this.hide();
2509
 
        }
2510
 
    },
2511
 
    onPlaylistClick: function(e) {
2512
 
        e.preventDefault();
2513
 
        if (e.target.get('nodeName').toLowerCase() === 'button' || e.target.ancestor("button") !== null) {
2514
 
            return;
2515
 
        }
2516
 
        var target = e.currentTarget,
2517
 
        playlistId = target.one('a').getAttribute('data-u1m-playlistid'),
2518
 
        history = this.get(HISTORY);
2519
 
        history.add(Y.mix({
2520
 
            playlist: playlistId,
2521
 
            view: PLAYLIST
2522
 
        }, HISTORYRESET));
2523
 
    },
2524
 
    onPlayAllClick: function(e) {
2525
 
        var playlistId = e.target.ancestor(".playlist-grid-listing").one('a').getAttribute('data-u1m-playlistid'),
2526
 
        datasource = this.get(DATASOURCE);
2527
 
        datasource.getPlaylist(playlistId, function(e) {
2528
 
            var songs = e.response.json.playlist.entry;
2529
 
            Y.Global.fire(MUSICADDSONGSTOPLAYLIST, {
2530
 
                songs: songs,
2531
 
                'injectSong': true,
2532
 
                'playNow': true
2533
 
            });
2534
 
        });
2535
 
    },
2536
 
    onPlaylistLoaded: function(e) {
2537
 
        var songs = e.response.json.playlist.entry;
2538
 
        if (songs.length === 0) { return; }
2539
 
        Y.Global.fire(MUSICSETPLAYLIST, { playlist: songs });
2540
 
        Y.Global.fire(EVENTPLAY);
2541
 
    },
2542
 
    onIOSuccess: function(e) {
2543
 
        this.set(PLAYLISTS, e.response.json.playlists.playlist);
2544
 
    },
2545
 
    afterPlaylistsChange: function() {
2546
 
        var playlists = this.get(PLAYLISTS),
2547
 
        count = playlists.length,
2548
 
        box = this.get('inner'),
2549
 
        escapeHtml = function(k,v){
2550
 
            return Y.Escape.html(v);
2551
 
        },
2552
 
        i,
2553
 
        playlist,
2554
 
        temp_vars,
2555
 
        img_id,
2556
 
        playlistNode,
2557
 
        placeholder,
2558
 
        link;
2559
 
 
2560
 
        box.setContent(EMPTY);
2561
 
        for (i=0; i<count; i++) {
2562
 
            playlist = playlists[i],
2563
 
            img_id = Y.guid(),
2564
 
            playlistNode = Y.Node.create(Y.substitute(Y.one("#playlist-grid-listing").getContent(), playlist, escapeHtml)),
2565
 
            temp_vars = { "img_id": img_id, "default_art_url": DEFAULT_PLAYLIST_ART },
2566
 
            placeholder = Y.Node.create(Y.substitute(Y.one("#tpl-art").getContent(), temp_vars, escapeHtml)),
2567
 
            link = playlistNode.one('[data-u1m-playlistid="'+playlist.id+'"]');
2568
 
 
2569
 
            playlistNode.insertBefore(placeholder, link);
2570
 
            box.appendChild(playlistNode);
2571
 
        }
2572
 
    },
2573
 
    beforeVisibleChange: function(e) {
2574
 
        if (e.newVal === true) {
2575
 
            this.get('playlistsds').getPlaylists(Y.bind(this.onIOSuccess, this));
2576
 
        }
2577
 
    },
2578
 
    afterViewChange: function(e) {
2579
 
        if (e.newVal === PLAYLISTS) {
2580
 
            this.show();
2581
 
        } else {
2582
 
            this.get('inner').all('.playlist-grid-listing').remove();
2583
 
            this.hide();
2584
 
        }
2585
 
    }
2586
 
}, {
2587
 
    ATTRS: {
2588
 
        CONTENT_TEMPLATE: { value: null },
2589
 
        playlists: { value: [] },
2590
 
        datasource: { value: null },
2591
 
        playlistsds: { value: null },
2592
 
        history: { value: null },
2593
 
        header: { value: null },
2594
 
        inner: { value: null },
2595
 
        visible: { value: false }
2596
 
    },
2597
 
    HTML_PARSER: {
2598
 
        header: '.view-header',
2599
 
        inner: '.view-inner'
2600
 
    }
2601
 
});