work on player ui
This commit is contained in:
		@ -543,9 +543,15 @@ class SectionPlaylist(Section):
 | 
			
		||||
        """
 | 
			
		||||
        fields = {
 | 
			
		||||
            'name': 'name',
 | 
			
		||||
            'duration': lambda e, o: (
 | 
			
		||||
                o.duration.hour, o.duration.minute, o.duration.second
 | 
			
		||||
            ),
 | 
			
		||||
            'embed': 'embed',
 | 
			
		||||
            'duration': lambda e, o:
 | 
			
		||||
                o.duration.hour * 3600 + o.duration.minute * 60 +
 | 
			
		||||
                o.duration.second
 | 
			
		||||
            ,
 | 
			
		||||
            'duration_str': lambda e, o:
 | 
			
		||||
                (str(o.duration.hour) + '"' if o.duration.hour else '') +
 | 
			
		||||
                str(o.duration.minute) + "'" + str(o.duration.second)
 | 
			
		||||
            ,
 | 
			
		||||
            'sources': lambda e, o: [ o.url() ],
 | 
			
		||||
            'detail_url':
 | 
			
		||||
                lambda e, o: o.diffusion and hasattr(o.diffusion, 'page') \
 | 
			
		||||
 | 
			
		||||
@ -1,22 +0,0 @@
 | 
			
		||||
.nav-submenu h2 {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nav-submenu .menu-item .info {
 | 
			
		||||
  margin-right: 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nav-submenu .menu-item a {
 | 
			
		||||
  white-space: normal;
 | 
			
		||||
  padding: 0.9em 1em 0.9em 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nav-submenu .menu-item a:hover {
 | 
			
		||||
  background-color: rgba(100, 100, 100, 0.2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nav-submenu li {
 | 
			
		||||
  border: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -408,7 +408,7 @@ ul.list, .list > ul {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.date_list_item.now {
 | 
			
		||||
    padding: 0.4em;    
 | 
			
		||||
    padding: 0.4em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    .date_list_item img.now {
 | 
			
		||||
@ -486,153 +486,111 @@ ul.list, .list > ul {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** content: player **/
 | 
			
		||||
.player {
 | 
			
		||||
/** component: sound **/
 | 
			
		||||
.component.sound {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    margin: 0.2em;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    .player:not([seekable]) > .controls > .progress {
 | 
			
		||||
        display: none;
 | 
			
		||||
    .component.sound[state="play"] button {
 | 
			
		||||
        animation-name: sound-blink;
 | 
			
		||||
        animation-duration: 4s;
 | 
			
		||||
        animation-iteration-count: infinite;
 | 
			
		||||
        animation-direction: alternate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @keyframes sound-blink {
 | 
			
		||||
        from { background-color: rgba(255, 255, 255, 0); }
 | 
			
		||||
        to { background-color: rgba(255, 255, 255, 0.6); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.player .controls {
 | 
			
		||||
    margin-top: 1em;
 | 
			
		||||
    text-align: right;
 | 
			
		||||
}
 | 
			
		||||
    .player .controls > * {
 | 
			
		||||
        margin: 0em 0.2em;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .player .controls .single {
 | 
			
		||||
        display: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        .player .controls .single + label {
 | 
			
		||||
            display: inline-block;
 | 
			
		||||
            font-size: 1em;
 | 
			
		||||
            padding: 0.1em;
 | 
			
		||||
            width: 1.5em;
 | 
			
		||||
            height: 1.0em;
 | 
			
		||||
            text-align: center;
 | 
			
		||||
            box-shadow: inset 0em 0em 0.1em #818181;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .player .controls .single:not(:checked) + label {
 | 
			
		||||
            border-left: 2px #818181 solid;
 | 
			
		||||
            color: black;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .player .controls .single:checked + label {
 | 
			
		||||
            border-right: 2px #818181 solid;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
.player .playlist .item {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    margin: 0em;
 | 
			
		||||
    padding: 0.2em 0.4em;
 | 
			
		||||
.component.sound .button {
 | 
			
		||||
    width: 4em;
 | 
			
		||||
    height: 4em;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    margin-right: 0.4em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    .player .playlist .item:hover {
 | 
			
		||||
        color: #007EDF;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .player .item > * {
 | 
			
		||||
        vertical-align: middle;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .player .playlist .item .actions {
 | 
			
		||||
        display: none;
 | 
			
		||||
    .component.sound .button > img {
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        height: 100%;
 | 
			
		||||
        max-width: 2.9em;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .component.sound button {
 | 
			
		||||
        transition: background-color 0.5s;
 | 
			
		||||
        background-color: rgba(255,255,255,0.1);
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        right: 0px;
 | 
			
		||||
        font-size: 1.2em;
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        top: 0;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        height: 100%;
 | 
			
		||||
        border: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .component.sound button:hover {
 | 
			
		||||
        background-color: rgba(255,255,255,0.5);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .component.sound button > img {
 | 
			
		||||
        background-color: rgba(255,255,255,0.9);
 | 
			
		||||
        border-radius: 50%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
.component.sound .content {
 | 
			
		||||
    position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    .component.sound .info {
 | 
			
		||||
        text-align: right;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        .player .playlist .item:hover .actions {
 | 
			
		||||
            display: inline;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    .player .playlist .item .action {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        width: 1.2em;
 | 
			
		||||
        height: 1.2em;
 | 
			
		||||
        border-radius: 0.2em;
 | 
			
		||||
        text-align: center;
 | 
			
		||||
        line-height: 1.2em;
 | 
			
		||||
        background-color: #F2F2F2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        .player .playlist .item .action:hover {
 | 
			
		||||
            background-color: rgba(0, 126, 223, 0.1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    .player .playlist .duration {
 | 
			
		||||
       text-align: right;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .player .playlist progress {
 | 
			
		||||
    .component.sound progress {
 | 
			
		||||
        width: 100%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.player .item[selected] {
 | 
			
		||||
    border-left: 1px #007EDF solid;
 | 
			
		||||
    font-size: 1.0em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.player .item:not([selected]) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.player .button {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    height: 2.0em;
 | 
			
		||||
    background: none;
 | 
			
		||||
    border: none;
 | 
			
		||||
    font-size: 1.4em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    .player .button > img {
 | 
			
		||||
        max-height: 2.0em;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .player:not([state]) .item[selected] .button > img:not(.play),
 | 
			
		||||
    .player[state="paused"] .item[selected] .button > img:not(.play),
 | 
			
		||||
    .player[state="playing"] .item[selected] .button > img:not(.pause),
 | 
			
		||||
    .player[state="loading"] .item[selected] .button > img:not(.loading)
 | 
			
		||||
    {
 | 
			
		||||
        display: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .player .item:not([selected]) .button > img.play {
 | 
			
		||||
        display: block;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .player .item:not([selected]) .button > img:not(.play):not(.cover) {
 | 
			
		||||
        display: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .player .item .button > img.cover {
 | 
			
		||||
        display: block;
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        transition: opacity 0.2s;
 | 
			
		||||
        bottom: 0;
 | 
			
		||||
        height: 0.4em;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .player .item:hover .button > img.cover {
 | 
			
		||||
        opacity: 0.2;
 | 
			
		||||
    .component.sound progress:hover {
 | 
			
		||||
        height: 1em;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** component: playlist **/
 | 
			
		||||
.component.playlist footer {
 | 
			
		||||
    text-align: right;
 | 
			
		||||
    display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main .player .actions .action:not(.add),
 | 
			
		||||
.section_player .actions .action.add,
 | 
			
		||||
.player .list_item.live:hover .actions {
 | 
			
		||||
.component.playlist .read_all {
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    .component.playlist .read_all + label {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        padding: 0.1em;
 | 
			
		||||
        margin-left: 0.2em;
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
        font-size: 1em;
 | 
			
		||||
        box-shadow: inset 0em 0em 0.1em #818181;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .component.playlist .read_all:not(:checked) + label {
 | 
			
		||||
        border-left: 0.1em #818181 solid;
 | 
			
		||||
        margin-right: 0em;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .component.playlist .read_all:checked + label {
 | 
			
		||||
        border-right: 0.1em #007EDF solid;
 | 
			
		||||
        box-shadow: inset 0em 0em 0.1em #007EDF;
 | 
			
		||||
        margin-right: 0em;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** content: page **/
 | 
			
		||||
main .body ~ section:not(.comments) {
 | 
			
		||||
 | 
			
		||||
@ -19,36 +19,8 @@
 | 
			
		||||
 | 
			
		||||
/** detail view **/
 | 
			
		||||
 | 
			
		||||
/** player **/
 | 
			
		||||
.player[state='playing'] .item[selected] .button > img {
 | 
			
		||||
    animation-duration: 4s;
 | 
			
		||||
    animation-iteration-count: infinite;
 | 
			
		||||
    animation-name: blink;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@keyframes blink {
 | 
			
		||||
    from {
 | 
			
		||||
        opacity: 1.0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    50% {
 | 
			
		||||
        opacity: 0.3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    to {
 | 
			
		||||
        opacity: 1.0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.player[state="loading"] .item[selected] .button > img.loading {
 | 
			
		||||
    animation-duration: 2s;
 | 
			
		||||
    animation-iteration-count: infinite;
 | 
			
		||||
    animation-name: rotate;
 | 
			
		||||
    animation-timing-function: linear;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@keyframes rotate {
 | 
			
		||||
    from {
 | 
			
		||||
        transform: rotate(0deg);
 | 
			
		||||
 | 
			
		||||
@ -1,21 +1,13 @@
 | 
			
		||||
/*  Implementation status: -- TODO
 | 
			
		||||
 *  - actions:
 | 
			
		||||
 *      - add to user playlist
 | 
			
		||||
 *      - go to detail
 | 
			
		||||
 *      - remove from playlist: for user playlist
 | 
			
		||||
 *  - save sound infos:
 | 
			
		||||
 *      - while playing: save current position
 | 
			
		||||
 *      - otherwise: remove from localstorage
 | 
			
		||||
 *      - save playlist in localstorage
 | 
			
		||||
 *  - proper design
 | 
			
		||||
 *  - mini-button integration in lists (list of diffusion articles)
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
var State = Object.freeze({
 | 
			
		||||
    Stop: Symbol('Stop'),
 | 
			
		||||
    Loading: Symbol('Loading'),
 | 
			
		||||
    Play: Symbol('Play'),
 | 
			
		||||
    Stop: 'stop',
 | 
			
		||||
    Loading: 'loading',
 | 
			
		||||
    Play: 'play',
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -110,28 +102,29 @@ var Sound = Vue.extend({
 | 
			
		||||
            state: State.Stop,
 | 
			
		||||
            // current position in playing sound
 | 
			
		||||
            position: 0,
 | 
			
		||||
            // estimated position when user mouse over progress bar
 | 
			
		||||
            seek_position: null,
 | 
			
		||||
            // url to the page related to the sound
 | 
			
		||||
            detail_url: '',
 | 
			
		||||
            // estimated position when user mouse over progress bar
 | 
			
		||||
            user_seek: null,
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    computed: {
 | 
			
		||||
        // sound can be seeked
 | 
			
		||||
        seekable: function() {
 | 
			
		||||
        seekable() {
 | 
			
		||||
            // seekable: for the moment only when we have a podcast file
 | 
			
		||||
            // note: need mounted because $refs is not reactive
 | 
			
		||||
            return this.mounted && this.duration && this.$refs.audio.seekable;
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        // sound duration in seconds
 | 
			
		||||
        duration: function() {
 | 
			
		||||
            if(this.track.duration)
 | 
			
		||||
                return this.track.duration[0] * 3600 +
 | 
			
		||||
                       this.track.duration[1] * 60 +
 | 
			
		||||
                       this.track.duration[2];
 | 
			
		||||
            return null;
 | 
			
		||||
        duration() {
 | 
			
		||||
            return this.track.duration;
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        seek_position() {
 | 
			
		||||
            return (this.user_seek === null && this.position) ||
 | 
			
		||||
                    this.user_seek;
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@ -196,6 +189,29 @@ var Sound = Vue.extend({
 | 
			
		||||
            tracks.splice(i, 1);
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
        // Utils functions
 | 
			
		||||
        //
 | 
			
		||||
        _as_progress_time(event) {
 | 
			
		||||
            bounding = this.$refs.progress.getBoundingClientRect()
 | 
			
		||||
            offset = (event.clientX - bounding.left);
 | 
			
		||||
            return offset * this.$refs.audio.duration / bounding.width;
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        // format seconds into time string such as: [h"m]m'ss
 | 
			
		||||
        format_time(seconds) {
 | 
			
		||||
            seconds = Math.floor(seconds);
 | 
			
		||||
            var hours = Math.floor(seconds / 3600);
 | 
			
		||||
            seconds -= hours * 3600;
 | 
			
		||||
            var minutes = Math.floor(seconds / 60);
 | 
			
		||||
            seconds -= minutes * 60;
 | 
			
		||||
 | 
			
		||||
            return  (hours ? ((hours < 10 ? '0' + hours : hours) + '"') : '') +
 | 
			
		||||
                    minutes + "'" + seconds
 | 
			
		||||
            ;
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
        // Events
 | 
			
		||||
        //
 | 
			
		||||
@ -214,21 +230,15 @@ var Sound = Vue.extend({
 | 
			
		||||
            this.$emit('ended', this);
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        _as_progress_time(event) {
 | 
			
		||||
            bounding = this.$refs.progress.getBoundingClientRect()
 | 
			
		||||
            offset = (event.clientX - bounding.left);
 | 
			
		||||
            return offset * this.$refs.audio.duration / bounding.width;
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        progress_mouse_out(event) {
 | 
			
		||||
            this.seek_position = null;
 | 
			
		||||
            this.user_seek = null;
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        progress_mouse_move(event) {
 | 
			
		||||
            if(this.$refs.audio.duration == Infinity ||
 | 
			
		||||
                    isNaN(this.$refs.audio.duration))
 | 
			
		||||
               return;
 | 
			
		||||
            this.seek_position = this._as_progress_time(event);
 | 
			
		||||
            this.user_seek = this._as_progress_time(event);
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        progress_clicked(event) {
 | 
			
		||||
@ -250,8 +260,8 @@ var Playlist = Vue.extend({
 | 
			
		||||
        return {
 | 
			
		||||
            // if true, use this playlist as user's default playlist
 | 
			
		||||
            default: false,
 | 
			
		||||
            // single mode enabled
 | 
			
		||||
            single_mode: false,
 | 
			
		||||
            // read all mode enabled
 | 
			
		||||
            read_all: false,
 | 
			
		||||
            // playlist can be modified by user
 | 
			
		||||
            modifiable: false,
 | 
			
		||||
            // if set, save items into localstorage using this root key
 | 
			
		||||
@ -261,6 +271,13 @@ var Playlist = Vue.extend({
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    computed: {
 | 
			
		||||
        // id of the read all mode checkbox switch
 | 
			
		||||
        read_all_id() {
 | 
			
		||||
            return this.id + "_read_all";
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    mounted() {
 | 
			
		||||
        // set default
 | 
			
		||||
        if(this.default) {
 | 
			
		||||
@ -283,8 +300,8 @@ var Playlist = Vue.extend({
 | 
			
		||||
            // ensure sound is stopped (beforeDestroy())
 | 
			
		||||
            sound.stop();
 | 
			
		||||
 | 
			
		||||
            // next only when single mode
 | 
			
		||||
            if(this.single_mode)
 | 
			
		||||
            // next only when read all mode
 | 
			
		||||
            if(!this.read_all)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            var sounds = this.$refs.sounds;
 | 
			
		||||
 | 
			
		||||
@ -9,16 +9,21 @@
 | 
			
		||||
<a-playlist class="playlist" id="{{ playlist_id }}">
 | 
			
		||||
    <noscript>
 | 
			
		||||
        {% for track in tracks %}
 | 
			
		||||
        <li class="item">
 | 
			
		||||
            <span class="name">{{ track.name }} ({{ track.duration|date:"H\"i's" }}): </span>
 | 
			
		||||
        <div class="item">
 | 
			
		||||
            <span class="name">
 | 
			
		||||
                {{ track.data.name }}
 | 
			
		||||
                {% if track.data.duration %}
 | 
			
		||||
                    ({{ track.data.duration_str }})
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            </span>
 | 
			
		||||
            <span class="podcast">
 | 
			
		||||
            {% if not track.embed %}
 | 
			
		||||
            {% if not track.data.embed %}
 | 
			
		||||
            <audio src="{{ track.url|escape }}" controls>
 | 
			
		||||
            {% else %}
 | 
			
		||||
            {{ track.embed|safe }}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            </span>
 | 
			
		||||
        </li>
 | 
			
		||||
        </div>
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
    </noscript>
 | 
			
		||||
    <script>
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,9 @@
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
 | 
			
		||||
<script type="text/x-template" id="template-sound">
 | 
			
		||||
    <div class="sound">
 | 
			
		||||
    <div class="component sound flex_row"
 | 
			
		||||
         :state="state"
 | 
			
		||||
    >
 | 
			
		||||
        <audio preload="metadata" ref="audio"
 | 
			
		||||
            @pause="state = State.Stop"
 | 
			
		||||
            @playing="state = State.Play"
 | 
			
		||||
@ -11,29 +13,41 @@
 | 
			
		||||
        >
 | 
			
		||||
            <source v-for="source in track.sources" :src="source">
 | 
			
		||||
        </audio>
 | 
			
		||||
        <img :src="track.cover" v-if="track.cover" class="icon cover">
 | 
			
		||||
        <button class="button" @click="play_stop">
 | 
			
		||||
            <img class="icon pause"
 | 
			
		||||
                 src="{% static "aircox/images/pause.png" %}"
 | 
			
		||||
                 title="{% trans "Click to pause" %}"
 | 
			
		||||
                 v-if="state === State.Play" >
 | 
			
		||||
            <img class="icon loading"
 | 
			
		||||
                 src="{% static "aircox/images/loading.png" %}"
 | 
			
		||||
                 title="{% trans "Loading... Click to pause" %}"
 | 
			
		||||
                 v-else-if="state === State.Loading" >
 | 
			
		||||
            <img class="icon play"
 | 
			
		||||
                 src="{% static "aircox/images/play.png" %}"
 | 
			
		||||
                 title="{% trans "Click to play" %}"
 | 
			
		||||
                 v-else >
 | 
			
		||||
        </button>
 | 
			
		||||
        <div>
 | 
			
		||||
            <h3>
 | 
			
		||||
        <div class="cover button">
 | 
			
		||||
            <img :src="track.cover" v-if="track.cover">
 | 
			
		||||
            <button @click="play_stop">
 | 
			
		||||
                <img class="icon pause"
 | 
			
		||||
                     src="{% static "aircox/images/pause.png" %}"
 | 
			
		||||
                     title="{% trans "Click to pause" %}"
 | 
			
		||||
                     v-if="state === State.Play" >
 | 
			
		||||
                <img class="icon loading"
 | 
			
		||||
                     src="{% static "aircox/images/loading.png" %}"
 | 
			
		||||
                     title="{% trans "Loading... Click to pause" %}"
 | 
			
		||||
                     v-else-if="state === State.Loading" >
 | 
			
		||||
                <img class="icon play"
 | 
			
		||||
                     src="{% static "aircox/images/play.png" %}"
 | 
			
		||||
                     title="{% trans "Click to play" %}"
 | 
			
		||||
                     v-else >
 | 
			
		||||
            </button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="content flex_item">
 | 
			
		||||
            <h3 class="flex_item">
 | 
			
		||||
                <a :href="detail_url">[[ track.name ]]</a>
 | 
			
		||||
            </h3>
 | 
			
		||||
            <span v-if="track.duration" class="info">
 | 
			
		||||
                [[ (track.duration[0] && track.duration[0] + '"') || '' ]]
 | 
			
		||||
                [[ track.duration[1] + "'" + track.duration[2] ]]
 | 
			
		||||
            </span>
 | 
			
		||||
            <div v-if="track.duration" class="info">
 | 
			
		||||
                <span v-if="seek_position !== null">
 | 
			
		||||
                [[ format_time(seek_position) ]] /
 | 
			
		||||
                </span>
 | 
			
		||||
                <span v-else-if="state == State.Play">[[ format_time(position) ]] /</span>
 | 
			
		||||
                [[ format_time(track.duration) ]]
 | 
			
		||||
            </div>
 | 
			
		||||
            <progress ref="progress"
 | 
			
		||||
                v-show="state == State.Play && track.duration"
 | 
			
		||||
                v-on:click.prevent="progress_clicked"
 | 
			
		||||
                v-on:mousemove = "progress_mouse_move"
 | 
			
		||||
                v-on:mouseout = "progress_mouse_out"
 | 
			
		||||
                :value="seek_position" :max="duration"
 | 
			
		||||
            ></progress>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="actions">
 | 
			
		||||
            <a class="action remove"
 | 
			
		||||
@ -47,35 +61,28 @@
 | 
			
		||||
                v-else
 | 
			
		||||
                >+</a>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="content flex_row" v-show="track.duration != null">
 | 
			
		||||
            <span v-if="seek_position !== null">[[ seek_position ]]</span>
 | 
			
		||||
            <span v-else>[[ position ]]</span>
 | 
			
		||||
            <progress class="flex_item progress" ref="progress"
 | 
			
		||||
                v-show="track.duration"
 | 
			
		||||
                v-on:click.prevent="progress_clicked"
 | 
			
		||||
                v-on:mousemove = "progress_mouse_move"
 | 
			
		||||
                v-on:mouseout = "progress_mouse_out"
 | 
			
		||||
                :value="position" :max="duration"
 | 
			
		||||
            ></progress>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<script type="text/x-template" id="template-playlist">
 | 
			
		||||
    <div class="playlist">
 | 
			
		||||
    <div class="component playlist">
 | 
			
		||||
        <a-sound v-for="track in tracks" ref="sounds"
 | 
			
		||||
            :id="track.id" :track="track"
 | 
			
		||||
            @ended="sound_ended"
 | 
			
		||||
            @beforeDestroy="sound_ended"
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <div v-show="tracks.length > 1" class="playlist_footer">
 | 
			
		||||
            <input type="checkbox" class="single" id="[[ playlist ]]_single_mode"
 | 
			
		||||
                value="true" v-model="single_mode">
 | 
			
		||||
            <label for="[[ playlist ]]_single_mode" class="info"
 | 
			
		||||
                title="{% trans "Enable and disable single mode" %}">↻</label>
 | 
			
		||||
        </div>
 | 
			
		||||
        <footer v-show="tracks.length > 1" class="info">
 | 
			
		||||
            <span v-show="read_all">{% trans "read all" %}</span>
 | 
			
		||||
            <input type="checkbox" class="read_all"
 | 
			
		||||
                :id="read_all_id"
 | 
			
		||||
                value="true" v-model="read_all">
 | 
			
		||||
            <label :for="read_all_id"
 | 
			
		||||
                title="{% trans "Read all the playlist" %}">
 | 
			
		||||
                <img src="{% static "aircox/images/list.png" %}" class="small icon">
 | 
			
		||||
            </label>
 | 
			
		||||
        </footer>
 | 
			
		||||
    </div>
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user