Paginate/infinite scroll for albums, playlists (#1234)
* Infinite scroll, pagination to album, playlists * move pagination below tracks * Make page size configurable * remove renderer
This commit is contained in:
parent
575ef649ef
commit
8967cf7647
8 changed files with 328 additions and 151 deletions
175
src/renderer/views/components/pagination.ejs
Normal file
175
src/renderer/views/components/pagination.ejs
Normal file
|
@ -0,0 +1,175 @@
|
|||
<script type="text/x-template" id="pagination">
|
||||
<div class="row" style="margin-bottom: 16px" v-if="!isInfinite">
|
||||
<button
|
||||
class="col md-btn page-btn"
|
||||
:disabled="effectivePage === 1"
|
||||
@click="goToPage(1)"
|
||||
>
|
||||
<img class="md-ico-first"/>
|
||||
</button>
|
||||
<button
|
||||
class="col md-btn page-btn prev"
|
||||
:disabled="effectivePage === 1"
|
||||
@click="goToPrevious()"
|
||||
>
|
||||
<img class="md-ico-prev"/>
|
||||
</button>
|
||||
<button
|
||||
:class="`col md-btn page-btn${ isCurrentPage(page) ? ' md-btn-primary': ''}`"
|
||||
@click="goToPage(page)"
|
||||
v-for="page in pagesToShow"
|
||||
>{{ page }}</button>
|
||||
<button
|
||||
class="col md-btn page-btn next"
|
||||
:disabled="effectivePage === numPages"
|
||||
@click="goToNext()"
|
||||
>
|
||||
<img class="md-ico-next"/>
|
||||
</button>
|
||||
<button
|
||||
class="col md-btn page-btn last"
|
||||
:disabled="effectivePage === numPages"
|
||||
@click="goToEnd()"
|
||||
>
|
||||
<img class="md-ico-last"/>
|
||||
</button>
|
||||
<div class="col page-btn" style="min-width: 12em;">
|
||||
<input type="number" min="1" :max="numPages" :value="effectivePage" @change="changePage" />
|
||||
<span>/ {{ numPages }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script>
|
||||
Vue.component('pagination', {
|
||||
template: "#pagination",
|
||||
props: {
|
||||
'length': { type: Number, required: true },
|
||||
'pageSize': { type: Number, required: true },
|
||||
'scroll': { type: String, required: true },
|
||||
'scrollSelector': { type: String, required: true }
|
||||
},
|
||||
data: function () {
|
||||
return { currentPage: 1 }
|
||||
},
|
||||
mounted() {
|
||||
document.querySelector(this.scrollSelector)
|
||||
.addEventListener("scroll", this.handleScroll)
|
||||
},
|
||||
destroyed() {
|
||||
document.querySelector(this.scrollSelector)
|
||||
.removeEventListener("scroll", this.handleScroll)
|
||||
},
|
||||
watch: {
|
||||
'length': function () {
|
||||
if (this.isInfinite) {
|
||||
// If a search reduces the number of things to show, we want to limit
|
||||
// the number of songs shown as well. This is to prevent you scrolling
|
||||
// to load your entire library, searching for one song, and then having
|
||||
// th re-render the entire library
|
||||
if (this.currentPage > this.numPages) {
|
||||
this.currentPage = this.numPages;
|
||||
this.$emit("onRangeChange", this.currentRange);
|
||||
}
|
||||
} else {
|
||||
this.$emit("onRangeChange", this.currentRange);
|
||||
}
|
||||
},
|
||||
'scroll': function () {
|
||||
// When changing modes, set the page to 1. This is primarily to
|
||||
// prevent going to a high page (e.g., 50) and then switching to infinite
|
||||
// and showing 12.5k songs
|
||||
this.currentPage = 1;
|
||||
this.$emit("onRangeChange", this.currentRange);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isInfinite: function () {
|
||||
return this.scroll === "infinite"
|
||||
},
|
||||
currentRange: function () {
|
||||
if (this.isInfinite) {
|
||||
return [0, this.currentPage * this.pageSize];
|
||||
} else {
|
||||
const startingPage = Math.min(this.numPages, this.currentPage);
|
||||
|
||||
return [
|
||||
(startingPage - 1) * this.pageSize,
|
||||
startingPage * this.pageSize
|
||||
];
|
||||
}
|
||||
},
|
||||
effectivePage: function () {
|
||||
return Math.min(this.currentPage, this.numPages)
|
||||
},
|
||||
numPages: function () {
|
||||
return Math.ceil(this.length / this.pageSize) || 1;
|
||||
},
|
||||
pagesToShow: function () {
|
||||
let start = this.currentPage - 2;
|
||||
let end = this.currentPage + 2;
|
||||
|
||||
if (start < 1) {
|
||||
end += (1 - start);
|
||||
start = 1;
|
||||
}
|
||||
|
||||
const endDifference = end - this.numPages;
|
||||
if (endDifference > 0) {
|
||||
end = this.numPages;
|
||||
start = Math.max(1, start - endDifference);
|
||||
}
|
||||
|
||||
const array = [];
|
||||
for (let idx = start; idx <= end; idx++) {
|
||||
array.push(idx);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// Infinite Scrolling
|
||||
handleScroll: function (event) {
|
||||
if (this.isInfinite &&
|
||||
this.currentPage < this.numPages &&
|
||||
event.target.scrollTop >= event.target.scrollHeight - event.target.clientHeight) {
|
||||
this.currentPage += 1;
|
||||
this.$emit("onRangeChange", this.currentRange);
|
||||
}
|
||||
},
|
||||
// Pagination
|
||||
isCurrentPage: function (idx) {
|
||||
return idx === this.currentPage ||
|
||||
(idx === this.numPages && this.currentPage > this.numPages);
|
||||
},
|
||||
changePage: function (event) {
|
||||
const value = event.target.valueAsNumber;
|
||||
|
||||
if (!isNaN(value) && value >= 1 && value <= this.numPages) {
|
||||
this.currentPage = value;
|
||||
this.$emit("onRangeChange", this.currentRange);
|
||||
}
|
||||
},
|
||||
goToPage: function (page) {
|
||||
this.currentPage = page;
|
||||
this.$emit("onRangeChange", this.currentRange);
|
||||
},
|
||||
goToPrevious: function () {
|
||||
if (this.currentPage > 1) {
|
||||
this.currentPage -= 1;
|
||||
this.$emit("onRangeChange", this.currentRange);
|
||||
}
|
||||
},
|
||||
goToNext: function () {
|
||||
if (this.currentPage < this.numPages) {
|
||||
this.currentPage += 1;
|
||||
this.$emit("onRangeChange", this.currentRange);
|
||||
}
|
||||
},
|
||||
goToEnd: function () {
|
||||
this.currentPage = this.numPages;
|
||||
this.$emit("onRangeChange", this.currentRange);
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
|
@ -1262,6 +1262,27 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
{{$root.getLz('settings.option.general.pagination')}}<br>
|
||||
<small>
|
||||
{{$root.getLz('settings.options.general.pagination.description')}}<br>
|
||||
</small>
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto">
|
||||
<label>
|
||||
<select class="md-select" style="width:180px;"
|
||||
v-model.number="$root.cfg.libraryPrefs.pageSize" type="number">
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
<option value="250">250</option>
|
||||
<option value="500">500</option>
|
||||
<option value="1000">1000</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -107,6 +107,12 @@
|
|||
<img :class="(!inLibrary) ? 'md-ico-add' : 'md-ico-remove'">
|
||||
{{app.getLz('term.confirm')}}
|
||||
</button>
|
||||
<select v-if="shouldPaginate" class="md-select" v-model="prefs.scroll">
|
||||
<optgroup :label="app.getLz('term.scroll')">
|
||||
<option value="infinite">{{app.getLz('term.scroll.infinite')}}</option>
|
||||
<option value="paged">{{app.getLz('term.scroll.paged').replace("${songsPerPage}", pageSize)}}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<div style="display: flex; float: right;">
|
||||
<button :style="{ 'background': '#' + hasHeroObject()?.textColor4 ?? '' }" :class="['search-btn', showSearch ? 'active' : '']" @click="toggleSearch()" :aria-label="showSearch ? app.getLz('term.hideSearch') : app.getLz('term.showSearch')">
|
||||
<svg-icon :style="{ 'width': '15px', 'background-color': '#' + hasHeroObject()?.bgColor ?? '' }" :url="showSearch ? './assets/search-alt.svg' : './assets/search.svg'">
|
||||
|
@ -161,6 +167,12 @@
|
|||
<img :class="(!inLibrary) ? 'md-ico-add' : 'md-ico-remove'">
|
||||
{{app.getLz('term.confirm')}}
|
||||
</button>
|
||||
<select v-if="shouldPaginate" class="md-select" v-model="prefs.scroll">
|
||||
<optgroup :label="app.getLz('term.scroll')">
|
||||
<option value="infinite">{{app.getLz('term.scroll.infinite')}}</option>
|
||||
<option value="paged">{{app.getLz('term.scroll.paged').replace("${songsPerPage}", pageSize)}}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto cider-flex-center">
|
||||
|
@ -172,7 +184,7 @@
|
|||
</div>
|
||||
<div class="playlist-body scrollbody">
|
||||
<b-tabs pills class="track-pills pilldim fancy-pills" align="center" content-class="mt-3" :nav-wrapper-class="navClass(data)">
|
||||
<b-tab :title="$root.getLz('term.tracks')">
|
||||
<b-tab :title="$root.getLz('term.tracks')" id="songList">
|
||||
<div @wheel="minClass(true)" @scroll="minClass(true)">
|
||||
<div class="">
|
||||
<div style="width:100%" @click="minClass(true)">
|
||||
|
@ -186,18 +198,27 @@
|
|||
class="search-input"
|
||||
ref="search-bar">
|
||||
</div>
|
||||
<pagination
|
||||
v-if="shouldPaginate"
|
||||
style="margin-top: 10px"
|
||||
:length="hasNestedPlaylist ? nestedDisplayLength: displayListing.length"
|
||||
:pageSize="pageSize"
|
||||
:scroll="prefs.scroll"
|
||||
scrollSelector="#songList"
|
||||
@onRangeChange="onRangeChange"
|
||||
/>
|
||||
<draggable :options="{disabled: !editing}"
|
||||
v-model="data.relationships.tracks.data" @start="drag=true"
|
||||
@end="drag=false;put()">
|
||||
<template v-if="!hasNestedPlaylist">
|
||||
<mediaitem-list-item :item="item" :parent="getItemParent(data)" :index="index"
|
||||
<mediaitem-list-item :item="item" :parent="getItemParent(data)" :index="index + start"
|
||||
:showIndex="true"
|
||||
:showIndexPlaylist="(data.attributes.playParams?.kind ?? data.type ?? '').includes('playlist')"
|
||||
:context-ext="buildContextMenu()"
|
||||
v-for="(item,index) in displayListing"></mediaitem-list-item>
|
||||
v-for="(item,index) in currentSlice"></mediaitem-list-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div v-for="disc in nestedPlaylist">
|
||||
<div v-for="disc in nestedSlices">
|
||||
<div class="playlist-time">{{($root.getLz("term.discNumber") ??
|
||||
"").replace("${discNumber}",disc.disc)}}
|
||||
</div>
|
||||
|
@ -278,6 +299,7 @@
|
|||
props: ["data"],
|
||||
|
||||
data: function () {
|
||||
const pageSize = this.$root.cfg.libraryPrefs.pageSize;
|
||||
return {
|
||||
editorialNotesExpanded: false,
|
||||
drag: false,
|
||||
|
@ -291,6 +313,7 @@
|
|||
headerVisible: true,
|
||||
useArtistChip: false,
|
||||
nestedPlaylist: [],
|
||||
nestedDisplayLength: 0,
|
||||
classes: [],
|
||||
editing: false,
|
||||
inPlaylist: false,
|
||||
|
@ -298,6 +321,10 @@
|
|||
displayListing: [],
|
||||
hasNestedPlaylist: false,
|
||||
showSearch: false,
|
||||
pageSize: pageSize,
|
||||
start: 0,
|
||||
end: pageSize,
|
||||
prefs: this.$root.cfg.libraryPrefs.playlists
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
|
@ -346,7 +373,48 @@
|
|||
deep: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentSlice() {
|
||||
return this.displayListing.slice(this.start, this.end);
|
||||
},
|
||||
nestedSlices() {
|
||||
if (this.shouldPaginate) {
|
||||
let songsSeen = 0;
|
||||
const discs = [];
|
||||
|
||||
for (const disc of this.nestedPlaylist) {
|
||||
songsSeen += disc.tracks.length;
|
||||
|
||||
if (songsSeen >= this.end) {
|
||||
discs.push({
|
||||
disc: disc.disc,
|
||||
tracks: disc.tracks.slice(0, this.end + disc.tracks.length - songsSeen)
|
||||
})
|
||||
break;
|
||||
} else if (songsSeen > this.start) {
|
||||
discs.push({
|
||||
disc: disc.disc,
|
||||
tracks: disc.tracks.slice(this.start - songsSeen)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return discs;
|
||||
} else {
|
||||
return this.nestedPlaylist
|
||||
}
|
||||
},
|
||||
shouldPaginate() {
|
||||
const result = this.data.relationships.tracks.data.length > this.pageSize
|
||||
console.log(result)
|
||||
return result
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onRangeChange(newRange) {
|
||||
this.start = newRange[0];
|
||||
this.end = newRange[1];
|
||||
},
|
||||
isAlbum() {
|
||||
return (this.data.attributes?.playParams?.kind ?? this.data.type ?? '').includes('album')
|
||||
},
|
||||
|
@ -372,6 +440,8 @@
|
|||
},
|
||||
generateNestedPlaylist(songlists) {
|
||||
this.nestedPlaylist = [];
|
||||
this.nestedDisplayLength = songlists.length;
|
||||
|
||||
if (this.data?.type?.includes("album")) {
|
||||
let discs = songlists.map(x => {
|
||||
return x.attributes.discNumber
|
||||
|
|
|
@ -46,17 +46,33 @@
|
|||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<select class="md-select" v-model="prefs.scroll">
|
||||
<optgroup :label="app.getLz('term.scroll')">
|
||||
<option value="infinite">{{app.getLz('term.scroll.infinite')}}</option>
|
||||
<option value="paged">{{app.getLz('term.scroll.paged').replace("${songsPerPage}", pageSize)}}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<pagination
|
||||
:length="app.library.albums.displayListing.length
|
||||
"
|
||||
:pageSize="pageSize"
|
||||
:scroll="prefs.scroll"
|
||||
scrollSelector="#app-content"
|
||||
@onRangeChange="onRangeChange"
|
||||
/>
|
||||
<div class="well">
|
||||
<div class="albums-square-container">
|
||||
<div>
|
||||
<mediaitem-square v-if="prefs.viewAs == 'covers'" :size="'300'" :item="item" v-for="item in library.albums.displayListing">
|
||||
<mediaitem-square v-if="prefs.viewAs == 'covers'" :size="'300'" :item="item" v-for="item in currentSlice">
|
||||
</mediaitem-square>
|
||||
</div>
|
||||
</div>
|
||||
<mediaitem-list-item v-if="prefs.viewAs == 'list'" :show-duration="false" :show-meta-data="true" :show-library-status="false" :item="item" v-for="item in library.albums.displayListing">
|
||||
<mediaitem-list-item v-if="prefs.viewAs == 'list'" :show-duration="false" :show-meta-data="true" :show-library-status="false" :item="item" v-for="item in currentSlice">
|
||||
</mediaitem-list-item>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -65,21 +81,34 @@
|
|||
Vue.component('cider-library-albums', {
|
||||
template: '#cider-library-albums',
|
||||
data: function () {
|
||||
const pageSize = this.$root.cfg.libraryPrefs.pageSize;
|
||||
return {
|
||||
library: this.$root.library,
|
||||
mediaItemSize: "compact",
|
||||
prefs: this.$root.cfg.libraryPrefs.albums,
|
||||
app : this.$root
|
||||
app: this.$root,
|
||||
pageSize: pageSize,
|
||||
start: 0,
|
||||
end: pageSize
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$root.getLibraryAlbumsFull(null, 1);
|
||||
this.$root.getAlbumSort();
|
||||
this.$root.searchLibraryAlbums(1);
|
||||
this.$root.getLibrarySongsFull() ;
|
||||
this.$root.getLibrarySongsFull();
|
||||
this.$root.searchLibraryAlbums(1);
|
||||
},
|
||||
computed: {
|
||||
currentSlice: function () {
|
||||
return this.library.albums.displayListing.slice(this.start, this.end);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onRangeChange: function (newRange) {
|
||||
this.start = newRange[0];
|
||||
this.end = newRange[1];
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -64,45 +64,13 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-bottom: 16px" v-if="!isInfinite">
|
||||
<button
|
||||
class="col md-btn page-btn"
|
||||
:disabled="currentPage === 1"
|
||||
@click="goToPage(1)"
|
||||
>
|
||||
<img class="md-ico-first"/>
|
||||
</button>
|
||||
<button
|
||||
class="col md-btn page-btn prev"
|
||||
:disabled="currentPage === 1"
|
||||
@click="goToPrevious()"
|
||||
>
|
||||
<img class="md-ico-prev"/>
|
||||
</button>
|
||||
<button
|
||||
:class="`col md-btn page-btn${ isCurrentPage(page) ? ' md-btn-primary': ''}`"
|
||||
@click="goToPage(page)"
|
||||
v-for="page in pagesToShow"
|
||||
>{{ page }}</button>
|
||||
<button
|
||||
class="col md-btn page-btn next"
|
||||
:disabled="currentPage === numPages"
|
||||
@click="goToNext()"
|
||||
>
|
||||
<img class="md-ico-next"/>
|
||||
</button>
|
||||
<button
|
||||
class="col md-btn page-btn last"
|
||||
:disabled="currentPage === numPages"
|
||||
@click="goToEnd()"
|
||||
>
|
||||
<img class="md-ico-last"/>
|
||||
</button>
|
||||
<div class="col page-btn" style="min-width: 12em;">
|
||||
<input type="number" min="1" :max="numPages" :value="currentPage" @change="changePage" />
|
||||
<span>/ {{ numPages }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<pagination
|
||||
:length="library.songs.displayListing.length"
|
||||
:pageSize="pageSize"
|
||||
:scroll="prefs.scroll"
|
||||
scrollSelector="#app-content"
|
||||
@onRangeChange="onRangeChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="library.songs.downloadState == 3">Library contains no songs.</div>
|
||||
|
@ -119,125 +87,31 @@
|
|||
Vue.component('cider-library-songs', {
|
||||
template: '#cider-library-songs',
|
||||
data: function () {
|
||||
const pageSize = this.$root.cfg.libraryPrefs.pageSize;
|
||||
return {
|
||||
// currentPage is oneIndexed
|
||||
currentPage: 1,
|
||||
library: this.$root.library,
|
||||
mediaItemSize: "compact",
|
||||
pageSize: 250,
|
||||
prefs: this.$root.cfg.libraryPrefs.songs,
|
||||
app: this.$root
|
||||
app: this.$root,
|
||||
pageSize: pageSize,
|
||||
start: 0,
|
||||
end: pageSize
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.querySelector("#app-content")
|
||||
.addEventListener("scroll", this.handleScroll)
|
||||
this.$root.getLibrarySongsFull()
|
||||
},
|
||||
destroyed() {
|
||||
document.querySelector("#app-content")
|
||||
.removeEventListener("scroll", this.handleScroll)
|
||||
},
|
||||
watch: {
|
||||
'library.songs.displayListing.length': function () {
|
||||
if (this.isInfinite) {
|
||||
// If a search reduces the number of things to show, we want to limit
|
||||
// the number of songs shown as well. This is to prevent you scrolling
|
||||
// to load your entire library, searching for one song, and then having
|
||||
// th re-render the entire library
|
||||
if (this.currentPage > this.numPages) {
|
||||
this.currentPage = this.numPages;
|
||||
}
|
||||
}
|
||||
},
|
||||
'prefs.scroll': function () {
|
||||
// When changing modes, set the page to 1. This is primarily to
|
||||
// prevent going to a high page (e.g., 50) and then switching to infinite
|
||||
// and showing 12.5k songs
|
||||
this.currentPage = 1;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isInfinite: function () {
|
||||
return this.prefs.scroll === "infinite"
|
||||
},
|
||||
currentSlice: function () {
|
||||
if (this.isInfinite) {
|
||||
return this.library.songs.displayListing.slice(
|
||||
0, this.currentPage * this.pageSize
|
||||
);
|
||||
} else {
|
||||
const startingPage = Math.min(this.numPages, this.currentPage);
|
||||
|
||||
return this.library.songs.displayListing.slice(
|
||||
(startingPage - 1) * this.pageSize,
|
||||
startingPage * this.pageSize
|
||||
);
|
||||
}
|
||||
},
|
||||
numPages: function () {
|
||||
return Math.ceil(this.library.songs.displayListing.length / this.pageSize) || 1;
|
||||
},
|
||||
pagesToShow: function () {
|
||||
let start = this.currentPage - 2;
|
||||
let end = this.currentPage + 2;
|
||||
|
||||
if (start < 1) {
|
||||
end += (1 - start);
|
||||
start = 1;
|
||||
}
|
||||
|
||||
const endDifference = end - this.numPages;
|
||||
if (endDifference > 0) {
|
||||
end = this.numPages;
|
||||
start = Math.max(1, start - endDifference);
|
||||
}
|
||||
|
||||
const array = [];
|
||||
for (let idx = start; idx <= end; idx++) {
|
||||
array.push(idx);
|
||||
}
|
||||
return array;
|
||||
return this.library.songs.displayListing.slice(this.start, this.end);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// Infinite Scrolling
|
||||
handleScroll: function (event) {
|
||||
if (this.isInfinite &&
|
||||
this.currentPage < this.numPages &&
|
||||
event.target.scrollTop >= event.target.scrollHeight - event.target.clientHeight) {
|
||||
this.currentPage += 1;
|
||||
}
|
||||
onRangeChange: function (newRange) {
|
||||
this.start = newRange[0];
|
||||
this.end = newRange[1];
|
||||
},
|
||||
// Pagination
|
||||
isCurrentPage: function (idx) {
|
||||
return idx === this.currentPage ||
|
||||
(idx === this.numPages && this.currentPage > this.numPages);
|
||||
},
|
||||
changePage: function (event) {
|
||||
const value = event.target.valueAsNumber;
|
||||
|
||||
if (!isNaN(value) && value >= 1 && value <= this.numPages) {
|
||||
this.currentPage = value;
|
||||
}
|
||||
},
|
||||
goToPage: function (page) {
|
||||
this.currentPage = page;
|
||||
},
|
||||
goToPrevious: function () {
|
||||
if (this.currentPage > 1) {
|
||||
this.currentPage -= 1;
|
||||
}
|
||||
},
|
||||
goToNext: function () {
|
||||
if (this.currentPage < this.numPages) {
|
||||
this.currentPage += 1;
|
||||
}
|
||||
},
|
||||
goToEnd: function () {
|
||||
this.currentPage = this.numPages;
|
||||
},
|
||||
// Miscellaneous
|
||||
play: function () {
|
||||
function shuffleArray(array) {
|
||||
for (var i = array.length - 1; i > 0; i--) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue