Assets from bouquins-bchs
This commit is contained in:
parent
52f327036a
commit
eaf4b6a239
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.swp
|
||||
*~
|
6
assets/css/bootstrap.min.css
vendored
Normal file
6
assets/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
73
assets/html/author.html
Normal file
73
assets/html/author.html
Normal file
@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<title>Auteur | Bouquins</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
<link rel="preload" href="js/author.min.js" as="script">
|
||||
<link rel="preload" href="js/vue.min.js" as="script">
|
||||
<link rel="prefetch" href="js/author.min.js">
|
||||
<link rel="prefetch" href="js/vue.min.js">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-inverse" id="nav">
|
||||
<div class="container">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="index.html">Accueil</a></li>
|
||||
<li><a href="search.html">Recherche</a></li>
|
||||
<li><a href="#">A propos</a></li>
|
||||
</ul>
|
||||
<form class="navbar-form navbar-right" role="search" method="get" action="search.html">
|
||||
<div class="form-group">
|
||||
<input name="q" type="text" class="form-control" placeholder="Recherche">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container" id="app">
|
||||
<div class="page-header" v-if="author.id">
|
||||
<h1>
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
{{ author.name }}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="alert alert-danger" role="alert" v-else>Aucun livre sélectionné</div>
|
||||
<ul class="nav nav-pills">
|
||||
<li role="presentation" :class="{ active: tab == 'books' }"><a href="#" @click="showBooks">Livres</a></li>
|
||||
<li v-if="author.series && author.series.length > 0" role="presentation" :class="{ active: tab == 'series' }"><a href="#" @click="showSeries">Series</a></li>
|
||||
<li v-if="author.authors && author.authors.length > 0"role="presentation" :class="{ active: tab == 'authors' }"><a href="#" @click="showAuthors">Co-auteurs</a></li>
|
||||
</ul>
|
||||
<div class="panel panel-default" :class="{ hidden: tab != 'books' }">
|
||||
<div class="panel-body">
|
||||
<ul v-for="book in author.books" class="list-unstyled">
|
||||
<li><span class="glyphicon glyphicon-book"></span>
|
||||
<a :href="'book.html?id='+book.id">{{ book.title }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default" :class="{ hidden: tab != 'series' }">
|
||||
<div class="panel-body">
|
||||
<ul v-for="series in author.series" class="list-unstyled">
|
||||
<li><span class="glyphicon glyphicon-list"></span>
|
||||
<a :href="'series.html?id='+series.id">{{ series.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default" :class="{ hidden: tab != 'authors' }">
|
||||
<div class="panel-body">
|
||||
<ul v-for="coauthor in author.authors" class="list-unstyled">
|
||||
<li> <span class="glyphicon glyphicon-user"></span>
|
||||
<a :href="'author.html?id='+coauthor.id">{{ coauthor.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/vue.min.js"></script>
|
||||
<script src="js/author.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
92
assets/html/book.html
Normal file
92
assets/html/book.html
Normal file
@ -0,0 +1,92 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<title>Livre | Bouquins</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
<link rel="preload" href="js/book.min.js" as="script">
|
||||
<link rel="preload" href="js/vue.min.js" as="script">
|
||||
<link rel="prefetch" href="js/book.min.js">
|
||||
<link rel="prefetch" href="js/vue.min.js">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-inverse" id="nav">
|
||||
<div class="container">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="index.html">Accueil</a></li>
|
||||
<li><a href="search.html">Recherche</a></li>
|
||||
<li><a href="#">A propos</a></li>
|
||||
</ul>
|
||||
<form class="navbar-form navbar-right" role="search" method="get" action="search.html">
|
||||
<div class="form-group">
|
||||
<input name="q" type="text" class="form-control" placeholder="Recherche">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container" id="app">
|
||||
<div class="page-header" v-if="book.id">
|
||||
<div class="row" v-if="book.has_cover">
|
||||
<img :src="bookCover(book)" alt="Pas de couverture" title="Couverture" class="img-responsive img-rounded"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-9">
|
||||
<h1>
|
||||
<span class="glyphicon glyphicon-book"></span>
|
||||
{{ book.title }}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-3 text-right">
|
||||
<template v-for="data in book.data">
|
||||
<a :href="bookLink(book, data)" class="btn btn-success">
|
||||
<span class="glyphicon glyphicon-download-alt"></span> Télécharger
|
||||
{{ data.format }} ({{ formatBytes(data.size) }})
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-danger" role="alert" v-else>Aucun livre sélectionné</div>
|
||||
<div class="row" v-if="book.id">
|
||||
<h2>
|
||||
<span class="glyphicon glyphicon-user"></span> Auteur{{ book.authors.length > 1 ? 's' : '' }}
|
||||
</h2>
|
||||
<ul>
|
||||
<li v-for="author in book.authors">
|
||||
<a :href="'author.html?id='+author.id">{{ author.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 v-if="book.series">
|
||||
<span class="glyphicon glyphicon-list"></span> Serie
|
||||
</h2>
|
||||
<div v-if="book.series">
|
||||
<a :href="'series.html?id='+book.series.id">{{ book.series.name }}</a>
|
||||
<span class="badge">{{ book.series.idx }}</span>
|
||||
</div>
|
||||
|
||||
<h2><span class="glyphicon glyphicon-globe"></span> Langue</h2>
|
||||
<ul><li>{{ book.lang.toUpperCase() }}</li></ul>
|
||||
|
||||
<h2 v-if="book.tags">
|
||||
<span class="glyphicon glyphicon-tags"></span> Tags
|
||||
</h2>
|
||||
<div v-if="book.tags">
|
||||
<template v-for="tag in book.tags">
|
||||
<span class="label label-info">{{ tag }}</span>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<h2>Détails</h2>
|
||||
<ul>
|
||||
<li v-if="book.pubdate"><strong>Date de publication</strong> {{ book.pubdate }}</li>
|
||||
<li v-if="book.publisher"><strong>Editeur</strong> {{ book.publisher }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- TODO cover -->
|
||||
</div>
|
||||
<script src="js/vue.min.js"></script>
|
||||
<script src="js/book.min.js"></script>
|
||||
</body>
|
||||
</html>
|
120
assets/html/index.html
Normal file
120
assets/html/index.html
Normal file
@ -0,0 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<title>Bouquins</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
<link rel="preload" href="js/index.min.js" as="script">
|
||||
<link rel="preload" href="js/vue.min.js" as="script">
|
||||
<link rel="prefetch" href="js/index.min.js">
|
||||
<link rel="prefetch" href="js/vue.min.js">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-inverse" id="nav">
|
||||
<div class="container">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="active"><a href="#">Accueil</a></li>
|
||||
<li><a href="search.html">Recherche</a></li>
|
||||
<li><a href="#">A propos</a></li>
|
||||
</ul>
|
||||
<form class="navbar-form navbar-right" role="search" method="get" action="search.html">
|
||||
<div class="form-group">
|
||||
<input name="q" type="text" class="form-control" placeholder="Recherche">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container" id="app">
|
||||
<div class="jumbotron">
|
||||
<h1>Bouquins</h1>
|
||||
<p>Cette bibliothèque contient actuellement <strong>{{ booksCount }}</strong> livres et BD en format papier ou électronique.</p>
|
||||
<button class="btn btn-primary" type="button" @click="showBooks">Livres</button>
|
||||
<button class="btn btn-primary" type="button" @click="showAuthors">Auteurs</button>
|
||||
<button class="btn btn-primary" type="button" @click="showSeries">Series</button>
|
||||
</div>
|
||||
<div class="table-responsive" v-if="books.length > 0 || authors.length > 0 || series.length > 0">
|
||||
<nav aria-label="Pages">
|
||||
<ul class="pager">
|
||||
<li class="previous" v-bind:class="{ disabled: page <= 1 }"><a href="#" @click="prevPage"><span aria-hidden="true">←</span> Précédents</a></li>
|
||||
<li class="next"><a href="#" @click="nextPage">Suivants <span aria-hidden="true">→</span></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<table class="table table-striped" v-if="series.length > 0">
|
||||
<tr>
|
||||
<th>
|
||||
<a href="#" @click="sortBy('name')">Nom</a>
|
||||
<span v-if="sort_by == 'name'" :class="['glyphicon', { 'glyphicon-chevron-up': order_desc , 'glyphicon-chevron-down': !order_desc}]"></span>
|
||||
</th>
|
||||
<th>Livre(s)</th>
|
||||
<th>Auteur(s)</th>
|
||||
</tr>
|
||||
<tr v-for="serie in series">
|
||||
<td>
|
||||
<span class="glyphicon glyphicon-list"></span>
|
||||
<a :href="'series.html?id='+serie.id">{{ serie.name }}</a>
|
||||
</td>
|
||||
<td>{{ serie.count }}</td>
|
||||
<td>
|
||||
<template v-for="author in serie.authors">
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
<a :href="'author.html?id='+author.id">{{ author.name }}</a>
|
||||
</template>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="table table-striped" v-if="authors.length > 0">
|
||||
<tr>
|
||||
<th>
|
||||
<a href="#" @click="sortBy('name')">Nom</a>
|
||||
<span v-if="sort_by == 'name'" :class="['glyphicon', { 'glyphicon-chevron-up': order_desc , 'glyphicon-chevron-down': !order_desc}]"></span>
|
||||
</th>
|
||||
<th>Livre(s)</th>
|
||||
</tr>
|
||||
<tr v-for="author in authors">
|
||||
<td>
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
<a :href="'author.html?id='+author.id">{{ author.name }}</a>
|
||||
</td>
|
||||
<td>{{ author.count }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="table table-striped" v-if="books.length > 0">
|
||||
<tr>
|
||||
<th>
|
||||
<a href="#" @click="sortBy('title')">Nom</a>
|
||||
<span v-if="sort_by == 'title'" :class="['glyphicon', { 'glyphicon-chevron-up': order_desc , 'glyphicon-chevron-down': !order_desc}]"></span>
|
||||
</th>
|
||||
<th>Auteur(s)</th>
|
||||
<th>Serie</th>
|
||||
</tr>
|
||||
<tr v-for="book in books">
|
||||
<td><span class="glyphicon glyphicon-book"></span>
|
||||
<a :href="'book.html?id='+book.id">{{ book.title }}</a></td>
|
||||
<td>
|
||||
<template v-for="author in book.authors">
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
<a :href="'author.html?id='+author.id">{{ author.name }}</a>
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<template v-if="book.series">
|
||||
<span class="glyphicon glyphicon-list"></span>
|
||||
<a :href="'series.html?id='+book.series.id">{{ book.series.name }}</a>
|
||||
<span class="badge">{{ book.series ? book.series.idx : '' }}</span>
|
||||
</template>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<nav aria-label="Pages">
|
||||
<ul class="pager">
|
||||
<li class="previous" v-bind:class="{ disabled: page <= 1 }"><a href="#" @click="prevPage"><span aria-hidden="true">←</span> Précédents</a></li>
|
||||
<li class="next"><a href="#" @click="nextPage">Suivants <span aria-hidden="true">→</span></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/vue.min.js"></script>
|
||||
<script src="js/index.min.js"></script>
|
||||
</body>
|
||||
</html>
|
114
assets/html/search.html
Normal file
114
assets/html/search.html
Normal file
@ -0,0 +1,114 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<title>Bouquins</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
<link rel="preload" href="js/search.min.js" as="script">
|
||||
<link rel="preload" href="js/vue.min.js" as="script">
|
||||
<link rel="prefetch" href="js/search.min.js">
|
||||
<link rel="prefetch" href="js/vue.min.js">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-inverse" id="nav">
|
||||
<div class="container">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="index.html">Accueil</a></li>
|
||||
<li class="active"><a href="search.html">Recherche</a></li>
|
||||
<li><a href="#">A propos</a></li>
|
||||
</ul>
|
||||
<form class="navbar-form navbar-right" role="search" method="get" action="search.html">
|
||||
<div class="form-group">
|
||||
<input name="q" type="text" class="form-control" placeholder="Recherche">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container" id="app">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3>Recherche</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form id="searchForm" @submit="searchFull" v-on:submit.prevent="onSubmit">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" placeholder="Recherche" v-model="q">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Parmi</label><br/>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="books" v-model="which"> livres
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="authors" v-model="which"> auteurs
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="series" v-model="which"> series
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="all" v-model="which"> tous
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Nombre de resultats</label><br/>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="10" v-model="perpage"> 10
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="20" v-model="perpage"> 20
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="50" v-model="perpage"> 50
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="100" v-model="perpage"> 100
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" v-model="all" disabled> Tous les mots
|
||||
</label>
|
||||
<p class="help-block">Cocher pour rechercher les élements contenant tous les mots saisis</p>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Rechercher</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="books.length > 0">
|
||||
<h2>{{ booksCount }} <template v-if="booksCount>1">livres</template><template v-else>livre</template></h2>
|
||||
<ul>
|
||||
<li v-for="book in books" class="list-unstyled">
|
||||
<span class="glyphicon glyphicon-book"></span>
|
||||
<a :href="'book.html?id='+book.id">{{ book.title }}</a>
|
||||
</li>
|
||||
<li v-if="books.length < booksCount" class="list-unstyled">...</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="authors.length > 0">
|
||||
<h2>{{ authorsCount }} <template v-if="authorsCount>1">auteurs</template><template v-else>auteur</template></h2>
|
||||
<ul>
|
||||
<li v-for="author in authors" class="list-unstyled">
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
<a :href="'author.html?id='+author.id">{{ author.name }}</a>
|
||||
</li>
|
||||
<li v-if="authors.length < authorsCount" class="list-unstyled">...</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="series.length > 0">
|
||||
<h2>{{ seriesCount }} <template v-if="seriesCount>1">series</template><template v-else>serie</template></h2>
|
||||
<ul>
|
||||
<li v-for="serie in series" class="list-unstyled">
|
||||
<span class="glyphicon glyphicon-list"></span>
|
||||
<a :href="'series.html?id='+serie.id">{{ serie.name }}</a>
|
||||
</li>
|
||||
<li v-if="series.length < seriesCount" class="list-unstyled">...</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/vue.min.js"></script>
|
||||
<script src="js/search.min.js"></script>
|
||||
</body>
|
||||
</html>
|
60
assets/html/series.html
Normal file
60
assets/html/series.html
Normal file
@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<title>Serie | Bouquins</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
<link rel="preload" href="js/series.min.js" as="script">
|
||||
<link rel="preload" href="js/vue.min.js" as="script">
|
||||
<link rel="prefetch" href="js/series.min.js">
|
||||
<link rel="prefetch" href="js/vue.min.js">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-inverse" id="nav">
|
||||
<div class="container">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="index.html">Accueil</a></li>
|
||||
<li><a href="search.html">Recherche</a></li>
|
||||
<li><a href="#">A propos</a></li>
|
||||
</ul>
|
||||
<form class="navbar-form navbar-right" role="search" method="get" action="search.html">
|
||||
<div class="form-group">
|
||||
<input name="q" type="text" class="form-control" placeholder="Recherche">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container" id="app">
|
||||
<div class="page-header" v-if="series">
|
||||
<h1>
|
||||
<span class="glyphicon glyphicon-list"></span>
|
||||
{{ series.name }}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="alert alert-danger" role="alert" v-else>Aucune série sélectionnée</div>
|
||||
<template v-if="series">
|
||||
<h2>
|
||||
<span class="glyphicon glyphicon-book"></span> Livre(s)
|
||||
</h2>
|
||||
<ul>
|
||||
<li v-for="book in series.books" class="list-unstyled">{{ book.series.idx }}.
|
||||
<a :href="'book.html?id='+book.id">{{ book.title }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<span class="glyphicon glyphicon-user"></span> Auteur(s)
|
||||
</h2>
|
||||
<ul>
|
||||
<li v-for="author in series.authors" class="list-unstyled">
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
<a :href="'author.html?id='+author.id">{{ author.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</div>
|
||||
<script src="js/vue.min.js"></script>
|
||||
<script src="js/series.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
101
assets/js/author.js
Normal file
101
assets/js/author.js
Normal file
@ -0,0 +1,101 @@
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
urlParams: {},
|
||||
author: {},
|
||||
tab: "books"
|
||||
},
|
||||
methods: {
|
||||
urlParse: function() {
|
||||
var match,
|
||||
pl = /\+/g, // Regex for replacing addition symbol with a space
|
||||
search = /([^&=]+)=?([^&]*)/g,
|
||||
decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
|
||||
query = window.location.search.substring(1);
|
||||
while (match = search.exec(query))
|
||||
this.urlParams[decode(match[1])] = decode(match[2]);
|
||||
},
|
||||
sendQuery: function(url, error, success) {
|
||||
var xmh = new XMLHttpRequest();
|
||||
var v;
|
||||
|
||||
xmh.onreadystatechange = function() {
|
||||
v = xmh.responseText;
|
||||
if (xmh.readyState === 4 && xmh.status === 200) {
|
||||
var res;
|
||||
try {
|
||||
res = JSON.parse(v);
|
||||
} catch (err) {
|
||||
if (null !== error)
|
||||
error(err.name, err.message);
|
||||
}
|
||||
if (null !== success)
|
||||
success(res);
|
||||
} else if (xmh.readyState === 4) {
|
||||
if (null !== error)
|
||||
error(xmh.status, v);
|
||||
}
|
||||
};
|
||||
|
||||
xmh.open('GET', url, true);
|
||||
xmh.send(null);
|
||||
},
|
||||
stdError: function(code, resp) {
|
||||
console.log('ERROR ' + code + ': ' + resp);
|
||||
},
|
||||
authorSuccess: function(resp) {
|
||||
this.author = resp;
|
||||
document.title = this.author.name +' | Bouquins';
|
||||
this.author.series=[];
|
||||
this.author.authors=[];
|
||||
if (this.author.books) {
|
||||
var series = [];
|
||||
var authors = [];
|
||||
for (var i=0;i<this.author.books.length;i++) {
|
||||
var book = this.author.books[i];
|
||||
if (book.series)
|
||||
series.push(book.series);
|
||||
if (book.authors)
|
||||
authors.push.apply(authors, book.authors);
|
||||
}
|
||||
if (series.length > 0) {
|
||||
series.sort();
|
||||
this.author.series = [series[0]];
|
||||
for (var i=1;i<series.length;i++) {
|
||||
if (series[i-1].id !== series[i].id)
|
||||
this.author.series.push(series[i]);
|
||||
}
|
||||
}
|
||||
if (authors.length > 0) {
|
||||
authors.sort();
|
||||
for (var i=0;i<authors.length;i++) {
|
||||
if ((this.author.authors.length == 0
|
||||
|| this.author.authors[this.author.authors.length] != authors[i])
|
||||
&& authors[i].id != this.author.id)
|
||||
this.author.authors.push(authors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
showBooks: function() {
|
||||
this.tab = "books";
|
||||
},
|
||||
showAuthors: function() {
|
||||
this.tab = "authors";
|
||||
},
|
||||
showSeries: function() {
|
||||
this.tab = "series";
|
||||
},
|
||||
loadAuthor: function() {
|
||||
if (this.urlParams.id)
|
||||
this.sendQuery('cgi-bin/bouquins/authors/' + this.urlParams.id, this.stdError, this.authorSuccess);
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
this.urlParse();
|
||||
},
|
||||
mounted: function() {
|
||||
this.loadAuthor();
|
||||
}
|
||||
})
|
73
assets/js/book.js
Normal file
73
assets/js/book.js
Normal file
@ -0,0 +1,73 @@
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
urlParams: {},
|
||||
book: {}
|
||||
},
|
||||
methods: {
|
||||
urlParse: function() {
|
||||
var match,
|
||||
pl = /\+/g, // Regex for replacing addition symbol with a space
|
||||
search = /([^&=]+)=?([^&]*)/g,
|
||||
decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
|
||||
query = window.location.search.substring(1);
|
||||
while (match = search.exec(query))
|
||||
this.urlParams[decode(match[1])] = decode(match[2]);
|
||||
},
|
||||
sendQuery: function(url, error, success) {
|
||||
var xmh = new XMLHttpRequest();
|
||||
var v;
|
||||
|
||||
xmh.onreadystatechange = function() {
|
||||
v = xmh.responseText;
|
||||
if (xmh.readyState === 4 && xmh.status === 200) {
|
||||
var res;
|
||||
try {
|
||||
res = JSON.parse(v);
|
||||
} catch (err) {
|
||||
if (null !== error)
|
||||
error(err.name, err.message);
|
||||
}
|
||||
if (null !== success)
|
||||
success(res);
|
||||
} else if (xmh.readyState === 4) {
|
||||
if (null !== error)
|
||||
error(xmh.status, v);
|
||||
}
|
||||
};
|
||||
|
||||
xmh.open('GET', url, true);
|
||||
xmh.send(null);
|
||||
},
|
||||
stdError: function(code, resp) {
|
||||
console.log('ERROR ' + code + ': ' + resp);
|
||||
},
|
||||
formatBytes: function(bytes) {
|
||||
if(bytes == 0) return '0';
|
||||
var k = 1024; // or 1024 for binary
|
||||
var sizes = ['Octets', 'Ko', 'Mo', 'Go', 'To', 'Po', 'Eo', 'Zo', 'Yo'];
|
||||
var i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
},
|
||||
bookCover: function(book) {
|
||||
return '/calibre/' + encodeURI(book.path) + '/cover.jpg';
|
||||
},
|
||||
bookLink: function(book, data) {
|
||||
return '/calibre/' + encodeURI(book.path) + '/' + encodeURI(data.name) + '.' + data.format.toLowerCase();
|
||||
},
|
||||
bookSuccess: function(resp) {
|
||||
this.book = resp;
|
||||
document.title = this.book.title +' | Bouquins';
|
||||
},
|
||||
loadBook: function() {
|
||||
if (this.urlParams.id)
|
||||
this.sendQuery('cgi-bin/bouquins/books/' + this.urlParams.id, this.stdError, this.bookSuccess);
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
this.urlParse();
|
||||
},
|
||||
mounted: function() {
|
||||
this.loadBook();
|
||||
}
|
||||
})
|
145
assets/js/index.js
Normal file
145
assets/js/index.js
Normal file
@ -0,0 +1,145 @@
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
books: [],
|
||||
authors: [],
|
||||
series: [],
|
||||
booksCount: 0,
|
||||
page: 1,
|
||||
perpage: 20,
|
||||
sort_by: null,
|
||||
order_desc: false
|
||||
},
|
||||
methods: {
|
||||
sendQuery: function(url, error, success) {
|
||||
var xmh = new XMLHttpRequest();
|
||||
var v;
|
||||
|
||||
xmh.onreadystatechange = function() {
|
||||
v = xmh.responseText;
|
||||
if (xmh.readyState === 4 && xmh.status === 200) {
|
||||
var res;
|
||||
try {
|
||||
res = JSON.parse(v);
|
||||
} catch (err) {
|
||||
if (null !== error)
|
||||
error(err.name, err.message);
|
||||
}
|
||||
if (null !== success)
|
||||
success(res);
|
||||
} else if (xmh.readyState === 4) {
|
||||
if (null !== error)
|
||||
error(xmh.status, v);
|
||||
}
|
||||
};
|
||||
|
||||
xmh.open('GET', url, true);
|
||||
xmh.send(null);
|
||||
},
|
||||
stdError: function(code, resp) {
|
||||
console.log('ERROR ' + code + ': ' + resp);
|
||||
},
|
||||
indexSuccess: function(resp) {
|
||||
this.booksCount = resp.count;
|
||||
},
|
||||
loadIndex: function() {
|
||||
this.sendQuery('cgi-bin/bouquins/index', this.stdError, this.indexSuccess);
|
||||
},
|
||||
authorsSuccess: function(resp) {
|
||||
this.authors = resp;
|
||||
},
|
||||
booksSuccess: function(resp) {
|
||||
this.books = resp;
|
||||
},
|
||||
seriesSuccess: function(resp) {
|
||||
this.series = resp;
|
||||
},
|
||||
sortBy: function(col) {
|
||||
if (this.sort_by == col) {
|
||||
if (this.order_desc) {
|
||||
this.order_desc = false;
|
||||
this.sort_by = null;
|
||||
} else {
|
||||
this.order_desc = true;
|
||||
}
|
||||
} else {
|
||||
this.order_desc = false;
|
||||
this.sort_by = col;
|
||||
}
|
||||
this.reload();
|
||||
},
|
||||
reload: function() {
|
||||
if (this.books.length > 0)
|
||||
this.loadBooks();
|
||||
if (this.authors.length > 0)
|
||||
this.loadAuthors();
|
||||
if (this.series.length > 0)
|
||||
this.loadSeries();
|
||||
},
|
||||
prevPage: function() {
|
||||
if (this.page > 1) {
|
||||
this.page--;
|
||||
this.reload();
|
||||
}
|
||||
},
|
||||
nextPage: function() {
|
||||
this.page++;
|
||||
this.reload();
|
||||
},
|
||||
order: function(query) {
|
||||
if (this.order_desc)
|
||||
return query + '&order=desc';
|
||||
return query;
|
||||
},
|
||||
sort: function(query) {
|
||||
if (this.sort_by)
|
||||
return query + '&sort=' + this.sort_by;
|
||||
return query;
|
||||
},
|
||||
paginate: function(query) {
|
||||
return query + '?page=' + this.page + '&perpage=' + this.perpage;
|
||||
},
|
||||
params: function(url) {
|
||||
return this.order(this.sort(this.paginate(url)));
|
||||
},
|
||||
loadAuthors: function() {
|
||||
this.sendQuery(this.params('cgi-bin/bouquins/authors'), this.stdError, this.authorsSuccess);
|
||||
},
|
||||
loadBooks: function() {
|
||||
this.sendQuery(this.params('cgi-bin/bouquins/books'), this.stdError, this.booksSuccess);
|
||||
},
|
||||
loadSeries: function() {
|
||||
this.sendQuery(this.params('cgi-bin/bouquins/series'), this.stdError, this.seriesSuccess);
|
||||
},
|
||||
showSeries: function() {
|
||||
this.books = [];
|
||||
this.authors = [];
|
||||
this.page = 1;
|
||||
this.perpage = 20;
|
||||
this.order_desc = false;
|
||||
this.sort_by = null;
|
||||
this.loadSeries();
|
||||
},
|
||||
showAuthors: function() {
|
||||
this.books = [];
|
||||
this.series = [];
|
||||
this.page = 1;
|
||||
this.perpage = 20;
|
||||
this.order_desc = false;
|
||||
this.sort_by = null;
|
||||
this.loadAuthors();
|
||||
},
|
||||
showBooks: function() {
|
||||
this.authors = [];
|
||||
this.series = [];
|
||||
this.page = 1;
|
||||
this.perpage = 20;
|
||||
this.order_desc = false;
|
||||
this.sort_by = null;
|
||||
this.loadBooks();
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
this.loadIndex();
|
||||
}
|
||||
})
|
136
assets/js/search.js
Normal file
136
assets/js/search.js
Normal file
@ -0,0 +1,136 @@
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
urlParams: {},
|
||||
authors: [],
|
||||
books: [],
|
||||
series: [],
|
||||
authorsCount: 0,
|
||||
booksCount: 0,
|
||||
seriesCount: 0,
|
||||
q: '',
|
||||
which: 'all',
|
||||
all: false,
|
||||
perpage: 10
|
||||
},
|
||||
methods: {
|
||||
urlParse: function() {
|
||||
var match,
|
||||
pl = /\+/g, // Regex for replacing addition symbol with a space
|
||||
search = /([^&=]+)=?([^&]*)/g,
|
||||
decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
|
||||
query = window.location.search.substring(1);
|
||||
while (match = search.exec(query))
|
||||
this.urlParams[decode(match[1])] = decode(match[2]);
|
||||
},
|
||||
sendQuery: function(url, error, success) {
|
||||
var xmh = new XMLHttpRequest();
|
||||
var v;
|
||||
|
||||
xmh.onreadystatechange = function() {
|
||||
v = xmh.responseText;
|
||||
if (xmh.readyState === 4 && xmh.status === 200) {
|
||||
var res;
|
||||
try {
|
||||
res = JSON.parse(v);
|
||||
} catch (err) {
|
||||
if (null !== error)
|
||||
error(err.name, err.message);
|
||||
}
|
||||
if (null !== success)
|
||||
success(res);
|
||||
} else if (xmh.readyState === 4) {
|
||||
if (null !== error)
|
||||
error(xmh.status, v);
|
||||
}
|
||||
};
|
||||
|
||||
xmh.open('GET', url, true);
|
||||
xmh.send(null);
|
||||
},
|
||||
stdError: function(code, resp) {
|
||||
console.log('ERROR ' + code + ': ' + resp);
|
||||
},
|
||||
searchParams: function(url) {
|
||||
var res = url;
|
||||
res += '?perpage=' + this.perpage;
|
||||
for (var i=0; i<this.terms.length; i++) {
|
||||
var t = this.terms[i];
|
||||
if (t.trim())
|
||||
res += '&term=' + encodeURIComponent(t.trim());
|
||||
}
|
||||
return res;
|
||||
},
|
||||
searchAuthorsSuccess: function(res) {
|
||||
this.authorsCount = res.count;
|
||||
this.authors = res.authors;
|
||||
},
|
||||
searchAuthors: function() {
|
||||
this.sendQuery(this.searchParams('cgi-bin/bouquins/authors'), this.stdError, this.searchAuthorsSuccess);
|
||||
},
|
||||
searchBooksSuccess: function(res) {
|
||||
this.booksCount = res.count;
|
||||
this.books = res.books;
|
||||
},
|
||||
searchBooks: function() {
|
||||
this.sendQuery(this.searchParams('cgi-bin/bouquins/books'), this.stdError, this.searchBooksSuccess);
|
||||
},
|
||||
searchSeriesSuccess: function(res) {
|
||||
this.seriesCount = res.count;
|
||||
this.series = res.series;
|
||||
},
|
||||
searchSeries: function() {
|
||||
this.sendQuery(this.searchParams('cgi-bin/bouquins/series'), this.stdError, this.searchSeriesSuccess);
|
||||
},
|
||||
searchAll: function() {
|
||||
this.clear();
|
||||
this.searchAuthors();
|
||||
this.searchBooks();
|
||||
this.searchSeries();
|
||||
},
|
||||
clear: function() {
|
||||
this.authors = [];
|
||||
this.books = [];
|
||||
this.series = [];
|
||||
this.authorsCount = 0;
|
||||
this.booksCount = 0;
|
||||
this.seriesCount = 0;
|
||||
},
|
||||
searchFull: function() {
|
||||
if (this.q) {
|
||||
this.terms = this.q.split(' ');
|
||||
switch (this.which) {
|
||||
case 'all':
|
||||
this.searchAll();
|
||||
break;
|
||||
case 'authors':
|
||||
this.clear();
|
||||
this.searchAuthors();
|
||||
break;
|
||||
case 'books':
|
||||
this.clear();
|
||||
this.searchBooks();
|
||||
break;
|
||||
case 'series':
|
||||
this.clear();
|
||||
this.searchSeries();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
searchUrl: function() {
|
||||
if (this.urlParams.q) {
|
||||
this.terms = this.urlParams.q.split(' ');
|
||||
this.searchAll();
|
||||
this.q = this.urlParams.q;
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
this.urlParse();
|
||||
},
|
||||
mounted: function() {
|
||||
this.searchUrl();
|
||||
}
|
||||
})
|
60
assets/js/series.js
Normal file
60
assets/js/series.js
Normal file
@ -0,0 +1,60 @@
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
urlParams: {},
|
||||
series: {}
|
||||
},
|
||||
methods: {
|
||||
urlParse: function() {
|
||||
var match,
|
||||
pl = /\+/g, // Regex for replacing addition symbol with a space
|
||||
search = /([^&=]+)=?([^&]*)/g,
|
||||
decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
|
||||
query = window.location.search.substring(1);
|
||||
while (match = search.exec(query))
|
||||
this.urlParams[decode(match[1])] = decode(match[2]);
|
||||
},
|
||||
sendQuery: function(url, error, success) {
|
||||
var xmh = new XMLHttpRequest();
|
||||
var v;
|
||||
|
||||
xmh.onreadystatechange = function() {
|
||||
v = xmh.responseText;
|
||||
if (xmh.readyState === 4 && xmh.status === 200) {
|
||||
var res;
|
||||
try {
|
||||
res = JSON.parse(v);
|
||||
} catch (err) {
|
||||
if (null !== error)
|
||||
error(err.name, err.message);
|
||||
}
|
||||
if (null !== success)
|
||||
success(res);
|
||||
} else if (xmh.readyState === 4) {
|
||||
if (null !== error)
|
||||
error(xmh.status, v);
|
||||
}
|
||||
};
|
||||
|
||||
xmh.open('GET', url, true);
|
||||
xmh.send(null);
|
||||
},
|
||||
stdError: function(code, resp) {
|
||||
console.log('ERROR ' + code + ': ' + resp);
|
||||
},
|
||||
seriesSucces: function(resp) {
|
||||
this.series = resp;
|
||||
document.title = this.series.name +' | Bouquins';
|
||||
},
|
||||
loadSeries: function() {
|
||||
if (this.urlParams.id)
|
||||
this.sendQuery('cgi-bin/bouquins/series/' + this.urlParams.id, this.stdError, this.seriesSucces);
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
this.urlParse();
|
||||
},
|
||||
mounted: function() {
|
||||
this.loadSeries();
|
||||
}
|
||||
})
|
Loading…
Reference in New Issue
Block a user