<commit_before>// Import Artist List as this can be huge

var artists = getArtistsArray();

function Player() // {{{
{
    ////////////////////////////////////////////////////////////////////////////////
    ////////////////////// PRIVATE SECTION /////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    // Get the focus to the visible playlist first
    //window._SBShowMainLibrary();

    // FIXME: need to test that we're playing - gMM.status.state
    // interval (seconds)
    function seek(interval, direction)
    {
        if (!gMM.playbackControl)
            return;

        interval = interval * 1000;

        let min = 0;
        let max = gMM.playbackControl.duration;

        let position = gMM.playbackControl.position + (direction ? interval : -interval);

        gMM.playbackControl.position = Math.min(Math.max(position, min), max);
    }

    function focusTrack(mediaItem)
    {
        SBGetBrowser().mediaTab.mediaPage.highlightItem(_SBGetCurrentView().getIndexForItem(mediaItem));
    }

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// MAPPINGS ////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    mappings.add([modes.PLAYER],
        ["x"], "Play track",
        function () { player.play(); });

    mappings.add([modes.PLAYER],
        ["z"], "Previous track",
        function () { player.previous(); });

    mappings.add([modes.PLAYER],
        ["c"], "Pause/unpause track",
        function () { player.togglePlayPause(); });

    mappings.add([modes.PLAYER],
        ["b"], "Next track",
        function () { player.next(); });

    mappings.add([modes.PLAYER],
        ["v"], "Stop track",
        function () { player.stop(); });

    mappings.add([modes.PLAYER],
        ["f"], "Filter library",
        function () { commandline.open(":", "filter ", modes.EX); });

    mappings.add([modes.PLAYER],
        ["F"], "Loads current view filtered by the keywords",
        function () { commandline.open(":", "Filter ", modes.EX); });

    mappings.add([modes.PLAYER],
        ["s"], "Toggle shuffle",
        function () { player.toggleShuffle(); });

    mappings.add([modes.PLAYER],
        ["r"], "Toggle repeat",
        function () { player.toggleRepeat(); });

    mappings.add([modes.PLAYER],
        ["h"], "Seek -10s",
        function (count) { player.seekBackward(Math.max(1, count) * 10); },
        { flags: Mappings.flags.COUNT });

    mappings.add([modes.PLAYER],
        ["l"], "Seek +10s",
        function (count) { player.seekForward(Math.max(1, count) * 10); },
        { flags: Mappings.flags.COUNT });

    mappings.add([modes.PLAYER],
        ["H"], "Seek -1m",
        function (count) { player.seekBackward(Math.max(1, count) * 60); },
        { flags: Mappings.flags.COUNT });

    mappings.add([modes.PLAYER],
        ["L"], "Seek +1m",
        function (count) { player.seekForward(Math.max(1, count) * 60); },
        { flags: Mappings.flags.COUNT });

    mappings.add([modes.PLAYER],
         ["=", "+"], "Increase volume by 10%",
         function () { player.increaseVolume(); });

    mappings.add([modes.PLAYER],
         ["-"], "Decrease volume by 10%",
         function () { player.decreaseVolume(); });

    ////////////////// ///////////////////////////////////////////////////////////}}}
    ////////////////////// COMMANDS ////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    // TODO: presumably this will eventually just filter the library view like cmus? --djk
    commands.add(["f[ilter]"],
        "Filter and play tracks",
        function (args)
        {
            // Store the old view
            // let prev_view = gMM.status.view;
            let library = LibraryUtils.mainLibrary;
            let mainView = library.createView();
            let sqncr = gMM.sequencer;
            let customProps = Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
                                  .createInstance(Ci.sbIMutablePropertyArray);

            // args
            switch (args.length)
            {
                case 3:
                    customProps.appendProperty(SBProperties.trackName, args[2].toString());
                case 2:
                    customProps.appendProperty(SBProperties.albumName, args[1].toString());
                case 1:
                    customProps.appendProperty(SBProperties.artistName, args[0].toString());
                    break;
                default:
                    break;
            }

            sqncr.playView(mainView, mainView.getIndexForItem(library.getItemsByProperties(customProps).queryElementAt(0, Ci.sbIMediaItem)));
            player.focusPlayingTrack();
        },
        {
            argCount: "+",
            completer: function (context, args) completion.song(context, args)
        });

    commands.add(["F[ilter]"],
            "Filter tracks based on keywords {artist/album/track}",
            function (args)
            {
                let library = LibraryUtils.mainLibrary;
                let myView = LibraryUtils.createStandardMediaListView(LibraryUtils.mainLibrary, args.string);
                if (myView.length == 0)
                    liberator.echoerr("No Tracks matching the keywords");
                else
                {
                     SBGetBrowser().loadMediaList(LibraryUtils.mainLibrary, null, null, myView,
                         "chrome://songbird/content/mediapages/filtersPage.xul");
                     // TODO: make this focusTrack work ?
                     focusTrack(myView.getItemByIndex(0));
                }
            },
            {
                argCount: "+",
                //completer: function (context, args) completion.tracks(context, args);
            });

    // TODO: better off as a single command (:player play) or cmus compatible (:player-play)? --djk
    commands.add(["playerp[lay]"],
        "Play track",
        function () { player.play(); });

    commands.add(["playerpa[use]"],
        "Pause/unpause track",
        function () { player.togglePlayPause(); });

    commands.add(["playern[ext]"],
        "Play next track",
        function () { player.next(); });

    commands.add(["playerpr[ev]"],
        "Play previous track",
        function () { player.previous(); });

    commands.add(["players[top]"],
        "Stop track",
        function () { player.stop(); });

    commands.add(["vol[ume]"],
        "Set the volume",
        function (args)
        {
            let arg = args[0];

            if (!/^[+-]?\d+$/.test(arg))
            {
                liberator.echoerr("E488: Trailing characters");
                return;
            }

            let level = parseInt(arg, 10) / 100;

            if (/^[+-]/.test(arg))
                level = player.volume + level;

            player.volume = Math.min(Math.max(level, 0), 1);
        },
        { argCount: "1" });

    commands.add(["load"],
       "Load a playlist",
       function (args)
       {

            if (args.length != 0)
            {
                //load the selected playlist/smart playlist
                let playlists = player.getPlaylists();
                let length = playlists.length;
                let playlistNames = [];

                for (var i=0; i < length; i++)
                {
                    playlistNames[i] = playlists[i].name.toLowerCase();
                }

                let playlist = args.string.replace("\\","");
                SBGetBrowser().loadMediaList(playlists[playlistNames.indexOf(playlist.toLowerCase())]);
                focusTrack(_SBGetCurrentView().getItemByIndex(0));
            }
            else
            {
                //load main library if there are no args
                _SBShowMainLibrary();
            }
       },
       {
            //args:
            completer: function(context, args) completion.playlist(context, args)
       });
    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// PUBLIC SECTION //////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    return {

        // TODO: check bounds and round, 0 - 1 or 0 - 100?
        get volume() gMM.volumeControl.volume,
        set volume(value)
        {
            gMM.volumeControl.volume = value;
        },

        play: function play()
        {
            // Check if there is any selection in place, else play first item of the visible view.
            if (_SBGetCurrentView().selection.count != 0)
            {
                // Play the selection.
                gMM.sequencer.playView(_SBGetCurrentView(), _SBGetCurrentView().getIndexForItem(_SBGetCurrentView().selection.currentMediaItem));
                focusTrack(gMM.sequencer.currentItem);
            }
            else
            {
                gMM.sequencer.playView(SBGetBrowser().currentMediaListView, 0);
                focusTrack(gMM.sequencer.currentItem);
            }
        },

        stop: function stop()
        {
            gMM.sequencer.stop();
        },

        next: function next()
        {
            gSongbirdWindowController.doCommand("cmd_control_next");
        },

        previous: function previous()
        {
            gSongbirdWindowController.doCommand("cmd_control_previous");
        },

        togglePlayPause: function togglePlayPause()
        {
            gSongbirdWindowController.doCommand("cmd_control_playpause");
            SBGetBrowser().mediaTab.mediaPage.highlightItem(_SBGetCurrentView().getIndexForItem(gMM.sequencer.currentItem));
        },

        toggleShuffle: function toggleShuffle()
        {
            if (gMM.sequencer.mode != gMM.sequencer.MODE_SHUFFLE)
                gMM.sequencer.mode = gMM.sequencer.MODE_SHUFFLE;
            else
                gMM.sequencer.mode = gMM.sequencer.MODE_FORWARD;
        },

        // FIXME: not really toggling - good enough for now.
        toggleRepeat: function toggleRepeat()
        {
            switch (gMM.sequencer.repeatMode)
            {
                case gMM.sequencer.MODE_REPEAT_NONE:
                    gMM.sequencer.repeatMode = gMM.sequencer.MODE_REPEAT_ONE;
                    break;
                case gMM.sequencer.MODE_REPEAT_ONE:
                    gMM.sequencer.repeatMode = gMM.sequencer.MODE_REPEAT_ALL;
                    break;
                case gMM.sequencer.MODE_REPEAT_ALL:
                    gMM.sequencer.repeatMode = gMM.sequencer.MODE_REPEAT_NONE;
                    break;
                default:
                    gMM.sequencer.repeatMode = gMM.sequencer.MODE_REPEAT_NONE;
                    break;
            }
        },

        seekForward: function seekForward(interval)
        {
            seek(interval, true);
        },

        seekBackward: function seekBackward(interval)
        {
            seek(interval, false);
        },

        // FIXME: 10% ?
        increaseVolume: function increaseVolume()
        {
            gMM.volumeControl.volume = gMM.volumeControl.volume * 1.1;
        },

        decreaseVolume: function decreaseVolume()
        {
            if (gMM.volumeControl.volume == 0)
                gMM.volumeControl.volume = 0.1;
            else
                gMM.volumeControl.volume = gMM.volumeControl.volume * 0.9;
        },

        focusPlayingTrack :function focusPlayingTrack()
        {
            focusTrack(gMM.sequencer.currentItem);
        },

        listTracks: function listTracks(view)
        {
            //let myView = LibraryUtils.createStandardMediaListView(LibraryUtils.mainLibrary, args);
            let length = view.length;
            let tracksList = [];

            for (var i = 0; i < length; i++)
            {
                var mediaItem = view.getItemByIndex(i);
                var trackName = mediaItem.getProperty(SBProperties.trackName);
                var albumName = mediaItem.getProperty(SBProperties.albumName);
                var artistName = mediaItem.getProperty(SBProperties.artistName);

                tracksList[i] = [trackName, "Album : " + albumName + " Artist : " + artistName];
            }

            return tracksList;
        },
        // TODO: Use this for implementing "/" and "?". -ken
        searchTracks: function searchTracks(args)
        {
            let currentView = _SBGetCurrentView();
            let mediaItemList = currentView.mediaList;
            let search = _getSearchString(currentView);
            let searchString = "";

            if (search != "")
                searchString = args + " " + search;
            else
                searchString = args;

            let myView = LibraryUtils.createStandardMediaListView(mediaItemList, searchString);
            focusTrack(myView.getItemByIndex(0));
        },

        getPlaylists: function getPlaylists()
        {
            var libraryManager = Components.classes["@songbirdnest.com/Songbird/library/Manager;1"]
                .getService(Components.interfaces.sbILibraryManager);
            var mainLibrary = libraryManager.mainLibrary;
            var playlists = [mainLibrary];
            var playlistsArray = [];
            var listener =
            {
                onEnumerationBegin: function() { },
                onEnumerationEnd: function() { },
                onEnumeratedItem: function(list, item)
                {
                    if (playlistsArray.indexOf(item.name)==-1)
                    {
                        playlists.push(item);
                        playlistsArray.push(item.name);
                    }
                    return Components.interfaces.sbIMediaListEnumerationListener.CONTINUE;
                }
            };
                  mainLibrary.enumerateItemsByProperty("http://songbirdnest.com/data/1.0#isList", "1", listener );
                  return playlists;
        },

        // Play track at 'row' in 'playlist'
        playPlaylist: function playPlaylist(playlist,row)
        {
            var gMM = Components.classes["@songbirdnest.com/Songbird/Mediacore/Manager;1"]
                .getService(Components.interfaces.sbIMediacoreManager);
            gMM.sequencer.playView(playlist.createView(),row);

        }

    };
    //}}}
} // }}}

