Compare commits

...

10 Commits

Author SHA1 Message Date
5569c4444b Make thing beeing able to be modified 2017-09-23 17:10:50 +02:00
d901943758 Corrects some path 2017-09-23 16:48:56 +02:00
91765d2c84 Makes request rate limited per second, not microseconds 2017-09-23 16:48:56 +02:00
b0eefe8de3 Makes all tests pass green again
* Removed a concurrent write condition that was happening while fetching
  BD
* Updated the local test database to match bedetheque.com state
* Note field being highly dynamic removed test from it.
2017-09-23 16:48:56 +02:00
2f526632fe Uses default gulp build task 2017-09-23 16:47:35 +02:00
ea64b0a6c5 Adds cleaning task and default task 2017-09-23 16:47:35 +02:00
78acb0a1c7 Removes some of the debug output 2017-09-23 16:47:35 +02:00
17c2f18193 Cleans the javascript 2017-09-23 16:47:35 +02:00
e6e1f6d6b3 Adds jshint reporting 2017-09-23 16:46:03 +02:00
c86e06fe23 adds a production, minified version 2017-09-23 16:46:03 +02:00
18 changed files with 237 additions and 124 deletions

3
.jshintrc Normal file
View File

@@ -0,0 +1,3 @@
{
"varstmt" : false
}

View File

@@ -25,7 +25,7 @@ COPY . /satbd
RUN go get $(go list -e -f "{{.Imports}} {{.TestImports}}" . | tr "[" " " | tr "]" " " | xargs go list -e -f "{{if not .Standard}}{{.ImportPath}}{{end}}") RUN go get $(go list -e -f "{{.Imports}} {{.TestImports}}" . | tr "[" " " | tr "]" " " | xargs go list -e -f "{{if not .Standard}}{{.ImportPath}}{{end}}")
RUN go build RUN go build
RUN go test RUN go test
RUN gulp build RUN gulp
EXPOSE 33276 EXPOSE 33276

View File

