Merge js, clean unused
This commit is contained in:
parent
ecdf3d5ae8
commit
e2edf6c944
@ -11,7 +11,6 @@ Bouquins in Go
|
||||
* tests
|
||||
* UI book: cover in background
|
||||
* auth downloads
|
||||
* clean unused js
|
||||
* version string in js url (cache)
|
||||
* csrf
|
||||
* vue.js dev/prod
|
||||
|
@ -1,17 +0,0 @@
|
||||
var author = new Vue({
|
||||
el: '#author',
|
||||
data: {
|
||||
tab: "books"
|
||||
},
|
||||
methods: {
|
||||
showBooks: function() {
|
||||
this.tab = "books";
|
||||
},
|
||||
showAuthors: function() {
|
||||
this.tab = "authors";
|
||||
},
|
||||
showSeries: function() {
|
||||
this.tab = "series";
|
||||
}
|
||||
}
|
||||
})
|
@ -1,73 +0,0 @@
|
||||
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();
|
||||
}
|
||||
})
|
408
assets/js/bouquins.js
Normal file
408
assets/js/bouquins.js
Normal file
@ -0,0 +1,408 @@
|
||||
var bus = new Vue();
|
||||
|
||||
// COMPONENTS //
|
||||
|
||||
Vue.component('results-list', {
|
||||
template: '#results-list-template',
|
||||
props: ['results', 'count', 'type'],
|
||||
methods: {
|
||||
url: function(item) {
|
||||
return '/'+this.type+'/'+item.id;
|
||||
},
|
||||
label: function(item) {
|
||||
switch (this.type) {
|
||||
case 'books':
|
||||
return item.title;
|
||||
case 'authors':
|
||||
case 'series':
|
||||
return item.name;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
},
|
||||
iconClass: function() {
|
||||
return 'glyphicon glyphicon-' + this.icon();
|
||||
},
|
||||
icon: function() {
|
||||
switch (this.type) {
|
||||
case 'books':
|
||||
return 'icon';
|
||||
case 'authors':
|
||||
return 'user';
|
||||
case 'series':
|
||||
return 'list';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
},
|
||||
countlabel: function() {
|
||||
switch (this.type) {
|
||||
case 'books':
|
||||
return this.count > 1 ? 'livres' : 'livre';
|
||||
case 'authors':
|
||||
return this.count > 1 ? 'auteurs' : 'auteur';
|
||||
case 'series':
|
||||
return this.count > 1 ? 'series' : 'serie';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Vue.component('results', {
|
||||
template: '#results-template',
|
||||
props: ['results', 'cols','sort_by','order_desc'],
|
||||
methods: {
|
||||
sortBy: function(col) {
|
||||
bus.$emit('sort-on', col);
|
||||
}
|
||||
}
|
||||
});
|
||||
Vue.component('result-cell', {
|
||||
render: function(h) {
|
||||
return h('td', this.cellContent(h));
|
||||
},
|
||||
props: ['item', 'col'],
|
||||
methods: {
|
||||
bookUrl: function(id) {
|
||||
return '/books/' + id;
|
||||
},
|
||||
authorUrl: function(id) {
|
||||
return '/authors/' + id;
|
||||
},
|
||||
seriesUrl: function(id) {
|
||||
return '/series/' + id;
|
||||
},
|
||||
link: function(h, icon, text, url) {
|
||||
return [
|
||||
h('span',{ attrs: { class: 'glyphicon glyphicon-'+icon } },''),
|
||||
' ',
|
||||
h('a', { attrs: { href: url } }, text)
|
||||
];
|
||||
},
|
||||
badge: function(h, num) {
|
||||
return h('span', { attrs: { class: 'badge' } }, num);
|
||||
},
|
||||
cellContent: function(h) {
|
||||
switch (this.col.id) {
|
||||
case 'author_name':
|
||||
return this.link(h, 'user', this.item.name, this.authorUrl(this.item.id));
|
||||
case 'serie_name':
|
||||
return this.link(h, 'list', this.item.name, this.authorUrl(this.item.id));
|
||||
case 'count':
|
||||
return this.item.count;
|
||||
case 'title':
|
||||
return this.link(h, 'book', this.item.title, this.bookUrl(this.item.id));
|
||||
case 'authors':
|
||||
var elts = [];
|
||||
var authors = this.item.authors;
|
||||
if (authors) {
|
||||
for (i=0;i<authors.length;i++) {
|
||||
elts[i] = this.link(h, 'user', authors[i].name, this.authorUrl(authors[i].id));
|
||||
}
|
||||
}
|
||||
return elts;
|
||||
case 'series':
|
||||
var series = this.item.series;
|
||||
if (series) {
|
||||
return [
|
||||
this.link(h, 'list', series.name, this.seriesUrl(series.id)),
|
||||
h('span', { attrs: { class: 'badge' } }, this.item.series_idx)
|
||||
];
|
||||
}
|
||||
return '';
|
||||
default:
|
||||
console.log('ERROR unknown col: ' + this.col.id)
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Vue.component('paginate', {
|
||||
template: '#paginate-template',
|
||||
props: ['page','more'],
|
||||
methods: {
|
||||
prevPage: function() {
|
||||
if (this.page > 1) bus.$emit('update-page', -1);
|
||||
},
|
||||
nextPage: function() {
|
||||
if (this.more) bus.$emit('update-page', 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// PAGES //
|
||||
|
||||
if (document.getElementById("index")) {
|
||||
new Vue({
|
||||
el: '#index',
|
||||
data: {
|
||||
url: '',
|
||||
page: 0,
|
||||
perpage: 20,
|
||||
more: false,
|
||||
sort_by: null,
|
||||
order_desc: false,
|
||||
cols: [],
|
||||
results: []
|
||||
},
|
||||
methods: {
|
||||
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.updateResults();
|
||||
},
|
||||
updatePage: function(p) {
|
||||
this.page += p;
|
||||
this.updateResults();
|
||||
},
|
||||
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)));
|
||||
},
|
||||
updateResults: function() {
|
||||
this.sendQuery(this.params(this.url), this.stdError, this.loadResults);
|
||||
},
|
||||
showSeries: function() {
|
||||
this.url = '/series/';
|
||||
this.updateResults();
|
||||
},
|
||||
showAuthors: function() {
|
||||
this.url = '/authors/';
|
||||
this.updateResults();
|
||||
},
|
||||
showBooks: function() {
|
||||
this.url = '/books/';
|
||||
this.updateResults();
|
||||
},
|
||||
loadCols: function(type) {
|
||||
switch (type) {
|
||||
case 'books':
|
||||
this.cols = [
|
||||
{ id: 'title', name: 'Titre', sort: 'title' },
|
||||
{ id: 'authors', name: 'Auteur(s)' },
|
||||
{ id: 'series', name: 'Serie' }
|
||||
];
|
||||
break;
|
||||
case 'series':
|
||||
this.cols = [
|
||||
{ id: 'serie_name', name: 'Nom', sort: 'name' },
|
||||
{ id: 'count', name: 'Livre(s)' },
|
||||
{ id: 'authors', name: 'Auteur(s)' }
|
||||
];
|
||||
break;
|
||||
case 'authors':
|
||||
this.cols = [
|
||||
{ id: 'author_name', name: 'Nom', sort: 'name' },
|
||||
{ id: 'count', name: 'Livre(s)' }
|
||||
];
|
||||
break;
|
||||
}
|
||||
},
|
||||
loadResults(resp) {
|
||||
this.results = [];
|
||||
this.more = resp.more;
|
||||
this.loadCols(resp.type);
|
||||
if (resp.results) {
|
||||
this.results = resp.results;
|
||||
if (this.page == 0) this.page = 1;
|
||||
} else {
|
||||
this.page = 0;
|
||||
}
|
||||
},
|
||||
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.setRequestHeader('Accept','application/json');
|
||||
xmh.send(null);
|
||||
},
|
||||
stdError: function(code, resp) {
|
||||
console.log('ERROR ' + code + ': ' + resp);
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
bus.$on('sort-on', this.sortBy);
|
||||
bus.$on('update-page', this.updatePage);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (document.getElementById("author")) {
|
||||
new Vue({
|
||||
el: '#author',
|
||||
data: {
|
||||
tab: "books"
|
||||
},
|
||||
methods: {
|
||||
showBooks: function() {
|
||||
this.tab = "books";
|
||||
},
|
||||
showAuthors: function() {
|
||||
this.tab = "authors";
|
||||
},
|
||||
showSeries: function() {
|
||||
this.tab = "series";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (document.getElementById("search")) {
|
||||
new Vue({
|
||||
el: '#search',
|
||||
data: {
|
||||
authors: [],
|
||||
books: [],
|
||||
series: [],
|
||||
authorsCount: 0,
|
||||
booksCount: 0,
|
||||
seriesCount: 0,
|
||||
q: '',
|
||||
which: 'all',
|
||||
all: false,
|
||||
perpage: 10
|
||||
},
|
||||
methods: {
|
||||
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.results;
|
||||
},
|
||||
searchAuthors: function() {
|
||||
this.sendQuery(this.searchParams('/authors/'), this.stdError, this.searchAuthorsSuccess);
|
||||
},
|
||||
searchBooksSuccess: function(res) {
|
||||
this.booksCount = res.count;
|
||||
this.books = res.results;
|
||||
},
|
||||
searchBooks: function() {
|
||||
this.sendQuery(this.searchParams('/books/'), this.stdError, this.searchBooksSuccess);
|
||||
},
|
||||
searchSeriesSuccess: function(res) {
|
||||
this.seriesCount = res.count;
|
||||
this.series = res.results;
|
||||
},
|
||||
searchSeries: function() {
|
||||
this.sendQuery(this.searchParams('/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;
|
||||
}
|
||||
},
|
||||
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.setRequestHeader('Accept','application/json');
|
||||
xmh.send(null);
|
||||
},
|
||||
stdError: function(code, resp) {
|
||||
console.log('ERROR ' + code + ': ' + resp);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
@ -1,212 +0,0 @@
|
||||
var bus = new Vue();
|
||||
Vue.component('results', {
|
||||
template: '#results-template',
|
||||
props: ['results', 'cols','sort_by','order_desc'],
|
||||
methods: {
|
||||
sortBy: function(col) {
|
||||
bus.$emit('sort-on', col);
|
||||
}
|
||||
}
|
||||
});
|
||||
Vue.component('result-cell', {
|
||||
render: function(h) {
|
||||
return h('td', this.cellContent(h));
|
||||
},
|
||||
props: ['item', 'col'],
|
||||
methods: {
|
||||
bookUrl: function(id) {
|
||||
return '/books/' + id;
|
||||
},
|
||||
authorUrl: function(id) {
|
||||
return '/authors/' + id;
|
||||
},
|
||||
seriesUrl: function(id) {
|
||||
return '/series/' + id;
|
||||
},
|
||||
link: function(h, icon, text, url) {
|
||||
return [
|
||||
h('span',{ attrs: { class: 'glyphicon glyphicon-'+icon } },''),
|
||||
' ',
|
||||
h('a', { attrs: { href: url } }, text)
|
||||
];
|
||||
},
|
||||
badge: function(h, num) {
|
||||
return h('span', { attrs: { class: 'badge' } }, num);
|
||||
},
|
||||
cellContent: function(h) {
|
||||
switch (this.col.id) {
|
||||
case 'author_name':
|
||||
return this.link(h, 'user', this.item.name, this.authorUrl(this.item.id));
|
||||
case 'serie_name':
|
||||
return this.link(h, 'list', this.item.name, this.authorUrl(this.item.id));
|
||||
case 'count':
|
||||
return this.item.count;
|
||||
case 'title':
|
||||
return this.link(h, 'book', this.item.title, this.bookUrl(this.item.id));
|
||||
case 'authors':
|
||||
var elts = [];
|
||||
var authors = this.item.authors;
|
||||
if (authors) {
|
||||
for (i=0;i<authors.length;i++) {
|
||||
elts[i] = this.link(h, 'user', authors[i].name, this.authorUrl(authors[i].id));
|
||||
}
|
||||
}
|
||||
return elts;
|
||||
case 'series':
|
||||
var series = this.item.series;
|
||||
if (series) {
|
||||
return [
|
||||
this.link(h, 'list', series.name, this.seriesUrl(series.id)),
|
||||
h('span', { attrs: { class: 'badge' } }, this.item.series_idx)
|
||||
];
|
||||
}
|
||||
return '';
|
||||
default:
|
||||
console.log('ERROR unknown col: ' + this.col.id)
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Vue.component('paginate', {
|
||||
template: '#paginate-template',
|
||||
props: ['page','more'],
|
||||
methods: {
|
||||
prevPage: function() {
|
||||
if (this.page > 1) bus.$emit('update-page', -1);
|
||||
},
|
||||
nextPage: function() {
|
||||
if (this.more) bus.$emit('update-page', 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
var index = new Vue({
|
||||
el: '#index',
|
||||
data: {
|
||||
url: '',
|
||||
page: 0,
|
||||
perpage: 20,
|
||||
more: false,
|
||||
sort_by: null,
|
||||
order_desc: false,
|
||||
cols: [],
|
||||
results: []
|
||||
},
|
||||
methods: {
|
||||
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.updateResults();
|
||||
},
|
||||
updatePage: function(p) {
|
||||
this.page += p;
|
||||
this.updateResults();
|
||||
},
|
||||
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)));
|
||||
},
|
||||
updateResults: function() {
|
||||
this.sendQuery(this.params(this.url), this.stdError, this.loadResults);
|
||||
},
|
||||
showSeries: function() {
|
||||
this.url = '/series/';
|
||||
this.updateResults();
|
||||
},
|
||||
showAuthors: function() {
|
||||
this.url = '/authors/';
|
||||
this.updateResults();
|
||||
},
|
||||
showBooks: function() {
|
||||
this.url = '/books/';
|
||||
this.updateResults();
|
||||
},
|
||||
loadCols: function(type) {
|
||||
switch (type) {
|
||||
case 'books':
|
||||
this.cols = [
|
||||
{ id: 'title', name: 'Titre', sort: 'title' },
|
||||
{ id: 'authors', name: 'Auteur(s)' },
|
||||
{ id: 'series', name: 'Serie' }
|
||||
];
|
||||
break;
|
||||
case 'series':
|
||||
this.cols = [
|
||||
{ id: 'serie_name', name: 'Nom', sort: 'name' },
|
||||
{ id: 'count', name: 'Livre(s)' },
|
||||
{ id: 'authors', name: 'Auteur(s)' }
|
||||
];
|
||||
break;
|
||||
case 'authors':
|
||||
this.cols = [
|
||||
{ id: 'author_name', name: 'Nom', sort: 'name' },
|
||||
{ id: 'count', name: 'Livre(s)' }
|
||||
];
|
||||
break;
|
||||
}
|
||||
},
|
||||
loadResults(resp) {
|
||||
this.results = [];
|
||||
this.more = resp.more;
|
||||
this.loadCols(resp.type);
|
||||
if (resp.results) {
|
||||
this.results = resp.results;
|
||||
if (this.page == 0) this.page = 1;
|
||||
} else {
|
||||
this.page = 0;
|
||||
}
|
||||
},
|
||||
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.setRequestHeader('Accept','application/json');
|
||||
xmh.send(null);
|
||||
},
|
||||
stdError: function(code, resp) {
|
||||
console.log('ERROR ' + code + ': ' + resp);
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
bus.$on('sort-on', this.sortBy);
|
||||
bus.$on('update-page', this.updatePage);
|
||||
}
|
||||
});
|
@ -1,166 +0,0 @@
|
||||
Vue.component('results-list', {
|
||||
template: '#results-list-template',
|
||||
props: ['results', 'count', 'type'],
|
||||
methods: {
|
||||
url: function(item) {
|
||||
return '/'+this.type+'/'+item.id;
|
||||
},
|
||||
label: function(item) {
|
||||
switch (this.type) {
|
||||
case 'books':
|
||||
return item.title;
|
||||
case 'authors':
|
||||
case 'series':
|
||||
return item.name;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
},
|
||||
iconClass: function() {
|
||||
return 'glyphicon glyphicon-' + this.icon();
|
||||
},
|
||||
icon: function() {
|
||||
switch (this.type) {
|
||||
case 'books':
|
||||
return 'icon';
|
||||
case 'authors':
|
||||
return 'user';
|
||||
case 'series':
|
||||
return 'list';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
},
|
||||
countlabel: function() {
|
||||
switch (this.type) {
|
||||
case 'books':
|
||||
return this.count > 1 ? 'livres' : 'livre';
|
||||
case 'authors':
|
||||
return this.count > 1 ? 'auteurs' : 'auteur';
|
||||
case 'series':
|
||||
return this.count > 1 ? 'series' : 'serie';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
var search = new Vue({
|
||||
el: '#search',
|
||||
data: {
|
||||
authors: [],
|
||||
books: [],
|
||||
series: [],
|
||||
authorsCount: 0,
|
||||
booksCount: 0,
|
||||
seriesCount: 0,
|
||||
q: '',
|
||||
which: 'all',
|
||||
all: false,
|
||||
perpage: 10
|
||||
},
|
||||
methods: {
|
||||
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.results;
|
||||
},
|
||||
searchAuthors: function() {
|
||||
this.sendQuery(this.searchParams('/authors/'), this.stdError, this.searchAuthorsSuccess);
|
||||
},
|
||||
searchBooksSuccess: function(res) {
|
||||
this.booksCount = res.count;
|
||||
this.books = res.results;
|
||||
},
|
||||
searchBooks: function() {
|
||||
this.sendQuery(this.searchParams('/books/'), this.stdError, this.searchBooksSuccess);
|
||||
},
|
||||
searchSeriesSuccess: function(res) {
|
||||
this.seriesCount = res.count;
|
||||
this.series = res.results;
|
||||
},
|
||||
searchSeries: function() {
|
||||
this.sendQuery(this.searchParams('/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;
|
||||
}
|
||||
},
|
||||
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.setRequestHeader('Accept','application/json');
|
||||
xmh.send(null);
|
||||
},
|
||||
stdError: function(code, resp) {
|
||||
console.log('ERROR ' + code + ': ' + resp);
|
||||
}
|
||||
}
|
||||
});
|
@ -1,60 +0,0 @@
|
||||
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();
|
||||
}
|
||||
})
|
@ -135,18 +135,11 @@ type SeriesFull struct {
|
||||
|
||||
type BouquinsModel struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
PageJs string `json:"-"`
|
||||
}
|
||||
|
||||
// Constructor BouquinsModel
|
||||
func NewBouquinsModel(title string) *BouquinsModel {
|
||||
return NewBouquinsModelJs(title, "")
|
||||
}
|
||||
func NewBouquinsModelJs(title, js string) *BouquinsModel {
|
||||
return &BouquinsModel{
|
||||
title,
|
||||
js,
|
||||
}
|
||||
return &BouquinsModel{title}
|
||||
}
|
||||
|
||||
type IndexModel struct {
|
||||
@ -155,11 +148,8 @@ type IndexModel struct {
|
||||
}
|
||||
|
||||
// Constructor IndexModel
|
||||
func NewIndexModel(title, js string, count int64) *IndexModel {
|
||||
return &IndexModel{
|
||||
*NewBouquinsModelJs(title, js),
|
||||
count,
|
||||
}
|
||||
func NewIndexModel(title string, count int64) *IndexModel {
|
||||
return &IndexModel{*NewBouquinsModel(title), count}
|
||||
}
|
||||
|
||||
type SearchModel struct {
|
||||
@ -167,7 +157,7 @@ type SearchModel struct {
|
||||
}
|
||||
|
||||
func NewSearchModel() *SearchModel {
|
||||
return &SearchModel{*NewBouquinsModelJs("Recherche", "search.js")}
|
||||
return &SearchModel{*NewBouquinsModel("Recherche")}
|
||||
}
|
||||
|
||||
type ResultsModel struct {
|
||||
@ -297,7 +287,7 @@ func (app *Bouquins) IndexPage(res http.ResponseWriter, req *http.Request) {
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
model := NewIndexModel("", "index.js", count)
|
||||
model := NewIndexModel("", count)
|
||||
if isJson(req) {
|
||||
err := writeJson(res, model)
|
||||
if err != nil {
|
||||
@ -369,7 +359,7 @@ func (app *Bouquins) AuthorPage(idParam string, res http.ResponseWriter, req *ht
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return app.render(res, TPL_AUTHORS, &AuthorModel{*NewBouquinsModelJs(author.Name, "author.js"), author})
|
||||
return app.render(res, TPL_AUTHORS, &AuthorModel{*NewBouquinsModel(author.Name), author})
|
||||
}
|
||||
func (app *Bouquins) AuthorsPage(res http.ResponseWriter, req *http.Request) {
|
||||
var err error
|
||||
|
@ -1,6 +1,4 @@
|
||||
<script src="/js/vue.js"></script>
|
||||
{{ if .PageJs }}
|
||||
<script src="/js/{{ .PageJs }}"></script>
|
||||
{{ end }}
|
||||
<script src="/js/bouquins.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -7,10 +7,8 @@
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
<link rel="preload" href="/js/vue.js" as="script">
|
||||
<link rel="prefetch" href="/js/vue.js">
|
||||
{{ if .PageJs }}
|
||||
<link rel="preload" href="/js/{{ .PageJs }}" as="script">
|
||||
<link rel="prefetch" href="/js/{{ .PageJs }}">
|
||||
{{ end }}
|
||||
<link rel="preload" href="/js/bouquins.js" as="script">
|
||||
<link rel="prefetch" href="/js/bouquins.js">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-inverse" id="nav">
|
||||
|
@ -1,18 +0,0 @@
|
||||
<table class="table table-striped">
|
||||
<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>
|
||||
{{ range . }}
|
||||
<tr>
|
||||
<td>
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
<a href="/authors/{{ .Id }}">{{ .Name }}</a>
|
||||
</td>
|
||||
<td>{{ .Count }}</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</table>
|
@ -1,27 +0,0 @@
|
||||
<table id="books" 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="'/books/'+book.id">{{ "{{" }} book.title {{ "}}" }}</a></td>
|
||||
<td>
|
||||
<template v-for="author in book.authors">
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
<a :href="'/authors/'+author.id">{{ "{{" }} author.name {{ "}}" }}</a>
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<template v-if="book.series">
|
||||
<span class="glyphicon glyphicon-list"></span>
|
||||
<a :href="'/series/'+book.series.id">{{ "{{" }} book.series.name {{ "}}" }}</a>
|
||||
<span class="badge">{{ "{{" }} book.series ? book.series.idx : '' {{ "}}" }}</span>
|
||||
</template>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
@ -1,6 +0,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>
|
@ -1,25 +0,0 @@
|
||||
<table class="table table-striped">
|
||||
<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">
|
||||
{{ range . }}
|
||||
<td>
|
||||
<span class="glyphicon glyphicon-list"></span>
|
||||
<a href="/series/{{ .Id }}">{{ .Name }}</a>
|
||||
</td>
|
||||
<td>{{ .Count }}</td>
|
||||
<td>
|
||||
{{ range .Authors }}
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
<a href="/authors/{{ .Id }}">{{ .Name }}</a>
|
||||
{{ end }}
|
||||
</td>
|
||||
{{ end }}
|
||||
</tr>
|
||||
</table>
|
Loading…
Reference in New Issue
Block a user