function getArtists()
{
    return this.artists;
}

function getArtistsArray()
{
    var list = LibraryUtils.mainLibrary;

    //  Create an enumeration listener to count each item
    var listener = {
        count: 0,
        onEnumerationBegin: function (aMediaList) {
            this.count = 0;
        },
        onEnumeratedItem: function (aMediaList, aMediaItem) {
            this.count++;
        },
        onEnumerationEnd: function (aMediaList, aStatusCode) {}
    };

    var artistCounts = {};
    var artists = list.getDistinctValuesForProperty(SBProperties.artistName);
    var artist;
    var artistArray = [];
    var i = 0;
    // Count the number of media items for each distinct artist
    while (artists.hasMore())
    {
        artist = artists.getNext();
        artistArray[i] = [artist, artist];
        list.enumerateItemsByProperty(SBProperties.artistName,
            artist,
            listener,
            Ci.sbIMediaList.ENUMERATIONTYPE_LOCKING);
        artistCounts[artist] = listener.count;
        i++;
    }

    //liberator.dump("Count : "+artistCounts.toSource());
    return artistArray;
}

function getAlbums(artist)
{
    var list = LibraryUtils.mainLibrary;
    var albumArray = [], returnArray = [];
    var items = list.getItemsByProperty(SBProperties.artistName, artist).enumerate();
    var i = 0, j = 0;

    while (items.hasMoreElements())
    {
        album = items.getNext().getProperty(SBProperties.albumName);
        albumArray[i] = [album, album];

        if (i == 0)
        {
            returnArray[j] = albumArray[i];
            j++;
        }
        else if (albumArray[i-1].toString() != albumArray[i].toString())
        {
             returnArray[i] = albumArray[i];
             j++;
        }
        i++;
    }

    return returnArray;
}

function getTracks(artist, album)
{
    var list = LibraryUtils.mainLibrary;
    var tracksArray = [];
    var pa = Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
                 .createInstance(Ci.sbIMutablePropertyArray);
    var i = 0;

    pa.appendProperty(SBProperties.artistName, artist.toString());
    pa.appendProperty(SBProperties.albumName, album.toString());
    var items = list.getItemsByProperties(pa).enumerate();

    while (items.hasMoreElements())
    {
        track = items.getNext().getProperty(SBProperties.trackName);
        tracksArray[i] = [track, track];
        i++;
    }

    return tracksArray;
}

// vim: set fdm=marker sw=4 ts=4 et:
<commit_msg>Formatting fixes.<commit_after>// Import Artist List as this can be huge

var artists = getArtistsArray();

function Player() // {{{
{
    ////////////////////////////////////////////////////////////////////////////////
    ////////////////////// PRIVATE SECTION /////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    // Get the focus to the visible playlist first
    //window._SBShowMainLibrary();

    // FIXME: need to test that we're playing - gMM.status.state
    // interval (seconds)
    function seek(interval, direction)
    {
        if (!gMM.playbackControl)
            return;

        interval = interval * 1000;

        let min = 0;
        let max = gMM.playbackControl.duration;

        let position = gMM.playbackControl.position + (direction ? interval : -interval);

        gMM.playbackControl.position = Math.min(Math.max(position, min), max);
    }

    function focusTrack(mediaItem)
    {
        SBGetBrowser().mediaTab.mediaPage.highlightItem(_SBGetCurrentView().getIndexForItem(mediaItem));
    }

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// MAPPINGS ////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    mappings.add([modes.PLAYER],
        ["x"], "Play track",
        function () { player.play(); });

    mappings.add([modes.PLAYER],
        ["z"], "Previous track",
        function () { player.previous(); });

    mappings.add([modes.PLAYER],
        ["c"], "Pause/unpause track",
        function () { player.togglePlayPause(); });

    mappings.add([modes.PLAYER],
        ["b"], "Next track",
        function () { player.next(); });

    mappings.add([modes.PLAYER],
        ["v"], "Stop track",
        function () { player.stop(); });

    mappings.add([modes.PLAYER],
        ["f"], "Filter library",
        function () { commandline.open(":", "filter ", modes.EX); });

    mappings.add([modes.PLAYER],
        ["F"], "Loads current view filtered by the keywords",
        function () { commandline.open(":", "Filter ", modes.EX); });

    mappings.add([modes.PLAYER],
        ["s"], "Toggle shuffle",
        function () { player.toggleShuffle(); });

    mappings.add([modes.PLAYER],
        ["r"], "Toggle repeat",
        function () { player.toggleRepeat(); });

    mappings.add([modes.PLAYER],
        ["h"], "Seek -10s",
        function (count) { player.seekBackward(Math.max(1, count) * 10); },
        { flags: Mappings.flags.COUNT });

    mappings.add([modes.PLAYER],
        ["l"], "Seek +10s",
        function (count) { player.seekForward(Math.max(1, count) * 10); },
        { flags: Mappings.flags.COUNT });

    mappings.add([modes.PLAYER],
        ["H"], "Seek -1m",
        function (count) { player.seekBackward(Math.max(1, count) * 60); },
        { flags: Mappings.flags.COUNT });

    mappings.add([modes.PLAYER],
        ["L"], "Seek +1m",
        function (count) { player.seekForward(Math.max(1, count) * 60); },
        { flags: Mappings.flags.COUNT });

    mappings.add([modes.PLAYER],
         ["=", "+"], "Increase volume by 10%",
         function () { player.increaseVolume(); });

    mappings.add([modes.PLAYER],
         ["-"], "Decrease volume by 10%",
         function () { player.decreaseVolume(); });

    ////////////////// ///////////////////////////////////////////////////////////}}}
    ////////////////////// COMMANDS ////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    // TODO: presumably this will eventually just filter the library view like cmus? --djk
    commands.add(["f[ilter]"],
        "Filter and play tracks",
        function (args)
        {
            // Store the old view
            // let prev_view = gMM.status.view;
            let library = LibraryUtils.mainLibrary;
            let mainView = library.createView();
            let sqncr = gMM.sequencer;
            let customProps = Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
                                  .createInstance(Ci.sbIMutablePropertyArray);

            // args
            switch (args.length)
            {
                case 3:
                    customProps.appendProperty(SBProperties.trackName, args[2].toString());
                case 2:
                    customProps.appendProperty(SBProperties.albumName, args[1].toString());
                case 1:
                    customProps.appendProperty(SBProperties.artistName, args[0].toString());
                    break;
                default:
                    break;
            }

            sqncr.playView(mainView, mainView.getIndexForItem(library.getItemsByProperties(customProps).queryElementAt(0, Ci.sbIMediaItem)));
            player.focusPlayingTrack();
        },
        {
            argCount: "+",
            completer: function (context, args) completion.song(context, args)
        });

    commands.add(["F[ilter]"],
            "Filter tracks based on keywords {artist/album/track}",
            function (args)
            {
                let library = LibraryUtils.mainLibrary;
                let myView = LibraryUtils.createStandardMediaListView(LibraryUtils.mainLibrary, args.string);
                if (myView.length == 0)
                    liberator.echoerr("No Tracks matching the keywords");
                else
                {
                     SBGetBrowser().loadMediaList(LibraryUtils.mainLibrary, null, null, myView,
                         "chrome://songbird/content/mediapages/filtersPage.xul");
                     // TODO: make this focusTrack work ?
                     focusTrack(myView.getItemByIndex(0));
                }
            },
            {
                argCount: "+",
                //completer: function (context, args) completion.tracks(context, args);
            });

    // TODO: better off as a single command (:player play) or cmus compatible (:player-play)? --djk
    commands.add(["playerp[lay]"],
        "Play track",
        function () { player.play(); });

    commands.add(["playerpa[use]"],
        "Pause/unpause track",
        function () { player.togglePlayPause(); });

    commands.add(["playern[ext]"],
        "Play next track",
        function () { player.next(); });

    commands.add(["playerpr[ev]"],
        "Play previous track",
        function () { player.previous(); });

    commands.add(["players[top]"],
        "Stop track",
        function () { player.stop(); });

    commands.add(["vol[ume]"],
        "Set the volume",
        function (args)
        {
            let arg = args[0];

            if (!/^[+-]?\d+$/.test(arg))
            {
                liberator.echoerr("E488: Trailing characters");
                return;
            }

            let level = parseInt(arg, 10) / 100;

            if (/^[+-]/.test(arg))
                level = player.volume + level;

            player.volume = Math.min(Math.max(level, 0), 1);
        },
        { argCount: "1" });

    commands.add(["load"],
       "Load a playlist",
       function (args)
       {

            if (args.length != 0)
            {
                //load the selected playlist/smart playlist
                let playlists = player.getPlaylists();
                let length = playlists.length;
                let playlistNames = [];

                for (var i=0; i < length; i++)
                {
                    playlistNames[i] = playlists[i].name.toLowerCase();
                }

                let playlist = args.string.replace("\\","");
                SBGetBrowser().loadMediaList(playlists[playlistNames.indexOf(playlist.toLowerCase())]);
                focusTrack(_SBGetCurrentView().getItemByIndex(0));
            }
            else
            {
                //load main library if there are no args
                _SBShowMainLibrary();
            }
       },
       {
            //args:
            completer: function(context, args) completion.playlist(context, args)
       });
    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// PUBLIC SECTION //////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    return {

        // TODO: check bounds and round, 0 - 1 or 0 - 100?
        get volume() gMM.volumeControl.volume,
        set volume(value)
        {
            gMM.volumeControl.volume = value;
        },

        play: function play()
        {
            // Check if there is any selection in place, else play first item of the visible view.
            if (_SBGetCurrentView().selection.count != 0)
            {
                // Play the selection.
                gMM.sequencer.playView(_SBGetCurrentView(), _SBGetCurrentView().getIndexForItem(_SBGetCurrentView().selection.currentMediaItem));
                focusTrack(gMM.sequencer.currentItem);
            }
            else
            {
                gMM.sequencer.playView(SBGetBrowser().currentMediaListView, 0);
                focusTrack(gMM.sequencer.currentItem);
            }
        },

        stop: function stop()
        {
            gMM.sequencer.stop();
        },

        next: function next()
        {
            gSongbirdWindowController.doCommand("cmd_control_next");
        },

        previous: function previous()
        {
            gSongbirdWindowController.doCommand("cmd_control_previous");
        },

        togglePlayPause: function togglePlayPause()
        {
            gSongbirdWindowController.doCommand("cmd_control_playpause");
            SBGetBrowser().mediaTab.mediaPage.highlightItem(_SBGetCurrentView().getIndexForItem(gMM.sequencer.currentItem));
        },

        toggleShuffle: function toggleShuffle()
        {
            if (gMM.sequencer.mode != gMM.sequencer.MODE_SHUFFLE)
                gMM.sequencer.mode = gMM.sequencer.MODE_SHUFFLE;
            else
                gMM.sequencer.mode = gMM.sequencer.MODE_FORWARD;
        },

        // FIXME: not really toggling - good enough for now.
        toggleRepeat: function toggleRepeat()
        {
            switch (gMM.sequencer.repeatMode)
            {
                case gMM.sequencer.MODE_REPEAT_NONE:
                    gMM.sequencer.repeatMode = gMM.sequencer.MODE_REPEAT_ONE;
                    break;
                case gMM.sequencer.MODE_REPEAT_ONE:
                    gMM.sequencer.repeatMode = gMM.sequencer.MODE_REPEAT_ALL;
                    break;
                case gMM.sequencer.MODE_REPEAT_ALL:
                    gMM.sequencer.repeatMode = gMM.sequencer.MODE_REPEAT_NONE;
                    break;
                default:
                    gMM.sequencer.repeatMode = gMM.sequencer.MODE_REPEAT_NONE;
                    break;
            }
        },

        seekForward: function seekForward(interval)
        {
            seek(interval, true);
        },

        seekBackward: function seekBackward(interval)
        {
            seek(interval, false);
        },

        // FIXME: 10% ?
        increaseVolume: function increaseVolume()
        {
            gMM.volumeControl.volume = gMM.volumeControl.volume * 1.1;
        },

        decreaseVolume: function decreaseVolume()
        {
            if (gMM.volumeControl.volume == 0)
                gMM.volumeControl.volume = 0.1;
            else
                gMM.volumeControl.volume = gMM.volumeControl.volume * 0.9;
        },

        focusPlayingTrack :function focusPlayingTrack()
        {
            focusTrack(gMM.sequencer.currentItem);
        },

        listTracks: function listTracks(view)
        {
            //let myView = LibraryUtils.createStandardMediaListView(LibraryUtils.mainLibrary, args);
            let length = view.length;
            let tracksList = [];

            for (let i = 0; i < length; i++)
            {
                let mediaItem = view.getItemByIndex(i);
                let trackName = mediaItem.getProperty(SBProperties.trackName);
                let albumName = mediaItem.getProperty(SBProperties.albumName);
                let artistName = mediaItem.getProperty(SBProperties.artistName);

                tracksList[i] = [trackName, "Album : " + albumName + " Artist : " + artistName];
            }

            return tracksList;
        },
        // TODO: Use this for implementing "/" and "?". -ken
        searchTracks: function searchTracks(args)
        {
            let currentView = _SBGetCurrentView();
            let mediaItemList = currentView.mediaList;
            let search = _getSearchString(currentView);
            let searchString = "";

            if (search != "")
                searchString = args + " " + search;
            else
                searchString = args;

            let myView = LibraryUtils.createStandardMediaListView(mediaItemList, searchString);
            focusTrack(myView.getItemByIndex(0));
        },

        getPlaylists: function getPlaylists()
        {
            let libraryManager = Components.classes["@songbirdnest.com/Songbird/library/Manager;1"]
                                           .getService(Components.interfaces.sbILibraryManager);
            let mainLibrary = libraryManager.mainLibrary;
            let playlists = [mainLibrary];
            let playlistsArray = [];
            let listener = {
                onEnumerationBegin: function() { },
                onEnumerationEnd: function() { },
                onEnumeratedItem: function(list, item)
                {
                    if (playlistsArray.indexOf(item.name) == -1)
                    {
                        playlists.push(item);
                        playlistsArray.push(item.name);
                    }
                    return Components.interfaces.sbIMediaListEnumerationListener.CONTINUE;
                }
            };
            mainLibrary.enumerateItemsByProperty("http://songbirdnest.com/data/1.0#isList", "1", listener );
            return playlists;
        },

        // Play track at 'row' in 'playlist'
        playPlaylist: function playPlaylist(playlist, row)
        {
            let gMM = Components.classes["@songbirdnest.com/Songbird/Mediacore/Manager;1"]
                                .getService(Components.interfaces.sbIMediacoreManager);
            gMM.sequencer.playView(playlist.createView(), row);
        }
    };
    //}}}
} // }}}

