1// Caches 2let authors = {}; 3let badges = {}; 4let books = {}; 5let chapters = {}; 6let translations = {}; 7let collections = {}; 8let glossary_terms = {}; 9let languages = []; 10let licenses = []; 11let pages = {}; 12let short_name_info = {}; 13let articles = {}; 14let site = null; 15 16// Maps 17let book_short_names = {}; 18let chapter_short_names = {}; 19let page_short_names = {}; 20 21// Current Identifier Shortcuts 22let current_book = null; 23let current_chapter = null; 24let current_page = null; 25let current_user = null; 26 27let system_msg_closed = false; 28 29// Universal POST Function 30async function API(url, data = {}, options = {}) { 31 return new Promise(async (resolve, reject) => { 32 let startTime = new Date().getTime(); 33 $.ajax({ 34 url: url, 35 type: 'POST', 36 contentType: 'application/json', 37 data: JSON.stringify(data), 38 success: async function (json) { 39 if (json.status == 'success') { 40 //console.warn(json); 41 console.log('API execution time: ' + (new Date().getTime() - startTime) / 1000); 42 if (json.msg) Toast('Notification', json.msg, 'bg-success text-light'); 43 resolve(json); 44 } else { 45 //console.error(json.error); 46 if (json.toast && !options.silent) Toast('API Error', json.error, 'bg-danger text-light'); 47 reject(json.error); 48 } 49 }, 50 error: function (xhr, status, error) { 51 console.error(error); 52 Toast('Error', error, 'bg-danger text-light'); 53 reject(error); 54 } 55 }); 56 }); 57} 58 59async function APIFile(url, file) { 60 return new Promise(async (resolve, reject) => { 61 let startTime = new Date().getTime(); 62 let formData = new FormData(); 63 formData.append('file', file); 64 $.ajax({ 65 url: url, 66 type: 'POST', 67 data: formData, 68 contentType: false, 69 processData: false, 70 success: async function (json) { 71 if (json.status == 'success') { 72 //console.warn(json); 73 console.log('API execution time: ' + (new Date().getTime() - startTime) / 1000); 74 if (json.msg) Toast('API Message', json.msg, 'bg-success text-light'); 75 resolve(json); 76 } else { 77 console.error(json.error); 78 if (json.toast) Toast('API Error', json.error, 'bg-danger text-light'); 79 reject(json.error); 80 } 81 }, 82 error: function (xhr, status, error) { 83 console.error(error); 84 Toast('Error', error, 'bg-danger text-light'); 85 reject(error); 86 } 87 }); 88 }); 89} 90 91// Authors 92async function GetAuthor(id, rebuild = false) { 93 if (authors[id] !== undefined && !rebuild) return authors[id]; 94 try { 95 console.warn('Getting author'); 96 let json = await API('/author/' + id + '/view'); 97 authors[json.entity.author_id] = new Author(json.entity); 98 return authors[json.entity.author_id]; 99 } catch (error) { console.error(error); return null; } 100} 101async function GetBookLinksForAuthor(id) { 102 try { 103 console.warn('Getting book links for author'); 104 let json = await API('/author/' + id + '/getBookLinks'); 105 return json.links; 106 } catch (error) { console.error(error); return null; } 107} 108 109async function GetChapterLinksForAuthor(id) { 110 try { 111 console.warn('Getting chapter links for author'); 112 let json = await API('/author/' + id + '/getChapterLinks'); 113 return json.links; 114 } catch (error) { console.error(error); return null; } 115} 116 117async function CreateArticle(id, category) { 118 try { 119 console.log('Adding article to category ' + category); 120 let json = await API('/author/' + id + '/createArticle', { category: category }); 121 articles[json.entity.article_id] = new Article(json.entity); 122 return articles[json.entity.article_id]; 123 } catch (error) { console.error(error); return false; } 124} 125async function DeleteArticle(author_id, article_id) { 126 try { 127 console.log('Deleting article ' + article_id); 128 let json = await API('/author/' + author_id + '/deleteArticle/' + article_id); 129 delete articles[article_id]; 130 return true; 131 } catch (error) { console.error(error); return false; } 132} 133 134async function GetArticlesForAuthor(id, rebuild = false) { 135 if (authors[id] !== undefined && authors[id].articles !== undefined && !rebuild) return authors[id].articles; 136 try { 137 console.warn('Getting articles for author'); 138 let json = await API('/author/' + id + '/getArticles'); 139 authors[id].articles = []; 140 for (let article of json.articles) { 141 articles[article.article_id] = new Article(article); 142 authors[id].articles.push(articles[article.article_id]); 143 } 144 return authors[id].articles; 145 } catch (error) { console.error(error); return null; } 146} 147 148async function Impersonate(btn) { 149 try { 150 console.warn('Impersonating user'); 151 let id = $(btn).attr('data-action-value'); 152 console.log(id); 153 let json = await API('/author/' + id + '/impersonate'); 154 await GetCurrentUser(true); 155 RenderFromPath('/', true); 156 return true; 157 } catch (error) { console.error(error); return false; } 158} 159 160async function GrantRole(id, role, conflicts = []) { 161 try { 162 console.warn('Granting role'); 163 let json = await API('/author/' + id + '/grantRole/' + role, conflicts); 164 authors[json.entity.author_id] = json.entity; 165 return json.entity; 166 } catch (error) { console.error(error); return null; } 167} 168 169async function RevokeRole(id, role) { 170 try { 171 console.warn('Revoking role'); 172 let json = await API('/author/' + id + '/revokeRole/' + role); 173 authors[json.entity.author_id] = json.entity; 174 return json.entity; 175 } catch (error) { console.error(error); return null; } 176} 177 178async function SetApprovalStatus(id, status) { 179 try { 180 console.warn('Setting approval status'); 181 let json = await API('/author/' + id + '/setApproval/' + status); 182 authors[json.entity.author_id] = json.entity; 183 return json.entity; 184 } catch (error) { console.error(error); return null; } 185} 186 187// Badges 188async function GetBadge(id) { 189 if (id == null) return null; 190 if (badges[id] !== undefined) return badges[id]; 191 try { 192 console.warn('Getting badge'); 193 let json = await API('/badge/' + id); 194 badges[json.entity.badge_id] = json.entity; 195 return json.entity; 196 } catch (error) { console.error(error); return null; } 197} 198async function ListBadges(entity_type) { 199 try { 200 console.warn('Listing badges'); 201 let json = await API('/badge/list/' + entity_type); 202 return json.entities; 203 } catch (error) { console.error(error); return []; } 204} 205async function GetBookLinksForBadge(id) { 206 try { 207 console.warn('Getting book links for badge'); 208 let json = await API('/badge/' + id + '/getBookLinks'); 209 return json.links; 210 } catch (error) { console.error(error); return []; } 211} 212 213// Books 214async function GetBook(id, rebuild = false, silent = false) { 215 if (books[id] !== undefined && !rebuild) return books[id]; 216 if (book_short_names[id] !== undefined && books[book_short_names[id]] !== undefined && !rebuild) return books[book_short_names[id]]; 217 try { 218 console.warn('Getting book'); 219 let json = await API('/book/' + id + '/view', {}, { silent });
220 book_short_names[json.entity.short_name] = json.entity.id; 221 books[json.entity.id] = new Book(json.entity); 222 return books[json.entity.id]; 223 } catch (error) { console.log(error); return null; } 224} 225async function GetBookChapterAuthors(id) { 226 try { 227 console.warn('Getting book chapter authors'); 228 let json = await API('/book/' + id + '/chapterAuthors'); 229 return json.entities; 230 } catch (error) { console.error(error); return []; } 231} 232async function GetIndexResults(id, term) { 233 try { 234 console.warn('Getting index results'); 235 let json = await API('/book/' + id + '/indexResults', { term: term }); 236 return json.entities; 237 } catch (error) { console.log(error); return []; } 238} 239async function RebuildPDF(btn) { 240 try { 241 console.warn('Rebuilding PDF'); 242 let id = $(btn).attr('data-action-value'); 243 await API('/pdf/' + id + '/rebuild'); 244 Toast('Rebuilding PDF', 'The PDF is being rebuilt. This may take some time.', 'bg-primary'); 245 return true; 246 } catch (error) { console.error(error); return null; } 247} 248async function SortBookChapters(data) { 249 try { 250 let book = books[$('#main-upper').attr('data-entity-id')]; 251 await API('/book/' + book.id + '/sortChapters', data); 252 return true; 253 } catch (error) { console.error(error); return false; } 254} 255async function RenameBook(book_id, short_name) { 256 try { 257 console.log("Changing the book URI for " + book_id + " to " + short_name); 258 let json = await API('/book/' + book_id + '/rename/' + short_name); 259 current_book = await GetBook(book_id, true); 260 ChangePath('/' + current_book.short_name); 261 return true; 262 } catch (error) { console.error(error); return false; } 263} 264async function CreateChapter(btn) { 265 try { 266 DisableEditors(); 267 let book_id = $(btn).closest('[data-entity-type="book"][data-entity-id]').attr('data-entity-id'); 268 let json = await API('/book/' + book_id + '/createChapter'); 269 current_book = await GetBook(book_id, true); 270 await current_book.RenderLower(); 271 ApplyPermissions(current_book.lowerContainer, current_book); 272 current_book.RenderNavLinks(true); 273 Toast('Content Created', 'The content was created and added to the publication.', 'bg-success text-light'); 274 let e = $('.toc-row').last()[0]; 275 ScrollTo(e); 276 $(e).attr('data-pulse', true); 277 return true; 278 } catch (error) { console.error(error); return false; } 279} 280async function SubmitChapter(book_id, data) { 281 try { 282 let json = await API('/book/' + book_id + '/submitChapter', data); 283 Toast('Content Created', 'The content was submitted to the publication.', 'bg-success text-light'); 284 return true; 285 } catch (error) { console.error(error); return false; } 286} 287async function ImportChapters(book_id, chapter_ids) { 288 try { 289 let json = await API('/book/' + book_id + '/importChapters', { chapter_ids: chapter_ids }); 290 Toast('Content Imported', 'The content was imported into the publication.', 'bg-success text-light'); 291 return true; 292 } catch (error) { console.error(error); return false; } 293} 294 295// Chapters 296async function GetChapter(book_short_name, chapter_short_name, rebuild = false) { 297 if (chapter_short_names[book_short_name] !== undefined && chapter_short_names[book_short_name][chapter_short_name] !== undefined && !rebuild) return await GetChapterFromID(chapter_short_names[book_short_name][chapter_short_name], rebuild); 298 try { 299 console.warn('Getting chapter id from path'); 300 let json = await API('/chapter/getIDFromPath/' + book_short_name + '/' + chapter_short_name); 301 let chapter = await GetChapterFromID(json.entity_id, rebuild); 302 await chapter.Initialize(); 303 return chapter; 304 } catch (error) { console.error(error); return null; } 305} 306async function GetRevisions(id, rebuild = false) { 307 if (chapters[id] === undefined) await GetChapterFromID(id); 308 if (chapters[id].revisions !== undefined && !rebuild) return chapters[id].revisions; 309 try { 310 console.warn('Getting revisions from id'); 311 let json = await API('/chapter/' + id + '/revisions'); 312 console.log(json); 313 chapters[id].revisions = json.entities; 314 return chapters[id].revisions; 315 } catch (error) { console.error(error); return null; } 316} 317async function GetRevisionsLean(id) { 318 try { 319 console.warn('Getting revisions lean from id'); 320 let json = await API('/chapter/' + id + '/revisions/lean'); 321 console.log(json); 322 return json.entities; 323 } catch (error) { console.error(error); return null; } 324} 325async function GetRevision(id, created) { 326 try { 327 console.warn('Getting revisions lean from id'); 328 let json = await API('/chapter/' + id + '/revision/' + created); 329 console.log(json); 330 return json.revision; 331 } catch (error) { console.error(error); return null; } 332} 333async function GetChapterReviews(id, rebuild = false) {
334 if (chapters[id] === undefined) await GetChapterFromID(id); 335 if (chapters[id].reviews !== undefined && !rebuild) return chapters[id].reviews; 336 try { 337 console.warn('Getting reviews from id'); 338 let json = await API('/chapter/' + id + '/reviews'); 339 chapters[id].reviews = []; 340 for (let entity of json.entities) { 341 let review = new Review(entity); 342 chapters[id].reviews.push(review); 343 } 344 return chapters[id].reviews; 345 } catch (error) { console.error(error); return null; } 346} 347async function AddChapterReviewer(chapter_id, author_id, round) { 348 try { 349 let json = await API('/chapter/' + chapter_id + '/addReviewer', { author_id: author_id, round: round }); 350 return json; 351 } catch (error) { console.error(error); return null; } 352} 353async function RemoveChapterReviewer(chapter_id, review_id) { 354 try { 355 let json = await API('/chapter/' + chapter_id + '/removeReviewer/' + review_id); 356 return json; 357 } catch (error) { console.error(error); return null; } 358} 359async function GetChapterFromID(id, rebuild = false) { 360 if (chapters[id] !== undefined && !rebuild) return chapters[id]; 361 try { 362 console.warn('Getting chapter from id'); 363 let json = await API('/chapter/' + id + '/view'); 364 if (chapter_short_names[json.entity.book_short_name] == undefined) chapter_short_names[json.entity.book_short_name] = {}; 365 chapter_short_names[json.entity.book_short_name][json.entity.short_name] = json.entity.id; 366 chapters[json.entity.id] = new Chapter(json.entity); 367 return chapters[json.entity.id]; 368 } catch (error) { console.error(error); return null; } 369} 370async function MoveChapter(chapter_id, book_id) { 371 try { 372 let json = await API('/book/' + book_id + '/moveChapter/' + chapter_id); 373 chapters[json.entity.book_short_name] = {}; 374 chapters[json.entity.book_short_name][json.entity.short_name] = json.entity; 375 current_book = await GetBook(json.entity.book_id, true); 376 current_chapter = await GetChapterFromID(chapter_id); 377 ChangePath('/' + json.entity.book_short_name + '/' + json.entity.short_name); 378 return true; 379 } catch (error) { console.error(error); return false; } 380} 381async function Rate(chapter_id, value) { 382 try { 383 let json = await API('/chapter/' + chapter_id + '/rate', { value: value }); 384 return true; 385 } catch (error) { console.error(error); return false; } 386} 387async function DeleteBook(btn) { 388 console.log('Deleting the book.'); 389 try { 390 let book_id = $(btn).closest('[data-entity-type="book"][data-entity-id]').attr('data-entity-id'); 391 let json = await API('/book/' + book_id + '/delete'); 392 current_book = null; 393 ChangePath('/'); 394 $('.modal').modal('hide'); 395 Toast('Content Deleted', 'The publication and all of its content was successfully deleted.', 'bg-danger text-light'); 396 return true; 397 } catch (error) { console.error(error); return false; } 398} 399async function DeleteChapter(btn) { 400 console.log('Deleting the chapter.'); 401 try { 402 let chapter_id = $(btn).closest('[data-entity-type="chapter"][data-entity-id]').attr('data-entity-id'); 403 let json = await API('/chapter/' + chapter_id + '/delete'); 404 current_book = await GetBook(current_book.book_id, true); 405 ChangePath('/' + current_book.short_name); 406 $('.modal').modal('hide'); 407 Toast('Content Deleted', 'The content was successfully deleted.', 'bg-danger text-light'); 408 return true; 409 } catch (error) { console.error(error); return false; } 410} 411async function RenameChapter(chapter_id, book_id, short_name) { 412 try { 413 let json = await API('/chapter/' + chapter_id + '/rename/' + short_name); 414 current_book = await GetBook(book_id, true); 415 current_chapter = await GetChapterFromID(chapter_id, true); 416 ChangePath('/' + current_book.short_name + '/' + current_chapter.short_name); 417 return true; 418 } catch (error) { console.error(error); return false; } 419} 420async function RebuildAudio(chapter_id) { 421 Toast('Rebuilding Audio', 'The audio is being rebuilt. This may take some time. Please try again later.', 'bg-primary'); 422 try { 423 let json = await API('/audioElevenLabs/' + chapter_id + '/rebuild'); 424 console.log(json); 425 Toast('Audio Rebuilt', 'The audio has been queued with ElevenLabs TTS.', 'bg-success'); 426 return true; 427 } catch (elevenLabsError) { 428 console.warn('ElevenLabs failed, trying fallback:', elevenLabsError); 429 return RebuildAudioLocal(chapter_id); 430 } 431} 432async function RebuildAudioLocal(chapter_id) { 433 Toast('Rebuilding Audio Locally', 'The audio is being rebuilt. This may take some time. Please try again later.', 'bg-primary'); 434 try { 435 // Fallback to old audio service 436 let json = await API('/audio/' + chapter_id + '/rebuild'); 437 Toast('Audio Rebuilt', 'The audio has been queued with local TTS.', 'bg-success'); 438 return true; 439 } catch (error) { 440 Toast('Audio Generation Error', 'There was an error generating the audio: ' + error.message, 'bg-danger'); 441 console.error(error); 442 return false;
443 } 444} 445async function GenerateChapterAbstract(chapter_id) { 446 try { 447 console.warn('Generating chapter abstract'); 448 let json = await API('/chapter/' + chapter_id + '/genai/abstract'); 449 console.log(json); 450 return json.abstract; 451 } catch (error) { console.error(error); return null; } 452} 453async function GenerateChapterKeywords(chapter_id) { 454 try { 455 console.warn('Generating chapter keywords'); 456 let json = await API('/chapter/' + chapter_id + '/genai/keywords'); 457 console.log(json); 458 return json.keywords; 459 } catch (error) { console.error(error); return null; } 460} 461async function GenerateBookKeywords(book_id) { 462 try { 463 console.warn('Generating book keywords'); 464 let json = await API('/book/' + book_id + '/genai/keywords'); 465 console.log(json); 466 return json.keywords; 467 } catch (error) { console.error(error); return null; } 468} 469 470// Collections 471async function GetCollection(id, rebuild = false) { 472 if (collections[id] !== undefined && !rebuild) return collections[id]; 473 try { 474 console.warn('Getting collection'); 475 let json = await API('/collection/' + id); 476 collections[json.entity.collection_id] = new Collection(json.entity); 477 return collections[json.entity.collection_id]; 478 } catch (error) { console.error(error); return null; } 479} 480async function ListCollections() { 481 try { 482 console.warn('Listing collections'); 483 let json = await API('/collection/list'); 484 return json.entities; 485 } catch (error) { console.error(error); return []; } 486} 487 488// Entity 489async function GetEntityInfoFromShortName(short_name, rebuild = false) { 490 if (short_name_info[short_name] !== undefined && !rebuild) return short_name_info[short_name]; 491 try { 492 console.warn('Getting entity info from short name'); 493 let json = await API('/entity/info/' + short_name); 494 short_name_info[short_name] = json.entity; 495 return json.entity; 496 } catch (error) { console.error(error); return null; } 497} 498async function GetAnalytics(entity, timeframe = 'monthly', verb = 'viewed') { 499 try { 500 console.warn('Getting entity analytics'); 501 let json = await API('/' + entity.type + '/' + entity.id + '/analytics/' + timeframe + '/' + verb); 502 console.log(json.data); 503 return json.data; 504 } catch (error) { console.error(error); return null; } 505} 506async function ChangeAuthorVisibility(entity, author_id, shadow_author) { 507 try { 508 let json = await API('/' + entity.type + '/' + entity.id + '/changeAuthorVisibility/' + author_id + '/' + shadow_author); 509 Toast('Author Changed', 'The author\'s visibility has changed.', 'bg-success text-light'); 510 return true; 511 } catch (error) { console.error(error); return false; } 512} 513async function RemoveAuthor(entity, author_id) { 514 try { 515 let json = await API('/' + entity.type + '/' + entity.id + '/removeAuthor/' + author_id); 516 Toast('Author Removed', 'The author was removed.', 'bg-warning text-light'); 517 return true; 518 } catch (error) { console.error(error); return false; } 519} 520async function AddAuthor(entity, author_id) { 521 try { 522 let json = await API('/' + entity.type + '/' + entity.id + '/addAuthor/' + author_id); 523 Toast('Author Added', 'The author was added.', 'bg-success text-light'); 524 return json; 525 } catch (error) { console.error(error); return false; } 526} 527async function SortAuthors(entity, data) { 528 try { 529 await API('/' + entity.type + '/' + entity.id + '/sortAuthors', data); 530 return true; 531 } catch (error) { console.error(error); return false; } 532} 533async function GetEntityImages(entity) { 534 try { 535 let json; 536 switch (entity.type) { 537 case 'book': 538 console.warn('Getting book images', entity); 539 json = await API('/book/' + entity.id + '/images'); 540 return json.entities; 541 case 'page': 542 console.warn('Getting page images'); 543 json = await API('/page/' + entity.id + '/images'); 544 return json.entities; 545 } 546 return []; 547 } catch (error) { console.error(error); return []; } 548} 549async function DeleteEntityImage(entity, filename) { 550 try { 551 let json; 552 switch (entity.type) { 553 case 'book': 554 console.warn('Deleting book image'); 555 json = await API('/book/' + entity.id + '/deleteImage', { filename: filename }); 556 return true; 557 } 558 return false;
559 } catch (error) { console.error(error); return false; } 560} 561async function RenameEntityImage(entity, old_name, new_name) { 562 try { 563 let json; 564 switch (entity.type) { 565 case 'book': 566 console.warn('Renaming book image'); 567 json = await API('/book/' + entity.id + '/renameImage', { old_filename: old_name, new_filename: new_name }); 568 return json; 569 } 570 return false; 571 } catch (error) { console.error(error); return false; } 572} 573 574// Glossary 575async function GetGlossaryTerm(term_id, rebuild = false) { 576 if (glossary_terms[term_id] !== undefined && !rebuild) return glossary_terms[term_id]; 577 try { 578 console.warn('Getting glossary term'); 579 let json = await API('/glossary_term/' + term_id + '/view'); 580 return new GlossaryTerm(json.entity); 581 } catch (error) { console.error(error); return null; } 582} 583async function CreateGlossaryTerm(book_id, word) { 584 try { 585 console.warn('Creating glossary term'); 586 let json = await API('/glossary_term/create', { book_id: book_id, word: word }); 587 return new GlossaryTerm(json.entity); 588 } catch (error) { console.error(error); return null; } 589} 590async function DeleteGlossaryTerm(term_id) { 591 try { 592 console.warn('Deleting glossary term'); 593 let json = await API('/glossary_term/' + term_id + '/delete'); 594 return true; 595 } catch (error) { console.error(error); return null; } 596} 597async function GetBookGlossaryTerms(book, rebuild = false) { 598 if (book.glossary_terms !== undefined && book.glossary_terms.length > 0 && !rebuild) return book.glossary_terms; 599 try { 600 console.warn('Listing glossary terms'); 601 book.glossary_terms = []; 602 let json = await API('/book/' + book.book_id + '/glossaryTerms'); 603 for (let entity of json.entities) { 604 book.glossary_terms.push(new GlossaryTerm(entity)); 605 glossary_terms[entity.term_id] = entity; 606 } 607 return book.glossary_terms; 608 } catch (error) { console.error(error); return []; } 609} 610async function DefineGlossaryTerm(term_id) { 611 try { 612 console.warn('Defining glossary term'); 613 let json = await API('/glossary_term/' + term_id + '/define'); 614 return json.term; 615 } catch (error) { console.error(error); return null; } 616} 617 618// Images 619async function UploadImage(url, file) { 620 try { 621 console.warn('Uploading the image', url, file); 622 let json = await APIFile(url, file); 623 return json.entity; 624 } catch (error) { console.error(error); return []; } 625} 626 627async function SaveImageLocally(src, book_id) { 628 let json = await API('/book/' + book_id + '/saveImageLocally', { src: src }); 629 return json.src; 630} 631 632// Keywords 633async function KeywordSearch(query) { 634 try { 635 console.warn('Searching for keywords'); 636 let json = await API('/keyword/search', { query: query }); 637 return json.keywords; 638 } catch (error) { console.error(error); return []; } 639} 640async function CreateKeywords(term) { 641 try { 642 console.warn('Creating keyword ' + term); 643 let json = await API('/keyword/create', { term: term }); 644 return json.keywords; 645 } catch (error) { console.error(error); return []; } 646} 647async function RemoveKeyword(keyword_id, entity_type, entity_id) { 648 try { 649 console.warn('Removing keyword'); 650 await API('/' + entity_type + '/' + entity_id + '/removeKeyword/' + keyword_id); 651 return true; 652 } catch (error) { console.error(error); return false; } 653} 654async function AddKeyword(keyword_id) { 655 try { 656 console.warn('Adding keyword'); 657 let entity_type = (current_chapter !== null) ? 'chapter' : 'book'; 658 let entity_id = (current_chapter !== null) ? current_chapter.id : current_book.id; 659 await API('/' + entity_type + '/' + entity_id + '/addKeyword/' + keyword_id); 660 return true; 661 } catch (error) { console.error(error); return false; } 662} 663async function GetKeyword(keyword_id) { 664 try { 665 console.warn('Getting keyword'); 666 let json = await API('/keyword/' + keyword_id); 667 return json.entity; 668 } catch (error) { console.error(error); return null; } 669} 670 671// Languages 672async function GetLanguage(id) { 673 try { 674 if (id == undefined || id == null) return null; 675 for (let language of await GetLanguages()) { 676 if (language.language_id == id) return language; 677 } 678 console.warn('Getting language'); 679 let json = await API('/language/' + id); 680 languages[json.entity.language_id] = json.entity; 681 return json.entity; 682 } catch (error) { console.error(error); return null; } 683} 684async function GetLanguages(rebuild = false) { 685 if (languages.length > 0 && !rebuild) return languages; 686 try { 687 console.warn('Getting languages'); 688 let json = await API('/language/list'); 689 languages = json.entities; 690 return languages; 691 } catch (error) { console.error(error); return []; } 692} 693async function GetTranslationID(chapter_id, language_id) { 694 try { 695 console.warn('Getting translation id'); 696 let json = await API('/translation/id/' + chapter_id + '/' + language_id); 697 return json.translation_id; 698 } catch (error) { console.error(error); return null; } 699} 700async function GetTranslation(id, rebuild = false) { 701 if (translations[id] !== undefined && !rebuild) return translations[id]; 702 try { 703 console.warn('Getting translation'); 704 let json = await API('/translation/' + id + '/view'); 705 translations[id] = json.entity; 706 return translations[id]; 707 } 708 catch (error) { console.error(error); return null; } 709}
710async function RebuildTranslation(chapter_id, language_id) { 711 try { 712 console.warn('Rebuilding translation'); 713 let json = await API(`/translation/${chapter_id}/${language_id}/rebuild`); 714 return true; 715 } 716 catch (error) { console.error(error); return null; } 717} 718 719// Licenses 720async function GetLicense(id) { // needs to be fixed 721 for (let license in await GetLicenses()) { 722 if (license.license_id == id) return license; 723 } 724 try { 725 console.warn('Getting license'); 726 let json = await API('/license/' + id); 727 licenses[json.entity.license_id] = json.entity; 728 return json.entity; 729 } catch (error) { console.error(error); return null; } 730} 731async function GetLicenses(rebuild = false) { 732 if (licenses.length > 0 && !rebuild) return licenses; 733 try { 734 console.warn('Getting licenses'); 735 let json = await API('/license/list'); 736 licenses = json.entities; 737 return licenses; 738 } catch (error) { console.error(error); return []; } 739} 740 741// Login 742async function SetRedirect() { 743 try { 744 console.warn('Setting the redirect'); 745 await API('/user/setRedirect', { url: window.location.href }); 746 return true; 747 } catch (error) { console.error(error); return false; } 748} 749 750// Pages 751async function GetPage(id, rebuild = false) { 752 if (pages[id] !== undefined && !rebuild) return pages[id]; 753 if (page_short_names[id] !== undefined && pages[page_short_names[id]] !== undefined && !rebuild) return pages[page_short_names[id]]; 754 try { 755 console.warn('Getting page'); 756 let json = await API('/page/' + id + '/view'); 757 page_short_names[json.entity.short_name] = json.entity.id; 758 pages[json.entity.id] = new Page(json.entity); 759 return pages[json.entity.id]; 760 } catch (error) { console.error(error); return null; } 761} 762 763// Saving 764async function SaveSetting(type, id, values = {}) { 765 try { 766 console.log('Saving'); 767 await API('/' + type + '/' + id + '/saveSettings', values); 768 switch (type) { // Clear caches 769 case 'author': 770 delete (authors[id]); 771 break; 772 case 'book': 773 delete (books[id]); 774 break; 775 case 'chapter': 776 if (chapters[id] !== undefined) delete (books[chapters[id].book_id]); 777 delete (chapters[id]); 778 break; 779 case 'page': 780 delete (pages[id]); 781 break; 782 case 'glossary': 783 delete (glossary_terms[id]); 784 break; 785 } 786 return true; 787 } catch (error) { console.error(error); return false; } 788} 789 790// Screenshot 791async function RebuildChapterScreenshotClick(btn) { 792 try { 793 let chapter_id = $(btn).attr('data-action-value'); 794 return await RebuildChapterScreenshot(chapter_id); 795 } catch (error) { console.error(error); return false; } 796} 797async function RebuildChapterScreenshot(chapter_id) { 798 try { 799 console.warn('Rebuilding screenshot'); 800 await API('/screenshot/' + chapter_id); 801 return true; 802 } catch (error) { console.error(error); return false; } 803} 804 805// Search 806async function Search(string, types, limit, vars = {}) { 807 try { 808 console.warn('Searching'); 809 let json = await API('/search', { term: string, types: types, limit: limit, vars: vars }); 810 return json.results; 811 } catch (error) { console.error(error); return null; } 812} 813 814// Site 815async function GetSite(rebuild = false) { 816 if (site !== null && !rebuild) return site; 817 try { 818 console.warn('Getting site'); 819 let json = await API('/site/info'); 820 site = new Site(json.entity); 821 site.logged_in = json.logged_in === true; 822 if (site.stylesheet !== null) LoadCSS(site.stylesheet); 823 return site; 824 } catch (error) { console.error(error); return null; } 825} 826async function GetSiteBookLinks(offset, limit) { 827 try { 828 console.warn('Getting site book links'); 829 let json = await API('/site/bookLinks', { offset: offset, limit: limit }); 830 return json.links; 831 } catch (error) { console.error(error); return []; } 832} 833async function GetSiteAnalytics() { 834 try { 835 console.warn('Getting site analytics'); 836 let json = await API('/site/analytics'); 837 return json.data; 838 } catch (error) { console.error(error); return []; } 839}
840async function GetSiteJournalIssues(offset, limit) { 841 try { 842 console.warn('Getting site book links'); 843 let json = await API('/site/journalIssues'); 844 return json.links; 845 } catch (error) { console.error(error); return []; } 846} 847async function GetSiteFeaturedBooks(rebuild = false) { 848 if (site !== null && site.featured_book_links != undefined && !rebuild) return site.featured_book_links; 849 try { 850 console.warn('Getting site featured book links'); 851 let json = await API('/site/featuredBookLinks'); 852 site.featured_book_links = json.links; 853 return json.links; 854 } catch (error) { console.error(error); return []; } 855} 856async function GetEndorsements() { 857 try { 858 console.warn('Getting site endorsements'); 859 let json = await API('/site/endorsements'); 860 return json.entities; 861 } catch (error) { console.error(error); return []; } 862} 863 864// Users 865async function GetCurrentUser(rebuild = false) { 866 if (current_user != null && !rebuild) return current_user; 867 try { 868 console.warn('Getting current user'); 869 let json = await API('/user/current'); 870 current_user = new Author(json.entity); 871 await UpdateUserIcons(); 872 await ChangeVisualMode(current_user.settings.visual_mode, false); 873 return current_user; 874 } catch (error) { return null; } 875}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.