web and gui changes

This commit is contained in:
openaudible 2018-07-18 09:55:06 -07:00
parent 7b37655531
commit 05ca633393
23 changed files with 305 additions and 232 deletions

View file

@ -310,6 +310,27 @@ public class Audible implements IQueueListener<Book> {
}
}
// Look for books with missing info and re-parse if needed.
// Information can be lost if
boolean needSave = false;
for (Book b:getBooks())
{
if (!b.has(BookElement.summary) && hasAAX(b))
{
task.setTask("Updating book information", "Reading "+b);
needSave = AAXParser.instance.parseBook(b);
}
}
if (needSave)
{
try {
save();
} catch (IOException e) {
LOG.error("Saving error..", e);
}
}
}
public void updateFileCache() {
@ -736,7 +757,7 @@ public class Audible implements IQueueListener<Book> {
if (audibleScraper != null)
audibleScraper.logout();
AudibleScraper.deleteCookies();
}
public String getAudibleURL() {

View file

@ -113,13 +113,33 @@ public class AudibleScraper {
}
public void logout() {
ConnectionNotifier.getInstance().signout();
try {
setURL("/signout");
} catch(Throwable th)
{
LOG.info("signout error, ignorning...");
}
CookieManager cm = getWebClient().getCookieManager();
cm.clearCookies();
File cookiesFile = Directories.META.getDir(cookiesFileName);
if (cookiesFile.exists()) {
cookiesFile.delete();
}
setLoggedIn(false);
}
public static void deleteCookies()
{
File cookiesFile = Directories.META.getDir(cookiesFileName);
if (cookiesFile.exists()) {
cookiesFile.delete();
}
}
public void saveCookies() throws IOException {

View file

@ -40,6 +40,10 @@ public class ConnectionNotifier extends EventNotifier<ConnectionListener> implem
return out;
}
public void signout()
{
state = State.SignedOut;
}
public State getState() {
return state;
@ -58,14 +62,17 @@ public class ConnectionNotifier extends EventNotifier<ConnectionListener> implem
// connected means in account
// disconnected means a password is being asked for.
enum State {
Not_Connected, Connected, Disconnected
Not_Connected, Connected, Disconnected, SignedOut
}
@Override
public void loginFailed(String url, String html) {
for (ConnectionListener l : getListeners()) {
l.loginFailed(url, html);
if (state!=State.SignedOut) {
for (ConnectionListener l : getListeners()) {
l.loginFailed(url, html);
}
}
}

View file

@ -288,4 +288,20 @@ public class Book implements Comparable<Book>, Serializable {
}
public String getPurchaseDateSortable() {
String date = getPurchaseDate();
if (!date.isEmpty()) {
String dt[] = date.split("-");
if (dt.length == 3) {
return "20"+ dt[2] + "-" + dt[0] + "-" + dt[1]; // yyyy-mm-dd for sorting and viewing
// warning, y3k bug
} else
{
}
}
return date;
}
}

View file

@ -180,8 +180,10 @@ public abstract class GUI implements ITranslatable {
break;
case linux:
cmdLine.add("gnome-open");
cmdLine.add("PATH");
// gio open %s
// gnome-open
cmdLine.add("nautilus");
cmdLine.add("--browser");
break;
}
if (cmdLine.isEmpty()) return;
@ -193,7 +195,7 @@ public abstract class GUI implements ITranslatable {
p.run();
Results r = p.getResults();
logger.info(r.getErrorString());
} catch (Throwable e) {
e.printStackTrace();

View file

@ -301,12 +301,15 @@ public class AudibleGUI implements BookListener, ConnectionListener {
}
public void convertMP3(Collection<Book> list) {
bookNotifier.setEnabled(false);
audible.convertQueue.addAll(list);
bookNotifier.setEnabled(true);
bookNotifier.booksUpdated();
if (list.size() > 0) {
bookNotifier.setEnabled(false);
audible.convertQueue.addAll(list);
bookNotifier.setEnabled(true);
bookNotifier.booksUpdated();
}
}
public boolean hasAAX(Book b) {
return audible.hasAAX(b);
}
@ -421,16 +424,26 @@ public class AudibleGUI implements BookListener, ConnectionListener {
msg = "You have " + dl.size() + " book(s) to download.\n";
if (conv.size() != 0)
msg = "You have " + conv.size() + " book(s) to convert to MP3\n";
msg += "Would you like to start these job(s) now?";
LOG.info(msg + " autoConvert=" + prefs.autoConvert);
if (prefs.autoConvert || prefs.autoDownload)
{
if (prefs.autoConvert)
convertMP3(conv);
if (prefs.autoDownload)
downloadAAX(dl);
boolean ok = prefs.autoConvert;
if (!ok) ok = MessageBoxFactory.showGeneralYesNo(null, "Start jobs?", msg);
if (ok) {
audible.convertQueue.addAll(conv);
audible.downloadQueue.addAll(dl);
} else {
msg += "Would you like to start these job(s) now?";
boolean ok = MessageBoxFactory.showGeneralYesNo(null, "Start jobs?", msg);
if (ok) {
convertMP3(conv);
downloadAAX(dl);
}
}
}
}
@ -611,8 +624,11 @@ public class AudibleGUI implements BookListener, ConnectionListener {
PageBuilderTask task = new PageBuilderTask(destDir, list);
ProgressDialog.doProgressTask(task);
File index = new File(destDir, "index.html");
File index = new File(destDir, "books.html");
if (index.exists()) {
LOG.info("Book html file is: "+index.getAbsolutePath());
try {
URI i = index.toURI();
String u = i.toString();
@ -621,6 +637,9 @@ public class AudibleGUI implements BookListener, ConnectionListener {
} catch (Exception e) {
showError(e, "displaying web page");
}
} else
{
assert(false);
}
} catch (Exception e) {
@ -837,7 +856,10 @@ public class AudibleGUI implements BookListener, ConnectionListener {
public boolean logout() {
SWTAsync.assertGUI();
if (browser != null && browser.isDisposed()) {
if (browser != null && !browser.isDisposed()) {
String url = userPass.audibleRegion.getBaseURL()+"/signout";
browser.setUrl(url);
browser.close();
}
@ -849,6 +871,7 @@ public class AudibleGUI implements BookListener, ConnectionListener {
LOG.info("unable to set cookies: ", e);
}
return false;
}
@ -929,6 +952,7 @@ public class AudibleGUI implements BookListener, ConnectionListener {
public void jobCompleted(final ThreadedQueue<Book> queue, final IQueueJob job, final Book o) {
booksUpdated();
bookNotifier.bookUpdated(o);
checkAutomation();
}
@Override
@ -954,9 +978,9 @@ public class AudibleGUI implements BookListener, ConnectionListener {
final WebPage pageBuilder;
final List<Book> books;
PageBuilderTask(File dest, final List<Book> list) {
PageBuilderTask(File destDir, final List<Book> list) {
super("Creating Your Audiobook Web Page");
pageBuilder = new WebPage(dest, this);
pageBuilder = new WebPage(destDir, this);
books = list;
}
@ -973,6 +997,26 @@ public class AudibleGUI implements BookListener, ConnectionListener {
}
}
// called after every book is downloaded or converted.
public void checkAutomation()
{
ArrayList<Book> dl = audible.toDownload();
ArrayList<Book> conv = audible.toConvert();
if (prefs.autoConvert)
convertMP3(conv);
if (prefs.autoDownload)
downloadAAX(dl);
if (dl.size()==0 && conv.size()==0 && prefs.autoWebPage)
{
exportWebPage();
}
}
public void load() throws IOException {
Audible.instance.load();
@ -985,6 +1029,14 @@ public class AudibleGUI implements BookListener, ConnectionListener {
prefs = gson.fromJson(content, Prefs.class);
}
if (prefs.concurrentConversions<1||prefs.concurrentConversions>10)
prefs.concurrentConversions = 5;
if (prefs.concurrentDownloads<1||prefs.concurrentDownloads>10)
prefs.concurrentDownloads = 3;
audible.convertQueue.setConcurrentJobs(prefs.concurrentConversions);
audible.convertQueue.setConcurrentJobs(prefs.concurrentDownloads);
} catch (Throwable th) {
LOG.info("Error loading prefs", th);

View file

@ -3,4 +3,10 @@ package org.openaudible.desktop.swt.manager;
// desktop app prefs.
public class Prefs {
public boolean autoConvert = true;
public boolean autoDownload = false;
public boolean autoWebPage = false;
int concurrentConversions = 3;
int concurrentDownloads = 3;
}

View file

@ -33,7 +33,7 @@ public class AppMenu implements ITranslatable, SelectionListener {
private final Command[] actionCommands = {Command.ViewInAudible, Command.Show_MP3, Command.Play, Command.Download,
Command.Convert, Command.Refresh_Book_Info};
private final Command[] controlCommands = {Command.Connect, Command.Quick_Refresh, Command.Rescan_Library, Command.Download_All, Command.Convert_All,
Command.MenuSeparator, Command.Browser}; // , Command.MenuSeparator, Command.Logout};
Command.MenuSeparator, Command.Browser, Command.Logout}; // , Command.MenuSeparator, Command.Logout};
private final Command[] aboutCommands = {Command.Help, Command.AppWebPage, Command.Check_For_Update, Command.About};

View file

@ -159,12 +159,8 @@ public class BookTable extends EnumTable<Book, BookTableColumn> implements BookL
case Title:
return b.getFullTitle();
case Purchased:
String date = b.getPurchaseDate();
String dt[] = date.split("-");
if (dt.length == 3) {
return dt[2] + "-" + dt[0] + "-" + dt[1]; // yy-mm-dd for sorting and viewing
}
return date;
return b.getPurchaseDateSortable();
default:
assert (false);
break;

View file

@ -2,7 +2,7 @@ package org.openaudible.desktop.swt.manager.views;
public enum BookTableColumn {
File, Title, Author, Narrated_By, Time, Purchased;
static int widths[] = {22, 250, 150, 150, 60, 60};
static int widths[] = {22, 250, 150, 150, 60, 90};
// HasAAX, HasMP3,
public static int[] getWidths() {

View file

@ -68,11 +68,12 @@ public class LogWindow {
data = new GridData(GridData.FILL_BOTH);
data.horizontalSpan = numCols;
textView.setLayoutData(data);
commandLine = new Text(shell, SWT.SINGLE | SWT.BORDER);
data = new GridData(GridData.VERTICAL_ALIGN_END);
data.horizontalSpan = numCols;
commandLine.setLayoutData(data);
if (false) {
commandLine = new Text(shell, SWT.SINGLE | SWT.BORDER);
data = new GridData(GridData.VERTICAL_ALIGN_END);
data.horizontalSpan = numCols;
commandLine.setLayoutData(data);
} else commandLine = null; // off.. not implemented
shell.addListener(SWT.Close, event -> close());

View file

@ -26,7 +26,7 @@ public class Preferences extends Dialog {
final String paths[] = new String[Directories.values().length];
final Text dirText[] = new Text[dirs.length];
Combo region;
Button autoConvert;
Button autoConvert, autoDownload, autoWebPage;
private Text email, password;
private boolean pathsChanged = false;
@ -75,6 +75,8 @@ public class Preferences extends Dialog {
}
autoConvert.setSelection(AudibleGUI.instance.prefs.autoConvert);
autoDownload.setSelection(AudibleGUI.instance.prefs.autoDownload);
autoWebPage.setSelection(AudibleGUI.instance.prefs.autoWebPage);
}
@ -99,6 +101,8 @@ public class Preferences extends Dialog {
}
AudibleGUI.instance.prefs.autoConvert = autoConvert.getSelection();
AudibleGUI.instance.prefs.autoDownload = autoDownload.getSelection();
AudibleGUI.instance.prefs.autoWebPage = autoWebPage.getSelection();
if (pathsChanged) {
@ -165,9 +169,18 @@ public class Preferences extends Dialog {
Group group = c.newGroup("Automation", 1);
GridData gd = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
group.setLayoutData(gd);
autoDownload = GridComposite.newCheck(group, "Automatically download books");
gd = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
autoDownload.setLayoutData(gd);
autoConvert = GridComposite.newCheck(group, "Automatically convert to MP3");
gd = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
autoConvert.setLayoutData(gd);
autoWebPage = GridComposite.newCheck(group, "Automatically Update Web Page");
gd = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
autoWebPage.setLayoutData(gd);
}
/**

View file

@ -29,12 +29,12 @@ public class DownloadQueue extends ThreadedQueue<Book> {
}
public boolean canAdd(Book b) {
assert (b.has(BookElement.user_id));
assert (b.has(BookElement.product_id));
if (!super.canAdd(b)) return false;
if (!b.has(BookElement.user_id)) return false;
if (!b.has(BookElement.product_id)) return false;
if (Audible.instance.hasAAX(b)) return false;
if (!super.canAdd(b)) return false;
if (!b.has(BookElement.user_id))
return false;
if (!b.has(BookElement.product_id))
return false;
return true;
}

View file

@ -24,7 +24,7 @@ public class WebPage {
final File webDir;
final IProgressTask progress; // required
int thumbSize = 200; // If changed, need to change html
final static String indexName = "books.html";
// final static String indexName = "books.html";
public WebPage(File dir, IProgressTask t) {
webDir = dir;
@ -43,7 +43,7 @@ public class WebPage {
i.rating_count = b.get(BookElement.rating_count);
i.audible = b.get(BookElement.infoLink);
i.description = b.get(BookElement.description);
i.purchased = b.get(BookElement.purchase_date);
i.purchased = b.getPurchaseDateSortable();
return i;
}

View file

@ -12,7 +12,7 @@ import java.util.LinkedList;
public abstract class ThreadedQueue<E> implements IQueueListener<E> {
private static final Log LOG = LogFactory.getLog(ThreadedQueue.class);
final int concurrentJobs; // number of jobs that can be run at once
int concurrentJobs; // number of jobs that can be run at once
volatile boolean quit = false;
int totalThreads = 0;
@ -43,6 +43,8 @@ public abstract class ThreadedQueue<E> implements IQueueListener<E> {
return ok;
}
public int size() {
return queue.size();
}
@ -281,5 +283,13 @@ public abstract class ThreadedQueue<E> implements IQueueListener<E> {
}
public int getConcurrentJobs() {
return concurrentJobs;
}
public void setConcurrentJobs(int concurrentJobs) {
this.concurrentJobs = concurrentJobs;
}
}

View file

@ -9,7 +9,7 @@ COMMAND_CONVERT=Convert to MP3
COMMAND_CONVERT_ALL=Convert All to MP3
COMMAND_COPY=Copy
COMMAND_CUT=Cut
COMMAND_DOWNLOAD=Download AAX
COMMAND_DOWNLOAD=Download
COMMAND_DOWNLOAD_ALL=Download All From Audible
COMMAND_EXPORT_WEB_PAGE=Export Web Page
COMMAND_FETCH_DECRYPTION_KEY=Fetch Decrypytion Key

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 B

View file

@ -1,50 +0,0 @@
#container {
width: 1300px;
}
/* customize */
select.tidy_table {
float: right;
margin: 10px 0px 10px 0px;
height:auto!important;
color:#666; padding: 1%;
font-size: 14px;
font-weight: normal;
word-wrap: break-word;
text-align: left;
}
table.tidy_table {
white-space:normal;
}
table.tidy_table th {
white-space: wrap;
}
table.tidy_table tr.check_on {
background-color: #F7F7F7;
}
table.tidy_table tr.check_off {
background-color: #FFFFFF;
}
table.tidy_table th {
background-color: #DDDDDD;
}
table.tidy_table th.sort_asc {
background: #EEEEEE url( assets/arrow_asc.gif ) no-repeat right center;
}
table.tidy_table th.sort_desc {
background: #EEEEEE url( assets/arrow_desc.gif ) no-repeat right center;
}
table.tidy_table td {
border-top: 1px solid #CCCCCC;
}

File diff suppressed because one or more lines are too long

View file

@ -1,9 +0,0 @@
/**
* Tidy Table
* Generate a sortable HTML table from JSON
*
* Copyright 2012-2015, Marc S. Brooks (http://mbrooks.info)
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*/
table.tidy_table{background-color:#FFFFFF;border-collapse:collapse;border:1px solid #CCCCCC;cursor:pointer;white-space:nowrap}table.tidy_table thead{border:none}table.tidy_table tr.check_on{background-color:#F5F5F5}table.tidy_table tr.check_off{background-color:#FFFFFF}table.tidy_table th{background-color:#DDD}table.tidy_table th.sort_asc{background:#EEEEEE url( images/arrow_asc.gif ) no-repeat right center}table.tidy_table th.sort_desc{background:#EEEEEE url( images/arrow_desc.gif ) no-repeat right center}table.tidy_table th,table.tidy_table td{padding:6px 20px 6px 20px}table.tidy_table td{border-right:1px solid #EEEEEE;padding:7px 20px 7px 20px}table.tidy_table th:nth-child(1),table.tidy_table td:nth-child(1){width:10px}table.tidy_table td:nth-child(1){border-right:1px solid #EEEEEE}table.tidy_table input[type=checkbox],table.tidy_table input[type=checkbox]{display:block;margin:0px auto 0px auto}table.tidy_table td:nth-last-child(1){border-right:none}table.tidy_table td{border-top:1px solid #CCCCCC}

View file

@ -1,9 +0,0 @@
/**
* Tidy Table
* Generate a sortable HTML table from JSON
*
* Copyright 2012-2015, Marc S. Brooks (http://mbrooks.info)
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*/
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('5(!V.B||(V.B&&19(V.B.1a.1B.W(\'.\',\'\'))<19(\'1.8.3\'.W(\'.\',\'\')))){1C 1D 1E(\'1F-1G 1H B 1.8.3 1I 1J.\');}(6($){4 r={"1b":6(c,d){4 e=$(u),2=e.2();4 f={1c:y,1d:y,1e:y};5(X.C>1){$.1f(f,c)}s{d=c}d=$.1f({K:6(a,b){5($.1K(a)){9 1L(b).W(/$|%|#/g,\'\')}}},d);5($.1M(2)){e.2({D:f,7:d})}9 e.v(\'Y\')},"1N":6(){$(u).1O()},"Y":6(l,m){4 n=$(u),2=n.2();4 o=$(\'<G></G>\').z(\'Z\');o.1P(6(){9 y});o.1Q(6(){9 y});4 p=$(\'<1g></1g>\'),E=$(\'<E></E>\');(6(){4 b=$(\'<F></F>\');H(4 i=0;i<2.7.1h.C;i++){4 c=2.7.1h[i];4 d=$(\'<L></L>\').t(c).M(\'1i\',c);b.t(d);4 e;5(!2.D.1e){5(m==\'N\'||!m){e=\'1j\';d.I=\'O\'}s{e=\'1k\';d.I=\'N\'}}s{5(m==\'O\'||!m){e=\'1j\';d.I=\'N\'}s{e=\'1k\';d.I=\'O\'}}5(l==i){d.z(e)}d.10(\'11\',{1l:i,1m:(l==i)?d.I:\'N\'},6(a){n.v(\'1n\',a.2.1l,a.2.1m)})}p.t(b)})();(6(){4 e=2.7.1o;H(4 j=0;j<e.C;j++){4 f=$(\'<F></F>\');H(4 k=0;k<e[j].C;k++){4 g=e[j][k];4 h=$(\'<P></P>\').t(g).M(\'1i\',g);f.t(h);5(2.7.w&&$.J(2.7.w.1p)){2.7.w.1p(h)}}E.t(f)}o.t(p);o.t(E);5(2.D&&2.D.1c){4 i=o.12(\'F\');i.13(6(b){4 c=$(\'<x></x>\').M(\'1R\',\'1q\');4 d;5(b===0){d=$(\'<L></L>\');c.10(\'11\',6(){n.v(\'14\',i)})}s{d=$(\'<P></P>\');c.10(\'11\',{1r:b},6(a){n.v(\'14\',i,a.2.1r)})}d.t(c);$(u).1S(d)})}})();5(2.7.w&&$.J(2.7.w.G)){2.7.w.G(o)}4 q=n.1T(\'G.Z\');5(q[0]){q.1U(o)}s{5(2.D&&2.D.1d){n.t(n.v(\'1s\',o,\'1V\'))}n.t(o)}9 o},"1s":6(c,d){4 e=$(u),2=e.2();4 f=$(\'<1t></1t>\').z(\'Z \'+d).1W(6(){4 a=$(u);4 b=2.7.15[a.1u()][1][\'1X\'];5($.J(b)){b(e.v(\'1v\',c))}a.1u(0)});$.13(2.7.15,6(a){4 b=$(\'<1w>\'+2.7.15[a][0]+\'</1w>\').M(\'1Y\',a);f.t(b)});5(2.7.w&&$.J(2.7.w.1x)){2.7.w.1x(f)}9 f},"1v":6(a){4 b=a.12(\'E > F\'),16=[];H(4 i=0;i<b.C;i++){4 c=b[i].1Z;5(c[0].20.A){4 d=[];H(4 j=1;j<c.C;j++){d[j-1]=c[j].21}16.22(d)}}9 16},"14":6(c,d){4 e=23;c.13(6(a){4 b=$(u),x=b.12(\':1q\').24();5(!d){5(a===0){e=(x.1y(\':A\'))?17:y;9}5(e){b.Q(\'R\').z(\'S\');x.T(\'A\',17)}s{b.Q(\'S\').z(\'R\');x.T(\'A\',y)}}s{5(a===0){9}5(x.1y(\':A\')){b.Q(\'R\').z(\'S\');x.T(\'A\',17)}s{b.Q(\'S\').z(\'R\');x.T(\'A\',y)}}})},"1n":6(d,e){4 f=$(u),2=f.2();5($.J(2.7.K)){4 g=(e==\'O\')?-1:1;2.7.1o.25(6(a,b){4 c=2.7.K(d,a[d]),U=2.7.K(d,b[d]);5(26(c)){9[g*18(c,U)]>[g*18(U,c)]?-1:1}s{9[g*1z(c,U)]}})}f.v(\'Y\',d,e)}};$.1a.v=6(a){5(r[a]){9 r[a].1A(u,27.28.29.2a(X,1))}s 5(2b a===\'2c\'||!a){9 r.1b.1A(u,X)}s{$.2d(\'2e \'+a+\' 2f 2g 2h 2i B.v\')}};6 18(a,b){9(a>b)?1:(a<b)?-1:0}6 1z(a,b){9 b-a}})(B);',62,143,'||data||var|if|function|config||return|||||||||||||||||||else|append|this|TidyTable|postProcess|input|false|addClass|checked|jQuery|length|settings|tbody|tr|table|for|order|isFunction|sortByPattern|th|attr|asc|desc|td|removeClass|check_off|check_on|prop|str2|window|replace|arguments|_createTable|tidy_table|on|click|find|each|_toggleSelRows|menuOptions|objs|true|cmpAny|parseInt|fn|init|enableCheckbox|enableMenu|reverseSortDir|extend|thead|columnTitles|title|sort_asc|sort_desc|col_number|sort_order|_sortByColumn|columnValues|column|checkbox|box_number|_createMenu|select|val|_getCheckedAsObj|option|menu|is|cmpInt|apply|jquery|throw|new|Error|Tidy|Table|requires|or|greater|trim|String|isEmptyObject|destroy|removeData|mousedown|mouseover|type|prepend|children|replaceWith|options|change|callback|value|childNodes|firstChild|textContent|push|null|first|sort|isNaN|Array|prototype|slice|call|typeof|object|error|Method|does|not|exist|in'.split('|'),0,{}));

View file

@ -1,139 +1,138 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="assets/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="assets/tidy-table.min.css">
<link rel="stylesheet" type="text/css" href="assets/books.css">
<script src="assets/tidy-table.min.js"></script>
<script src="books.js" type="text/javascript"></script>
<!-- JavaScript insert start -->
<script>
$(document).ready(function() {
if (false)
{
$.ajax({
dataType: "json",
url: "books.json",
success: populateBooks
});
} else
{
populateBooks(window.myBooks);
}
});
function renderTable(columnValues)
{
$('#container')
.TidyTable({
enableCheckbox: false,
enableMenu: false,
reverseSortDir: false
},
{
columnTitles: ['Link', 'Info', 'Summary'],
columnValues: columnValues,
menuOptions: [
],
postProcess: {
table: resizeSummary,
column: doColClick
}
});
}
function trunc(str, len)
{
if (str===undefined) return "";
return str.substring(0, len);
}
<meta charset="utf-8">
function resizeSummary(table) {
table.find('th:nth-child(2), td:nth-child(2)').css('width','200');
}
function populateBooks(arr)
{
var i;
var out = [];
for(i = 0; i < arr.length; i++)
{
var book = arr[i];
var rating = book.rating_average = (book.rating_average !== undefined) ? book.rating_average:"";
var author = book.author = (book.author !== undefined) ? trunc(book.author, 20):"";
var narratedBy = book.narratedBy = (book.narratedBy !== undefined) ? trunc(book.narratedBy, 20):"";
var run_time = book.run_time = (book.run_time !== undefined) ? book.run_time:"";
var summary = trunc(book.description, 2048);
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.10.1/bootstrap-table.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.10.1/bootstrap-table.min.js"></script>
<link rel="stylesheet" type="text/css" href="assets/books.css">
<!-- Created by OpenAudible export web page. -->
<script src="books.js" type="text/javascript"></script>
summary = summary.replace(/(?:\r\n|\r|\n)/g, '<p />');
var thumb = "<img src='assets/download.jpg'>";
if (book.image !== undefined)
thumb = "<img src='thumb/"+encodeURIComponent(book.image)+"' width='200' height='200'>"; // Thumbnail size
var linkName = trunc(book.title, 90);
var mp3 = encodeURIComponent(book.mp3);
var thumbLink = "<a href='mp3/"+mp3+"'>"+thumb+"</a> ";
<script>
var info = "<strong>"+trunc(book.title, 50)+"</strong><br>";
if (author.length>0)
info += "by <i>"+author+"</i><br>";
if (narratedBy.length>0)
info += "Narrated by "+narratedBy+"<br>";
info += run_time;
info += " ";
info += trunc(book.rating_average, 99);
var row = [thumbLink, info, summary];
out.push(row);
}
renderTable(out);
}
function filter() {
var text = $("#filter").val();
var rex = new RegExp(text, 'i');
$('.searchable tr').hide();
$('.searchable tr').filter(function () {
return rex.test($(this).text());
}).show();
}
$(document).ready(function () {
populateBooks(window.myBooks);
$('#filter').keyup(filter);
});
function trunc(str, len) {
if (str === undefined) return "";
return str.substring(0, len);
}
// post-process DOM elements
function doTableClick(table) {
table.on('hover', function() {
// alert('doTableClick(table)');
});
}
function resizeSummary(table) {
table.find('th:nth-child(2), td:nth-child(2)').css('width', '200');
}
function doColClick(col)
{
col.on('click', function() {
// alert('doColClick(value=' + $(this).text() + ')');
});
}
function populateBooks(arr) {
var i;
var data = [];
for (i = 0; i < arr.length; i++) {
var book = arr[i];
var rating = book.rating_average = (book.rating_average !== undefined) ? book.rating_average : "";
var author = book.author = (book.author !== undefined) ? trunc(book.author, 20) : "";
var narratedBy = book.narratedBy = (book.narratedBy !== undefined) ? trunc(book.narratedBy, 20) : "";
var run_time = book.run_time = (book.run_time !== undefined) ? book.run_time : "";
var summary = trunc(book.description, 2048);
</script>
summary = summary.replace(/(?:\r\n|\r|\n)/g, '<p />');
<title>OpenAudible collection of audiobooks</title>
var thumb = "<img src='assets/download.jpg'>";
if (book.image !== undefined)
thumb = "<img src='thumb/" + encodeURIComponent(book.image) + "' width='200' height='200'>"; // Thumbnail size
var linkName = trunc(book.title, 90);
var mp3 = encodeURIComponent(book.mp3);
var thumbLink = "<a href='mp3/" + mp3 + "'>" + thumb + "</a> ";
var info = "<strong>" + trunc(book.title, 50) + "</strong><br>";
if (author.length > 0)
info += "by <i>" + author + "</i><br>";
if (narratedBy.length > 0)
info += "Narrated by " + narratedBy + "<br>";
info += run_time;
info += " ";
info += trunc(book.rating_average, 99);
var row = {};
row['title'] = thumbLink;
row['info'] = info;
row['purchased'] = book.purchased;
row['summary'] = summary;
data.push(row);
}
$('#table').bootstrapTable({data: data});
}
</script>
<title>OpenAudible collection of audiobooks</title>
</head>
<body>
<div id="container"></div>
<div id="container">
<div class="row">
<div class="col-md-4 col-md-offset-2">
<div class="input-group">
<div class="input-group-btn">
<button class="btn btn-default" type="submit"><i class="glyphicon glyphicon-search"></i></button>
</div>
<input type="text" class="form-control" placeholder="Search" name="srch-term" id="filter">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="table" class="table table-striped">
<thead>
<tr>
<th data-field="title" data-sortable="true">Title</th>
<th data-field="info" data-sortable="true">Info</th>
<th data-field="purchased" data-sortable="true">Purchased</th>
<th data-field="summary" data-sortable="false">Summary</th>
</tr>
</thead>
<tbody class="searchable">
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>