@@ -5,6 +5,7 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
) )
@@ -65,6 +66,7 @@ type Album struct {
Designers []string `json:"dessins"` Designers []string `json:"dessins"`
Colorists []string `json:"couleurs"` Colorists []string `json:"couleurs"`
mx sync.Mutex
Links map[string]string Links map[string]string
FetchDate time.Time FetchDate time.Time
@@ -114,3 +116,14 @@ func AlbumCoverExt(URL string) string {
} }
return strings.ToLower(ext) return strings.ToLower(ext)
} }
func (a *Album) addLink(title string, value string) {
a.mx.Lock()
defer a.mx.Unlock()
if a.Links == nil {
a.Links = make(map[string]string)
}
a.Links[title] = value
}

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"log" "log"
"path/filepath" "path/filepath"
"time" "time"
@@ -43,14 +44,19 @@ func (s *AlbumDatabaseSuite) TestCanGet(c *C) {
func (s *AlbumDatabaseSuite) TestCanSort(c *C) { func (s *AlbumDatabaseSuite) TestCanSort(c *C) {
start := time.Now() start := time.Now()
data := []AlbumID{160366, 58595, 15875, 9935, 84448, 46005, 19762, 164, 52100, 8179, 44989, 32043, 22737, 754}
sorted, err := s.db.ByPurchaseDate() sorted, err := s.db.ByPurchaseDate()
log.Printf("sorting took %s", time.Since(start)) log.Printf("sorting took %s", time.Since(start))
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(len(sorted), Equals, len(data)) c.Assert(len(sorted) > 0, Equals, true)
for i, a := range sorted { last := time.Now()
c.Check(a, Equals, data[i], Commentf("expected %d", a)) for _, a := range sorted {
al, err := s.db.get(fmt.Sprintf("%d", a))
if c.Check(err, IsNil) == false {
continue
}
c.Check(last.After(al.PurchaseDate), Equals, true)
} }
} }

View File

@@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"log"
"path" "path"
"regexp" "regexp"
"strconv" "strconv"
@@ -29,11 +30,13 @@ func linkFromSelection(s *goquery.Selection) Link {
// Get fetches data from www.bedetheque.com and parses it to a // Get fetches data from www.bedetheque.com and parses it to a
func (g *AlbumDescriptionGetter) Get(a *Album) error { func (g *AlbumDescriptionGetter) Get(a *Album) error {
URL := path.Join("www.bedetheque.com", fmt.Sprintf("BD--%d.html", a.ID)) URL := path.Join("www.bedetheque.com", fmt.Sprintf("BD--%d.html", a.ID))
resp, err := g.getter.Get("http://" + URL) resp, err := g.getter.Get("https://" + URL)
if err != nil { if err != nil {
return err return err
} }
defer closeOrPanic(resp.Body, "GET:http://"+URL) defer closeOrPanic(resp.Body, "GET:https://"+URL)
log.Printf("Fetched %s", URL)
doc, err := goquery.NewDocumentFromReader(resp.Body) doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil { if err != nil {
@@ -100,7 +103,6 @@ func (g *AlbumDescriptionGetter) Get(a *Album) error {
details := map[string][]*goquery.Selection{} details := map[string][]*goquery.Selection{}
previous := "" previous := ""
a.Links = map[string]string{}
doc.Find("div.detail-album ul.infos-albums li").Each(func(i int, s *goquery.Selection) { doc.Find("div.detail-album ul.infos-albums li").Each(func(i int, s *goquery.Selection) {
labelSelection := s.Find("label") labelSelection := s.Find("label")
if labelSelection.Size() != 1 { if labelSelection.Size() != 1 {
@@ -124,7 +126,7 @@ func (g *AlbumDescriptionGetter) Get(a *Album) error {
l := linkFromSelection(s.Find("a")) l := linkFromSelection(s.Find("a"))
if len(l.Title) > 0 { if len(l.Title) > 0 {
a.Scenarists = append(a.Scenarists, l.Title) a.Scenarists = append(a.Scenarists, l.Title)
a.Links[l.Title] = l.Target a.addLink(l.Title, l.Target)
} }
} }
errors <- nil errors <- nil
@@ -141,7 +143,7 @@ func (g *AlbumDescriptionGetter) Get(a *Album) error {
l := linkFromSelection(s.Find("a")) l := linkFromSelection(s.Find("a"))
if len(l.Title) > 0 { if len(l.Title) > 0 {
a.Designers = append(a.Designers, l.Title) a.Designers = append(a.Designers, l.Title)
a.Links[l.Title] = l.Target a.addLink(l.Title, l.Target)
} }
} }
errors <- nil errors <- nil
@@ -158,7 +160,7 @@ func (g *AlbumDescriptionGetter) Get(a *Album) error {
l := linkFromSelection(s.Find("a")) l := linkFromSelection(s.Find("a"))
if len(l.Title) > 0 { if len(l.Title) > 0 {
a.Colorists = append(a.Colorists, l.Title) a.Colorists = append(a.Colorists, l.Title)
a.Links[l.Title] = l.Target a.addLink(l.Title, l.Target)
} }
} }
errors <- nil errors <- nil
@@ -173,9 +175,11 @@ func (g *AlbumDescriptionGetter) Get(a *Album) error {
} }
if len(errorList) != 0 { if len(errorList) != 0 {
return fmt.Errorf("Could not parse description from http://%s:%s", return fmt.Errorf("Could not parse description from https://%s:%s\n%s",
URL, URL,
strings.Join(append([]string{""}, errorList...), "\n * ")) strings.Join(append([]string{""}, errorList...), "\n * "),
resp.Body)
} }
a.FetchDate = time.Now() a.FetchDate = time.Now()

View File

@@ -28,6 +28,8 @@ func (s *AlbumDescriptionGetterSuite) TestGet(c *C) {
if c.Check(s.g.Get(&aStripped), IsNil) == true { if c.Check(s.g.Get(&aStripped), IsNil) == true {
//we skip the fetch date, for sure it will always expire //we skip the fetch date, for sure it will always expire
aStripped.FetchDate = a.FetchDate aStripped.FetchDate = a.FetchDate
//we skip the note as it is a dynamic value, and may change over time
aStripped.Note = a.Note
c.Check(aStripped, DeepEquals, a) c.Check(aStripped, DeepEquals, a)
} }
} }

View File

@@ -4,6 +4,8 @@ var karma = require('karma');
var es = require('event-stream'); var es = require('event-stream');
var mainBowerFiles = require('main-bower-files'); var mainBowerFiles = require('main-bower-files');
var exists = require('path-exists').sync var exists = require('path-exists').sync
var Q = require('q');
var del = require('del');
var pipes = {}; var pipes = {};
@@ -18,51 +20,58 @@ var paths = {
} }
var bowerFilesMinified = mainBowerFiles({ pipes.bowerFiles = function() {
overrides : { return gulp.src(mainBowerFiles({
jquery : { overrides : {
main: [] jquery : {
}, main: []
bootstrap : { },
main: [ "dist/**/*.css" ] bootstrap : {
} main: [
} "dist/**/*.min.css",
}).map(function(path, index,arr) { './dist/fonts/*.*'
var newPath = path.replace(/.([^.]+)$/g, '.min.$1'); ]
return exists ( newPath ) ? newPath : path; }
}) }
}),{base: 'webapp/bower_components'});
}
var bowerFiles = mainBowerFiles({ pipes.bowerFilesMinified = function() {
overrides : { return pipes.bowerFiles()
jquery : { .pipe(plugins.rename(function (path) {
main: [] testpath = 'webapp/bower_components/' + path.dirname + '/' + path.basename + '.min' + path.extname;
}, if (exists(testpath)) {
bootstrap : { path.extname = '.min' + path.extname;
main: [ "dist/**/*.css" ] }
} }));
} }
})
pipes.validatedAppScripts = function() { pipes.validatedAppScripts = function() {
return gulp.src(paths.appFiles) return gulp.src(paths.appFiles)
.pipe(plugins.jshint()) .pipe(plugins.jshint())
.pipe(plugins.debug({title: 'validated-js:'})); .pipe(plugins.jshint.reporter('jshint-stylish'));
} }
pipes.validatedIndex = function() { pipes.validatedIndex = function() {
return gulp.src(paths.index); return gulp.src(paths.index)
.pipe(plugins.htmlhint())
.pipe(plugins.htmlhint.reporter());
} }
pipes.validatedPartials = function() { pipes.validatedPartials = function() {
return gulp.src(paths.partials); return gulp.src(paths.partials)
.pipe(plugins.htmlhint({'doctype-first': false}))
.pipe(plugins.htmlhint.reporter());
} }
pipes.validatedPages = function() { pipes.validatedPages = function() {
return gulp.src(paths.pages) return gulp.src(paths.pages)
.pipe(plugins.htmlhint({'doctype-first': false}))
.pipe(plugins.htmlhint.reporter());
} }
pipes.builtIndexDev = function() { pipes.builtIndexDev = function() {
var vendorFiles = gulp.src(bowerFilesMinified) var vendorFiles = pipes.bowerFilesMinified()
.pipe(gulp.dest(paths.distDev + '/vendor')); .pipe(gulp.dest(paths.distDev + '/vendor'));
var appScript = pipes.validatedAppScripts() var appScript = pipes.validatedAppScripts()
.pipe(plugins.angularFilesort()) .pipe(plugins.angularFilesort())
@@ -84,6 +93,45 @@ pipes.builtIndexDev = function() {
} }
pipes.builtIndexProd = function() {
var filterJS = plugins.filter('**/*.js',{restore: true});
var vendorFiles = pipes.bowerFiles()
.pipe(filterJS)
.pipe(plugins.concat('vendor.min.js'))
.pipe(plugins.uglify())
.pipe(filterJS.restore)
.pipe(gulp.dest(paths.distProd + '/vendor'));
var appScript = pipes.validatedAppScripts()
.pipe(plugins.angularFilesort())
.pipe(plugins.ngAnnotate())
.pipe(plugins.concat('app.min.js'))
.pipe(plugins.uglify())
.pipe(gulp.dest(paths.distProd + '/js'));
var appStyle = gulp.src(paths.cssFiles)
.pipe(plugins.minifyCss())
.pipe(plugins.rename(function (path) {
path.extname = '.min'+ path.extname;
}))
.pipe(gulp.dest(paths.distProd + '/css'))
var partials = pipes.validatedPartials()
.pipe(gulp.dest(paths.distProd + '/js'))
var pages = pipes.validatedPages()
.pipe(gulp.dest(paths.distProd + '/html'))
return pipes.validatedIndex()
.pipe(plugins.inject(vendorFiles, {ignorePath: '/' + paths.distProd, name : 'bower'}))
.pipe(plugins.inject(es.merge(appStyle,partials,pages), {ignorePath: '/' + paths.distProd}))
.pipe(plugins.inject(appScript, {ignorePath: '/' + paths.distProd}))
.pipe(gulp.dest(paths.distProd));
}
gulp.task('test', function(done) { gulp.task('test', function(done) {
server = new karma.Server({ server = new karma.Server({
@@ -99,3 +147,23 @@ gulp.task('test', function(done) {
gulp.task('build-dev', pipes.builtIndexDev); gulp.task('build-dev', pipes.builtIndexDev);
gulp.task('build-prod', pipes.builtIndexProd);
pipes.cleanDir = function(dir) {
}
gulp.task('clean-dev', function() {
return del([paths.distDev]);
});
gulp.task('clean-prod', function() {
return del([paths.distProd]);
});
gulp.task('clean-build-dev', ['clean-dev'], pipes.builtIndexDev);
gulp.task('clean-build-prod', ['clean-prod'] , pipes.builtIndexProd);
gulp.task('default',['clean-build-prod'])

View File

@@ -78,7 +78,7 @@ func newAppData(opts Options) (*appData, error) {
res.db = OpenAlbumDatabase(filepath.Join(basepath, "db")) res.db = OpenAlbumDatabase(filepath.Join(basepath, "db"))
res.getter = &AlbumDescriptionGetter{ res.getter = &AlbumDescriptionGetter{
getter: NewRateLimitedGetter(10, 10), getter: NewRateLimitedGetter(opts.MaxRequests, opts.RequestWindow),
} }
res.cover = NewAlbumCoverCache(filepath.Join(basepath, "covers"), opts.MaxRequests, opts.RequestWindow) res.cover = NewAlbumCoverCache(filepath.Join(basepath, "covers"), opts.MaxRequests, opts.RequestWindow)

View File

@@ -4,18 +4,27 @@
"description": "Small website for exploring privately satellite.bar bedetheque", "description": "Small website for exploring privately satellite.bar bedetheque",
"devDependencies": { "devDependencies": {
"bower": "^1.7.7", "bower": "^1.7.7",
"del": "^2.2.0",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-angular-filesort": "^1.1.1", "gulp-angular-filesort": "^1.1.1",
"gulp-concat": "^2.6.0",
"gulp-debug": "^2.1.2", "gulp-debug": "^2.1.2",
"gulp-filter": "^3.0.1",
"gulp-htmlhint": "^0.3.1",
"gulp-htmlmin": "^1.3.0",
"gulp-inject": "^3.0.0", "gulp-inject": "^3.0.0",
"gulp-jasmine": "^2.2.1", "gulp-jasmine": "^2.2.1",
"gulp-jshint": "^2.0.0", "gulp-jshint": "^2.0.0",
"gulp-karma": "0.0.5", "gulp-karma": "0.0.5",
"gulp-load-plugins": "^1.2.0", "gulp-load-plugins": "^1.2.0",
"gulp-minify-css": "^1.2.3",
"gulp-ng-annotate": "^1.1.0",
"gulp-rename": "^1.2.2", "gulp-rename": "^1.2.2",
"gulp-uglify": "^1.5.3",
"gulp-util": "^3.0.7", "gulp-util": "^3.0.7",
"jasmine-core": "^2.4.1", "jasmine-core": "^2.4.1",
"jshint": "^2.9.1", "jshint": "^2.9.1",
"jshint-stylish": "^2.1.0",
"karma": "^0.13.19", "karma": "^0.13.19",
"karma-chrome-launcher": "^0.2.2", "karma-chrome-launcher": "^0.2.2",
"karma-firefox-launcher": "^0.1.7", "karma-firefox-launcher": "^0.1.7",

View File

@@ -1,5 +1,5 @@
[ [
{ {
"ID": 8179, "ID": 8179,
"ISBN": "2-8001-1161-5", "ISBN": "2-8001-1161-5",
"série": "Mortes saisons", "série": "Mortes saisons",
@@ -12,8 +12,8 @@
"ref": "BERT-9", "ref": "BERT-9",
"LegalDeposit": "1985-10-01T00:00:00Z", "LegalDeposit": "1985-10-01T00:00:00Z",
"PrintDate": "0001-01-01T00:00:00Z", "PrintDate": "0001-01-01T00:00:00Z",
"PurchaseDate": "2013-09-04T00:00:00Z", "PurchaseDate": "2013-09-02T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/Couv_8179.jpg", "CoverURL": "https://www.bedetheque.com/media/Couvertures/Couv_8179.jpg",
"description": "", "description": "",
"Note": 3.4, "Note": 3.4,
"scenario": [ "scenario": [
@@ -26,9 +26,9 @@
"Berthet, Philippe" "Berthet, Philippe"
], ],
"Links": { "Links": {
"Andreas":"http://www.bedetheque.com/auteur-267-BD-Andreas.html", "Andreas":"https://www.bedetheque.com/auteur-267-BD-Andreas.html",
"Berthet, Philippe": "http://www.bedetheque.com/auteur-13-BD-Berthet-Philippe.html" "Berthet, Philippe": "https://www.bedetheque.com/auteur-13-BD-Berthet-Philippe.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
{ {
@@ -45,8 +45,8 @@
"ref": "ZCO-35", "ref": "ZCO-35",
"LegalDeposit": "1979-01-01T00:00:00Z", "LegalDeposit": "1979-01-01T00:00:00Z",
"PrintDate": "0001-01-01T00:00:00Z", "PrintDate": "0001-01-01T00:00:00Z",
"PurchaseDate": "2013-09-04T00:00:00Z", "PurchaseDate": "2013-09-01T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/abeldopeulapeul02.JPG", "CoverURL": "https://www.bedetheque.com/media/Couvertures/Couv_44989.jpg",
"description": "", "description": "",
"Note": 4, "Note": 4,
"scenario": [ "scenario": [
@@ -55,8 +55,12 @@
"dessins": [ "dessins": [
"Loro" "Loro"
], ],
"couleurs": [
"<Quadrichromie>"
],
"Links": { "Links": {
"Loro": "http://www.bedetheque.com/auteur-839-BD-Loro.html" "Loro": "https://www.bedetheque.com/auteur-839-BD-Loro.html",
"<Quadrichromie>" : "https://www.bedetheque.com/auteur-7691-BD-Quadrichromie.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
@@ -75,7 +79,7 @@
"LegalDeposit": "0001-01-01T00:00:00Z", "LegalDeposit": "0001-01-01T00:00:00Z",
"PrintDate": "0001-01-01T00:00:00Z", "PrintDate": "0001-01-01T00:00:00Z",
"PurchaseDate": "2013-12-07T00:00:00Z", "PurchaseDate": "2013-12-07T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/plantufourmicouverture.JPG", "CoverURL": "https://www.bedetheque.com/media/Couvertures/plantufourmicouverture.JPG",
"description": "", "description": "",
"Note": 3, "Note": 3,
"scenario": [ "scenario": [
@@ -88,8 +92,8 @@
"\u003cN\u0026B\u003e" "\u003cN\u0026B\u003e"
], ],
"Links" : { "Links" : {
"Plantu": "http://www.bedetheque.com/auteur-5996-BD-Plantu.html", "Plantu": "https://www.bedetheque.com/auteur-5996-BD-Plantu.html",
"\u003cN\u0026B\u003e": "http://www.bedetheque.com/auteur-477-BD-NB.html" "\u003cN\u0026B\u003e": "https://www.bedetheque.com/auteur-477-BD-NB.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
@@ -107,8 +111,8 @@
"ref": "DARK-2", "ref": "DARK-2",
"LegalDeposit": "2002-09-01T00:00:00Z", "LegalDeposit": "2002-09-01T00:00:00Z",
"PrintDate": "2002-09-01T00:00:00Z", "PrintDate": "2002-09-01T00:00:00Z",
"PurchaseDate": "2013-09-06T00:00:00Z", "PurchaseDate": "2013-08-31T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/Darken02_22102002.jpg", "CoverURL": "https://www.bedetheque.com/media/Couvertures/Darken02_22102002.jpg",
"description": "Kelmar le Darken a retrouvé Lorik, son frère, engagé dans la résistance au Nouvel Ordre Humain. Celui-ci aurait découvert l'endroit où leur mère est tenue prisonnière : le Mangeforêt, un monstre mécanique qui défriche la forêt héossienne et sert de bagne aux autochtones récalcitrants. Mais pour les chefs de la résistance, ce n'est pas un objectif prioritaire. Alors Kelmar part à l'assaut de la machine avec Langue Agile la voleuse feling, Doonda la Woon, et une poignée d'enfants-soldats. Et s'il avait été prévu dès le début... Lire la suite", "description": "Kelmar le Darken a retrouvé Lorik, son frère, engagé dans la résistance au Nouvel Ordre Humain. Celui-ci aurait découvert l'endroit où leur mère est tenue prisonnière : le Mangeforêt, un monstre mécanique qui défriche la forêt héossienne et sert de bagne aux autochtones récalcitrants. Mais pour les chefs de la résistance, ce n'est pas un objectif prioritaire. Alors Kelmar part à l'assaut de la machine avec Langue Agile la voleuse feling, Doonda la Woon, et une poignée d'enfants-soldats. Et s'il avait été prévu dès le début... Lire la suite",
"Note": 5, "Note": 5,
"scenario": [ "scenario": [
@@ -116,16 +120,16 @@
"Rastoin, Bernard" "Rastoin, Bernard"
], ],
"dessins": [ "dessins": [
"Swal, Christophe" "Swal, Christophe"
], ],
"couleurs": [ "couleurs": [
"Robert, Jacky" "Robert, Jacky"
], ],
"Links": { "Links": {
"Polouchine, Igor": "http://www.bedetheque.com/auteur-2556-BD-Polouchine-Igor.html", "Polouchine, Igor": "https://www.bedetheque.com/auteur-2556-BD-Polouchine-Igor.html",
"Rastoin, Bernard": "http://www.bedetheque.com/auteur-31834-BD-Rastoin-Bernard.html", "Rastoin, Bernard": "https://www.bedetheque.com/auteur-31834-BD-Rastoin-Bernard.html",
"Swal, Christophe": "http://www.bedetheque.com/auteur-2557-BD-Swal-Christophe.html", "Swal, Christophe": "https://www.bedetheque.com/auteur-2557-BD-Swal-Christophe.html",
"Robert, Jacky": "http://www.bedetheque.com/auteur-820-BD-Robert-Jacky.html" "Robert, Jacky": "https://www.bedetheque.com/auteur-820-BD-Robert-Jacky.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
@@ -144,7 +148,7 @@
"LegalDeposit": "1992-10-01T00:00:00Z", "LegalDeposit": "1992-10-01T00:00:00Z",
"PrintDate": "0001-01-01T00:00:00Z", "PrintDate": "0001-01-01T00:00:00Z",
"PurchaseDate": "2013-10-31T00:00:00Z", "PurchaseDate": "2013-10-31T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/bizu05.jpg", "CoverURL": "https://www.bedetheque.com/media/Couvertures/bizu05.jpg",
"description": "", "description": "",
"Note": 3, "Note": 3,
"scenario": [ "scenario": [
@@ -157,8 +161,8 @@
"D'Authenay, Anne-Marie" "D'Authenay, Anne-Marie"
], ],
"Links": { "Links": {
"Fournier, Jean-Claude": "http://www.bedetheque.com/auteur-755-BD-Fournier-Jean-Claude.html", "Fournier, Jean-Claude": "https://www.bedetheque.com/auteur-755-BD-Fournier-Jean-Claude.html",
"D'Authenay, Anne-Marie":"http://www.bedetheque.com/auteur-550-BD-D-Authenay-Anne-Marie.html" "D'Authenay, Anne-Marie":"https://www.bedetheque.com/auteur-550-BD-D-Authenay-Anne-Marie.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
@@ -176,8 +180,8 @@
"ref": "EAU-5", "ref": "EAU-5",
"LegalDeposit": "1992-02-01T00:00:00Z", "LegalDeposit": "1992-02-01T00:00:00Z",
"PrintDate": "0001-01-01T00:00:00Z", "PrintDate": "0001-01-01T00:00:00Z",
"PurchaseDate": "2013-09-06T00:00:00Z", "PurchaseDate": "2013-08-30T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/Couv_164.jpg", "CoverURL": "https://www.bedetheque.com/media/Couvertures/Couv_164.jpg",
"description": "", "description": "",
"Note": 3.9, "Note": 3.9,
"scenario": [ "scenario": [
@@ -190,8 +194,8 @@
"Adamov, Philippe" "Adamov, Philippe"
], ],
"Links": { "Links": {
"Cothias, Patrick": "http://www.bedetheque.com/auteur-36-BD-Cothias-Patrick.html", "Cothias, Patrick": "https://www.bedetheque.com/auteur-36-BD-Cothias-Patrick.html",
"Adamov, Philippe": "http://www.bedetheque.com/auteur-471-BD-Adamov-Philippe.html" "Adamov, Philippe": "https://www.bedetheque.com/auteur-471-BD-Adamov-Philippe.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
@@ -209,8 +213,8 @@
"ref": "KEN-2", "ref": "KEN-2",
"LegalDeposit": "2003-01-01T00:00:00Z", "LegalDeposit": "2003-01-01T00:00:00Z",
"PrintDate": "0001-01-01T00:00:00Z", "PrintDate": "0001-01-01T00:00:00Z",
"PurchaseDate": "2013-09-03T00:00:00Z", "PurchaseDate": "2013-08-29T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/kenyaco02.jpg", "CoverURL": "https://www.bedetheque.com/media/Couvertures/kenyaco02.jpg",
"description": "", "description": "",
"Note": 4, "Note": 4,
"scenario": [ "scenario": [
@@ -224,9 +228,9 @@
"Scarlett" "Scarlett"
], ],
"Links": { "Links": {
"Leo": "http://www.bedetheque.com/auteur-97-BD-Leo.html", "Leo": "https://www.bedetheque.com/auteur-97-BD-Leo.html",
"Rodolphe": "http://www.bedetheque.com/auteur-128-BD-Rodolphe.html", "Rodolphe": "https://www.bedetheque.com/auteur-128-BD-Rodolphe.html",
"Scarlett": "http://www.bedetheque.com/auteur-1296-BD-Scarlett.html" "Scarlett": "https://www.bedetheque.com/auteur-1296-BD-Scarlett.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
@@ -244,8 +248,8 @@
"ref": "ACC-2", "ref": "ACC-2",
"LegalDeposit": "2003-12-01T00:00:00Z", "LegalDeposit": "2003-12-01T00:00:00Z",
"PrintDate": "0001-01-01T00:00:00Z", "PrintDate": "0001-01-01T00:00:00Z",
"PurchaseDate": "2013-09-04T00:00:00Z", "PurchaseDate": "2013-08-28T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/accrocderap.jpg", "CoverURL": "https://www.bedetheque.com/media/Couvertures/accrocderap.jpg",
"description": "", "description": "",
"Note": -1, "Note": -1,
"scenario": [ "scenario": [
@@ -258,9 +262,9 @@
"Ptiluc" "Ptiluc"
], ],
"Links": { "Links": {
"Harty": "http://www.bedetheque.com/auteur-6505-BD-Harty.html", "Harty": "https://www.bedetheque.com/auteur-6505-BD-Harty.html",
"Joan": "http://www.bedetheque.com/auteur-4285-BD-Joan.html", "Joan": "https://www.bedetheque.com/auteur-4285-BD-Joan.html",
"Ptiluc": "http://www.bedetheque.com/auteur-125-BD-Ptiluc.html" "Ptiluc": "https://www.bedetheque.com/auteur-125-BD-Ptiluc.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
@@ -278,8 +282,8 @@
"ref": "CHI-23", "ref": "CHI-23",
"LegalDeposit": "1978-03-01T00:00:00Z", "LegalDeposit": "1978-03-01T00:00:00Z",
"PrintDate": "0001-01-01T00:00:00Z", "PrintDate": "0001-01-01T00:00:00Z",
"PurchaseDate": "2013-09-06T00:00:00Z", "PurchaseDate": "2013-08-27T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/Couv_46005.jpg", "CoverURL": "https://www.bedetheque.com/media/Couvertures/Couv_46005.jpg",
"description": "", "description": "",
"Note": 3, "Note": 3,
"scenario": [ "scenario": [
@@ -292,7 +296,7 @@
"Tibet" "Tibet"
], ],
"Links": { "Links": {
"Tibet": "http://www.bedetheque.com/auteur-1791-BD-Tibet.html" "Tibet": "https://www.bedetheque.com/auteur-1791-BD-Tibet.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
@@ -311,7 +315,7 @@
"LegalDeposit": "1990-10-01T00:00:00Z", "LegalDeposit": "1990-10-01T00:00:00Z",
"PrintDate": "0001-01-01T00:00:00Z", "PrintDate": "0001-01-01T00:00:00Z",
"PurchaseDate": "2013-12-02T00:00:00Z", "PurchaseDate": "2013-12-02T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/Couv_15875.jpg", "CoverURL": "https://www.bedetheque.com/media/Couvertures/Couv_15875.jpg",
"description": "", "description": "",
"Note": 4, "Note": 4,
"scenario": [ "scenario": [
@@ -324,8 +328,8 @@
"\u003cBichromie\u003e" "\u003cBichromie\u003e"
], ],
"Links": { "Links": {
"Veyron, Martin": "http://www.bedetheque.com/auteur-1491-BD-Veyron-Martin.html", "Veyron, Martin": "https://www.bedetheque.com/auteur-1491-BD-Veyron-Martin.html",
"\u003cBichromie\u003e": "http://www.bedetheque.com/auteur-6563-BD-Bichromie.html" "\u003cBichromie\u003e": "https://www.bedetheque.com/auteur-6563-BD-Bichromie.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
@@ -344,7 +348,7 @@
"LegalDeposit": "2009-02-01T00:00:00Z", "LegalDeposit": "2009-02-01T00:00:00Z",
"PrintDate": "2009-03-01T00:00:00Z", "PrintDate": "2009-03-01T00:00:00Z",
"PurchaseDate": "2013-10-14T00:00:00Z", "PurchaseDate": "2013-10-14T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/84448_c.jpg", "CoverURL": "https://www.bedetheque.com/media/Couvertures/84448_c.jpg",
"description": "À travers lhistoire dune frégate, devenez le témoin de lindépendance du peuple américain au siècle des lumières. En 1778, dans l'arsenal de Rochefort la frégate LHermione est mise en chantier daprès les plans de lingénieur Chevillard Aîné. Pendant près dun an, charpentiers, perceurs, calfats, forgerons et bagnards se relaient pour construire ce navire de près de 45 mètres, doté dune voilure de 1500 m2, de 26 canons pouvant tirer des boulets de 12 livres. En 1779, Gilbert Motier, marquis de La Fayette, cherche... Lire la suite", "description": "À travers lhistoire dune frégate, devenez le témoin de lindépendance du peuple américain au siècle des lumières. En 1778, dans l'arsenal de Rochefort la frégate LHermione est mise en chantier daprès les plans de lingénieur Chevillard Aîné. Pendant près dun an, charpentiers, perceurs, calfats, forgerons et bagnards se relaient pour construire ce navire de près de 45 mètres, doté dune voilure de 1500 m2, de 26 canons pouvant tirer des boulets de 12 livres. En 1779, Gilbert Motier, marquis de La Fayette, cherche... Lire la suite",
"Note": 3.7, "Note": 3.7,
"scenario": [ "scenario": [
@@ -357,7 +361,7 @@
"Delitte, Jean-Yves" "Delitte, Jean-Yves"
], ],
"Links": { "Links": {
"Delitte, Jean-Yves":"http://www.bedetheque.com/auteur-808-BD-Delitte-Jean-Yves.html" "Delitte, Jean-Yves":"https://www.bedetheque.com/auteur-808-BD-Delitte-Jean-Yves.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
@@ -376,7 +380,7 @@
"LegalDeposit": "2012-04-01T00:00:00Z", "LegalDeposit": "2012-04-01T00:00:00Z",
"PrintDate": "0001-01-01T00:00:00Z", "PrintDate": "0001-01-01T00:00:00Z",
"PurchaseDate": "2014-03-31T00:00:00Z", "PurchaseDate": "2014-03-31T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/Couv_160366.jpg", "CoverURL": "https://www.bedetheque.com/media/Couvertures/Couv_160366.jpg",
"description": "Ralph, toujours accompagné par les magiciens Yassou et maître Migachi, a décidé de voler la couronne de Tanghor, dont le pouvoir pourrait rendre la mémoire à sa soeur. Il s'associe à trois voleurs rencontrés sur le bord de la route et se rend à Onophalae, où la couronne magique est conservée en haut d'un pic particulièrement bien protégé. Du moins pour le commun des mortels, car rien ne résiste aux méthodes non- conventionnelles de Ralph. De son côté, le père de Ralph, qui a survécu à l'effondrement du barrage, s'installe à... Lire la suite", "description": "Ralph, toujours accompagné par les magiciens Yassou et maître Migachi, a décidé de voler la couronne de Tanghor, dont le pouvoir pourrait rendre la mémoire à sa soeur. Il s'associe à trois voleurs rencontrés sur le bord de la route et se rend à Onophalae, où la couronne magique est conservée en haut d'un pic particulièrement bien protégé. Du moins pour le commun des mortels, car rien ne résiste aux méthodes non- conventionnelles de Ralph. De son côté, le père de Ralph, qui a survécu à l'effondrement du barrage, s'installe à... Lire la suite",
"Note": 4.1, "Note": 4.1,
"scenario": [ "scenario": [
@@ -389,8 +393,8 @@
"Findakly, Brigitte" "Findakly, Brigitte"
], ],
"Links": { "Links": {
"Trondheim, Lewis": "http://www.bedetheque.com/auteur-145-BD-Trondheim-Lewis.html", "Trondheim, Lewis": "https://www.bedetheque.com/auteur-145-BD-Trondheim-Lewis.html",
"Findakly, Brigitte": "http://www.bedetheque.com/auteur-1382-BD-Findakly-Brigitte.html" "Findakly, Brigitte": "https://www.bedetheque.com/auteur-1382-BD-Findakly-Brigitte.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
@@ -408,8 +412,8 @@
"ref": "MAX-2", "ref": "MAX-2",
"LegalDeposit": "1993-05-01T00:00:00Z", "LegalDeposit": "1993-05-01T00:00:00Z",
"PrintDate": "0001-01-01T00:00:00Z", "PrintDate": "0001-01-01T00:00:00Z",
"PurchaseDate": "2013-09-03T00:00:00Z", "PurchaseDate": "2013-08-26T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/Couv_754.jpg", "CoverURL": "https://www.bedetheque.com/media/Couvertures/Couv_754.jpg",
"description": "", "description": "",
"Note": -1, "Note": -1,
"scenario": [ "scenario": [
@@ -419,7 +423,7 @@
"Bruyninx, Marc" "Bruyninx, Marc"
], ],
"Links": { "Links": {
"Bruyninx, Marc": "http://www.bedetheque.com/auteur-17-BD-Bruyninx-Marc.html" "Bruyninx, Marc": "https://www.bedetheque.com/auteur-17-BD-Bruyninx-Marc.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
}, },
@@ -437,8 +441,8 @@
"ref": "YIU-5", "ref": "YIU-5",
"LegalDeposit": "2005-11-01T00:00:00Z", "LegalDeposit": "2005-11-01T00:00:00Z",
"PrintDate": "0001-01-01T00:00:00Z", "PrintDate": "0001-01-01T00:00:00Z",
"PurchaseDate": "2013-09-04T00:00:00Z", "PurchaseDate": "2013-08-25T00:00:00Z",
"CoverURL": "http://www.bedetheque.com/media/Couvertures/Couv_52100.jpg", "CoverURL": "https://www.bedetheque.com/media/Couvertures/Couv_52100.jpg",
"description": "", "description": "",
"Note": 4.4, "Note": 4.4,
"scenario": [ "scenario": [
@@ -452,9 +456,9 @@
"Guénet, Nicolas" "Guénet, Nicolas"
], ],
"Links" : { "Links" : {
"Téhy": "http://www.bedetheque.com/auteur-205-BD-Tehy.html", "Téhy": "https://www.bedetheque.com/auteur-205-BD-Tehy.html",
"Vee, J.M.": "http://www.bedetheque.com/auteur-8231-BD-Vee-JM.html", "Vee, J.M.": "https://www.bedetheque.com/auteur-8231-BD-Vee-JM.html",
"Guénet, Nicolas": "http://www.bedetheque.com/auteur-2601-BD-Guenet-Nicolas.html" "Guénet, Nicolas": "https://www.bedetheque.com/auteur-2601-BD-Guenet-Nicolas.html"
}, },
"FetchDate": "0001-01-01T00:00:00Z" "FetchDate": "0001-01-01T00:00:00Z"
} }

View File

@@ -38,7 +38,7 @@ album.component('album', {
id: '<albumId', id: '<albumId',
ratio: '<' ratio: '<'
} }
}) });
album.controller('AlbumModalInstanceCtrl', function($scope,$uibModalInstance,album) { album.controller('AlbumModalInstanceCtrl', function($scope,$uibModalInstance,album) {
$scope.album = album; $scope.album = album;
@@ -47,5 +47,5 @@ album.controller('AlbumModalInstanceCtrl', function($scope,$uibModalInstance,alb
}; };
$scope.getLink = function(n) { $scope.getLink = function(n) {
return $scope.album[n]; return $scope.album[n];
} };
}); });

View File

@@ -1,11 +1,11 @@
'use strict';
var controllers = angular.module('satbd.satellite.bar.controllers',[ var controllers = angular.module('satbd.satellite.bar.controllers',[
'ui.bootstrap', 'ui.bootstrap',
'ngAnimate' 'ngAnimate'
]); ]);
controllers.controller('NavBarCtrl',function($scope,$location,$uibModal) { controllers.controller('NavBarCtrl',function($scope,$location,$uibModal) {
"use strict";
$scope.isCollapsed = true; $scope.isCollapsed = true;
$scope.isActive = function(loc) { $scope.isActive = function(loc) {
return loc === $location.path(); return loc === $location.path();
@@ -15,7 +15,7 @@ controllers.controller('NavBarCtrl',function($scope,$location,$uibModal) {
if (searchTerms) { if (searchTerms) {
$location.url('/search?q='+ encodeURIComponent(searchTerms)); $location.url('/search?q='+ encodeURIComponent(searchTerms));
} }
} };
$scope.openHelpModal = function () { $scope.openHelpModal = function () {
var modalInstance = $uibModal.open({ var modalInstance = $uibModal.open({

View File

@@ -1,5 +1,3 @@
'use strict';
angular.module('satbd.satellite.bar',[ angular.module('satbd.satellite.bar',[
'ngRoute', 'ngRoute',
'satbd.satellite.bar.controllers', 'satbd.satellite.bar.controllers',
@@ -9,5 +7,7 @@ angular.module('satbd.satellite.bar',[
'satbd.satellite.bar.views.authors', 'satbd.satellite.bar.views.authors',
'satbd.satellite.bar.views.search' 'satbd.satellite.bar.views.search'
]).config(function($routeProvider) { ]).config(function($routeProvider) {
'use strict';
$routeProvider.otherwise({redirectTo: '/recents'}); $routeProvider.otherwise({redirectTo: '/recents'});
}); });

View File

@@ -1,4 +1,4 @@
var services = angular.module('satbd.satellite.bar.services',[]) var services = angular.module('satbd.satellite.bar.services',[]);
services.factory('albumService',['$http','$log','$q', function($http,$log,$q) { services.factory('albumService',['$http','$log','$q', function($http,$log,$q) {
var determinantRegexp = /^(.*) \(([Ll][eas']+)\)$/; var determinantRegexp = /^(.*) \(([Ll][eas']+)\)$/;
@@ -6,8 +6,8 @@ services.factory('albumService',['$http','$log','$q', function($http,$log,$q) {
var removeSerie = /^\(.*\) .*$/; var removeSerie = /^\(.*\) .*$/;
function cleanupName(name) { function cleanupName(name) {
matches = name.match(determinantRegexp); var matches = name.match(determinantRegexp);
if (matches == null) { if (matches === null) {
return name; return name;
} }
return matches[2] + ' ' + matches[1]; return matches[2] + ' ' + matches[1];
@@ -18,7 +18,7 @@ services.factory('albumService',['$http','$log','$q', function($http,$log,$q) {
album.série = undefined; album.série = undefined;
album.editeur = album.éditeur; album.editeur = album.éditeur;
album.éditeur = undefined; album.éditeur = undefined;
if (removeSerie.test(album.serie) == true) { if (removeSerie.test(album.serie) === true) {
album.serieDisplay = ''; album.serieDisplay = '';
} else { } else {
album.serieDisplay = cleanupName(album.serie); album.serieDisplay = cleanupName(album.serie);
@@ -64,7 +64,7 @@ services.factory('albumService',['$http','$log','$q', function($http,$log,$q) {
"query": terms "query": terms
} }
}).then(function(response) { }).then(function(response) {
if ( fuzzify == true && response.data.total_hits == 0) { if ( fuzzify === true && response.data.total_hits === 0) {
return search(fuzzifyTerms(terms),from,false,response.data.took); return search(fuzzifyTerms(terms),from,false,response.data.took);
} }
return processResult(response.data,previousTime); return processResult(response.data,previousTime);

View File

@@ -1,9 +1,10 @@
'use strict';
angular.module('satbd.satellite.bar.views.authors',[ angular.module('satbd.satellite.bar.views.authors',[
'ngRoute' 'ngRoute'
]).config(function($routeProvider) { ]).config(function($routeProvider) {
'use strict';
$routeProvider.when('/authors', { $routeProvider.when('/authors', {
templateUrl: 'js/views/authors/authors.html', templateUrl: 'js/views/authors/authors.html',
}); });
}) });

View File

@@ -1,15 +1,17 @@
'use strict';
angular.module('satbd.satellite.bar.views.recents',[ angular.module('satbd.satellite.bar.views.recents',[
'ngRoute', 'ngRoute',
'satbd.satellite.bar.components.album', 'satbd.satellite.bar.components.album',
'angular-inview' 'angular-inview'
]).config(function($routeProvider) { ]).config(function($routeProvider) {
'use strict';
$routeProvider.when('/recents', { $routeProvider.when('/recents', {
templateUrl: 'js/views/recents/recents.html', templateUrl: 'js/views/recents/recents.html',
controller: 'RecentsCtrl' controller: 'RecentsCtrl'
}); });
}).controller('RecentsCtrl', function( $scope, $http, $log) { }).controller('RecentsCtrl', function( $scope, $http, $log) {
'use strict';
$scope.albumIDs = []; $scope.albumIDs = [];
$scope.allAlbums = []; $scope.allAlbums = [];
$scope.scrollMore = false; $scope.scrollMore = false;
@@ -29,13 +31,12 @@ angular.module('satbd.satellite.bar.views.recents',[
$log.info('Loading more components'); $log.info('Loading more components');
if (!$scope.scrollMore || $scope.albumIDs.length >= $scope.allAlbums.length) { if (!$scope.scrollMore || $scope.albumIDs.length >= $scope.allAlbums.length) {
$scope.scrollMore = false; $scope.scrollMore = false;
$log.info('reached the end of albums') $log.info('reached the end of albums');
return; return;
} }
var newSize = Math.min($scope.albumIDs.length + batchSize, $scope.allAlbums.length); var newSize = Math.min($scope.albumIDs.length + batchSize, $scope.allAlbums.length);
for( var i = $scope.albumIDs.length; i < newSize; i++) { for( var i = $scope.albumIDs.length; i < newSize; i++) {
$scope.albumIDs.push($scope.allAlbums[i]); $scope.albumIDs.push($scope.allAlbums[i]);
} }
} };
}); });

View File

@@ -1,5 +1,3 @@
'use strict';
angular.module('satbd.satellite.bar.views.search',[ angular.module('satbd.satellite.bar.views.search',[
'ngRoute', 'ngRoute',
'ngSanitize', 'ngSanitize',
@@ -7,11 +5,15 @@ angular.module('satbd.satellite.bar.views.search',[
'satbd.satellite.bar.components.album', 'satbd.satellite.bar.components.album',
'angular-inview' 'angular-inview'
]).config(function($routeProvider) { ]).config(function($routeProvider) {
'use strict';
$routeProvider.when('/search', { $routeProvider.when('/search', {
templateUrl: 'js/views/search/search.html', templateUrl: 'js/views/search/search.html',
controller: 'SearchCtrl' controller: 'SearchCtrl'
}); });
}).controller('SearchCtrl', function($scope,$routeParams,$log,$sce,albumService) { }).controller('SearchCtrl', function($scope,$routeParams,$log,$sce,albumService) {
'use strict';
$scope.terms=$routeParams.q; $scope.terms=$routeParams.q;
$scope.scrollMore = false; $scope.scrollMore = false;
$scope.allHits = []; $scope.allHits = [];
@@ -43,14 +45,14 @@ angular.module('satbd.satellite.bar.views.search',[
}; };
$scope.inSecond= function(bleveValue) { $scope.inSecond= function(bleveValue) {
return bleveValue / 1e9; return bleveValue / 1e6;
} };
$scope.trustedFragment = function(f) { $scope.trustedFragment = function(f) {
return $sce.trustAsHtml(f); return $sce.trustAsHtml(f);
} };
$scope.round4Dec = function(value) { $scope.round4Dec = function(value) {
return Math.round(value * 10000) / 10000; return Math.round(value * 10000) / 10000;
} };
}); });

View File

@@ -1,9 +1,9 @@
'use strict';
angular.module('satbd.satellite.bar.views.series',[ angular.module('satbd.satellite.bar.views.series',[
'ngRoute', 'ngRoute',
'satbd.satellite.bar.directives.responsive-ratio' 'satbd.satellite.bar.directives.responsive-ratio'
]).config(function($routeProvider) { ]).config(function($routeProvider) {
'use strict';
$routeProvider.when('/series', { $routeProvider.when('/series', {
templateUrl: 'js/views/series/series.html' templateUrl: 'js/views/series/series.html'
}); });