Darep/Beatstream

View on GitHub
app/assets/javascripts/songlist.js

Summary

Maintainability
D
1 day
Test Coverage
//= require jquery.cookie
//= require lib/utils
//= require lib/key_codes
//= require lib/mediator
//= require dragtooltip
/*!
 * Abstracts SlickGrid away into oblivion!
 */

(function ($, window, document, undefined) {

    jQuery.event.special.drag.defaults.distance = 7;

    function Songlist(events_in) {
        var events = $.extend({
            onPlay : function () {}
        }, events_in);

        this.events = events;
        this.initializeSlickGrid(this.events);

        App.Mediator.subscribe(MediatorEvents.PLAYLIST_SHOW_CURRENT_SONG, function () {
            var row = this.dataView.getRowById(this.grid.playingSongId);
            this.grid.scrollRowIntoView(row);
        }.bind(this));
    }

    Songlist.prototype.initializeSlickGrid = function (events) {
        var initDone = false;

        var columns = [
            { id: 'np', resizable: false, width: 22 },
            { id: 'artist', name: 'Artist', field: 'artist', sortable: true },
            { id: 'tracknum', name: '', field: 'tracknum', sortable: false, resizable: false, cssClass: 'tracknum', width: 30 },
            { id: 'title', name: 'Title', field: 'title', sortable: true },
            { id: 'album', name: 'Album', field: 'album', sortable: true },
            { id: 'duration', name: 'Duration', field: 'nice_length', sortable: true, cssClass: 'duration', width: 30 },
            { id: 'path', name: '', field: 'path' }
        ];

        var seekbar = $('#seekbar-slider');

        var options = {
            editable: false,
            forceFitColumns: true,
            enableAutoTooltips: true,
            enableCellNavigation: true,
            enableColumnReorder: false,
            multiSelect: true,
            rowHeight: 22
        };

        var dataView = new Slick.Data.DataView({ inlineFilters: true });
        var grid = new Slick.Grid("#slickgrid", dataView, columns, options);
        grid.setSelectionModel(new Slick.RowSelectionModel());

        // remove 'path' column
        grid.setColumns(columns.slice(0, -1));


        // events:

        grid.onClick.subscribe(function (e) {
            var cell = grid.getCellFromEvent(e);
            grid.setSelectedRows([cell.row]);
        });

        grid.onDblClick.subscribe(function (e) {
            var cell = grid.getCellFromEvent(e);
            var dataItem = grid.getDataItem(cell.row);

            grid.playSong(dataItem.id);

            e.stopPropagation();
        });

        grid.onKeyDown.subscribe(function (e) {
            if (e.keyCode == keyCode.ENTER) {

                var rows = grid.getSelectedRows();
                if (!rows || rows.length <= 0) {
                    return;
                }

                var dataItem = grid.getDataItem(rows[0]);

                grid.playSong(dataItem.id);

                e.stopPropagation();
            }

            if (e.keyCode == keyCode.A && (e.ctrlKey || e.metaKey)) {
                e.preventDefault();
                var i = 1;
                var rows = [];
                while (dataView.getRowById(i) !== undefined) {
                    rows.push(dataView.getRowById(i));
                    i++;
                }
                grid.setSelectedRows(rows);
            }
        });

        grid.onSelectedRowsChanged.subscribe(function (e) {
            //var row = grid.getSelectedRows()[0];
        });

        grid.onSort.subscribe(function (e, args) {
            var sortcol = args.sortCol.field;

            function comparer(a, b) {
                var x = a[sortcol],
                    y = b[sortcol];

                if (!x) { return -1; }
                if (!y) { return 1; }

                if (sortcol == 'album') {
                    x = a['album'] + ' ' + a['tracknum'];
                    y = b['album'] + ' ' + b['tracknum'];
                }
                else if (sortcol == 'artist') {
                    x = a['artist'] + ' ' + a['album'] + ' ' + a['tracknum'];
                    y = b['artist'] + ' ' + b['album'] + ' ' + b['tracknum'];
                }

                return naturalsort(x, y);
            }

            dataView.sort(comparer, args.sortAsc);
        });

        // dragging

        grid.onDragInit.subscribe(function (e, dd) {
            // we're handling drags! Don't you come here knockin'!
            e.stopImmediatePropagation();
        });

        grid.onDragStart.subscribe(function (e, dd) {

            var cell = grid.getCellFromEvent(e);
            var data = {};
            var song_count = 0;

            // check if dragging selected rows
            var rows = grid.getSelectedRows();
            var draggingSelectedRows = false;

            for (var i = 0; i < rows.length; i++) {
                var dataItem = grid.getDataItem(rows[i]);
                data[i] = dataItem;
                if (rows[i] == cell.row) {
                    draggingSelectedRows = true;
                }
                song_count++;
            }

            if (draggingSelectedRows === false) {
                var dataItem = grid.getDataItem(cell.row);
                data = {};
                data[0] = dataItem;
                song_count = 1;
            }

            dd.bestDataEver = data;

            DragTooltip.show(dd.startX, dd.startY, song_count + ' song');

            if (song_count != 1) {
                DragTooltip.append('s');
            }

            // make playlists hilight
            $('#sidebar .playlists li').addClass('targeted');

            // tell grid that we're handling drags!
            e.stopImmediatePropagation();
        });

        grid.onDrag.subscribe(function (e, dd) {
            DragTooltip.update(e.clientX, e.clientY);

            var drop_target = $(document.elementFromPoint(e.clientX, e.clientY));

            if (drop_target === undefined || drop_target.parent().parent().hasClass('playlists') === false) {
                // these are not the drops you are looking for
                $('#sidebar .playlists li').removeClass('hover');
                return;
            }

            $('#sidebar .playlists li').removeClass('hover');
            drop_target.parent().addClass('hover');
        });

        grid.onDragEnd.subscribe(function (e, dd) {
            DragTooltip.hide();

            $('#sidebar .playlists li').removeClass('targeted').removeClass('hover');

            var drop_target = $(document.elementFromPoint(e.clientX, e.clientY));

            if (drop_target === undefined || drop_target.parent().hasClass('.playlists') === false) {
                // these are not the drops you are looking for
                return;
            }

            // TODO: add dragged things into playlist (if things can be added)
        });


        // own extensions:

        grid.playingSongId = null;

        grid.getPlayingSong = function () {
            if (grid.playingSongId === null) {
                return null;
            }
            else {
                return dataView.getItemById(grid.playingSongId);
            }
        };

        grid.playSong = function (id) {
            var row = dataView.getRowById(id);

            if (row === undefined) {
                return; // song is not on the current list
            }

            var song = dataView.getItemById(id);

            events.onPlay(song);

            grid.playingSongId = song.id;

            // now playing icon
            grid.removeCellCssStyles('currentSong_playing');

            var np_cells = {}; np_cells[row] = { np: 'playing' };
            grid.addCellCssStyles('currentSong_playing', np_cells);

            grid.setSelectedRows([row]);
            grid.scrollRowIntoView(row);

            var arr = JSON.parse($.cookie('history')) || [];
            arr.push(id);

            // save song history in a cookie
            $.cookie('history', JSON.stringify(arr));
            $.cookie('isPlaying', true);
        };

        grid.playSongAtRow = function (row) {
            var song = dataView.getItem(row); // getItem == getItemAtRow
            grid.playSong(song.id);
        };

        grid.prevSong = function (shuffle) {
            if (shuffle) {
                var arr = JSON.parse($.cookie('history'));
                var new_id;

                // extract last song from history (pop twice because current song is there also)
                new_id = arr.pop();
                if (grid.playingSongId !== null){
                    new_id = arr.pop();
                }

                if (!new_id) {
                    return;
                }

                $.cookie('history', JSON.stringify(arr));
                grid.playSong(new_id);
            } else {
                var number_of_rows = grid.getDataLength();
                var new_row = number_of_rows - 1;
                var current_row = dataView.getRowById(grid.playingSongId);

                if (current_row === undefined) {
                    // current song is not in the grid, stop playing
                    stop();
                    return;
                }

                if ((current_row - 1) >= 0) {
                    new_row = current_row - 1;
                }

                grid.playSongAtRow(new_row);
            }
        };

        grid.nextSong = function (shuffle, repeat, manual) {
            var number_of_rows = grid.getDataLength();
            var new_row = 0;
            var current_row = -1;

            if (grid.playingSongId !== null) {
                current_row = dataView.getRowById(grid.playingSongId);

                if (current_row === undefined) {
                    // current song is not in the grid, stop playing
                    stop();
                    return;
                }
            }

            if (shuffle) {
                new_row = randomToN(number_of_rows);
                while(new_row == current_row && number_of_rows > 1){
                    //reshuffles if same as previous and more than one song
                    new_row = randomToN(number_of_rows);
                }
            }
            else if ((current_row + 1) < number_of_rows) {
                // normal operation, move to next song
                new_row = current_row + 1;
            }
            else if ((manual === undefined || manual === false) && repeat === false) {
                // last song and not repeating -> stop playing
                stop();
                return;
            }
            else {
                // start at the top!
                new_row = 0;
            }

            grid.playSongAtRow(new_row);
        };

        // wire up model events to drive the grid
        dataView.onRowCountChanged.subscribe(function (e, args) {
            grid.updateRowCount();
            grid.render();

            // Start playing the last song that was playing on init
            if ($.cookie('isPlaying') == 'true' && !initDone) {
                grid.prevSong(true);  // FIXME: ugh, dirty hack
                initDone = true;
            }
        });

        dataView.onRowsChanged.subscribe(function (e, args) {
            grid.invalidateRows(args.rows);
            grid.render();
        });

        function myFilter(item, args) {
            if (args.searchString === "") {
                return true;
            }

            var searchStr = args.searchString.toLowerCase();

            searchStr = searchStr.split(' ');

            var match = true;

            for (var i = 0; i < searchStr.length; i++) {
                var str = searchStr[i];
                if ((item.title && item.title.toLowerCase().indexOf(str) != -1) ||
                    (item.artist && item.artist.toLowerCase().indexOf(str) != -1) ||
                    (item.album && item.album.toLowerCase().indexOf(str) != -1))
                {
                    match = true;
                }
                else {
                    return false;
                }
            }

            return match;
        }
        this.myFilter = myFilter;

        this.grid = grid;
        this.dataView = dataView;
    }

    Songlist.prototype.resizeCanvas = function () {
        this.grid.resizeCanvas();
    };

    Songlist.prototype.nextSong = function (shuffle, manual) {
        this.grid.nextSong(shuffle, manual);
    };

    Songlist.prototype.prevSong = function (shuffle) {
        this.grid.prevSong(shuffle);
    };

    Songlist.prototype.isPlaying = function () {
        return (this.grid.playingSongId !== null);
    };

    Songlist.prototype.resetPlaying = function () {
        this.grid.playingSongId = null;
    };

    Songlist.prototype.loadData = function (data) {
        // initialize data view model
        this.dataView.beginUpdate();
        this.dataView.setItems(data);
        this.dataView.setFilterArgs({
            searchString: ''
        });
        this.dataView.setFilter(this.myFilter);
        this.dataView.endUpdate();

        this.dataView.syncGridSelection(this.grid, false);
        this.dataView.syncGridCellCssStyles(this.grid, 'currentSong_playing');
    };

    Songlist.prototype.setFilter = function (filter) {
        this.dataView.setFilterArgs({
            searchString: filter
        });
        this.dataView.refresh();
    };

    window.Songlist = Songlist;

})(jQuery, window, document);