1class TOCEditor { 2 constructor(editorWindow, book) { 3 this.editorWindow = editorWindow; 4 this.book = book; 5 this.dragging = null; 6 this.tocStartX = 0; 7 this.Initialize(); 8 editors.push(this); 9 } 10 async Initialize() { 11 $(this.editorWindow).empty(); 12 this.ribbon = $(await RenderTemplate('book-toc-ribbon', this.book))[0]; 13 $(this.ribbon).appendTo(this.editorWindow); 14 $(this.ribbon).find('[data-ribbon-action]').on('click', async (event) => { 15 try { 16 return await this[$(event.target).attr('data-ribbon-action')](); 17 } catch (error) { 18 console.error(error); 19 return error; 20 } 21 }); 22 for (let chapter_link of this.book.chapter_links) { 23 chapter_link.book_short_name = this.book.short_name; 24 let e = $(await RenderTemplate('book-toc-row-edit', chapter_link)); 25 e.css({ 'margin-left': (chapter_link.chapter_level * 20) + 'px' }).attr('data-children', chapter_link.children.join(',')).appendTo($('#book-nav-links')); 26 $(this.editorWindow).append(e); 27 $(e).find('[data-role="grip"]').off().on('mousedown', (event) => { 28 this.dragging = event.target; 29 this.tocStartX = event.pageX; 30 }); 31 $(e).find('input').off('change').on('change', (event) => { 32 console.log('Saving'); 33 SaveSettingFromElement(event.target); 34 }); 35 } 36 $(this.editorWindow).sortable({ 37 handle: 'grip[data-role="grip"]', 38 items: 'div[data-entity-id]', 39 axis: 'y', 40 scrollSpeed: 6, 41 opacity: 0.8, 42 start: (event) => { 43 $(this.editorWindow).on('mousemove', (event) => this.Dragging(event)); 44 }, 45 stop: (event) => { 46 this.dragging = null; 47 $(this.editorWindow).find('[data-chapter-level-target]').each((index, element) => { 48 $(element).attr('data-chapter-level', $(element).attr('data-chapter-level-target')).removeAttr('data-chapter-level-target'); 49 }); 50 $(this.editorWindow).off('mousemove'); 51 this.SetDirty(); 52 }, 53 update: (event) => { 54 // Nothing for now 55 } 56 }); 57 } 58 async Save() { 59 $(this.editorWindow).sortable('disable'); 60 try { 61 let chapters = []; 62 $(this.editorWindow).children('div').each((index, element) => { 63 if(!HasAttr(element, 'data-entity-id')) return; 64 let chapter = {}; 65 chapter.id = $(element).attr('data-entity-id'); 66 chapter.chapter_level = $(element).attr('data-chapter-level'); 67 chapters.push(chapter); 68 }); 69 if (await SortBookChapters(chapters)) { 70 this.SetClean(); 71 Toast('Chapter Order Saved', 'The chapter order has been saved.', 'bg-success text-light'); 72 } 73 } finally { 74 $(this.editorWindow).sortable('enable'); 75 } 76 } 77 SetClean() { 78 if (!this.dirty) return; 79 console.log('Setting clean'); 80 this.dirty = false; 81 if(this.ribbon!==undefined) $(this.ribbon).find('#save-status').addClass('bi-check-lg').removeClass('bi-floppy2').removeClass('btn-danger').text('Saved'); 82 } 83 SetDirty() { 84 if (this.dirty) return; 85 console.log('Setting dirty'); 86 this.dirty = true; 87 if (this.ribbon !== undefined) $(this.ribbon).find('#save-status').removeClass('bi-check-lg').addClass('bi-floppy2').addClass('btn-danger').text('Unsaved'); 88 } 89 Destroy() { 90 $(this.editorWindow).sortable('destroy') 91 } 92 Dragging(event) { 93 if (this.dragging !== null && $(this.dragging).hasClass('bi-arrows-move')) { 94 let currentX = event.pageX; 95 let distance = currentX - this.tocStartX; 96 let chapter_level = parseInt($(this.dragging).parent().attr('data-chapter-level')); 97 let chapter_level_adjustment = Math.round(distance / 20); 98 chapter_level += chapter_level_adjustment; 99 if (chapter_level < 0) chapter_level = 0; 100 if (chapter_level > 4) chapter_level = 4; 101 $(this.dragging).parent().css({ 'margin-left': (chapter_level * 20) + 'px' }).attr('data-chapter-level-target', chapter_level); 102 } 103 } 104}
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.