3 var WL_INITIAL = WL + '/media/api/mobile/initial/initial.db';
4 var WL_UPDATE = WL + '/api/changes/SINCE.json?book_fields=author,html,parent,parent_number,sort_key,title' +
5 '&tag_fields=books,category,name,sort_key' +
6 '&tag_categories=author,epoch,genre,kind';
10 var categories = {'author': 'autor',
17 // FIXME: htmlescape strings!
20 // for preparing sql statements
22 // var s = new Sql("INSERT ... '{0}', '{1}' ...";
23 // s.prepare("abc", ...)
24 var Sql = function(scheme) {
28 self.sql_escape = function(term) {
29 return term.toString().replace("'", "''");
32 self.prepare = function() {
34 return self.text.replace(/{(\d+)}/g, function(match, number) {
35 return self.sql_escape(args[parseInt(number)]);
41 var Catalogue = new function() {
42 /* API for database */
47 this.init = function(success, error) {
48 console.log('Catalogue.init');
50 self.updateDB(function() {
51 self.db = window.openDatabase("wolnelektury", "1.0", "WL Catalogue", 1);
54 onFunctionCall: function(val) {
55 var re = new RegExp(val.getString(0));
56 if (val.getString(1).match(re))
62 self.db.createFunction("REGEXP", 2, regexp);*/
66 error && error('Nie mogę otworzyć bazy danych: ' + err);
70 error && error('Błąd migracji: ' + err);
74 self.sqlSanitize = function(term) {
75 return term.toString().replace("'", "''");
79 /* check if DB needs updating and upload a fresh copy, if so */
80 this.updateDB = function(success, error) {
81 var has_ver = window.localStorage.getItem('db_ver');
82 if (has_ver == DB_VER) {
83 console.log('db ok, skipping')
88 var done = function() {
90 window.localStorage.setItem('db_ver', DB_VER);
91 console.log('db updated');
96 // this is Android-specific for now
97 var android = device.version.split('.')[0];
99 self.upload_db_android(done, error);
101 error && error("Nieobsługiwana wersja systemu. Wymagany Android>=2.0.");
106 this.upload_db_android = function(success, error) {
107 // TODO: this should be downloaded from teh net, not stored in res
109 console.log('upload db for Android 2.x+');
110 // upload databases description file
112 var dbname = "wolnelektury";
113 var db = window.openDatabase(dbname, "1.0", "WL Catalogue", 500000);
115 console.log('db created successfully');
116 DBPut.fetch(WL_INITIAL, function(data) {
117 console.log('db fetch successful');
118 success && success();
120 error && error('Błąd podczas pobierania bazy danych: ' + data);
123 error && error('Błąd podczas inicjowania bazy danych: ' + data);
128 this.withState = function(callback) {
129 self.db.transaction(function(tx) {
130 tx.executeSql("SELECT * FROM state", [],
131 function(tx, results) {
132 if (results.rows.length) {
133 callback(results.rows.item(0));
136 callback({last_checked: 0});
143 this.withBook = function(id, callback, error) {
144 console.log('withBook '+id)
145 self.db.transaction(function(tx) {
146 tx.executeSql("SELECT * FROM book WHERE id="+id, [],
147 function(tx, results) {
148 if (results.rows.length) {
149 callback(results.rows.item(0));
158 this.withBooks = function(ids, callback) {
159 console.log('withBooks ' + ids)
160 self.db.transaction(function(tx) {
161 tx.executeSql("SELECT * FROM book WHERE id IN ("+ids+") ORDER BY sort_key", [],
162 function(tx, results) {
164 var count = results.rows.length;
165 for (var i=0; i<count; ++i) {
166 items.push(results.rows.item(i));
174 this.withChildren = function(id, callback) {
175 console.log('withChildren ' + id)
176 self.db.transaction(function(tx) {
177 tx.executeSql("SELECT * FROM book WHERE parent="+id+" ORDER BY parent_number, sort_key", [],
178 function(tx, results) {
180 var count = results.rows.length;
181 for (var i=0; i<count; ++i) {
182 books.push(results.rows.item(i));
189 this.withTag = function(id, callback, error) {
190 console.log('withTag '+id)
191 self.db.transaction(function(tx) {
192 tx.executeSql("SELECT * FROM tag WHERE id="+id, [],
193 function(tx, results) {
194 if (results.rows.length) {
195 callback(results.rows.item(0));
204 this.withCategory = function(category, callback) {
205 console.log('withCategory ' + category)
206 self.db.transaction(function(tx) {
207 tx.executeSql("SELECT * FROM tag WHERE category='"+category+"' ORDER BY sort_key", [],
208 function(tx, results) {
210 var count = results.rows.length;
211 for (var i=0; i<count; ++i)
212 items.push(results.rows.item(i));
219 /* takes a query, returns a list of {view,id,label} objects to a callback */
220 this.withSearch = function(term, callback) {
221 console.log('searching...');
222 term = term.replace(/^\s+|\s+$/g, '') ;
225 function booksFound(tx, results) {
226 var len = results.rows.length;
227 console.log('found books: ' + len);
228 for (var i=0; i<len; i++) {
229 var item = results.rows.item(i);
237 function tagsFound(tx, results) {
238 var len = results.rows.length;
239 console.log('found tags: ' + len);
240 for (var i=0; i<len; i++) {
241 var item = results.rows.item(i);
247 // TODO error handling
253 // TODO pliterki, start of the word match
254 self.db.transaction(function(tx) {
255 sql_term = self.sqlSanitize(term); // this is still insane, % and _
256 tx.executeSql("SELECT * FROM book WHERE title LIKE '%"+sql_term+"%' ORDER BY sort_key LIMIT 10", [],
257 //tx.executeSql("SELECT * FROM book WHERE title REGEXP '.*"+sql_term+".*' ORDER BY sort_key", [],
258 function(tx, results) {
260 booksFound(tx, results);
261 // and proceed to tags
262 tx.executeSql("SELECT * FROM tag WHERE name LIKE '%"+sql_term+"%' ORDER BY sort_key LIMIT 10",
266 console.log('ERROR:search: '+err.code);
272 self.chainSqls = function(sqls, success, error) {
273 self.db.transaction(function(tx) {
274 var do_next = function() {
276 var sql = sqls.shift();
278 tx.executeSql(sql, [], do_next, error);
281 success && success();
289 self.update = function(data, success, error) {
290 var addBookSql = new Sql("\
291 INSERT OR REPLACE INTO book \
292 (id, title, html_file, html_file_size, parent, parent_number, sort_key, pretty_size, authors) \
294 ('{0}', '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}', '{8}')");
295 var addTagSql = new Sql("INSERT OR REPLACE INTO tag (id, category, name, sort_key, books) VALUES ('{0}', '{1}', '{2}', '{3}', '{4}')");
300 for (i in data.deleted.books) {
301 var book_id = data.deleted.books[i];
302 sqls.push("DELETE FROM book WHERE id=" + book_id);
303 FileRepo.deleteIfExists(book_id);
306 for (i in data.deleted.tags) {
307 var tag_id = data.deleted.tags[i];
308 sqls.push("DELETE FROM tag WHERE id=" + tag_id);
313 for (i in data.updated.books) {
314 var book = data.updated.books[i];
315 if (!book.html) book.html = {};
316 if (!book.html.url) book.html.url = '';
317 if (!book.html.size) book.html.size = '';
318 if (!book.parent) book.parent = '';
319 if (!book.parent_number) book.parent_number = '';
320 var pretty_size = prettySize(book.html.size);
321 sqls.push(addBookSql.prepare(
322 book.id, book.title, book.html.url, book.html.size,
323 book.parent, book.parent_number, book.sort_key, pretty_size, book.author
325 FileRepo.deleteIfExists(book.id);
328 for (i in data.updated.tags) {
329 var tag = data.updated.tags[i];
330 var category = categories[tag.category];
331 var books = tag.books.join(',');
332 sqls.push(addTagSql.prepare(tag.id, category, tag.name, tag.sort_key, books));
336 sqls.push("UPDATE state SET last_checked=" + data.time_checked);
338 self.chainSqls(sqls, success, error);
342 this.sync = function(success, error) {
343 self.withState(function(state) {
344 var url = WL_UPDATE.replace("SINCE", state.last_checked);
345 console.log('sync: ' + url);
346 var xhr = new XMLHttpRequest();
347 xhr.open("GET", url);
348 xhr.onload = function() {
349 console.log('sync: fetched by ajax: ' + url);
350 self.update(JSON.parse(xhr.responseText), success, error);
352 xhr.onerror = function(e) {
353 error && error("Błąd aktualizacji bazy danych." + e);
359 this.updateLocal = function() {
360 FileRepo.withLocal(function(local) {
361 self.db.transaction(function(tx) {
362 tx.executeSql("UPDATE book SET _local=0", [], function(tx, results) {
365 for (var i = 0; i < ll; i ++) {
366 ids.push(local[i].name);
369 tx.executeSql("UPDATE book SET _local=1 where id in ("+ids+")");
373 self.db.transaction(function(tx) {
374 tx.executeSql("UPDATE book SET _local=0");