function getArtists()
{
    return this.artists;
}

function getArtistsArray()
{
    var list = LibraryUtils.mainLibrary;

    //  Create an enumeration listener to count each item
    var listener = {
        count: 0,
        onEnumerationBegin: function (aMediaList) {
            this.count = 0;
        },
        onEnumeratedItem: function (aMediaList, aMediaItem) {
            this.count++;
        },
        onEnumerationEnd: function (aMediaList, aStatusCode) {}
    };

    var artistCounts = {};
    var artists = list.getDistinctValuesForProperty(SBProperties.artistName);
    var artist;
    var artistArray = [];
    var i = 0;
    // Count the number of media items for each distinct artist
    while (artists.hasMore())
    {
        artist = artists.getNext();
        artistArray[i] = [artist, artist];
        list.enumerateItemsByProperty(SBProperties.artistName,
            artist,
            listener,
            Ci.sbIMediaList.ENUMERATIONTYPE_LOCKING);
        artistCounts[artist] = listener.count;
        i++;
    }

    //liberator.dump("Count : "+artistCounts.toSource());
    return artistArray;
}

function getAlbums(artist)
{
    var list = LibraryUtils.mainLibrary;
    var albumArray = [], returnArray = [];
    var items = list.getItemsByProperty(SBProperties.artistName, artist).enumerate();
    var i = 0, j = 0;

    while (items.hasMoreElements())
    {
        album = items.getNext().getProperty(SBProperties.albumName);
        albumArray[i] = [album, album];

        if (i == 0)
        {
            returnArray[j] = albumArray[i];
            j++;
        }
        else if (albumArray[i-1].toString() != albumArray[i].toString())
        {
             returnArray[i] = albumArray[i];
             j++;
        }
        i++;
    }

    return returnArray;
}

function getTracks(artist, album)
{
    var list = LibraryUtils.mainLibrary;
    var tracksArray = [];
    var pa = Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
                 .createInstance(Ci.sbIMutablePropertyArray);
    var i = 0;

    pa.appendProperty(SBProperties.artistName, artist.toString());
    pa.appendProperty(SBProperties.albumName, album.toString());
    var items = list.getItemsByProperties(pa).enumerate();

    while (items.hasMoreElements())
    {
        track = items.getNext().getProperty(SBProperties.trackName);
        tracksArray[i] = [track, track];
        i++;
    }

    return tracksArray;
}

// vim: set fdm=marker sw=4 ts=4 et:
<|endoftext|>


<commit_before>#!/usr/bin/env node

// Native
const http = require('http');
const path = require('path');
const fs = require('fs');
const {promisify} = require('util');
const {URL} = require('url');

// Packages
const Ajv = require('ajv');
const checkForUpdate = require('update-check');
const chalk = require('chalk');
const arg = require('arg');
const handler = require('serve-handler');
const schema = require('@zeit/schemas/deployment/config-static');

// Utilities
const pkg = require('../package');

const readFile = promisify(fs.readFile);

const warning = message => chalk`{yellow WARNING:} ${message}`;
const info = message => chalk`{magenta INFO:} ${message}`;
const error = message => chalk`{red ERROR:} ${message}`;

const updateCheck = async isDebugging => {
	let update = null;

	try {
		update = await checkForUpdate(pkg);
	} catch (err) {
		const suffix = isDebugging ? ':' : ' (use `--debug` to see full error)';
		console.error(warning(`Checking for updates failed${suffix}`));

		if (isDebugging) {
			console.error(err);
		}
	}

	if (!update) {
		return;
	}

	console.log(`${chalk.bgRed('UPDATE AVAILABLE')} The latest version of \`serve\` is ${update.latest}`);
};

const getHelp = () => chalk`
  {bold.cyan serve} - Static file serving and directory listing

  {bold USAGE}

      {bold $} {cyan serve} --help
      {bold $} {cyan serve} --version
      {bold $} {cyan serve} [-l {underline listen_uri} [-l ...]] [{underline directory}]

      By default, {cyan serve} will listen on {bold 0.0.0.0:3000} and serve the
      current working directory on that address.

      Specifying a single {bold --listen} argument will overwrite the default, not supplement it.

  {bold OPTIONS}

      --help                              Shows this help message

      -v, --version                       Displays the current version of serve

      -l, --listen {underline listen_uri}             Specify a URI endpoint on which to listen (see below) -
                                          more than one may be specified to listen in multiple places

      -d, --debug                         Show debugging information

      -s, --single                        Rewrite all not-found requests to \`index.html\`

  {bold ENDPOINTS}

      Listen endpoints (specified by the {bold --listen} or {bold -l} options above) instruct {cyan serve}
      to listen on one or more interfaces/ports, UNIX domain sockets, or Windows named pipes.

      For TCP ports on hostname "localhost":

          {bold $} {cyan serve} -l {underline 1234}

      For TCP (traditional host/port) endpoints:

          {bold $} {cyan serve} -l tcp://{underline hostname}:{underline 1234}

      For UNIX domain socket endpoints:

          {bold $} {cyan serve} -l unix:{underline /path/to/socket.sock}

      For Windows named pipe endpoints:

          {bold $} {cyan serve} -l pipe:\\\\.\\pipe\\{underline PipeName}
`;

const parseEndpoint = str => {
	if (!isNaN(str)) {
		return [str];
	}

	const url = new URL(str);

	switch (url.protocol) {
	case 'pipe:': {
		// some special handling
		const cutStr = str.replace(/^pipe:/, '');

		if (cutStr.slice(0, 4) !== '\\\\.\\') {
			throw new Error(`Invalid Windows named pipe endpoint: ${str}`);
		}

		return [cutStr];
	}
	case 'unix:':
		if (!url.pathname) {
			throw new Error(`Invalid UNIX domain socket endpoint: ${str}`);
		}

		return [url.pathname];
	case 'tcp:':
		url.port = url.port || '3000';
		return [parseInt(url.port, 10), url.hostname];
	default:
		throw new Error(`Unknown --listen endpoint scheme (protocol): ${url.protocol}`);
	}
};

const registerShutdown = fn => {
	let run = false;

	const wrapper = () => {
		if (!run) {
			run = true;
			fn();
		}
	};

	process.on('SIGINT', wrapper);
	process.on('SIGTERM', wrapper);
	process.on('exit', wrapper);
};

const startEndpoint = (endpoint, config) => {
	const server = http.createServer((request, response) => handler(request, response, config));

	server.on('error', err => {
		console.error('serve:', err.stack);
		process.exit(1);
	});

	server.listen(...endpoint, () => {
		const details = server.address();
		registerShutdown(() => server.close());

		if (typeof details === 'string') {
			console.log(info(`Accepting connections at ${details}`));
		} else if (typeof details === 'object' && details.port) {
			const address = details.address === '::' ? 'localhost' : details.address;
			console.log(info(`Accepting connections at http://${address}:${details.port}`));
		} else {
			console.log(info('Accepting connections'));
		}
	});
};

const loadConfig = async (cwd, entry) => {
	const files = [
		'serve.json',
		'now.json',
		'package.json'
	];

	const config = {};

	for (const file of files) {
		const location = path.join(entry, file);
		let content = null;

		try {
			content = await readFile(location, 'utf8');
		} catch (err) {
			if (err.code === 'ENOENT') {
				continue;
			}

			console.error(error(`Not able to read ${location}: ${err.message}`));
			process.exit(1);
		}

		try {
			content = JSON.parse(content);
		} catch (err) {
			console.error(error(`Could not parse ${location} as JSON: ${err.message}`));
			process.exit(1);
		}

		if (typeof content !== 'object') {
			console.error(warning(`Didn't find a valid object in ${location}. Skipping...`));
			continue;
		}

		try {
			switch (file) {
			case 'now.json':
				content = content.static;
				break;
			case 'package.json':
				content = content.now.static;
				break;
			}
		} catch (err) {
			continue;
		}

		Object.assign(config, content);
		console.log(info(`Discovered configuration in \`${file}\``));

		break;
	}

	if (entry) {
		const {public} = config;
		config.public = path.relative(cwd, (public ? path.join(entry, public) : entry));
	}

	if (Object.keys(config).length !== 0) {
		const ajv = new Ajv();
		const validateSchema = ajv.compile(schema);

		if (!validateSchema(config)) {
			const defaultMessage = error('The configuration you provided is wrong:');
			const {message, params} = validateSchema.errors[0];

			console.error(`${defaultMessage}\n${message}\n${JSON.stringify(params)}`);
			process.exit(1);
		}
	}

	return config;
};

(async () => {
	let args = null;

	try {
		args = arg({
			'--help': Boolean,
			'--version': Boolean,
			'--listen': [parseEndpoint],
			'--single': Boolean,
			'--debug': Boolean,
			'-h': '--help',
			'-v': '--version',
			'-l': '--listen',
			'-d': '--debug',
			'-s': '--single',
			// This is deprecated and only for backwards-compatibility.
			'-p': '--listen'
		});
	} catch (err) {
		console.error(error(err.message));
		process.exit(1);
	}

	await updateCheck(args['--debug']);

	if (args['--version']) {
		console.log(pkg.version);
		return;
	}

	if (args['--help']) {
		console.log(getHelp());
		return;
	}

	if (!args['--listen']) {
		// Default endpoint
		args['--listen'] = [[3000]];
	}

	if (args._.length > 1) {
		console.error(error('Please provide one path argument at maximum'));
		process.exit(1);
	}

	const cwd = process.cwd();
	const entry = args._.length > 0 ? path.join(cwd, args._[0]) : cwd;

	const config = await loadConfig(cwd, entry);

	if (args['--single']) {
		const {rewrites} = config;
		const existingRewrites = Array.isArray(rewrites) ? rewrites : [];

		// As the first rewrite rule, make `--single` work
		config.rewrites = [{
			source: '**',
			destination: '/index.html'
		}, ...existingRewrites];
	}

	for (const endpoint of args['--listen']) {
		startEndpoint(endpoint, config);
	}

	registerShutdown(() => {
		console.log(`\n${info('Gracefully shutting down. Please wait...')}`);

		process.on('SIGINT', () => {
			console.log(`\n${warning('Force-closing all open sockets...')}`);
			process.exit(0);
		});
	});
})();
<commit_msg>Add `--config` for custom paths to `serve.json` (#418)<commit_after>#!/usr/bin/env node

// Native
const http = require('http');
const path = require('path');
const fs = require('fs');
const {promisify} = require('util');
const {URL} = require('url');

// Packages
const Ajv = require('ajv');
const checkForUpdate = require('update-check');
const chalk = require('chalk');
const arg = require('arg');
const handler = require('serve-handler');
const schema = require('@zeit/schemas/deployment/config-static');

// Utilities
const pkg = require('../package');

const readFile = promisify(fs.readFile);

const warning = message => chalk`{yellow WARNING:} ${message}`;
const info = message => chalk`{magenta INFO:} ${message}`;
const error = message => chalk`{red ERROR:} ${message}`;

const updateCheck = async isDebugging => {
	let update = null;

	try {
		update = await checkForUpdate(pkg);
	} catch (err) {
		const suffix = isDebugging ? ':' : ' (use `--debug` to see full error)';
		console.error(warning(`Checking for updates failed${suffix}`));

		if (isDebugging) {
			console.error(err);
		}
	}

	if (!update) {
		return;
	}

	console.log(`${chalk.bgRed('UPDATE AVAILABLE')} The latest version of \`serve\` is ${update.latest}`);
};

const getHelp = () => chalk`
  {bold.cyan serve} - Static file serving and directory listing

  {bold USAGE}

      {bold $} {cyan serve} --help
      {bold $} {cyan serve} --version
      {bold $} {cyan serve} [-l {underline listen_uri} [-l ...]] [{underline directory}]

      By default, {cyan serve} will listen on {bold 0.0.0.0:3000} and serve the
      current working directory on that address.

      Specifying a single {bold --listen} argument will overwrite the default, not supplement it.

  {bold OPTIONS}

      --help                              Shows this help message

      -v, --version                       Displays the current version of serve

      -l, --listen {underline listen_uri}             Specify a URI endpoint on which to listen (see below) -
                                          more than one may be specified to listen in multiple places

      -d, --debug                         Show debugging information

      -s, --single                        Rewrite all not-found requests to \`index.html\`

      -c, --config                        Specify custom path to \`serve.json\`

  {bold ENDPOINTS}

      Listen endpoints (specified by the {bold --listen} or {bold -l} options above) instruct {cyan serve}
      to listen on one or more interfaces/ports, UNIX domain sockets, or Windows named pipes.

      For TCP ports on hostname "localhost":

          {bold $} {cyan serve} -l {underline 1234}

      For TCP (traditional host/port) endpoints:

          {bold $} {cyan serve} -l tcp://{underline hostname}:{underline 1234}

      For UNIX domain socket endpoints:

          {bold $} {cyan serve} -l unix:{underline /path/to/socket.sock}

      For Windows named pipe endpoints:

          {bold $} {cyan serve} -l pipe:\\\\.\\pipe\\{underline PipeName}
`;

const parseEndpoint = str => {
	if (!isNaN(str)) {
		return [str];
	}

	const url = new URL(str);

	switch (url.protocol) {
	case 'pipe:': {
		// some special handling
		const cutStr = str.replace(/^pipe:/, '');

		if (cutStr.slice(0, 4) !== '\\\\.\\') {
			throw new Error(`Invalid Windows named pipe endpoint: ${str}`);
		}

		return [cutStr];
	}
	case 'unix:':
		if (!url.pathname) {
			throw new Error(`Invalid UNIX domain socket endpoint: ${str}`);
		}

		return [url.pathname];
	case 'tcp:':
		url.port = url.port || '3000';
		return [parseInt(url.port, 10), url.hostname];
	default:
		throw new Error(`Unknown --listen endpoint scheme (protocol): ${url.protocol}`);
	}
};

const registerShutdown = fn => {
	let run = false;

	const wrapper = () => {
		if (!run) {
			run = true;
			fn();
		}
	};

	process.on('SIGINT', wrapper);
	process.on('SIGTERM', wrapper);
	process.on('exit', wrapper);
};

const startEndpoint = (endpoint, config) => {
	const server = http.createServer((request, response) => handler(request, response, config));

	server.on('error', err => {
		console.error('serve:', err.stack);
		process.exit(1);
	});

	server.listen(...endpoint, () => {
		const details = server.address();
		registerShutdown(() => server.close());

		if (typeof details === 'string') {
			console.log(info(`Accepting connections at ${details}`));
		} else if (typeof details === 'object' && details.port) {
			const address = details.address === '::' ? 'localhost' : details.address;
			console.log(info(`Accepting connections at http://${address}:${details.port}`));
		} else {
			console.log(info('Accepting connections'));
		}
	});
};

const loadConfig = async (cwd, entry, args) => {
	const files = [
		'serve.json',
		'now.json',
		'package.json'
	];

	if (args['--config']) {
		files.unshift(args['--config']);
	}

	const config = {};

	for (const file of files) {
		const location = path.join(entry, file);
		let content = null;

		try {
			content = await readFile(location, 'utf8');
		} catch (err) {
			if (err.code === 'ENOENT') {
				continue;
			}

			console.error(error(`Not able to read ${location}: ${err.message}`));
			process.exit(1);
		}

		try {
			content = JSON.parse(content);
		} catch (err) {
			console.error(error(`Could not parse ${location} as JSON: ${err.message}`));
			process.exit(1);
		}

		if (typeof content !== 'object') {
			console.error(warning(`Didn't find a valid object in ${location}. Skipping...`));
			continue;
		}

		try {
			switch (file) {
			case 'now.json':
				content = content.static;
				break;
			case 'package.json':
				content = content.now.static;
				break;
			}
		} catch (err) {
			continue;
		}

		Object.assign(config, content);
		console.log(info(`Discovered configuration in \`${file}\``));

		break;
	}

	if (entry) {
		const {public} = config;
		config.public = path.relative(cwd, (public ? path.join(entry, public) : entry));
	}

	if (Object.keys(config).length !== 0) {
		const ajv = new Ajv();
		const validateSchema = ajv.compile(schema);

		if (!validateSchema(config)) {
			const defaultMessage = error('The configuration you provided is wrong:');
			const {message, params} = validateSchema.errors[0];

			console.error(`${defaultMessage}\n${message}\n${JSON.stringify(params)}`);
			process.exit(1);
		}
	}

	return config;
};

(async () => {
	let args = null;

	try {
		args = arg({
			'--help': Boolean,
			'--version': Boolean,
			'--listen': [parseEndpoint],
			'--single': Boolean,
			'--debug': Boolean,
			'--config': String,
			'-h': '--help',
			'-v': '--version',
			'-l': '--listen',
			'-s': '--single',
			'-d': '--debug',
			'-c': '--config',
			// This is deprecated and only for backwards-compatibility.
			'-p': '--listen'
		});
	} catch (err) {
		console.error(error(err.message));
		process.exit(1);
	}

	await updateCheck(args['--debug']);

	if (args['--version']) {
		console.log(pkg.version);
		return;
	}

	if (args['--help']) {
		console.log(getHelp());
		return;
	}

	if (!args['--listen']) {
		// Default endpoint
		args['--listen'] = [[3000]];
	}

	if (args._.length > 1) {
		console.error(error('Please provide one path argument at maximum'));
		process.exit(1);
	}

	const cwd = process.cwd();
	const entry = args._.length > 0 ? path.join(cwd, args._[0]) : cwd;

	const config = await loadConfig(cwd, entry, args);

	if (args['--single']) {
		const {rewrites} = config;
		const existingRewrites = Array.isArray(rewrites) ? rewrites : [];

		// As the first rewrite rule, make `--single` work
		config.rewrites = [{
			source: '**',
			destination: '/index.html'
		}, ...existingRewrites];
	}

	for (const endpoint of args['--listen']) {
		startEndpoint(endpoint, config);
	}

	registerShutdown(() => {
		console.log(`\n${info('Gracefully shutting down. Please wait...')}`);

		process.on('SIGINT', () => {
			console.log(`\n${warning('Force-closing all open sockets...')}`);
			process.exit(0);
		});
	});
})();
<|endoftext|>


<commit_before>test('Dates | Spanish', function () {

  var now = new Date();
  var then = new Date(2011, 7, 25, 15, 45, 50);
  Date.setLocale('es');

  dateEqual(Date.create('15 de mayo 2011'), new Date(2011, 4, 15), 'Date#create | basic Spanish date');
  dateEqual(Date.create('5 de enero de 2012'), new Date(2012, 0, 5), 'Date#create | Spanish | 2012-01-05');
  dateEqual(Date.create('mayo de 2011'), new Date(2011, 4), 'Date#create | Spanish | year and month');
  dateEqual(Date.create('15 de mayo'), new Date(now.getFullYear(), 4, 15), 'Date#create | Spanish | month and date');
  dateEqual(Date.create('2011'), new Date(2011, 0), 'Date#create | Spanish | year');
  dateEqual(Date.create('mayo'), new Date(now.getFullYear(), 4), 'Date#create | Spanish | month');
  dateEqual(Date.create('lunes'), getDateWithWeekdayAndOffset(1), 'Date#create | Spanish | Monday');

  dateEqual(Date.create('5 de enero de 2012 3:45'), new Date(2012, 0, 5, 3, 45), 'Date#create | Spanish | 2012-01-05 3:45');
  dateEqual(Date.create('5 de enero de 2012 3:45pm'), new Date(2012, 0, 5, 15, 45), 'Date#create | Spanish | 2012-01-05 3:45pm');

  dateEqual(Date.create('hace 1 milisegundo'), getRelativeDate(null, null, null, null, null, null,-1), 'Date#create | Spanish | one millisecond ago');
  dateEqual(Date.create('hace 1 segundo'), getRelativeDate(null, null, null, null, null, -1), 'Date#create | Spanish | one second ago');
  dateEqual(Date.create('hace 1 minuto'), getRelativeDate(null, null, null, null, -1), 'Date#create | Spanish | one minute ago');
  dateEqual(Date.create('hace 1 hora'), getRelativeDate(null, null, null, -1), 'Date#create | Spanish | one hour ago');
  dateEqual(Date.create('hace 1 día'), getRelativeDate(null, null, -1), 'Date#create | Spanish | one day ago');
  dateEqual(Date.create('hace 1 semana'), getRelativeDate(null, null, -7), 'Date#create | Spanish | one week');
  dateEqual(Date.create('hace 1 mes'), getRelativeDate(null, -1), 'Date#create | Spanish | one month ago');
  dateEqual(Date.create('hace 1 año'), getRelativeDate(-1), 'Date#create | Spanish | one year ago');

  dateEqual(Date.create('dentro de 5 milisegundos'), getRelativeDate(null, null, null, null, null, null,5), 'Date#create | Spanish | five milliseconds from now');
  dateEqual(Date.create('dentro de 5 segundos'), getRelativeDate(null, null, null, null, null, 5), 'Date#create | Spanish | five second from now');
  dateEqual(Date.create('dentro de 5 minutos'), getRelativeDate(null, null, null, null, 5), 'Date#create | Spanish | five minute from now');
  dateEqual(Date.create('dentro de 5 horas'), getRelativeDate(null, null, null, 5), 'Date#create | Spanish | five hour from now');
  dateEqual(Date.create('dentro de 5 días'), getRelativeDate(null, null, 5), 'Date#create | Spanish | five day from now');
  dateEqual(Date.create('dentro de 5 semanas'), getRelativeDate(null, null, 35), 'Date#create | Spanish | five weeks from now');
  dateEqual(Date.create('dentro de 5 meses'), getRelativeDate(null, 5), 'Date#create | Spanish | five months from now');
  dateEqual(Date.create('dentro de 5 años'), getRelativeDate(5), 'Date#create | Spanish | five years from now');

  dateEqual(Date.create('anteayer'), getRelativeDate(null, null, -2).reset(), 'Date#create | Spanish | 一昨日');
  dateEqual(Date.create('ayer'), getRelativeDate(null, null, -1).reset(), 'Date#create | Spanish | yesterday');
  dateEqual(Date.create('hoy'), getRelativeDate(null, null, 0).reset(), 'Date#create | Spanish | today');
  dateEqual(Date.create('mañana'), getRelativeDate(null, null, 1).reset(), 'Date#create | Spanish | tomorrow');

  dateEqual(Date.create('la semana pasada'), getRelativeDate(null, null, -7), 'Date#create | Spanish | Last week');
  dateEqual(Date.create('la próxima semana'), getRelativeDate(null, null, 7), 'Date#create | Spanish | Next week');

  dateEqual(Date.create('el mes pasado'), getRelativeDate(null, -1), 'Date#create | Spanish | last month');
  dateEqual(Date.create('el próximo mes'), getRelativeDate(null, 1), 'Date#create | Spanish | Next month');

  dateEqual(Date.create('proximo lunes'), Date.create('next monday', 'en'), 'Date#create | Spanish | next monday no accent');
  dateEqual(Date.create('próximo lunes'), Date.create('next monday', 'en'), 'Date#create | Spanish | next monday accent');
  dateEqual(Date.create('pasado lunes'), Date.create('last monday', 'en'), 'Date#create | Spanish | last monday front');
  dateEqual(Date.create('lunes pasado'), Date.create('last monday', 'en'), 'Date#create | Spanish | last monday back');

  dateEqual(Date.create('lunes pasado 3:45'), Date.create('last monday', 'en').set({ hour: 3, minute: 45 }, true), 'Date#create | Spanish | last monday back 3:45');
  dateEqual(Date.create('proximo lunes 3:45'), Date.create('next monday', 'en').set({ hour: 3, minute: 45 }, true), 'Date#create | Spanish | next monday no accent 3:45');

  dateEqual(Date.create('el año pasado'), getRelativeDate(-1), 'Date#create | Spanish | Last year');
  dateEqual(Date.create('el próximo año'), getRelativeDate(1), 'Date#create | Spanish | Next year');

  // no accents
  dateEqual(Date.create('hace 1 dia'), getRelativeDate(null, null, -1), 'Date#create | Spanish | one day ago');
  dateEqual(Date.create('proximo mes'), getRelativeDate(null, 1), 'Date#create | Spanish | Next month');
  dateEqual(Date.create('oroxima semana'), getRelativeDate(null, null, 7), 'Date#create | Spanish | Next week');
  dateEqual(Date.create('manana'), getRelativeDate(null, null, 1).reset(), 'Date#create | Spanish | tomorrow');
  dateEqual(Date.create('hace 1 ano'), getRelativeDate(-1), 'Date#create | Spanish | one year ago');

  equal(then.format(), '25 agosto 2011 15:45', 'Date#create | Spanish | standard format');
  equal(then.format('{dd} de {month} de {yyyy}'), '25 de agosto de 2011', 'Date#create | Spanish | format');

  // Format shortcuts
  equal(then.format('long'), '25 agosto 2011 15:45', 'Date#create | Spanish | long format');
  equal(then.long(), '25 agosto 2011 15:45', 'Date#create | Spanish | long shortcut');
  equal(then.format('full'), 'Jueves 25 agosto 2011 15:45:50', 'Date#create | Spanish | full format');
  equal(then.full(), 'Jueves 25 agosto 2011 15:45:50', 'Date#create | Spanish | full shortcut');
  equal(then.format('short'), '25 agosto 2011', 'Date#create | Spanish | short format');
  equal(then.short(), '25 agosto 2011', 'Date#create | Spanish | short shortcut');

  equal(Date.create('1 second ago', 'en').relative(), 'hace 1 segundo', 'Date#create | Spanish | relative format past');
  equal(Date.create('1 minute ago', 'en').relative(), 'hace 1 minuto',  'Date#create | Spanish | relative format past');
  equal(Date.create('1 hour ago', 'en').relative(),   'hace 1 hora',     'Date#create | Spanish | relative format past');
  equal(Date.create('1 day ago', 'en').relative(),    'hace 1 día',    'Date#create | Spanish | relative format past');
  equal(Date.create('1 week ago', 'en').relative(),   'hace 1 semana',  'Date#create | Spanish | relative format past');
  equal(Date.create('1 month ago', 'en').relative(),  'hace 1 mes',   'Date#create | Spanish | relative format past');
  equal(Date.create('1 year ago', 'en').relative(),   'hace 1 año',     'Date#create | Spanish | relative format past');

  equal(Date.create('2 seconds ago', 'en').relative(), 'hace 2 segundos', 'Date#create | Spanish | relative format past');
  equal(Date.create('2 minutes ago', 'en').relative(), 'hace 2 minutos',  'Date#create | Spanish | relative format past');
  equal(Date.create('2 hours ago', 'en').relative(),   'hace 2 horas',     'Date#create | Spanish | relative format past');
  equal(Date.create('2 days ago', 'en').relative(),    'hace 2 días',    'Date#create | Spanish | relative format past');
  equal(Date.create('2 weeks ago', 'en').relative(),   'hace 2 semanas',  'Date#create | Spanish | relative format past');
  equal(Date.create('2 months ago', 'en').relative(),  'hace 2 meses',   'Date#create | Spanish | relative format past');
  equal(Date.create('2 years ago', 'en').relative(),   'hace 2 años',     'Date#create | Spanish | relative format past');

  equal(Date.create('1 second from now', 'en').relative(), 'dentro de 1 segundo', 'Date#create | Spanish | relative format future');
  equal(Date.create('1 minute from now', 'en').relative(), 'dentro de 1 minuto',  'Date#create | Spanish | relative format future');
  equal(Date.create('1 hour from now', 'en').relative(),   'dentro de 1 hora',     'Date#create | Spanish | relative format future');
  equal(Date.create('1 day from now', 'en').relative(),    'dentro de 1 día',    'Date#create | Spanish | relative format future');
  equal(Date.create('1 week from now', 'en').relative(),   'dentro de 1 semana',  'Date#create | Spanish | relative format future');
  equal(Date.create('1 month from now', 'en').relative(),  'dentro de 1 mes',   'Date#create | Spanish | relative format future');
  equal(Date.create('1 year from now', 'en').relative(),   'dentro de 1 año',     'Date#create | Spanish | relative format future');

  equal(Date.create('5 second from now', 'en').relative(), 'dentro de 5 segundos', 'Date#create | Spanish | relative format future');
  equal(Date.create('5 minute from now', 'en').relative(), 'dentro de 5 minutos',  'Date#create | Spanish | relative format future');
  equal(Date.create('5 hour from now', 'en').relative(),   'dentro de 5 horas',     'Date#create | Spanish | relative format future');
  equal(Date.create('5 day from now', 'en').relative(),    'dentro de 5 días',    'Date#create | Spanish | relative format future');
  equal(Date.create('5 week from now', 'en').relative(),   'dentro de 5 semanas',  'Date#create | Spanish | relative format future');
  equal(Date.create('5 month from now', 'en').relative(),  'dentro de 5 meses',   'Date#create | Spanish | relative format future');
  equal(Date.create('5 year from now', 'en').relative(),   'dentro de 5 años',     'Date#create | Spanish | relative format future');

  dateEqual(Date.create('mañana a las 3:30'), getRelativeDate(null, null, 1).set({hours:3,minutes:30}, true), 'Date#create | Spanish | tomorrow at 3:30');

  equal((5).hours().duration('es'),   '5 horas',     'Date#create | Spanish | simple duration');

});
<commit_msg>Fixed typo<commit_after>test('Dates | Spanish', function () {

  var now = new Date();
  var then = new Date(2011, 7, 25, 15, 45, 50);
  Date.setLocale('es');

  dateEqual(Date.create('15 de mayo 2011'), new Date(2011, 4, 15), 'Date#create | basic Spanish date');
  dateEqual(Date.create('5 de enero de 2012'), new Date(2012, 0, 5), 'Date#create | Spanish | 2012-01-05');
  dateEqual(Date.create('mayo de 2011'), new Date(2011, 4), 'Date#create | Spanish | year and month');
  dateEqual(Date.create('15 de mayo'), new Date(now.getFullYear(), 4, 15), 'Date#create | Spanish | month and date');
  dateEqual(Date.create('2011'), new Date(2011, 0), 'Date#create | Spanish | year');
  dateEqual(Date.create('mayo'), new Date(now.getFullYear(), 4), 'Date#create | Spanish | month');
  dateEqual(Date.create('lunes'), getDateWithWeekdayAndOffset(1), 'Date#create | Spanish | Monday');

  dateEqual(Date.create('5 de enero de 2012 3:45'), new Date(2012, 0, 5, 3, 45), 'Date#create | Spanish | 2012-01-05 3:45');
  dateEqual(Date.create('5 de enero de 2012 3:45pm'), new Date(2012, 0, 5, 15, 45), 'Date#create | Spanish | 2012-01-05 3:45pm');

  dateEqual(Date.create('hace 1 milisegundo'), getRelativeDate(null, null, null, null, null, null,-1), 'Date#create | Spanish | one millisecond ago');
  dateEqual(Date.create('hace 1 segundo'), getRelativeDate(null, null, null, null, null, -1), 'Date#create | Spanish | one second ago');
  dateEqual(Date.create('hace 1 minuto'), getRelativeDate(null, null, null, null, -1), 'Date#create | Spanish | one minute ago');
  dateEqual(Date.create('hace 1 hora'), getRelativeDate(null, null, null, -1), 'Date#create | Spanish | one hour ago');
  dateEqual(Date.create('hace 1 día'), getRelativeDate(null, null, -1), 'Date#create | Spanish | one day ago');
  dateEqual(Date.create('hace 1 semana'), getRelativeDate(null, null, -7), 'Date#create | Spanish | one week');
  dateEqual(Date.create('hace 1 mes'), getRelativeDate(null, -1), 'Date#create | Spanish | one month ago');
  dateEqual(Date.create('hace 1 año'), getRelativeDate(-1), 'Date#create | Spanish | one year ago');

  dateEqual(Date.create('dentro de 5 milisegundos'), getRelativeDate(null, null, null, null, null, null,5), 'Date#create | Spanish | five milliseconds from now');
  dateEqual(Date.create('dentro de 5 segundos'), getRelativeDate(null, null, null, null, null, 5), 'Date#create | Spanish | five second from now');
  dateEqual(Date.create('dentro de 5 minutos'), getRelativeDate(null, null, null, null, 5), 'Date#create | Spanish | five minute from now');
  dateEqual(Date.create('dentro de 5 horas'), getRelativeDate(null, null, null, 5), 'Date#create | Spanish | five hour from now');
  dateEqual(Date.create('dentro de 5 días'), getRelativeDate(null, null, 5), 'Date#create | Spanish | five day from now');
  dateEqual(Date.create('dentro de 5 semanas'), getRelativeDate(null, null, 35), 'Date#create | Spanish | five weeks from now');
  dateEqual(Date.create('dentro de 5 meses'), getRelativeDate(null, 5), 'Date#create | Spanish | five months from now');
  dateEqual(Date.create('dentro de 5 años'), getRelativeDate(5), 'Date#create | Spanish | five years from now');

  dateEqual(Date.create('anteayer'), getRelativeDate(null, null, -2).reset(), 'Date#create | Spanish | 一昨日');
  dateEqual(Date.create('ayer'), getRelativeDate(null, null, -1).reset(), 'Date#create | Spanish | yesterday');
  dateEqual(Date.create('hoy'), getRelativeDate(null, null, 0).reset(), 'Date#create | Spanish | today');
  dateEqual(Date.create('mañana'), getRelativeDate(null, null, 1).reset(), 'Date#create | Spanish | tomorrow');

  dateEqual(Date.create('la semana pasada'), getRelativeDate(null, null, -7), 'Date#create | Spanish | Last week');
  dateEqual(Date.create('la próxima semana'), getRelativeDate(null, null, 7), 'Date#create | Spanish | Next week');

  dateEqual(Date.create('el mes pasado'), getRelativeDate(null, -1), 'Date#create | Spanish | last month');
  dateEqual(Date.create('el próximo mes'), getRelativeDate(null, 1), 'Date#create | Spanish | Next month');

  dateEqual(Date.create('proximo lunes'), Date.create('next monday', 'en'), 'Date#create | Spanish | next monday no accent');
  dateEqual(Date.create('próximo lunes'), Date.create('next monday', 'en'), 'Date#create | Spanish | next monday accent');
  dateEqual(Date.create('pasado lunes'), Date.create('last monday', 'en'), 'Date#create | Spanish | last monday front');
  dateEqual(Date.create('lunes pasado'), Date.create('last monday', 'en'), 'Date#create | Spanish | last monday back');

  dateEqual(Date.create('lunes pasado 3:45'), Date.create('last monday', 'en').set({ hour: 3, minute: 45 }, true), 'Date#create | Spanish | last monday back 3:45');
  dateEqual(Date.create('proximo lunes 3:45'), Date.create('next monday', 'en').set({ hour: 3, minute: 45 }, true), 'Date#create | Spanish | next monday no accent 3:45');

  dateEqual(Date.create('el año pasado'), getRelativeDate(-1), 'Date#create | Spanish | Last year');
  dateEqual(Date.create('el próximo año'), getRelativeDate(1), 'Date#create | Spanish | Next year');

  // no accents
  dateEqual(Date.create('hace 1 dia'), getRelativeDate(null, null, -1), 'Date#create | Spanish | one day ago');
  dateEqual(Date.create('proximo mes'), getRelativeDate(null, 1), 'Date#create | Spanish | Next month');
  dateEqual(Date.create('proxima semana'), getRelativeDate(null, null, 7), 'Date#create | Spanish | Next week');
  dateEqual(Date.create('manana'), getRelativeDate(null, null, 1).reset(), 'Date#create | Spanish | tomorrow');
  dateEqual(Date.create('hace 1 ano'), getRelativeDate(-1), 'Date#create | Spanish | one year ago');

  equal(then.format(), '25 agosto 2011 15:45', 'Date#create | Spanish | standard format');
  equal(then.format('{dd} de {month} de {yyyy}'), '25 de agosto de 2011', 'Date#create | Spanish | format');

  // Format shortcuts
  equal(then.format('long'), '25 agosto 2011 15:45', 'Date#create | Spanish | long format');
  equal(then.long(), '25 agosto 2011 15:45', 'Date#create | Spanish | long shortcut');
  equal(then.format('full'), 'Jueves 25 agosto 2011 15:45:50', 'Date#create | Spanish | full format');
  equal(then.full(), 'Jueves 25 agosto 2011 15:45:50', 'Date#create | Spanish | full shortcut');
  equal(then.format('short'), '25 agosto 2011', 'Date#create | Spanish | short format');
  equal(then.short(), '25 agosto 2011', 'Date#create | Spanish | short shortcut');

  equal(Date.create('1 second ago', 'en').relative(), 'hace 1 segundo', 'Date#create | Spanish | relative format past');
  equal(Date.create('1 minute ago', 'en').relative(), 'hace 1 minuto',  'Date#create | Spanish | relative format past');
  equal(Date.create('1 hour ago', 'en').relative(),   'hace 1 hora',     'Date#create | Spanish | relative format past');
  equal(Date.create('1 day ago', 'en').relative(),    'hace 1 día',    'Date#create | Spanish | relative format past');
  equal(Date.create('1 week ago', 'en').relative(),   'hace 1 semana',  'Date#create | Spanish | relative format past');
  equal(Date.create('1 month ago', 'en').relative(),  'hace 1 mes',   'Date#create | Spanish | relative format past');
  equal(Date.create('1 year ago', 'en').relative(),   'hace 1 año',     'Date#create | Spanish | relative format past');

  equal(Date.create('2 seconds ago', 'en').relative(), 'hace 2 segundos', 'Date#create | Spanish | relative format past');
  equal(Date.create('2 minutes ago', 'en').relative(), 'hace 2 minutos',  'Date#create | Spanish | relative format past');
  equal(Date.create('2 hours ago', 'en').relative(),   'hace 2 horas',     'Date#create | Spanish | relative format past');
  equal(Date.create('2 days ago', 'en').relative(),    'hace 2 días',    'Date#create | Spanish | relative format past');
  equal(Date.create('2 weeks ago', 'en').relative(),   'hace 2 semanas',  'Date#create | Spanish | relative format past');
  equal(Date.create('2 months ago', 'en').relative(),  'hace 2 meses',   'Date#create | Spanish | relative format past');
  equal(Date.create('2 years ago', 'en').relative(),   'hace 2 años',     'Date#create | Spanish | relative format past');

  equal(Date.create('1 second from now', 'en').relative(), 'dentro de 1 segundo', 'Date#create | Spanish | relative format future');
  equal(Date.create('1 minute from now', 'en').relative(), 'dentro de 1 minuto',  'Date#create | Spanish | relative format future');
  equal(Date.create('1 hour from now', 'en').relative(),   'dentro de 1 hora',     'Date#create | Spanish | relative format future');
  equal(Date.create('1 day from now', 'en').relative(),    'dentro de 1 día',    'Date#create | Spanish | relative format future');
  equal(Date.create('1 week from now', 'en').relative(),   'dentro de 1 semana',  'Date#create | Spanish | relative format future');
  equal(Date.create('1 month from now', 'en').relative(),  'dentro de 1 mes',   'Date#create | Spanish | relative format future');
  equal(Date.create('1 year from now', 'en').relative(),   'dentro de 1 año',     'Date#create | Spanish | relative format future');

  equal(Date.create('5 second from now', 'en').relative(), 'dentro de 5 segundos', 'Date#create | Spanish | relative format future');
  equal(Date.create('5 minute from now', 'en').relative(), 'dentro de 5 minutos',  'Date#create | Spanish | relative format future');
  equal(Date.create('5 hour from now', 'en').relative(),   'dentro de 5 horas',     'Date#create | Spanish | relative format future');
  equal(Date.create('5 day from now', 'en').relative(),    'dentro de 5 días',    'Date#create | Spanish | relative format future');
  equal(Date.create('5 week from now', 'en').relative(),   'dentro de 5 semanas',  'Date#create | Spanish | relative format future');
  equal(Date.create('5 month from now', 'en').relative(),  'dentro de 5 meses',   'Date#create | Spanish | relative format future');
  equal(Date.create('5 year from now', 'en').relative(),   'dentro de 5 años',     'Date#create | Spanish | relative format future');

  dateEqual(Date.create('mañana a las 3:30'), getRelativeDate(null, null, 1).set({hours:3,minutes:30}, true), 'Date#create | Spanish | tomorrow at 3:30');

  equal((5).hours().duration('es'),   '5 horas',     'Date#create | Spanish | simple duration');

});
<|endoftext|>


<commit_before>import * as d3 from 'd3';
import {crossfilter, units, legend, lineChart, geoChoroplethChart, bubbleChart, renderAll, redrawAll, filterAll, pieChart, barChart, dataCount, dataTable, pluck, round} from 'dc';
import * as colorbrewer from "colorbrewer";
import reductio from 'reductio';
//we can call export at the top of the class declaration
export default class NOAADashDC {

  constructor(el, props = {}) {
    //we initiate charts in constructor
    this.myCharts = NOAADashDC.initCharts();
  }

  render() {
    //de-structure myCharts object
    //const {heightChart, periodChart} = this.myCharts;
    const {heightChart, periodChart, moveChart} = this.myCharts;

    d3.csv('data/fullyear_wv_dec_cc.csv', (error, data) => {
      //format the data
      const buoyData = this.formatData(data);
      const wavesx = crossfilter(buoyData);
      // build the x dimensions
      const xDims = this.buildXDimensions(wavesx);
      //destructure the xDims object
      const {heightDim, periodDim, monthDim} = xDims;
      //build the Y groups
      const yGroups = this.buildYGroups(wavesx, xDims);
      //de-structure they yGroups object
      const {heightGroup, periodGroup, waveAverageHeightGroup, waveMoveHeightGroup, myMonthGroup} = yGroups;
      //call number format
      const numberFormat =  this.numberFormat();
      //dc.js Charts chained configuration

      this.buildMoveChart(moveChart, data, monthDim, myMonthGroup);

      heightChart
        .width(300)
        .height(180)
        .margins({top: 10, right: 50, bottom: 30, left: 40})
        .dimension(heightDim)
        .group(heightGroup)
        .elasticY(true)
      // (optional) whether bar should be center to its x value. Not needed for ordinal chart, :default=false
        .centerBar(true)
      // (optional) set gap between bars manually in px, :default=2
        .gap(65)
      // (optional) set filter brush rounding
        .round(round.floor)
        .x(d3.scale.linear().domain([0, 7]))
        .renderHorizontalGridLines(true)
      // customize the filter displayed in the control span
        .filterPrinter((filters) => {
          var filter = filters[0], s = "";
          s += numberFormat(filter[0]) + "met -> " + numberFormat(filter[1]) + "met";
          return s;
        });

      // Customize axis
      heightChart.xAxis().tickFormat((v) => {
        return v + "met";
      });
      heightChart.yAxis().ticks(5);

      //dc.barChart("#period-chart")
      periodChart
        .width(300)
        .height(180)
        .margins({top: 10, right: 50, bottom: 30, left: 40})
        .dimension(periodDim)
        .group(periodGroup)
        .elasticY(true)
      // (optional) whether bar should be center to its x value. Not needed for ordinal chart, :default=false
        .centerBar(true)
      // (optional) set gap between bars manually in px, :default=2
        .gap(45)
      // (optional) set filter brush rounding
        .round(round.floor)
        .x(d3.scale.linear().domain([0, 30]))
        .renderHorizontalGridLines(true)
      // customize the filter displayed in the control span
        .filterPrinter(function (filters) {
          var filter = filters[0], s = "";
          s += numberFormat(filter[0]) + "sec -> " + numberFormat(filter[1]) + "sec";
          return s;
        });

      // Customize axis
      periodChart.xAxis().tickFormat(
        function (v) { return v + "sec"; });
      periodChart.yAxis().ticks(5);

      //draw the viz!
      renderAll();

    });
  }

  buildMoveChart(moveChart, data, monthDim, myMonthGroup){

    moveChart /* dc.lineChart('#monthly-move-chart', 'chartGroup') */
      .renderArea(true)
      .width(990)
      .height(200)
      .transitionDuration(1000)
      .margins({top: 30, right: 50, bottom: 25, left: 40})
      .dimension(monthDim)
      .mouseZoomable(true)
    // Specify a "range chart" to link its brush extent with the zoom of the current "focus chart".
    //   .rangeChart(volumeChart)
    //.x(d3.time.scale().domain([new Date(2004, 0, 1), new Date(2012, 11, 31)]))
      .x(d3.time.scale().domain(d3.extent(data, (d) => { return d.dd; })))  //use extent to auto scale the axis
      .round(d3.time.month.round)
      .xUnits(d3.time.months)
      .elasticY(true)
      .renderHorizontalGridLines(true)
    //##### Legend

    // Position the legend relative to the chart origin and specify items' height and separation.
      .legend(legend().x(800).y(10).itemHeight(13).gap(5))
      .brushOn(false)
    // Add the base layer of the stack with group. The second parameter specifies a series name for use in the
    // legend.
    // The `.valueAccessor` will be used for the base layer
      .group(myMonthGroup, 'Monthly Height Average')
      .valueAccessor((d) =>  {
        return d.value.avg;
      })
    // Stack additional layers with `.stack`. The first paramenter is a new group.
    // The second parameter is the series name. The third is a value accessor.
    /*  .stack(waveMoveHeightGroup, 'Monthly Index Move', function (d) {
       //console.log("val " + d.value);
       return d.value;
       })
       // Title can be called by any stack layer.
       .title(function (d) {
       var value = d.value.avg ? d.value.avg : d.value;
       if (isNaN(value)) {
       value = 0;
       }
       return dateFormat(d.key) + '\n' + numberFormat(value);
       });*/

  }

  static initCharts() {
    const heightChart = barChart('#chart-height');
    const periodChart = barChart('#chart-period');
    const moveChart = lineChart('#chart-month-move');

    //const myCharts = {heightChart, periodChart}
    const myCharts = {heightChart, periodChart, moveChart}

    return myCharts;
  }

  resetChart(chartName) {

    let {heightChart, periodChart} = this.myCharts;

    switch (chartName) {
      case "height-chart":
        heightChart.filterAll();
        break;
      case "period-chart":
        heightChart.filterAll();
        break;
      default:
        //Statements executed when none of the values match the value of the expression
        break;
    }

    redrawAll();
  }

  formatData(data){

    const buoyData = data;

    const dateFormat = d3.time.format("%Y-%m-%d %H:%M:%S");

    buoyData.forEach(d => {
      d.dd = dateFormat.parse(d.origintime);
      d.day = d3.time.day(d.dd); // pre-calculate day for better performance
      d.week = d3.time.week(d.dd);
      d.month = d3.time.month(d.dd);
      d.year = d3.time.year(d.dd);
      d.wvdp   = d3.round(+d.wvdp,1);
      //d.wvht = d3.round((+d.wvht * 3.2),1); //convert to feet
      d.wvht = d3.round((+d.wvht * 3.2)); //convert to feet
      d.wndir = +d.wndir;
    });

    return buoyData;
  }

  numberFormat(){

    return d3.format(".2f");
  }

  buildXDimensions(xwaves){
    // create dimensions (x-axis values)

    const heightDim  = xwaves.dimension(pluck("wvht"));
    const periodDim  = xwaves.dimension(pluck("wvdp"));
    const monthDim  = xwaves.dimension(pluck("month"));

    const xDims = { heightDim, periodDim, monthDim };
    return xDims;

  }

  buildYGroups(wavesx, xDims){

    const {heightDim, periodDim, monthDim} = xDims;

    // create groups (y-axis values)
    const heightGroup = heightDim.group();
    const periodGroup = periodDim.group();
    let myMonthGroup = monthDim.group();
    let reducer = reductio().count(true).sum('wvht').avg(true);
    reducer(myMonthGroup);
    //console.log(myMonthGroup.all());
    const waveMoveHeightGroup = monthDim.group().reduceSum((d) => {
      //console.log(d.wvht);
      //console.log("hello");
      return d.wvht;
    });
    //map reduce functions
    const waveAverageHeightGroup = this.buildWaveGAH(monthDim);
    const yGroups = {heightGroup, periodGroup, waveAverageHeightGroup, waveMoveHeightGroup, myMonthGroup};
    return yGroups;

  }

  buildWaveGAH(monthDim){
    const waveAverageHeightGroup = monthDim.group().reduce(
      (p,v) => {
          ++p.sample;
        //p.total += (v.open + v.close) / 2;
        p.total += v.wvht;
        //p.avg = Math.round(p.total / p.days);
        p.avg = Math.round(p.total / p.sample)
        //console.log(v);
        //console.log(p);
        return p;
      },
      (p,v) => {
          --p.sample;
        //p.total -= (v.open + v.close) / 2;
        p.total -= v.wvht;
        //p.avg = p.days ? Math.round(p.total / p.days) : 0;
        p.avg = p.sample ? Math.round(p.total / p.sample) : 0;
        return p;

      },
      () => {
        return {
          sample: 0,
          total: 0,
          avg: 0
        };
      }

    );

    return waveAverageHeightGroup;
  }

  //end of class
}
<commit_msg>add median<commit_after>import * as d3 from 'd3';
import {crossfilter, units, legend, lineChart, geoChoroplethChart, bubbleChart, renderAll, redrawAll, filterAll, pieChart, barChart, dataCount, dataTable, pluck, round} from 'dc';
import * as colorbrewer from "colorbrewer";
import reductio from 'reductio';
//we can call export at the top of the class declaration
export default class NOAADashDC {

  constructor(el, props = {}) {
    //we initiate charts in constructor
    this.myCharts = NOAADashDC.initCharts();
  }

  render() {
    //de-structure myCharts object
    //const {heightChart, periodChart} = this.myCharts;
    const {heightChart, periodChart, moveChart} = this.myCharts;

    d3.csv('data/fullyear_wv_dec_cc.csv', (error, data) => {
      //format the data
      const buoyData = this.formatData(data);
      const wavesx = crossfilter(buoyData);
      // build the x dimensions
      const xDims = this.buildXDimensions(wavesx);
      //destructure the xDims object
      const {heightDim, periodDim, monthDim} = xDims;
      //build the Y groups
      const yGroups = this.buildYGroups(wavesx, xDims);
      //de-structure they yGroups object
      const {heightGroup, periodGroup, waveAverageHeightGroup, waveMoveHeightGroup, myMonthGroup} = yGroups;
      //call number format
      const numberFormat =  this.numberFormat();
      //dc.js Charts chained configuration

      this.buildMoveChart(moveChart, data, monthDim, myMonthGroup);

      heightChart
        .width(300)
        .height(180)
        .margins({top: 10, right: 50, bottom: 30, left: 40})
        .dimension(heightDim)
        .group(heightGroup)
        .elasticY(true)
      // (optional) whether bar should be center to its x value. Not needed for ordinal chart, :default=false
        .centerBar(true)
      // (optional) set gap between bars manually in px, :default=2
        .gap(65)
      // (optional) set filter brush rounding
        .round(round.floor)
        .x(d3.scale.linear().domain([0, 7]))
        .renderHorizontalGridLines(true)
      // customize the filter displayed in the control span
        .filterPrinter((filters) => {
          var filter = filters[0], s = "";
          s += numberFormat(filter[0]) + "met -> " + numberFormat(filter[1]) + "met";
          return s;
        });

      // Customize axis
      heightChart.xAxis().tickFormat((v) => {
        return v + "met";
      });
      heightChart.yAxis().ticks(5);

      //dc.barChart("#period-chart")
      periodChart
        .width(300)
        .height(180)
        .margins({top: 10, right: 50, bottom: 30, left: 40})
        .dimension(periodDim)
        .group(periodGroup)
        .elasticY(true)
      // (optional) whether bar should be center to its x value. Not needed for ordinal chart, :default=false
        .centerBar(true)
      // (optional) set gap between bars manually in px, :default=2
        .gap(45)
      // (optional) set filter brush rounding
        .round(round.floor)
        .x(d3.scale.linear().domain([0, 30]))
        .renderHorizontalGridLines(true)
      // customize the filter displayed in the control span
        .filterPrinter(function (filters) {
          var filter = filters[0], s = "";
          s += numberFormat(filter[0]) + "sec -> " + numberFormat(filter[1]) + "sec";
          return s;
        });

      // Customize axis
      periodChart.xAxis().tickFormat(
        function (v) { return v + "sec"; });
      periodChart.yAxis().ticks(5);

      //draw the viz!
      renderAll();

    });
  }

  buildMoveChart(moveChart, data, monthDim, myMonthGroup){

    moveChart /* dc.lineChart('#monthly-move-chart', 'chartGroup') */
      .renderArea(true)
      .width(990)
      .height(200)
      .transitionDuration(1000)
      .margins({top: 30, right: 50, bottom: 25, left: 40})
      .dimension(monthDim)
      .mouseZoomable(true)
    // Specify a "range chart" to link its brush extent with the zoom of the current "focus chart".
    //   .rangeChart(volumeChart)
    //.x(d3.time.scale().domain([new Date(2004, 0, 1), new Date(2012, 11, 31)]))
      .x(d3.time.scale().domain(d3.extent(data, (d) => { return d.dd; })))  //use extent to auto scale the axis
      .round(d3.time.month.round)
      .xUnits(d3.time.months)
      .elasticY(true)
      .renderHorizontalGridLines(true)
    //##### Legend

    // Position the legend relative to the chart origin and specify items' height and separation.
      .legend(legend().x(800).y(10).itemHeight(13).gap(5))
      .brushOn(false)
    // Add the base layer of the stack with group. The second parameter specifies a series name for use in the
    // legend.
    // The `.valueAccessor` will be used for the base layer
      .group(myMonthGroup, 'Monthly Height Average')
      .valueAccessor((d) =>  {
        return d.value.avg;
      })
    // Stack additional layers with `.stack`. The first paramenter is a new group.
    // The second parameter is the series name. The third is a value accessor.
    /*  .stack(waveMoveHeightGroup, 'Monthly Index Move', function (d) {
       //console.log("val " + d.value);
       return d.value;
       })
       // Title can be called by any stack layer.
       .title(function (d) {
       var value = d.value.avg ? d.value.avg : d.value;
       if (isNaN(value)) {
       value = 0;
       }
       return dateFormat(d.key) + '\n' + numberFormat(value);
       });*/

  }

  static initCharts() {
    const heightChart = barChart('#chart-height');
    const periodChart = barChart('#chart-period');
    const moveChart = lineChart('#chart-month-move');

    //const myCharts = {heightChart, periodChart}
    const myCharts = {heightChart, periodChart, moveChart}

    return myCharts;
  }

  resetChart(chartName) {

    let {heightChart, periodChart} = this.myCharts;

    switch (chartName) {
      case "height-chart":
        heightChart.filterAll();
        break;
      case "period-chart":
        heightChart.filterAll();
        break;
      default:
        //Statements executed when none of the values match the value of the expression
        break;
    }

    redrawAll();
  }

  formatData(data){

    const buoyData = data;

    const dateFormat = d3.time.format("%Y-%m-%d %H:%M:%S");

    buoyData.forEach(d => {
      d.dd = dateFormat.parse(d.origintime);
      d.day = d3.time.day(d.dd); // pre-calculate day for better performance
      d.week = d3.time.week(d.dd);
      d.month = d3.time.month(d.dd);
      d.year = d3.time.year(d.dd);
      d.wvdp   = d3.round(+d.wvdp,1);
      //d.wvht = d3.round((+d.wvht * 3.2),1); //convert to feet
      d.wvht = d3.round((+d.wvht * 3.2)); //convert to feet
      d.wndir = +d.wndir;
    });

    return buoyData;
  }

  numberFormat(){

    return d3.format(".2f");
  }

  buildXDimensions(xwaves){
    // create dimensions (x-axis values)

    const heightDim  = xwaves.dimension(pluck("wvht"));
    const periodDim  = xwaves.dimension(pluck("wvdp"));
    const monthDim  = xwaves.dimension(pluck("month"));

    const xDims = { heightDim, periodDim, monthDim };
    return xDims;

  }

  buildYGroups(wavesx, xDims){

    const {heightDim, periodDim, monthDim} = xDims;

    // create groups (y-axis values)
    const heightGroup = heightDim.group();
    const periodGroup = periodDim.group();
    let myMonthGroup = monthDim.group();
    let reducer = reductio().count(true).sum('wvht').avg(true).min('wvht').max(true).median(true);
    reducer(myMonthGroup);
    console.log(myMonthGroup.all());
    const waveMoveHeightGroup = monthDim.group().reduceSum((d) => {
      //console.log(d.wvht);
      //console.log("hello");
      return d.wvht;
    });
    //map reduce functions
    const waveAverageHeightGroup = this.buildWaveGAH(monthDim);
    const yGroups = {heightGroup, periodGroup, waveAverageHeightGroup, waveMoveHeightGroup, myMonthGroup};
    return yGroups;

  }

  buildWaveGAH(monthDim){
    const waveAverageHeightGroup = monthDim.group().reduce(
      (p,v) => {
          ++p.sample;
        //p.total += (v.open + v.close) / 2;
        p.total += v.wvht;
        //p.avg = Math.round(p.total / p.days);
        p.avg = Math.round(p.total / p.sample)
        //console.log(v);
        //console.log(p);
        return p;
      },
      (p,v) => {
          --p.sample;
        //p.total -= (v.open + v.close) / 2;
        p.total -= v.wvht;
        //p.avg = p.days ? Math.round(p.total / p.days) : 0;
        p.avg = p.sample ? Math.round(p.total / p.sample) : 0;
        return p;

      },
      () => {
        return {
          sample: 0,
          total: 0,
          avg: 0
        };
      }

    );

    return waveAverageHeightGroup;
  }

  //end of class
}
<|endoftext|>


<commit_before>var tessel = require('tessel');
var climatelib = require('climate-si7005');
var climate = climatelib.use(tessel.port['A']);
var http = require('http');
var led = require('tessel-led');

var PREFS = {
    location: 'home-dining-table',
    timeout: 5*1000,
    host: '192.168.1.147',
    port: 3000,
    path: '/api/weather'
};

var readClimate = function () {
    resetLEDs();

    var o = {};

    // get celsius temperature
    climate.readTemperature(function (err, tempC) {
        led.green.hide();

        if (err) {
            console.log('TESSEL TEMP C ERROR');
            console.log(err);
            led.red.show();
            return;
        }

        o.temp = {
            c: tempC
        };

        // get fahrenheit temperature (lazy, yes)
        climate.readTemperature('f', function (err, tempF) {
            led.green.hide();

            if (err) {
                console.log('TESSEL TEMP F ERROR');
                console.log(err);
                led.red.show();
                return;
            }

            o.temp.f = tempF;

            // get humidity
            climate.readHumidity(function (err, humid) {
                led.green.hide();

                if (err) {
                    console.log('TESSEL HUMID ERROR');
                    console.log(err);
                    led.red.show();
                    return;
                }

                o.humid = humid;

                o.location = PREFS.location;
                o.date = new Date();
                saveToDb(o);
            });
        })
    });
};

var saveToDb = function (o) {
    // stringify data
    var postDataString = JSON.stringify(o);

    var headers = {
        'Content-Type': 'application/json',
        'Content-Length': postDataString.length
    };

    var options = {
        method : 'POST',
        host : PREFS.host,
        port : PREFS.port,
        path : PREFS.path,
        headers: headers
    };

    // define POST request
    var request = http.request(options, function (res) {
        res.setEncoding('utf-8');
        var responseString = '';

        res.on('data', function (data) {
            console.log('data received');
            responseString += data;
        });

        res.on('end', function () {
            led.green.flash(1, 500);
            console.log('data written to:', options.path, JSON.parse(responseString));
            reset();
        });
    });

    request.on('error', function (err) {
        led.red.show();
        console.log('request error', err);
        reset();
    });

    // send POST request + flash blue LED
    led.blue.flash(1, 250);
    request.write(postDataString);

    request.end();
};

var reset = function () {
    setTimeout(readClimate, PREFS.timeout);
};

var resetLEDs = function () {
    led.amber.hide();
    led.blue.hide();
    led.green.hide();
    led.red.hide();
};

climate.on('ready', readClimate);

<commit_msg>cleanup - removed comments.<commit_after>var PREFS = {
    location: 'home-dining-table',
    timeout: 5*1000,
    host: '192.168.1.147',
    port: 3000,
    path: '/api/weather'
};

// dependencies
var tessel = require('tessel');
var climatelib = require('climate-si7005');
var climate = climatelib.use(tessel.port['A']);
var http = require('http');
var led = require('tessel-led');

var readClimate = function () {
    resetLEDs();

    var o = {};

    // get celsius temperature
    climate.readTemperature(function (err, tempC) {
        led.green.hide();

        if (err) {
            console.log('TESSEL TEMP C ERROR');
            console.log(err);
            led.red.show();
            return;
        }

        o.temp = {
            c: tempC
        };

        // get fahrenheit temperature (lazy, yes)
        climate.readTemperature('f', function (err, tempF) {
            led.green.hide();

            if (err) {
                console.log('TESSEL TEMP F ERROR');
                console.log(err);
                led.red.show();
                return;
            }

            o.temp.f = tempF;

            // get humidity
            climate.readHumidity(function (err, humid) {
                led.green.hide();

                if (err) {
                    console.log('TESSEL HUMID ERROR');
                    console.log(err);
                    led.red.show();
                    return;
                }

                o.humid = humid;

                o.location = PREFS.location;
                o.date = new Date();
                saveToDb(o);
            });
        })
    });
};

var saveToDb = function (o) {
    // stringify data
    var postDataString = JSON.stringify(o);

    var headers = {
        'Content-Type': 'application/json',
        'Content-Length': postDataString.length
    };

    var options = {
        method : 'POST',
        host : PREFS.host,
        port : PREFS.port,
        path : PREFS.path,
        headers: headers
    };

    // define POST request
    var request = http.request(options, function (res) {
        res.setEncoding('utf-8');
        var responseString = '';

        res.on('data', function (data) {
            responseString += data;
        });

        res.on('end', function () {
            led.green.flash(1, 500);
            reset();
        });
    });

    request.on('error', function (err) {
        led.red.show();
        console.log('request error', err);
        reset();
    });

    // send POST request + flash blue LED
    led.blue.flash(1, 250);
    request.write(postDataString);

    request.end();
};

var reset = function () {
    setTimeout(readClimate, PREFS.timeout);
};

var resetLEDs = function () {
    led.amber.hide();
    led.blue.hide();
    led.green.hide();
    led.red.hide();
};

climate.on('ready', readClimate);

<|endoftext|>


<commit_before>// When application runs in development mode this
// module activates convenient shortcuts for you.

(function () {
    'use strict';

    var gui = require('nw.gui');

    var registerShortcuts = function () {
        var keyUp = function (e) {
            if ((e.ctrlKey || e.metaKey) && e.keyCode == 82) {
                // CTRL (CMD) + R reloads the page
                gui.Window.get().reload();
            }
            if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.keyCode == 73) {
                // CTRL (CMD) + SHIFT + I shows devtools window
                gui.Window.get().showDevTools();
            }
        }
        document.addEventListener('keyup', keyUp, false);
    };

    if (gui.App.manifest.developmentMode) {
        registerShortcuts();
    }

}());
<commit_msg>Fixed keyboard shortcuts for mac<commit_after>// When application runs in development mode this
// module activates convenient shortcuts for you.

(function () {
    'use strict';

    var gui = require('nw.gui');

    var registerShortcuts = function () {
        var cmdPressed = false;

        var keyDown = function () {
            if (e.keyCode == 91 || e.keyCode == 93) {
                cmdPressed = true;
            }
        };

        var keyUp = function (e) {
            if (e.keyCode == 91 || e.keyCode == 93) {
                cmdPressed = false;
            }
            if ((e.ctrlKey || cmdPressed) && e.keyCode == 82) {
                // CTRL (CMD) + R reloads the page
                gui.Window.get().reload();
            }
            if ((e.ctrlKey || cmdPressed) && e.shiftKey && e.keyCode == 73) {
                // CTRL (CMD) + SHIFT + I shows devtools window
                gui.Window.get().showDevTools();
            }
        };

        document.addEventListener('keydown', keyDown, false);
        document.addEventListener('keyup', keyUp, false);
    };

    if (gui.App.manifest.developmentMode) {
        registerShortcuts();
    }

}());
<|endoftext|>