epub-flow 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2249 @@
1
+ import * as i0 from '@angular/core';
2
+ import { signal, Injectable, Injector, HostListener, Component, Input, effect, EventEmitter, Output, PLATFORM_ID, Inject, input, CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
3
+ import { map, Subject, of, takeUntil, finalize, forkJoin, from, mergeMap, toArray } from 'rxjs';
4
+ import * as i1 from '@angular/common/http';
5
+ import { HttpHeaders, HttpClientModule } from '@angular/common/http';
6
+ import * as i1$2 from '@angular/common';
7
+ import { CommonModule, isPlatformBrowser } from '@angular/common';
8
+ import * as i3 from '@angular/router';
9
+ import { RouterModule } from '@angular/router';
10
+ import * as i7 from '@angular/forms';
11
+ import { FormGroup, FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
12
+ import * as i6 from 'ngx-editor';
13
+ import { Editor, NgxEditorModule } from 'ngx-editor';
14
+ import * as i1$1 from '@angular/cdk/overlay';
15
+ import { OverlayConfig } from '@angular/cdk/overlay';
16
+ import { ComponentPortal } from '@angular/cdk/portal';
17
+ import * as i4 from 'primeng/api';
18
+ import * as i1$3 from '@angular/platform-browser';
19
+ import * as i8 from 'primeng/tabview';
20
+ import { TabViewModule } from 'primeng/tabview';
21
+ import ePub from 'epubjs';
22
+ import * as i1$4 from 'primeng/progressbar';
23
+ import { ProgressBarModule } from 'primeng/progressbar';
24
+ import { TableModule } from 'primeng/table';
25
+ import { ButtonModule } from 'primeng/button';
26
+
27
+ class EpubReaderService {
28
+ http;
29
+ ePubChapters = signal(null);
30
+ noteToEdit = signal(null);
31
+ searchKey = signal('');
32
+ searchResponseData = signal(null);
33
+ constructor(http) {
34
+ this.http = http;
35
+ }
36
+ /*
37
+ *@Desc : To update Epub chapters
38
+ *@Author: Jaisankar
39
+ */
40
+ updateChapters(data) {
41
+ this.ePubChapters.set(data);
42
+ }
43
+ /*
44
+ *@Desc : To update Search key
45
+ *@Author: Jaisankar
46
+ */
47
+ toUpdateSearchKey(data) {
48
+ this.searchKey.set(data);
49
+ }
50
+ /*
51
+ *@Desc : To update Search key
52
+ *@Author: Jaisankar
53
+ */
54
+ updateSearchData(data) {
55
+ this.searchResponseData.set(data);
56
+ }
57
+ /*
58
+ *@Desc : TO STORE SELECTED NOTES
59
+ *@Author: ABOOBACKER
60
+ */
61
+ setNote(data) {
62
+ this.noteToEdit.set(data);
63
+ }
64
+ /*
65
+ *@Desc : TO GET SELECTED NOTES
66
+ *@Author: ABOOBACKER
67
+ */
68
+ getNote() {
69
+ return this.noteToEdit();
70
+ }
71
+ getDefinition(payload) {
72
+ const headers = new HttpHeaders({
73
+ skip: 'true', // Add the custom header
74
+ });
75
+ return this.http
76
+ .get(`https://api.dictionaryapi.dev/api/v2/entries/en/${payload}`, {
77
+ headers,
78
+ })
79
+ .pipe(map((x) => x?.[0] ?? []));
80
+ }
81
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EpubReaderService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
82
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EpubReaderService });
83
+ }
84
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EpubReaderService, decorators: [{
85
+ type: Injectable
86
+ }], ctorParameters: () => [{ type: i1.HttpClient }] });
87
+
88
+ class CustomDialogService {
89
+ overlay;
90
+ injector;
91
+ constructor(overlay, injector) {
92
+ this.overlay = overlay;
93
+ this.injector = injector;
94
+ }
95
+ open(component, options = {}) {
96
+ // 1. Position Strategy
97
+ const positionStrategy = this.overlay.position()
98
+ .global()
99
+ .centerHorizontally()
100
+ .centerVertically();
101
+ // 2. Overlay Config
102
+ const overlayConfig = new OverlayConfig({
103
+ positionStrategy,
104
+ hasBackdrop: true,
105
+ backdropClass: 'cdk-overlay-dark-backdrop',
106
+ scrollStrategy: this.overlay.scrollStrategies.block(),
107
+ // You can map options.windowClass to panelClass if needed, or pass width/height
108
+ panelClass: options.windowClass
109
+ });
110
+ // 3. Create Overlay
111
+ const overlayRef = this.overlay.create(overlayConfig);
112
+ // 4. Create DialogRef
113
+ const ref = new CustomDialogRef();
114
+ ref.data = options.data;
115
+ // 5. Create Injector
116
+ const injector = Injector.create({
117
+ providers: [
118
+ { provide: CustomDialogRef, useValue: ref },
119
+ { provide: CustomDialogService, useValue: this }
120
+ ],
121
+ parent: this.injector
122
+ });
123
+ // 6. Attach Component
124
+ const portal = new ComponentPortal(component, null, injector);
125
+ overlayRef.attach(portal);
126
+ // 7. Handle Close
127
+ ref.afterClosed$.subscribe(() => {
128
+ overlayRef.dispose();
129
+ });
130
+ overlayRef.backdropClick().subscribe(() => {
131
+ if (options.enableClose !== false && !options.closeButton) {
132
+ // If closeButton is true, usually that handles closing.
133
+ // But for backdrop click, we check enableClose. Default is usually true.
134
+ ref.close();
135
+ }
136
+ else if (options.enableClose === undefined) {
137
+ ref.close();
138
+ }
139
+ });
140
+ return ref;
141
+ }
142
+ closeAll() {
143
+ // This is a simplified version. To properly close all, we'd need to track active refs.
144
+ // For now, individual dialogs manage their own closure via ref.
145
+ }
146
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: CustomDialogService, deps: [{ token: i1$1.Overlay }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
147
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: CustomDialogService, providedIn: 'root' });
148
+ }
149
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: CustomDialogService, decorators: [{
150
+ type: Injectable,
151
+ args: [{
152
+ providedIn: 'root'
153
+ }]
154
+ }], ctorParameters: () => [{ type: i1$1.Overlay }, { type: i0.Injector }] });
155
+ class CustomDialogRef {
156
+ afterClosed = new Subject();
157
+ afterClosed$ = this.afterClosed.asObservable();
158
+ data;
159
+ close(result) {
160
+ this.afterClosed.next(result);
161
+ this.afterClosed.complete();
162
+ }
163
+ }
164
+
165
+ class SharedService {
166
+ showHide(val) { }
167
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SharedService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
168
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SharedService, providedIn: 'root' });
169
+ }
170
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SharedService, decorators: [{
171
+ type: Injectable,
172
+ args: [{ providedIn: 'root' }]
173
+ }] });
174
+ class HttpApiService {
175
+ getAnnotations(params) { return of({}); }
176
+ getReadProgress(params) { return of({}); }
177
+ getFreeSample(id) { return of(new Blob()); }
178
+ getBook(id) { return of(new Blob()); }
179
+ saveAnnotations(params) { return of({}); }
180
+ saveReadProgress(params) { return of({}); }
181
+ deleteNotes(id) { return of({}); }
182
+ updateSearchData(data) { }
183
+ addorEditeNote(payload) { return of({}); }
184
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: HttpApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
185
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: HttpApiService, providedIn: 'root' });
186
+ }
187
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: HttpApiService, decorators: [{
188
+ type: Injectable,
189
+ args: [{ providedIn: 'root' }]
190
+ }] });
191
+ class AuthService {
192
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
193
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AuthService, providedIn: 'root' });
194
+ }
195
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AuthService, decorators: [{
196
+ type: Injectable,
197
+ args: [{ providedIn: 'root' }]
198
+ }] });
199
+
200
+ class AddNotesComponent {
201
+ dialog;
202
+ sharedService;
203
+ epubReader;
204
+ userApi;
205
+ dialogRef;
206
+ messageService;
207
+ editor; // Editor instance
208
+ textToAddorEdit;
209
+ toolbar = [
210
+ ['bold', 'italic'],
211
+ ['underline', 'strike'],
212
+ ['code', 'blockquote'],
213
+ ['ordered_list', 'bullet_list'],
214
+ [
215
+ {
216
+ heading: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
217
+ },
218
+ ],
219
+ ['text_color', 'background_color'],
220
+ ['align_left', 'align_center', 'align_right', 'align_justify'],
221
+ ];
222
+ isSubmitting = false;
223
+ // FormGroup for handling editor content
224
+ form = new FormGroup({
225
+ editorContent: new FormControl(this.textToAddorEdit),
226
+ });
227
+ data;
228
+ // Getter for editor content
229
+ get editorContent() {
230
+ return this.form.get('editorContent');
231
+ }
232
+ onDestroy$ = new Subject();
233
+ constructor(dialog, sharedService, epubReader, userApi, dialogRef, messageService) {
234
+ this.dialog = dialog;
235
+ this.sharedService = sharedService;
236
+ this.epubReader = epubReader;
237
+ this.userApi = userApi;
238
+ this.dialogRef = dialogRef;
239
+ this.messageService = messageService;
240
+ }
241
+ ngOnInit() {
242
+ // Initialize the editor
243
+ this.editor = new Editor();
244
+ this.textToAddorEdit = this.dialogRef.data.notetoAddorEdit; // Access THE SELECTED NOTE
245
+ this.form.patchValue({ editorContent: this.textToAddorEdit });
246
+ }
247
+ onSubmit() {
248
+ if (this.form.valid) {
249
+ const content = this.editorContent.value; // Retrieve content as HTML
250
+ }
251
+ }
252
+ close() {
253
+ this.dialogRef.close();
254
+ }
255
+ onDragOver(event) {
256
+ event.preventDefault();
257
+ event.stopPropagation();
258
+ console.log('Drag over blocked');
259
+ }
260
+ // Prevent drop
261
+ onDrop(event) {
262
+ event.preventDefault();
263
+ event.stopPropagation();
264
+ console.log('Drop blocked');
265
+ }
266
+ // Optional: Prevent drag start
267
+ onDragStart(event) {
268
+ event.preventDefault();
269
+ event.stopPropagation();
270
+ console.log('Drag start blocked');
271
+ }
272
+ onRightClick(event) {
273
+ event.preventDefault(); // Disable the right-click menu
274
+ }
275
+ onKeyDown(event) {
276
+ if (event.ctrlKey && event.key === 'c') {
277
+ event.preventDefault(); // Prevent the default copy action
278
+ }
279
+ }
280
+ onCopy(event) {
281
+ event.preventDefault(); // Prevent the copy event
282
+ }
283
+ ngOnDestroy() {
284
+ // Destroy the editor instance to prevent memory leaks
285
+ this.editor.destroy();
286
+ }
287
+ updateNote() {
288
+ this.isSubmitting = true;
289
+ const content = this.editorContent.value; // Retrieve content as HTML
290
+ const payload = {
291
+ typeId: 1,
292
+ id: this.dialogRef.data.notedetails.id,
293
+ bookId: this.dialogRef.data.notedetails.bookId,
294
+ cfiLocation: this.dialogRef.data.notedetails.cfiLocation,
295
+ annotatedNotes: this.dialogRef.data.notedetails.annotatedNotes,
296
+ customNotes: content,
297
+ colorCode: '',
298
+ };
299
+ this.userApi
300
+ .addorEditeNote(payload)
301
+ .pipe(takeUntil(this.onDestroy$))
302
+ .subscribe({
303
+ next: (res) => {
304
+ this.messageService.add({
305
+ key: 'notifyToast',
306
+ severity: 'success', // Use 'warn' for orange color
307
+ summary: 'Success',
308
+ detail: res.message,
309
+ });
310
+ this.dialog.closeAll();
311
+ },
312
+ error: (err) => {
313
+ this.isSubmitting = false; // re-enable button
314
+ console.error('Error fetching eBook details:', err);
315
+ },
316
+ complete: () => {
317
+ this.isSubmitting = false; // re-enable button
318
+ },
319
+ });
320
+ }
321
+ /**
322
+ * @Desc : Checks if the provided content has valid, non-empty, and non-whitespace-only text.
323
+ * @Author : Aboobacker
324
+ * @Param : content (string) - The content to validate
325
+ * @Returns: boolean - true if valid content exists, false otherwise
326
+ */
327
+ hasValidContent(content) {
328
+ if (!content)
329
+ return false;
330
+ const textOnly = content.replace(/<[^>]*>/g, '').trim(); // Remove HTML tags
331
+ return textOnly.length > 0;
332
+ }
333
+ addNote() {
334
+ this.isSubmitting = true; // re-enable button
335
+ const content = this.editorContent.value; // Retrieve content as HTML
336
+ const payload = {
337
+ typeId: 1,
338
+ id: 0,
339
+ bookId: this.dialogRef.data.notedetails.bookId,
340
+ cfiLocation: this.dialogRef.data.notedetails.cfiLocation,
341
+ annotatedNotes: this.dialogRef.data.notetoAddorEdit,
342
+ customNotes: content,
343
+ colorCode: '',
344
+ };
345
+ this.userApi
346
+ .addorEditeNote(payload)
347
+ .pipe(takeUntil(this.onDestroy$))
348
+ .subscribe({
349
+ next: (res) => {
350
+ this.messageService.add({
351
+ key: 'notifyToast',
352
+ severity: 'success', // Use 'warn' for orange color
353
+ summary: 'Success',
354
+ detail: res.message,
355
+ });
356
+ this.dialog.closeAll();
357
+ },
358
+ error: (err) => {
359
+ this.isSubmitting = false; // re-enable button
360
+ console.error('Error fetching eBook details:', err);
361
+ },
362
+ complete: () => {
363
+ this.isSubmitting = false; // re-enable button
364
+ },
365
+ });
366
+ }
367
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AddNotesComponent, deps: [{ token: CustomDialogService }, { token: SharedService }, { token: EpubReaderService }, { token: HttpApiService }, { token: CustomDialogRef }, { token: i4.MessageService }], target: i0.ɵɵFactoryTarget.Component });
368
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: AddNotesComponent, isStandalone: false, selector: "app-add-notes", host: { listeners: { "document:dragover": "onDragOver($event)", "document:drop": "onDrop($event)", "document:dragstart": "onDragStart($event)", "document:contextmenu": "onRightClick($event)", "document:keydown": "onKeyDown($event)", "document:copy": "onCopy($event)" } }, ngImport: i0, template: "<div class=\"note-container\">\n <div class=\"popup-header\">\n <h2 class=\"popup-title\">{{ dialogRef.data.addNote ? 'Add Note' : 'Edit Note' }}</h2>\n <button class=\"close-btn\" (click)=\"close()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n <form class=\"note-form\" [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <!-- Editor Section -->\n <section class=\"editor-section\">\n <ngx-editor-menu [editor]=\"editor\" [toolbar]=\"toolbar\">\n </ngx-editor-menu>\n\n <ngx-editor class=\"editor-body\" [editor]=\"editor\" formControlName=\"editorContent\">\n </ngx-editor>\n </section>\n\n <!-- Validation -->\n <p class=\"error\" *ngIf=\"editorContent.invalid && editorContent.touched\">\n Content is required.\n </p>\n\n <!-- Footer / Actions -->\n <footer class=\"action-bar\">\n @if (dialogRef.data.addNote) {\n <button type=\"submit\" class=\"btn btn-primary\" (click)=\"addNote()\"\n [disabled]=\"!hasValidContent(editorContent.value) || isSubmitting\">\n Add Note\n </button>\n } @else {\n <button type=\"submit\" class=\"btn btn-primary\" (click)=\"updateNote()\"\n [disabled]=\"!hasValidContent(editorContent.value) || isSubmitting\">\n Update Note\n </button>\n }\n </footer>\n </form>\n</div>", styles: [":host{display:block;font-family:Figtree,sans-serif;--primary: #34c759;--primary-hover: #2ebd50;--bg: #ffffff;--text: #1f2937;--border: #e5e7eb}:host-context(.dark-mode){--bg: #1f2937;--text: #f9fafb;--border: #374151}:host-context(.dark-mode) ::ng-deep .NgxEditor__MenuBar{background:#1f2937;border-color:#374151}:host-context(.dark-mode) ::ng-deep .NgxEditor__MenuItem{color:#e5e7eb}:host-context(.dark-mode) ::ng-deep .NgxEditor__MenuItem:hover{background-color:#374151;color:#fff}:host-context(.dark-mode) ::ng-deep .NgxEditor__MenuItem.NgxEditor__MenuItem--Active{background-color:#374151;color:#34c759}:host-context(.dark-mode) ::ng-deep .NgxEditor__MenuItem svg{fill:currentColor}:host-context(.dark-mode) ::ng-deep .NgxEditor__Dropdown .NgxEditor__Dropdown--Selected,:host-context(.dark-mode) ::ng-deep .NgxEditor__Dropdown .NgxEditor__Dropdown--Open,:host-context(.dark-mode) ::ng-deep .NgxEditor__Dropdown:hover{background-color:#374151;color:#fff}.note-container{width:100%;max-width:560px;height:auto;min-height:480px;max-height:85vh;background:var(--bg);color:var(--text);border-radius:16px;box-shadow:0 20px 25px -5px #00000026;overflow:hidden;display:flex;flex-direction:column}.popup-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px 0}.popup-title{margin:0;font-size:1.125rem;font-weight:600;color:var(--text)}.close-btn{background:transparent;border:none;cursor:pointer;padding:8px;margin:-8px -8px -8px 0;border-radius:50%;color:var(--text);display:flex;align-items:center;justify-content:center;opacity:.7;transition:all .2s}.close-btn:hover{background-color:#0000000d;opacity:1}:host-context(.dark-mode) .close-btn:hover{background-color:#ffffff1a}.note-form{display:flex;flex-direction:column;flex:1;padding:1.5rem;overflow:hidden}.editor-section{display:flex;flex-direction:column;flex:1;min-height:250px;border:1px solid var(--border);border-radius:12px;overflow:hidden;transition:border-color .2s ease;background:var(--bg)}.editor-section:focus-within{border-color:var(--primary);box-shadow:0 0 0 3px #34c75926}::ng-deep .NgxEditor__MenuBar{background:var(--bg);border-bottom:1px solid var(--border);padding:.5rem;gap:.25rem;flex-wrap:wrap}::ng-deep .NgxEditor{flex:1;height:100%;min-height:300px;padding:1rem;border:none!important;background:var(--bg);color:var(--text);font-size:.95rem;line-height:1.6;overflow-y:auto}.error{color:#ef4444;font-size:.85rem;margin:.5rem 0 0}.action-bar{display:flex;justify-content:flex-end;padding-top:.75rem}::ng-deep .NgxEditor__Popup{position:absolute;top:calc(var(--ngx-editor-menubar-height) + 2px);box-shadow:var(--ngx-editor-popup-shadow);border-radius:var(--ngx-editor-popup-border-radius);background-color:var(--ngx-editor-popup-bg-color);z-index:100;min-width:101px;padding:8px;right:2px}.btn{min-width:140px;padding:10px 24px;border-radius:10px;font-weight:600;border:none;cursor:pointer;transition:all .2s ease}.btn-primary{background:var(--primary);color:#fff;box-shadow:0 4px 6px #34c7594d}.btn-primary:hover:not(:disabled){background:var(--primary-hover);transform:translateY(-1px)}.btn-primary:disabled{opacity:.6;cursor:not-allowed;box-shadow:none}@media (max-width: 640px){.note-container{height:100vh;border-radius:0}.note-form{padding:.75rem}.action-bar{justify-content:stretch}.btn{width:100%}}\n"], dependencies: [{ kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i6.NgxEditorComponent, selector: "ngx-editor", inputs: ["editor", "outputFormat", "placeholder"], outputs: ["focusOut", "focusIn"] }, { kind: "component", type: i6.NgxEditorMenuComponent, selector: "ngx-editor-menu", inputs: ["toolbar", "colorPresets", "disabled", "editor", "customMenuRef", "dropdownPlacement"] }, { kind: "directive", type: i7.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i7.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i7.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i7.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
369
+ }
370
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AddNotesComponent, decorators: [{
371
+ type: Component,
372
+ args: [{ selector: 'app-add-notes', standalone: false, template: "<div class=\"note-container\">\n <div class=\"popup-header\">\n <h2 class=\"popup-title\">{{ dialogRef.data.addNote ? 'Add Note' : 'Edit Note' }}</h2>\n <button class=\"close-btn\" (click)=\"close()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n <form class=\"note-form\" [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <!-- Editor Section -->\n <section class=\"editor-section\">\n <ngx-editor-menu [editor]=\"editor\" [toolbar]=\"toolbar\">\n </ngx-editor-menu>\n\n <ngx-editor class=\"editor-body\" [editor]=\"editor\" formControlName=\"editorContent\">\n </ngx-editor>\n </section>\n\n <!-- Validation -->\n <p class=\"error\" *ngIf=\"editorContent.invalid && editorContent.touched\">\n Content is required.\n </p>\n\n <!-- Footer / Actions -->\n <footer class=\"action-bar\">\n @if (dialogRef.data.addNote) {\n <button type=\"submit\" class=\"btn btn-primary\" (click)=\"addNote()\"\n [disabled]=\"!hasValidContent(editorContent.value) || isSubmitting\">\n Add Note\n </button>\n } @else {\n <button type=\"submit\" class=\"btn btn-primary\" (click)=\"updateNote()\"\n [disabled]=\"!hasValidContent(editorContent.value) || isSubmitting\">\n Update Note\n </button>\n }\n </footer>\n </form>\n</div>", styles: [":host{display:block;font-family:Figtree,sans-serif;--primary: #34c759;--primary-hover: #2ebd50;--bg: #ffffff;--text: #1f2937;--border: #e5e7eb}:host-context(.dark-mode){--bg: #1f2937;--text: #f9fafb;--border: #374151}:host-context(.dark-mode) ::ng-deep .NgxEditor__MenuBar{background:#1f2937;border-color:#374151}:host-context(.dark-mode) ::ng-deep .NgxEditor__MenuItem{color:#e5e7eb}:host-context(.dark-mode) ::ng-deep .NgxEditor__MenuItem:hover{background-color:#374151;color:#fff}:host-context(.dark-mode) ::ng-deep .NgxEditor__MenuItem.NgxEditor__MenuItem--Active{background-color:#374151;color:#34c759}:host-context(.dark-mode) ::ng-deep .NgxEditor__MenuItem svg{fill:currentColor}:host-context(.dark-mode) ::ng-deep .NgxEditor__Dropdown .NgxEditor__Dropdown--Selected,:host-context(.dark-mode) ::ng-deep .NgxEditor__Dropdown .NgxEditor__Dropdown--Open,:host-context(.dark-mode) ::ng-deep .NgxEditor__Dropdown:hover{background-color:#374151;color:#fff}.note-container{width:100%;max-width:560px;height:auto;min-height:480px;max-height:85vh;background:var(--bg);color:var(--text);border-radius:16px;box-shadow:0 20px 25px -5px #00000026;overflow:hidden;display:flex;flex-direction:column}.popup-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px 0}.popup-title{margin:0;font-size:1.125rem;font-weight:600;color:var(--text)}.close-btn{background:transparent;border:none;cursor:pointer;padding:8px;margin:-8px -8px -8px 0;border-radius:50%;color:var(--text);display:flex;align-items:center;justify-content:center;opacity:.7;transition:all .2s}.close-btn:hover{background-color:#0000000d;opacity:1}:host-context(.dark-mode) .close-btn:hover{background-color:#ffffff1a}.note-form{display:flex;flex-direction:column;flex:1;padding:1.5rem;overflow:hidden}.editor-section{display:flex;flex-direction:column;flex:1;min-height:250px;border:1px solid var(--border);border-radius:12px;overflow:hidden;transition:border-color .2s ease;background:var(--bg)}.editor-section:focus-within{border-color:var(--primary);box-shadow:0 0 0 3px #34c75926}::ng-deep .NgxEditor__MenuBar{background:var(--bg);border-bottom:1px solid var(--border);padding:.5rem;gap:.25rem;flex-wrap:wrap}::ng-deep .NgxEditor{flex:1;height:100%;min-height:300px;padding:1rem;border:none!important;background:var(--bg);color:var(--text);font-size:.95rem;line-height:1.6;overflow-y:auto}.error{color:#ef4444;font-size:.85rem;margin:.5rem 0 0}.action-bar{display:flex;justify-content:flex-end;padding-top:.75rem}::ng-deep .NgxEditor__Popup{position:absolute;top:calc(var(--ngx-editor-menubar-height) + 2px);box-shadow:var(--ngx-editor-popup-shadow);border-radius:var(--ngx-editor-popup-border-radius);background-color:var(--ngx-editor-popup-bg-color);z-index:100;min-width:101px;padding:8px;right:2px}.btn{min-width:140px;padding:10px 24px;border-radius:10px;font-weight:600;border:none;cursor:pointer;transition:all .2s ease}.btn-primary{background:var(--primary);color:#fff;box-shadow:0 4px 6px #34c7594d}.btn-primary:hover:not(:disabled){background:var(--primary-hover);transform:translateY(-1px)}.btn-primary:disabled{opacity:.6;cursor:not-allowed;box-shadow:none}@media (max-width: 640px){.note-container{height:100vh;border-radius:0}.note-form{padding:.75rem}.action-bar{justify-content:stretch}.btn{width:100%}}\n"] }]
373
+ }], ctorParameters: () => [{ type: CustomDialogService }, { type: SharedService }, { type: EpubReaderService }, { type: HttpApiService }, { type: CustomDialogRef }, { type: i4.MessageService }], propDecorators: { onDragOver: [{
374
+ type: HostListener,
375
+ args: ['document:dragover', ['$event']]
376
+ }], onDrop: [{
377
+ type: HostListener,
378
+ args: ['document:drop', ['$event']]
379
+ }], onDragStart: [{
380
+ type: HostListener,
381
+ args: ['document:dragstart', ['$event']]
382
+ }], onRightClick: [{
383
+ type: HostListener,
384
+ args: ['document:contextmenu', ['$event']]
385
+ }], onKeyDown: [{
386
+ type: HostListener,
387
+ args: ['document:keydown', ['$event']]
388
+ }], onCopy: [{
389
+ type: HostListener,
390
+ args: ['document:copy', ['$event']]
391
+ }] } });
392
+
393
+ class NoDataFoundComponent {
394
+ message = 'No Data Found';
395
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NoDataFoundComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
396
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: NoDataFoundComponent, isStandalone: true, selector: "app-no-data-found", inputs: { message: "message" }, ngImport: i0, template: `<div class="no-data-found">{{ message }}</div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
397
+ }
398
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NoDataFoundComponent, decorators: [{
399
+ type: Component,
400
+ args: [{
401
+ selector: 'app-no-data-found',
402
+ standalone: true,
403
+ imports: [CommonModule],
404
+ template: `<div class="no-data-found">{{ message }}</div>`
405
+ }]
406
+ }], propDecorators: { message: [{
407
+ type: Input
408
+ }] } });
409
+
410
+ class SkeletonLoaderComponent {
411
+ count = 3;
412
+ type = 'list';
413
+ items = [];
414
+ ngOnChanges() {
415
+ this.items = Array(this.count).fill(0);
416
+ }
417
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SkeletonLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
418
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: SkeletonLoaderComponent, isStandalone: true, selector: "app-skeleton-loader", inputs: { count: "count", type: "type" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"skeleton-container\">\n <div *ngFor=\"let item of items\" class=\"skeleton-item\" [ngClass]=\"type\">\n <ng-container *ngIf=\"type === 'list'\">\n <div class=\"skeleton-icon\"></div>\n <div class=\"skeleton-content\">\n <div class=\"skeleton-line title\"></div>\n <div class=\"skeleton-line meta\"></div>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"type === 'text'\">\n <div class=\"skeleton-line text-long\"></div>\n <div class=\"skeleton-line text-medium\"></div>\n <div class=\"skeleton-line text-short\"></div>\n </ng-container>\n </div>\n</div>", styles: [".skeleton-container{width:100%;display:flex;flex-direction:column;gap:16px;padding:16px}.skeleton-item{display:flex;gap:16px}.skeleton-item.text{flex-direction:column;gap:8px;padding:0}.skeleton-icon{width:32px;height:32px;border-radius:50%;background-color:var(--border-soft, #e2e8f0);flex-shrink:0;animation:pulse 1.5s infinite}.skeleton-content{flex:1;display:flex;flex-direction:column;gap:8px}.skeleton-line{height:12px;background-color:var(--border-soft, #e2e8f0);border-radius:4px;animation:pulse 1.5s infinite}.skeleton-line.title{width:80%;height:16px}.skeleton-line.meta{width:40%}.skeleton-line.text-long{width:100%;height:14px}.skeleton-line.text-medium{width:80%;height:14px}.skeleton-line.text-short{width:60%;height:14px}@keyframes pulse{0%{opacity:1}50%{opacity:.5}to{opacity:1}}:host-context(.dark-mode) .skeleton-icon,:host-context(.dark-mode) .skeleton-line{background-color:#374151}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
419
+ }
420
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SkeletonLoaderComponent, decorators: [{
421
+ type: Component,
422
+ args: [{ selector: 'app-skeleton-loader', standalone: true, imports: [CommonModule], template: "<div class=\"skeleton-container\">\n <div *ngFor=\"let item of items\" class=\"skeleton-item\" [ngClass]=\"type\">\n <ng-container *ngIf=\"type === 'list'\">\n <div class=\"skeleton-icon\"></div>\n <div class=\"skeleton-content\">\n <div class=\"skeleton-line title\"></div>\n <div class=\"skeleton-line meta\"></div>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"type === 'text'\">\n <div class=\"skeleton-line text-long\"></div>\n <div class=\"skeleton-line text-medium\"></div>\n <div class=\"skeleton-line text-short\"></div>\n </ng-container>\n </div>\n</div>", styles: [".skeleton-container{width:100%;display:flex;flex-direction:column;gap:16px;padding:16px}.skeleton-item{display:flex;gap:16px}.skeleton-item.text{flex-direction:column;gap:8px;padding:0}.skeleton-icon{width:32px;height:32px;border-radius:50%;background-color:var(--border-soft, #e2e8f0);flex-shrink:0;animation:pulse 1.5s infinite}.skeleton-content{flex:1;display:flex;flex-direction:column;gap:8px}.skeleton-line{height:12px;background-color:var(--border-soft, #e2e8f0);border-radius:4px;animation:pulse 1.5s infinite}.skeleton-line.title{width:80%;height:16px}.skeleton-line.meta{width:40%}.skeleton-line.text-long{width:100%;height:14px}.skeleton-line.text-medium{width:80%;height:14px}.skeleton-line.text-short{width:60%;height:14px}@keyframes pulse{0%{opacity:1}50%{opacity:.5}to{opacity:1}}:host-context(.dark-mode) .skeleton-icon,:host-context(.dark-mode) .skeleton-line{background-color:#374151}\n"] }]
423
+ }], propDecorators: { count: [{
424
+ type: Input
425
+ }], type: [{
426
+ type: Input
427
+ }] } });
428
+
429
+ class EpubCommonPopupComponent {
430
+ sanitizer;
431
+ userApi;
432
+ messageService;
433
+ dialog;
434
+ dialogRef;
435
+ loaderService;
436
+ epubService;
437
+ sharedService;
438
+ staticText = {
439
+ validationText: { atleastThreeChara: 'Enter at least 3 characters to begin searching' },
440
+ btnText: { cancel: 'Cancel', edit: 'Edit', remove: 'Remove' },
441
+ htmlText: { nohighlightsavailable: 'No highlights' }
442
+ };
443
+ notesandHighlights;
444
+ bookMarkList;
445
+ singlenoteDetails;
446
+ notes;
447
+ isNotEndOfList = true;
448
+ template = 2;
449
+ htmlString;
450
+ htmlStaticText = {
451
+ validationText: { atleastThreeChara: 'Min 3 chars' },
452
+ btnText: { cancel: 'Cancel', edit: 'Edit', remove: 'Remove' },
453
+ htmlText: { nohighlightsavailable: 'No highlights' }
454
+ };
455
+ paginationData = {
456
+ totalCount: 0,
457
+ pageSize: 1000,
458
+ currentPage: 1,
459
+ };
460
+ searchKey = '';
461
+ searchData = null;
462
+ searchCharaSmall = false;
463
+ onDestroy$ = new Subject();
464
+ tabviewIndex = 1;
465
+ Definition = [];
466
+ isLoading = false;
467
+ isSearchDataAvailable = false;
468
+ constructor(sanitizer, userApi, messageService, dialog, dialogRef, loaderService, epubService, sharedService) {
469
+ this.sanitizer = sanitizer;
470
+ this.userApi = userApi;
471
+ this.messageService = messageService;
472
+ this.dialog = dialog;
473
+ this.dialogRef = dialogRef;
474
+ this.loaderService = loaderService;
475
+ this.epubService = epubService;
476
+ this.sharedService = sharedService;
477
+ effect(() => {
478
+ this.searchData = this.epubService.searchResponseData();
479
+ this.isSearchDataAvailable = false; // Reset loading when data arrives
480
+ });
481
+ }
482
+ ngOnInit() {
483
+ this.template = this.dialogRef.data.template;
484
+ if (this.dialogRef.data.template === 1) {
485
+ this.singlenoteDetails = this.dialogRef.data.notedetails;
486
+ const rawHtml = this.singlenoteDetails.customNotes;
487
+ this.htmlString = this.sanitizer.bypassSecurityTrustHtml(rawHtml);
488
+ }
489
+ if (this.dialogRef.data.template === 2) {
490
+ this.getHighlightNotes();
491
+ }
492
+ if (this.dialogRef.data.template === 4) {
493
+ this.getBookmark();
494
+ }
495
+ if (this.dialogRef.data.template === 5) {
496
+ this.getDefinition();
497
+ }
498
+ }
499
+ /*
500
+ *@Desc : Handles tab view changes
501
+ *@Author: Aboobacker
502
+ */
503
+ onTabChange(data) {
504
+ this.paginationData = {
505
+ totalCount: 0,
506
+ pageSize: 10,
507
+ currentPage: 1,
508
+ };
509
+ if (data.index === 1) {
510
+ //noteas
511
+ this.tabviewIndex = data.index;
512
+ this.getNotes();
513
+ }
514
+ else {
515
+ //highlights
516
+ this.getHighlightNotes();
517
+ }
518
+ }
519
+ /*
520
+ *@Desc : get highlightNotes
521
+ *@Author: Aboobacker
522
+ */
523
+ getHighlightNotes() {
524
+ this.isLoading = true;
525
+ const payload = {
526
+ keyword: '',
527
+ pageSize: this.paginationData.pageSize,
528
+ currentPage: this.paginationData.currentPage,
529
+ id: '',
530
+ type: 2,
531
+ bookId: this.dialogRef.data.bookId,
532
+ };
533
+ this.userApi
534
+ .getAnnotations(payload)
535
+ .pipe(takeUntil(this.onDestroy$), finalize(() => this.isLoading = false))
536
+ .subscribe({
537
+ next: (res) => {
538
+ this.notesandHighlights = res.highlightedNotes?.items;
539
+ },
540
+ error: (err) => {
541
+ console.error('Error fetching highlightedNotes details:', err);
542
+ }
543
+ });
544
+ }
545
+ /*
546
+ *@Desc : get bookMarks
547
+ *@Author: Aboobacker
548
+ */
549
+ getBookmark() {
550
+ this.isLoading = true;
551
+ const payload = {
552
+ keyword: '',
553
+ pageSize: 1000,
554
+ currentPage: 1,
555
+ id: '',
556
+ type: 4, // type 4 is bookmark
557
+ bookId: this.dialogRef.data.bookId,
558
+ };
559
+ this.userApi
560
+ .getAnnotations(payload)
561
+ .pipe(takeUntil(this.onDestroy$), finalize(() => this.isLoading = false))
562
+ .subscribe({
563
+ next: (res) => {
564
+ this.bookMarkList = res?.bookmarks?.items;
565
+ }
566
+ });
567
+ }
568
+ /*
569
+ *@Desc : to get Definition
570
+ *@Author: Aboobacker
571
+ */
572
+ getDefinition() {
573
+ this.isLoading = true;
574
+ this.epubService
575
+ .getDefinition(this.dialogRef.data.selectedText)
576
+ .pipe(takeUntil(this.onDestroy$), finalize(() => this.isLoading = false))
577
+ .subscribe({
578
+ next: (res) => {
579
+ // Ensure Definition is always an array to avoid template errors
580
+ this.Definition = Array.isArray(res?.meanings) ? res.meanings : [];
581
+ },
582
+ error: () => {
583
+ this.Definition = [];
584
+ }
585
+ });
586
+ }
587
+ /*
588
+ *@Desc : get Notes
589
+ *@Author: Aboobacker
590
+ */
591
+ getNotes() {
592
+ this.isLoading = true;
593
+ const payload = {
594
+ keyword: '',
595
+ pageSize: this.paginationData.pageSize,
596
+ currentPage: this.paginationData.currentPage,
597
+ id: '',
598
+ type: 1,
599
+ bookId: this.dialogRef.data.bookId,
600
+ };
601
+ this.userApi
602
+ .getAnnotations(payload)
603
+ .pipe(takeUntil(this.onDestroy$), finalize(() => this.isLoading = false))
604
+ .subscribe({
605
+ next: (res) => {
606
+ if (!res.notes.items.length) {
607
+ this.isNotEndOfList = false;
608
+ }
609
+ if (this.paginationData.currentPage === 1) {
610
+ this.notes = res.notes?.items;
611
+ }
612
+ else {
613
+ this.notes = [...this.notes, ...res.notes.items];
614
+ }
615
+ },
616
+ error: (err) => {
617
+ console.error('Error fetching eBook details:', err);
618
+ }
619
+ });
620
+ }
621
+ /*
622
+ *@Desc : delete all type of annotations
623
+ *@Author: Aboobacker
624
+ */
625
+ deleteAnnotations(typeId, id) {
626
+ this.loaderService.showHide(true);
627
+ const payload = {
628
+ typeId: typeId,
629
+ id: id,
630
+ };
631
+ this.userApi
632
+ .deleteNotes(payload)
633
+ .pipe(takeUntil(this.onDestroy$))
634
+ .subscribe({
635
+ next: (res) => {
636
+ this.loaderService.showHide(false);
637
+ this.messageService.add({
638
+ key: 'notifyToast',
639
+ severity: 'success',
640
+ summary: 'Success',
641
+ detail: res.message,
642
+ });
643
+ if (this.template == 1) {
644
+ this.dialog.closeAll();
645
+ }
646
+ if (this.template == 2) {
647
+ this.getNotes();
648
+ this.getHighlightNotes();
649
+ }
650
+ if (this.template == 4) {
651
+ this.getBookmark();
652
+ }
653
+ },
654
+ error: (err) => {
655
+ console.error('Error dlt Annotations:', err);
656
+ },
657
+ complete: () => { },
658
+ });
659
+ }
660
+ /*
661
+ *@Desc : open the added note
662
+ *@Author: Aboobacker
663
+ */
664
+ openNote(data) {
665
+ this.dialogRef.close(data.cfiLocation);
666
+ const dialog = this.dialog.open(EpubCommonPopupComponent, {
667
+ closeButton: true,
668
+ enableClose: false,
669
+ windowClass: 'highlightandNotes',
670
+ data: {
671
+ template: 1,
672
+ notedetails: data,
673
+ },
674
+ });
675
+ }
676
+ editNote() {
677
+ this.epubService.setNote(this.singlenoteDetails.customNotes);
678
+ const dialog = this.dialog.open(AddNotesComponent, {
679
+ closeButton: false,
680
+ windowClass: 'addandeditNotepopup',
681
+ data: {
682
+ notedetails: this.singlenoteDetails,
683
+ notetoAddorEdit: this.singlenoteDetails.customNotes,
684
+ addNote: false,
685
+ },
686
+ });
687
+ }
688
+ updateSearchKey() {
689
+ this.epubService.toUpdateSearchKey(this.searchKey);
690
+ if (this.searchKey.length >= 3) {
691
+ this.searchCharaSmall = false;
692
+ this.isLoading = true; // start local loader
693
+ }
694
+ else {
695
+ this.searchCharaSmall = true;
696
+ }
697
+ }
698
+ clickSearchItem(cfi) {
699
+ this.searchKey = '';
700
+ this.epubService.toUpdateSearchKey('');
701
+ this.epubService.updateSearchData(null);
702
+ this.dialogRef.close(cfi);
703
+ }
704
+ gotoLocation(data) {
705
+ this.dialogRef.close(data);
706
+ }
707
+ clearSearch() {
708
+ this.searchKey = '';
709
+ this.epubService.toUpdateSearchKey('');
710
+ this.epubService.updateSearchData(null);
711
+ this.searchCharaSmall = false;
712
+ }
713
+ close() {
714
+ this.epubService.updateSearchData(null);
715
+ this.dialogRef.close();
716
+ this.onDestroy$.next();
717
+ this.onDestroy$.complete();
718
+ }
719
+ closePopup(cfi) {
720
+ this.dialogRef.close(cfi);
721
+ }
722
+ ngOnDestroy() {
723
+ this.onDestroy$.next();
724
+ this.onDestroy$.complete();
725
+ }
726
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EpubCommonPopupComponent, deps: [{ token: i1$3.DomSanitizer }, { token: HttpApiService }, { token: i4.MessageService }, { token: CustomDialogService }, { token: CustomDialogRef }, { token: SharedService }, { token: EpubReaderService }, { token: SharedService }], target: i0.ɵɵFactoryTarget.Component });
727
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: EpubCommonPopupComponent, isStandalone: false, selector: "app-epub-common-popup", ngImport: i0, template: "<!-- Template 1: Single Note View -->\n@if (dialogRef.data.template == 1) {\n<div class=\"popup-container\">\n <div class=\"popup-header\">\n <h2 class=\"popup-title\">Note Details</h2>\n <div class=\"header-actions\">\n <button class=\"close-btn\" (click)=\"close()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n </div>\n\n <div class=\"popup-content content-padded\">\n <div class=\"inner-html-class text-lg text-primary mb-6\" [innerHTML]=\"htmlString\"></div>\n </div>\n\n <div class=\"dialog-footer\">\n <button class=\"btn btn-secondary\" (click)=\"close()\">\n {{ htmlStaticText.btnText.cancel }}\n </button>\n <button class=\"btn btn-primary\" (click)=\"editNote()\">\n {{ htmlStaticText.btnText.edit }}\n </button>\n <button class=\"btn btn-danger\" (click)=\"deleteAnnotations(1, this.singlenoteDetails.id)\">\n {{ htmlStaticText.btnText.remove }}\n </button>\n </div>\n</div>\n}\n\n<!-- Template 2: Highlights & Notes Tabs -->\n@if (dialogRef.data.template == 2) {\n<div class=\"popup-container\">\n <div class=\"popup-header\">\n <h2 class=\"popup-title\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M16.035 2.977l4.988 4.989-12.022 12.021H4v-4.988L16.035 2.977z\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n Highlights & Notes\n </h2>\n <button class=\"close-btn\" (click)=\"close()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"popup-content\">\n <p-tabView (onChange)=\"onTabChange($event)\">\n\n <!-- Highlights Tab -->\n <p-tabPanel header=\"Highlights\">\n <div *ngIf=\"isLoading; else highlightsContent\">\n <app-skeleton-loader [count]=\"4\" type=\"list\"></app-skeleton-loader>\n </div>\n <ng-template #highlightsContent>\n <div *ngIf=\"!notesandHighlights?.length; else noteList\" class=\"empty-state\">\n {{ htmlStaticText.htmlText.nohighlightsavailable }}\n </div>\n\n <ng-template #noteList>\n <div *ngFor=\"let note of notesandHighlights\" class=\"popup-row\">\n <div class=\"row-icon-container\">\n <div class=\"w-2 h-8 rounded-full\" [style.background-color]=\"note.colorCode\"\n style=\"width: 4px; height: 32px; border-radius: 4px;\"></div>\n </div>\n\n <div class=\"row-content\" (click)=\"gotoLocation(note.cfiLocation)\">\n <div class=\"row-title line-clamp\">{{ note.annotatedNotes }}</div>\n <div class=\"row-meta\">\n <span>Page {{ note.pageNumber || 'N/A' }}</span>\n </div>\n </div>\n\n <div class=\"row-actions\">\n <button class=\"action-btn\" (click)=\"deleteAnnotations(2, note.id)\" title=\"Remove\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n </div>\n </ng-template>\n </ng-template>\n </p-tabPanel>\n\n <!-- Notes Tab -->\n <p-tabPanel header=\"Notes\">\n <div *ngIf=\"isLoading; else notesContent\">\n <app-skeleton-loader [count]=\"4\" type=\"list\"></app-skeleton-loader>\n </div>\n <ng-template #notesContent>\n <div *ngIf=\"!notes?.length; else notesList\" class=\"empty-state\">\n No notes available.\n </div>\n\n <ng-template #notesList>\n <div *ngFor=\"let item of notes\" class=\"popup-row\">\n <div class=\"row-icon-container\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path d=\"M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </div>\n <div class=\"row-content\" (click)=\"openNote(item)\">\n <div class=\"row-title line-clamp\"\n [innerHTML]=\"item.annotatedNotes ? item.annotatedNotes : item.customNotes\"></div>\n </div>\n <div class=\"row-actions\">\n <button class=\"action-btn\" (click)=\"deleteAnnotations(1, item.id)\" title=\"Remove\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n </div>\n </ng-template>\n </ng-template>\n </p-tabPanel>\n\n </p-tabView>\n </div>\n</div>\n}\n\n<!-- Template 3: Search -->\n@if (dialogRef.data.template == 3) {\n<div class=\"popup-container\">\n <div class=\"popup-header\">\n <h2 class=\"popup-title\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n Search\n </h2>\n <button class=\"close-btn\" (click)=\"close()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"search-container\">\n <div class=\"search-input-wrapper\">\n <svg class=\"search-icon\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\">\n <path d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <input [(ngModel)]=\"searchKey\" (keyup.enter)=\"updateSearchKey()\" type=\"text\" placeholder=\"Search in book...\"\n class=\"search-input\" autofocus />\n <div class=\"input-actions-wrapper\">\n <button *ngIf=\"searchKey\" class=\"icon-btn-clear\" (click)=\"clearSearch()\" title=\"Clear search\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n <button class=\"btn btn-primary btn-sm\" (click)=\"updateSearchKey()\">\n Search\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"popup-content\">\n <div *ngIf=\"searchCharaSmall\" class=\"info-banner\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\" />\n <line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\" />\n </svg>\n <span>{{ staticText.validationText.atleastThreeChara }}</span>\n </div>\n\n <div *ngIf=\"isSearchDataAvailable; else searchResults\">\n <app-skeleton-loader [count]=\"4\" type=\"list\"></app-skeleton-loader>\n </div>\n\n <ng-template #searchResults>\n <div *ngIf=\"searchKey?.length && searchData && !searchData?.length\" class=\"empty-state\">\n <app-no-data-found></app-no-data-found>\n </div>\n\n <div class=\"search-results\">\n <div *ngFor=\"let item of searchData\" class=\"popup-row\" (click)=\"clickSearchItem(item.cfi)\">\n <div class=\"row-content\">\n <div class=\"row-title\" [innerHTML]=\"item.highlightedSentence\"></div>\n </div>\n </div>\n </div>\n </ng-template>\n </div>\n</div>\n}\n\n<!-- Template 4: Bookmarks -->\n@if (dialogRef.data.template == 4) {\n<div class=\"popup-container\">\n <div class=\"popup-header\">\n <h2 class=\"popup-title\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n Bookmarks\n </h2>\n <button class=\"close-btn\" (click)=\"close()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"popup-content\">\n <div *ngIf=\"isLoading; else bookmarkContent\">\n <app-skeleton-loader [count]=\"4\" type=\"list\"></app-skeleton-loader>\n </div>\n\n <ng-template #bookmarkContent>\n <div *ngIf=\"!bookMarkList || bookMarkList.length === 0\" class=\"empty-state\">\n No bookmarks yet.\n </div>\n\n <div *ngIf=\"bookMarkList && bookMarkList.length > 0\">\n <div *ngFor=\"let bookmark of bookMarkList\" class=\"popup-row\">\n <div class=\"row-icon-container\">\n <!-- Filled Bookmark Icon for list -->\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"\n [style.color]=\"bookmark.colorCode || '#3b82f6'\">\n <path d=\"M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z\" />\n </svg>\n </div>\n\n <div class=\"row-content\" (click)=\"closePopup(bookmark.cfiLocation)\">\n <div class=\"row-title line-clamp\">{{ bookmark.annotatedNotes || 'Bookmark' }}</div>\n <div class=\"row-meta mt-2\">\n <div class=\"progress-container\">\n <div class=\"progress-fill\" [style.width.%]=\"bookmark.customNotes\"></div>\n </div>\n <span>{{ bookmark.customNotes }}%</span>\n </div>\n </div>\n\n <div class=\"row-actions\">\n <button class=\"action-btn\" (click)=\"deleteAnnotations(4, bookmark.id)\" title=\"Remove\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n </div>\n </div>\n </ng-template>\n </div>\n</div>\n}\n\n<!-- Template 5: Definitions -->\n@if (dialogRef.data.template == 5) {\n<div class=\"popup-container\">\n <div class=\"popup-header\">\n <h2 class=\"popup-title\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M4 19.5A2.5 2.5 0 016.5 17H20\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n <path d=\"M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n Definition\n </h2>\n <button class=\"close-btn\" (click)=\"close()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"popup-content content-padded\">\n <h3 class=\"text-xl font-bold text-primary mb-4 select-text\">\n \"{{ dialogRef.data.selectedText }}\"\n </h3>\n\n <div *ngIf=\"isLoading; else defsContent\">\n <app-skeleton-loader [count]=\"3\" type=\"text\"></app-skeleton-loader>\n </div>\n\n <ng-template #defsContent>\n <div *ngIf=\"Definition?.length > 0; else noDefs\">\n <div *ngFor=\"let entry of Definition\" class=\"definition-group\">\n <span class=\"pos-tag\">{{ entry.partOfSpeech }}</span>\n\n <div *ngFor=\"let def of entry.definitions\" class=\"def-item\">\n <p class=\"def-text\">{{ def.definition }}</p>\n <p *ngIf=\"def.example\" class=\"def-example\">\"{{ def.example }}\"</p>\n </div>\n </div>\n </div>\n\n <ng-template #noDefs>\n <div class=\"empty-state\">No definitions found.</div>\n </ng-template>\n </ng-template>\n </div>\n</div>\n}", styles: [":host{--bg-primary: #ffffff;--bg-secondary: #f8fafc;--text-primary: #0f172a;--text-secondary: #64748b;--border-soft: #e2e8f0;--accent: #3b82f6;--accent-hover: #2563eb;--danger: #ef4444;--danger-bg: #fef2f2}:host-context(.dark-mode){--bg-primary: rgb(32 33 32);--bg-secondary: rgb(42 43 42);--text-primary: #f8fafc;--text-secondary: #94a3b8;--border-soft: #334155;--accent: #60a5fa;--accent-hover: #3b82f6;--danger: #f87171;--danger-bg: #450a0a}.popup-container{display:flex;flex-direction:column;width:500px;height:600px;max-width:95vw;max-height:85vh;background:var(--bg-primary);color:var(--text-primary);border-radius:16px;overflow:hidden;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}.popup-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;background:var(--bg-secondary);border-bottom:1px solid var(--border-soft)}.popup-title{font-size:1.15rem;font-weight:600;color:var(--text-primary);display:flex;align-items:center;gap:12px;margin:0}.popup-title svg{color:var(--text-secondary)}.close-btn{background:transparent;border:none;color:var(--text-secondary);padding:8px;border-radius:50%;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center}.close-btn:hover{background-color:#e2e8f0;color:var(--text-primary)}.popup-content{flex:1;overflow-y:auto;position:relative}.content-padded{padding:24px}.search-container{padding:16px 24px;background:var(--bg-primary);border-bottom:1px solid var(--border-soft);position:sticky;top:0;z-index:10}.search-input-wrapper{position:relative;display:flex;align-items:center}.search-input-wrapper svg.search-icon{position:absolute;left:12px;color:var(--text-secondary);z-index:1}.search-input{width:100%;padding:12px 110px 12px 40px;border-radius:10px;border:1px solid var(--border-soft);font-size:.95rem;background:var(--bg-secondary);color:var(--text-primary);transition:all .2s}.search-input:focus{outline:none;background:var(--bg-primary);border-color:var(--accent);box-shadow:0 0 0 3px #3b82f61a}.input-actions-wrapper{position:absolute;right:8px;display:flex;align-items:center;gap:8px}.icon-btn-clear{background:transparent;border:none;color:var(--text-secondary);display:flex;align-items:center;justify-content:center;padding:4px;cursor:pointer;border-radius:4px}.icon-btn-clear:hover{background-color:var(--border-soft);color:var(--text-primary)}.info-banner{display:flex;align-items:center;gap:12px;margin:12px 24px;padding:12px 16px;background:var(--primary-lite);color:var(--primary);border-radius:8px;font-size:.85rem;font-weight:500;border:1px solid var(--border-color)}.info-banner svg{flex-shrink:0;opacity:.8}.btn-sm{padding:4px 12px;font-size:.85rem;height:32px}.popup-row{display:flex;align-items:flex-start;gap:16px;padding:16px 24px;border-bottom:1px solid var(--border-soft);cursor:pointer;transition:background-color .2s}.popup-row:hover{background-color:var(--bg-secondary)}.popup-row:hover .row-actions{opacity:1}.popup-row:last-child{border-bottom:none}.row-icon-container{flex-shrink:0;margin-top:2px;color:var(--text-secondary)}.row-content{flex:1;min-width:0}.row-title{font-size:.95rem;font-weight:500;color:var(--text-primary);line-height:1.5;margin-bottom:4px}.row-meta{font-size:.8rem;color:var(--text-secondary);display:flex;align-items:center;gap:8px}.row-actions{opacity:0;transition:opacity .2s;display:flex;align-items:center}.action-btn{background:transparent;border:none;color:var(--text-secondary);padding:6px;border-radius:6px;cursor:pointer;transition:all .2s}.action-btn:hover{background-color:var(--danger-bg);color:var(--danger)}.dialog-footer{padding:16px 24px;border-top:1px solid var(--border-soft);background:var(--bg-primary);display:flex;justify-content:flex-end;gap:12px}.btn{padding:8px 16px;border-radius:8px;font-size:.9rem;font-weight:500;cursor:pointer;transition:all .2s;border:1px solid transparent}.btn-secondary{background:var(--bg-secondary);color:var(--text-primary);border-color:var(--border-soft)}.btn-secondary:hover{background:#e2e8f0}.btn-primary{background:var(--accent);color:#fff}.btn-primary:hover{background:var(--accent-hover)}.btn-danger{background:var(--danger-bg);color:var(--danger)}.btn-danger:hover{background:#fee2e2}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.text-primary{color:var(--accent)}.mb-4{margin-bottom:1rem}.select-text{user-select:text;-webkit-user-select:text}.definition-group{margin-bottom:24px}.pos-tag{display:inline-block;font-size:.75rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--accent);background:#3b82f61a;padding:4px 8px;border-radius:6px;margin-bottom:12px}.def-item{padding-bottom:16px;margin-bottom:16px;border-bottom:1px dashed var(--border-soft)}.def-item:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}.def-text{font-size:.95rem;color:var(--text-primary);line-height:1.5;margin-bottom:4px}.def-example{font-size:.85rem;color:var(--text-secondary);font-style:italic;padding-left:12px;border-left:2px solid var(--border-soft)}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;color:var(--text-secondary);text-align:center;font-style:italic}.progress-container{width:100px;height:6px;background:var(--bg-secondary);border-radius:3px;overflow:hidden}.progress-fill{height:100%;background:var(--accent);border-radius:3px}.line-clamp{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}:host::ng-deep .p-tabview-nav{background:var(--bg-primary)!important;border-bottom:1px solid var(--border-soft)!important;justify-content:center;padding:0}:host::ng-deep .p-tabview-nav li .p-tabview-nav-link{background:transparent!important;border:none!important;color:var(--text-secondary)!important;font-weight:500;padding:16px 20px!important;box-shadow:none!important;font-size:.95rem}:host::ng-deep .p-tabview-nav li.p-highlight .p-tabview-nav-link{color:var(--accent)!important;border-bottom:2px solid var(--accent)!important}:host::ng-deep .p-tabview-panels{padding:0!important;background:transparent!important}:host::ng-deep .highlight-key{color:var(--danger)!important;font-weight:700;background-color:#ef444426;padding:0 4px;border-radius:3px}\n"], dependencies: [{ kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i7.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i8.TabView, selector: "p-tabView", inputs: ["style", "styleClass", "controlClose", "scrollable", "activeIndex", "selectOnFocus", "nextButtonAriaLabel", "prevButtonAriaLabel", "autoHideButtons", "tabindex"], outputs: ["onChange", "onClose", "activeIndexChange"] }, { kind: "component", type: i8.TabPanel, selector: "p-tabPanel", inputs: ["closable", "headerStyle", "headerStyleClass", "cache", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "selected", "disabled", "header", "leftIcon", "rightIcon"] }, { kind: "component", type: NoDataFoundComponent, selector: "app-no-data-found", inputs: ["message"] }, { kind: "component", type: SkeletonLoaderComponent, selector: "app-skeleton-loader", inputs: ["count", "type"] }] });
728
+ }
729
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EpubCommonPopupComponent, decorators: [{
730
+ type: Component,
731
+ args: [{ selector: 'app-epub-common-popup', standalone: false, template: "<!-- Template 1: Single Note View -->\n@if (dialogRef.data.template == 1) {\n<div class=\"popup-container\">\n <div class=\"popup-header\">\n <h2 class=\"popup-title\">Note Details</h2>\n <div class=\"header-actions\">\n <button class=\"close-btn\" (click)=\"close()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n </div>\n\n <div class=\"popup-content content-padded\">\n <div class=\"inner-html-class text-lg text-primary mb-6\" [innerHTML]=\"htmlString\"></div>\n </div>\n\n <div class=\"dialog-footer\">\n <button class=\"btn btn-secondary\" (click)=\"close()\">\n {{ htmlStaticText.btnText.cancel }}\n </button>\n <button class=\"btn btn-primary\" (click)=\"editNote()\">\n {{ htmlStaticText.btnText.edit }}\n </button>\n <button class=\"btn btn-danger\" (click)=\"deleteAnnotations(1, this.singlenoteDetails.id)\">\n {{ htmlStaticText.btnText.remove }}\n </button>\n </div>\n</div>\n}\n\n<!-- Template 2: Highlights & Notes Tabs -->\n@if (dialogRef.data.template == 2) {\n<div class=\"popup-container\">\n <div class=\"popup-header\">\n <h2 class=\"popup-title\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M16.035 2.977l4.988 4.989-12.022 12.021H4v-4.988L16.035 2.977z\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n Highlights & Notes\n </h2>\n <button class=\"close-btn\" (click)=\"close()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"popup-content\">\n <p-tabView (onChange)=\"onTabChange($event)\">\n\n <!-- Highlights Tab -->\n <p-tabPanel header=\"Highlights\">\n <div *ngIf=\"isLoading; else highlightsContent\">\n <app-skeleton-loader [count]=\"4\" type=\"list\"></app-skeleton-loader>\n </div>\n <ng-template #highlightsContent>\n <div *ngIf=\"!notesandHighlights?.length; else noteList\" class=\"empty-state\">\n {{ htmlStaticText.htmlText.nohighlightsavailable }}\n </div>\n\n <ng-template #noteList>\n <div *ngFor=\"let note of notesandHighlights\" class=\"popup-row\">\n <div class=\"row-icon-container\">\n <div class=\"w-2 h-8 rounded-full\" [style.background-color]=\"note.colorCode\"\n style=\"width: 4px; height: 32px; border-radius: 4px;\"></div>\n </div>\n\n <div class=\"row-content\" (click)=\"gotoLocation(note.cfiLocation)\">\n <div class=\"row-title line-clamp\">{{ note.annotatedNotes }}</div>\n <div class=\"row-meta\">\n <span>Page {{ note.pageNumber || 'N/A' }}</span>\n </div>\n </div>\n\n <div class=\"row-actions\">\n <button class=\"action-btn\" (click)=\"deleteAnnotations(2, note.id)\" title=\"Remove\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n </div>\n </ng-template>\n </ng-template>\n </p-tabPanel>\n\n <!-- Notes Tab -->\n <p-tabPanel header=\"Notes\">\n <div *ngIf=\"isLoading; else notesContent\">\n <app-skeleton-loader [count]=\"4\" type=\"list\"></app-skeleton-loader>\n </div>\n <ng-template #notesContent>\n <div *ngIf=\"!notes?.length; else notesList\" class=\"empty-state\">\n No notes available.\n </div>\n\n <ng-template #notesList>\n <div *ngFor=\"let item of notes\" class=\"popup-row\">\n <div class=\"row-icon-container\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path d=\"M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </div>\n <div class=\"row-content\" (click)=\"openNote(item)\">\n <div class=\"row-title line-clamp\"\n [innerHTML]=\"item.annotatedNotes ? item.annotatedNotes : item.customNotes\"></div>\n </div>\n <div class=\"row-actions\">\n <button class=\"action-btn\" (click)=\"deleteAnnotations(1, item.id)\" title=\"Remove\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n </div>\n </ng-template>\n </ng-template>\n </p-tabPanel>\n\n </p-tabView>\n </div>\n</div>\n}\n\n<!-- Template 3: Search -->\n@if (dialogRef.data.template == 3) {\n<div class=\"popup-container\">\n <div class=\"popup-header\">\n <h2 class=\"popup-title\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n Search\n </h2>\n <button class=\"close-btn\" (click)=\"close()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"search-container\">\n <div class=\"search-input-wrapper\">\n <svg class=\"search-icon\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\">\n <path d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <input [(ngModel)]=\"searchKey\" (keyup.enter)=\"updateSearchKey()\" type=\"text\" placeholder=\"Search in book...\"\n class=\"search-input\" autofocus />\n <div class=\"input-actions-wrapper\">\n <button *ngIf=\"searchKey\" class=\"icon-btn-clear\" (click)=\"clearSearch()\" title=\"Clear search\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n <button class=\"btn btn-primary btn-sm\" (click)=\"updateSearchKey()\">\n Search\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"popup-content\">\n <div *ngIf=\"searchCharaSmall\" class=\"info-banner\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\" />\n <line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\" />\n </svg>\n <span>{{ staticText.validationText.atleastThreeChara }}</span>\n </div>\n\n <div *ngIf=\"isSearchDataAvailable; else searchResults\">\n <app-skeleton-loader [count]=\"4\" type=\"list\"></app-skeleton-loader>\n </div>\n\n <ng-template #searchResults>\n <div *ngIf=\"searchKey?.length && searchData && !searchData?.length\" class=\"empty-state\">\n <app-no-data-found></app-no-data-found>\n </div>\n\n <div class=\"search-results\">\n <div *ngFor=\"let item of searchData\" class=\"popup-row\" (click)=\"clickSearchItem(item.cfi)\">\n <div class=\"row-content\">\n <div class=\"row-title\" [innerHTML]=\"item.highlightedSentence\"></div>\n </div>\n </div>\n </div>\n </ng-template>\n </div>\n</div>\n}\n\n<!-- Template 4: Bookmarks -->\n@if (dialogRef.data.template == 4) {\n<div class=\"popup-container\">\n <div class=\"popup-header\">\n <h2 class=\"popup-title\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n Bookmarks\n </h2>\n <button class=\"close-btn\" (click)=\"close()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"popup-content\">\n <div *ngIf=\"isLoading; else bookmarkContent\">\n <app-skeleton-loader [count]=\"4\" type=\"list\"></app-skeleton-loader>\n </div>\n\n <ng-template #bookmarkContent>\n <div *ngIf=\"!bookMarkList || bookMarkList.length === 0\" class=\"empty-state\">\n No bookmarks yet.\n </div>\n\n <div *ngIf=\"bookMarkList && bookMarkList.length > 0\">\n <div *ngFor=\"let bookmark of bookMarkList\" class=\"popup-row\">\n <div class=\"row-icon-container\">\n <!-- Filled Bookmark Icon for list -->\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"\n [style.color]=\"bookmark.colorCode || '#3b82f6'\">\n <path d=\"M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z\" />\n </svg>\n </div>\n\n <div class=\"row-content\" (click)=\"closePopup(bookmark.cfiLocation)\">\n <div class=\"row-title line-clamp\">{{ bookmark.annotatedNotes || 'Bookmark' }}</div>\n <div class=\"row-meta mt-2\">\n <div class=\"progress-container\">\n <div class=\"progress-fill\" [style.width.%]=\"bookmark.customNotes\"></div>\n </div>\n <span>{{ bookmark.customNotes }}%</span>\n </div>\n </div>\n\n <div class=\"row-actions\">\n <button class=\"action-btn\" (click)=\"deleteAnnotations(4, bookmark.id)\" title=\"Remove\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n </div>\n </div>\n </ng-template>\n </div>\n</div>\n}\n\n<!-- Template 5: Definitions -->\n@if (dialogRef.data.template == 5) {\n<div class=\"popup-container\">\n <div class=\"popup-header\">\n <h2 class=\"popup-title\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M4 19.5A2.5 2.5 0 016.5 17H20\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n <path d=\"M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n Definition\n </h2>\n <button class=\"close-btn\" (click)=\"close()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"popup-content content-padded\">\n <h3 class=\"text-xl font-bold text-primary mb-4 select-text\">\n \"{{ dialogRef.data.selectedText }}\"\n </h3>\n\n <div *ngIf=\"isLoading; else defsContent\">\n <app-skeleton-loader [count]=\"3\" type=\"text\"></app-skeleton-loader>\n </div>\n\n <ng-template #defsContent>\n <div *ngIf=\"Definition?.length > 0; else noDefs\">\n <div *ngFor=\"let entry of Definition\" class=\"definition-group\">\n <span class=\"pos-tag\">{{ entry.partOfSpeech }}</span>\n\n <div *ngFor=\"let def of entry.definitions\" class=\"def-item\">\n <p class=\"def-text\">{{ def.definition }}</p>\n <p *ngIf=\"def.example\" class=\"def-example\">\"{{ def.example }}\"</p>\n </div>\n </div>\n </div>\n\n <ng-template #noDefs>\n <div class=\"empty-state\">No definitions found.</div>\n </ng-template>\n </ng-template>\n </div>\n</div>\n}", styles: [":host{--bg-primary: #ffffff;--bg-secondary: #f8fafc;--text-primary: #0f172a;--text-secondary: #64748b;--border-soft: #e2e8f0;--accent: #3b82f6;--accent-hover: #2563eb;--danger: #ef4444;--danger-bg: #fef2f2}:host-context(.dark-mode){--bg-primary: rgb(32 33 32);--bg-secondary: rgb(42 43 42);--text-primary: #f8fafc;--text-secondary: #94a3b8;--border-soft: #334155;--accent: #60a5fa;--accent-hover: #3b82f6;--danger: #f87171;--danger-bg: #450a0a}.popup-container{display:flex;flex-direction:column;width:500px;height:600px;max-width:95vw;max-height:85vh;background:var(--bg-primary);color:var(--text-primary);border-radius:16px;overflow:hidden;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}.popup-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;background:var(--bg-secondary);border-bottom:1px solid var(--border-soft)}.popup-title{font-size:1.15rem;font-weight:600;color:var(--text-primary);display:flex;align-items:center;gap:12px;margin:0}.popup-title svg{color:var(--text-secondary)}.close-btn{background:transparent;border:none;color:var(--text-secondary);padding:8px;border-radius:50%;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center}.close-btn:hover{background-color:#e2e8f0;color:var(--text-primary)}.popup-content{flex:1;overflow-y:auto;position:relative}.content-padded{padding:24px}.search-container{padding:16px 24px;background:var(--bg-primary);border-bottom:1px solid var(--border-soft);position:sticky;top:0;z-index:10}.search-input-wrapper{position:relative;display:flex;align-items:center}.search-input-wrapper svg.search-icon{position:absolute;left:12px;color:var(--text-secondary);z-index:1}.search-input{width:100%;padding:12px 110px 12px 40px;border-radius:10px;border:1px solid var(--border-soft);font-size:.95rem;background:var(--bg-secondary);color:var(--text-primary);transition:all .2s}.search-input:focus{outline:none;background:var(--bg-primary);border-color:var(--accent);box-shadow:0 0 0 3px #3b82f61a}.input-actions-wrapper{position:absolute;right:8px;display:flex;align-items:center;gap:8px}.icon-btn-clear{background:transparent;border:none;color:var(--text-secondary);display:flex;align-items:center;justify-content:center;padding:4px;cursor:pointer;border-radius:4px}.icon-btn-clear:hover{background-color:var(--border-soft);color:var(--text-primary)}.info-banner{display:flex;align-items:center;gap:12px;margin:12px 24px;padding:12px 16px;background:var(--primary-lite);color:var(--primary);border-radius:8px;font-size:.85rem;font-weight:500;border:1px solid var(--border-color)}.info-banner svg{flex-shrink:0;opacity:.8}.btn-sm{padding:4px 12px;font-size:.85rem;height:32px}.popup-row{display:flex;align-items:flex-start;gap:16px;padding:16px 24px;border-bottom:1px solid var(--border-soft);cursor:pointer;transition:background-color .2s}.popup-row:hover{background-color:var(--bg-secondary)}.popup-row:hover .row-actions{opacity:1}.popup-row:last-child{border-bottom:none}.row-icon-container{flex-shrink:0;margin-top:2px;color:var(--text-secondary)}.row-content{flex:1;min-width:0}.row-title{font-size:.95rem;font-weight:500;color:var(--text-primary);line-height:1.5;margin-bottom:4px}.row-meta{font-size:.8rem;color:var(--text-secondary);display:flex;align-items:center;gap:8px}.row-actions{opacity:0;transition:opacity .2s;display:flex;align-items:center}.action-btn{background:transparent;border:none;color:var(--text-secondary);padding:6px;border-radius:6px;cursor:pointer;transition:all .2s}.action-btn:hover{background-color:var(--danger-bg);color:var(--danger)}.dialog-footer{padding:16px 24px;border-top:1px solid var(--border-soft);background:var(--bg-primary);display:flex;justify-content:flex-end;gap:12px}.btn{padding:8px 16px;border-radius:8px;font-size:.9rem;font-weight:500;cursor:pointer;transition:all .2s;border:1px solid transparent}.btn-secondary{background:var(--bg-secondary);color:var(--text-primary);border-color:var(--border-soft)}.btn-secondary:hover{background:#e2e8f0}.btn-primary{background:var(--accent);color:#fff}.btn-primary:hover{background:var(--accent-hover)}.btn-danger{background:var(--danger-bg);color:var(--danger)}.btn-danger:hover{background:#fee2e2}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.text-primary{color:var(--accent)}.mb-4{margin-bottom:1rem}.select-text{user-select:text;-webkit-user-select:text}.definition-group{margin-bottom:24px}.pos-tag{display:inline-block;font-size:.75rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--accent);background:#3b82f61a;padding:4px 8px;border-radius:6px;margin-bottom:12px}.def-item{padding-bottom:16px;margin-bottom:16px;border-bottom:1px dashed var(--border-soft)}.def-item:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}.def-text{font-size:.95rem;color:var(--text-primary);line-height:1.5;margin-bottom:4px}.def-example{font-size:.85rem;color:var(--text-secondary);font-style:italic;padding-left:12px;border-left:2px solid var(--border-soft)}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;color:var(--text-secondary);text-align:center;font-style:italic}.progress-container{width:100px;height:6px;background:var(--bg-secondary);border-radius:3px;overflow:hidden}.progress-fill{height:100%;background:var(--accent);border-radius:3px}.line-clamp{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}:host::ng-deep .p-tabview-nav{background:var(--bg-primary)!important;border-bottom:1px solid var(--border-soft)!important;justify-content:center;padding:0}:host::ng-deep .p-tabview-nav li .p-tabview-nav-link{background:transparent!important;border:none!important;color:var(--text-secondary)!important;font-weight:500;padding:16px 20px!important;box-shadow:none!important;font-size:.95rem}:host::ng-deep .p-tabview-nav li.p-highlight .p-tabview-nav-link{color:var(--accent)!important;border-bottom:2px solid var(--accent)!important}:host::ng-deep .p-tabview-panels{padding:0!important;background:transparent!important}:host::ng-deep .highlight-key{color:var(--danger)!important;font-weight:700;background-color:#ef444426;padding:0 4px;border-radius:3px}\n"] }]
732
+ }], ctorParameters: () => [{ type: i1$3.DomSanitizer }, { type: HttpApiService }, { type: i4.MessageService }, { type: CustomDialogService }, { type: CustomDialogRef }, { type: SharedService }, { type: EpubReaderService }, { type: SharedService }] });
733
+
734
+ class ConfirmPopupComponent {
735
+ ref;
736
+ data;
737
+ constructor(ref) {
738
+ this.ref = ref;
739
+ this.data = this.ref.data;
740
+ }
741
+ close(result) {
742
+ this.ref.close(result);
743
+ }
744
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ConfirmPopupComponent, deps: [{ token: CustomDialogRef }], target: i0.ɵɵFactoryTarget.Component });
745
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: ConfirmPopupComponent, isStandalone: true, selector: "app-confirm-popup", ngImport: i0, template: `
746
+ <div class="confirm-popup-container">
747
+ <h3 class="title">{{ data.heading }}</h3>
748
+ <p class="message">{{ data.message }}</p>
749
+ <div class="actions">
750
+ <button class="btn-cancel" (click)="close(false)">{{ data.btnOneTxt }}</button>
751
+ <button class="btn-confirm" (click)="close(true)">{{ data.btnTwoTxt }}</button>
752
+ </div>
753
+ </div>
754
+ `, isInline: true, styles: [".confirm-popup-container{padding:24px;background:#fff;border-radius:8px;min-width:300px;box-shadow:0 4px 6px -1px #0000001a}.title{margin:0 0 12px;font-size:1.125rem;font-weight:600;color:#111827}.message{margin:0 0 24px;color:#4b5563;font-size:.95rem}.actions{display:flex;justify-content:flex-end;gap:12px}button{padding:8px 16px;border-radius:6px;font-weight:500;cursor:pointer;transition:all .2s;border:none}.btn-cancel{background:#f3f4f6;color:#374151}.btn-cancel:hover{background:#e5e7eb}.btn-confirm{background:#dc2626;color:#fff}.btn-confirm:hover{background:#b91c1c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
755
+ }
756
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ConfirmPopupComponent, decorators: [{
757
+ type: Component,
758
+ args: [{ selector: 'app-confirm-popup', standalone: true, imports: [CommonModule], template: `
759
+ <div class="confirm-popup-container">
760
+ <h3 class="title">{{ data.heading }}</h3>
761
+ <p class="message">{{ data.message }}</p>
762
+ <div class="actions">
763
+ <button class="btn-cancel" (click)="close(false)">{{ data.btnOneTxt }}</button>
764
+ <button class="btn-confirm" (click)="close(true)">{{ data.btnTwoTxt }}</button>
765
+ </div>
766
+ </div>
767
+ `, styles: [".confirm-popup-container{padding:24px;background:#fff;border-radius:8px;min-width:300px;box-shadow:0 4px 6px -1px #0000001a}.title{margin:0 0 12px;font-size:1.125rem;font-weight:600;color:#111827}.message{margin:0 0 24px;color:#4b5563;font-size:.95rem}.actions{display:flex;justify-content:flex-end;gap:12px}button{padding:8px 16px;border-radius:6px;font-weight:500;cursor:pointer;transition:all .2s;border:none}.btn-cancel{background:#f3f4f6;color:#374151}.btn-cancel:hover{background:#e5e7eb}.btn-confirm{background:#dc2626;color:#fff}.btn-confirm:hover{background:#b91c1c}\n"] }]
768
+ }], ctorParameters: () => [{ type: CustomDialogRef }] });
769
+
770
+ class ChapterlistComponent {
771
+ chapterData = [];
772
+ chapterClick = new EventEmitter();
773
+ closeDialog = new EventEmitter();
774
+ isExpanded = [];
775
+ ngOnInit() {
776
+ if (this.chapterData) {
777
+ this.isExpanded = new Array(this.chapterData.length).fill(false);
778
+ }
779
+ }
780
+ toggleAccordion(index) {
781
+ this.isExpanded[index] = !this.isExpanded[index];
782
+ }
783
+ onChapterClick(href) {
784
+ this.chapterClick.emit(href);
785
+ }
786
+ onClose() {
787
+ this.closeDialog.emit();
788
+ }
789
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ChapterlistComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
790
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: ChapterlistComponent, isStandalone: false, selector: "app-chapterlist", inputs: { chapterData: "chapterData" }, outputs: { chapterClick: "chapterClick", closeDialog: "closeDialog" }, ngImport: i0, template: "<div id=\"reader-content\" class=\"chapter-list-container bg-white\">\n <!-- Header -->\n <div class=\"chapter-header\">\n\n <h2 class=\"chapter-title\">\n Contents\n </h2>\n\n <button class=\"close-btn\" aria-label=\"Close\" (click)=\"onClose()\">\n <svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </button>\n\n </div>\n\n\n\n <!-- Chapter List -->\n <div class=\"flex-1 overflow-y-auto custom-scrollbar\">\n <!-- No chapters -->\n <div *ngIf=\"!chapterData || chapterData.length === 0\" class=\"text-sm text-gray-500 p-8 text-center italic\">\n No chapters available in this book.\n </div>\n\n <ng-container *ngFor=\"let chapter of chapterData\">\n\n <!-- Main Chapter -->\n <div class=\"chapter-row main-chapter\" (click)=\"onChapterClick(chapter.href)\">\n <div class=\"title-container\">\n <span class=\"title\">{{ chapter.title }}</span>\n <span *ngIf=\"chapter.page\" class=\"dots\"></span>\n </div>\n <span *ngIf=\"chapter.page\" class=\"page\">\n {{ chapter.page }}\n </span>\n </div>\n\n <!-- Subchapters -->\n <ng-container *ngIf=\"chapter.subchapters?.length\">\n <div *ngFor=\"let subchapter of chapter.subchapters\" class=\"chapter-row sub-chapter\"\n (click)=\"onChapterClick(subchapter.href)\">\n <div class=\"title-container\">\n <span class=\"title\">{{ subchapter.title }}</span>\n <span *ngIf=\"subchapter.page\" class=\"dots\"></span>\n </div>\n <span *ngIf=\"subchapter.page\" class=\"page\">\n {{ subchapter.page }}\n </span>\n </div>\n </ng-container>\n </ng-container>\n </div>\n</div>", styles: [".chapter-list-container{display:flex;flex-direction:column;height:600px;max-height:80vh;background-color:var(--bg-primary);overflow:hidden;border-radius:12px;transition:background-color .3s ease;border:1px solid transparent}:host-context(.dark-mode) .chapter-list-container{border:1px solid var(--border-base)}.custom-scrollbar{flex:1;overflow-y:auto;scroll-behavior:smooth}.custom-scrollbar::-webkit-scrollbar{width:8px}.custom-scrollbar::-webkit-scrollbar-thumb{background-color:#cbd5e1;border-radius:4px}.custom-scrollbar::-webkit-scrollbar-track{background:var(--bg-secondary)}.chapter-row{display:flex;align-items:baseline;padding:14px 24px;cursor:pointer;transition:background-color .2s ease}.chapter-row:hover{background-color:var(--bg-secondary)}.chapter-row:hover .dots{border-bottom-color:var(--text-secondary)}.chapter-row:hover .title{color:var(--text-primary)}.chapter-row .title-container{display:flex;align-items:baseline;flex-grow:1;overflow:hidden}.chapter-row .title{flex-shrink:0;max-width:90%;line-height:1.5;color:var(--text-primary)}.chapter-row .dots{flex-grow:1;border-bottom:1px dotted var(--border-base);margin:0 10px;height:1px;opacity:.8;position:relative;top:-4px}.chapter-row .page{flex-shrink:0;font-size:.95rem;color:var(--text-secondary);font-variant-numeric:tabular-nums;font-weight:500;min-width:32px;text-align:right}.chapter-row.main-chapter{font-size:18px;font-weight:700;color:var(--text-primary);padding-top:20px;padding-bottom:12px;border-bottom:1px solid var(--border-base)}.chapter-row.main-chapter .title{letter-spacing:-.01em}.chapter-row.main-chapter .page{font-size:1rem;color:var(--text-primary)}.chapter-row.sub-chapter{font-size:15px;font-weight:400;color:var(--text-secondary);padding-left:52px;padding-top:10px;padding-bottom:10px}.chapter-row.sub-chapter:hover .title{color:var(--text-primary)}.chapter-row.sub-chapter .dots{border-bottom-color:var(--border-base)}.header-title{font-family:inherit;font-weight:800;letter-spacing:-.02em;color:var(--text-primary);font-size:1.25rem}.chapter-header{padding:18px;background:var(--bg-secondary);display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border-base)}.chapter-title{font-size:1.25rem;font-weight:700;color:var(--text-primary);margin:0}.close-btn{display:flex;align-items:center;justify-content:center;padding:8px;border:none;background:transparent;color:#9ca3af;border-radius:9999px;cursor:pointer;transition:background-color .2s ease,color .2s ease}.close-btn:hover{background-color:var(--border-base);color:var(--text-primary)}.close-btn svg{pointer-events:none}\n"], dependencies: [{ kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
791
+ }
792
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ChapterlistComponent, decorators: [{
793
+ type: Component,
794
+ args: [{ selector: 'app-chapterlist', standalone: false, template: "<div id=\"reader-content\" class=\"chapter-list-container bg-white\">\n <!-- Header -->\n <div class=\"chapter-header\">\n\n <h2 class=\"chapter-title\">\n Contents\n </h2>\n\n <button class=\"close-btn\" aria-label=\"Close\" (click)=\"onClose()\">\n <svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </button>\n\n </div>\n\n\n\n <!-- Chapter List -->\n <div class=\"flex-1 overflow-y-auto custom-scrollbar\">\n <!-- No chapters -->\n <div *ngIf=\"!chapterData || chapterData.length === 0\" class=\"text-sm text-gray-500 p-8 text-center italic\">\n No chapters available in this book.\n </div>\n\n <ng-container *ngFor=\"let chapter of chapterData\">\n\n <!-- Main Chapter -->\n <div class=\"chapter-row main-chapter\" (click)=\"onChapterClick(chapter.href)\">\n <div class=\"title-container\">\n <span class=\"title\">{{ chapter.title }}</span>\n <span *ngIf=\"chapter.page\" class=\"dots\"></span>\n </div>\n <span *ngIf=\"chapter.page\" class=\"page\">\n {{ chapter.page }}\n </span>\n </div>\n\n <!-- Subchapters -->\n <ng-container *ngIf=\"chapter.subchapters?.length\">\n <div *ngFor=\"let subchapter of chapter.subchapters\" class=\"chapter-row sub-chapter\"\n (click)=\"onChapterClick(subchapter.href)\">\n <div class=\"title-container\">\n <span class=\"title\">{{ subchapter.title }}</span>\n <span *ngIf=\"subchapter.page\" class=\"dots\"></span>\n </div>\n <span *ngIf=\"subchapter.page\" class=\"page\">\n {{ subchapter.page }}\n </span>\n </div>\n </ng-container>\n </ng-container>\n </div>\n</div>", styles: [".chapter-list-container{display:flex;flex-direction:column;height:600px;max-height:80vh;background-color:var(--bg-primary);overflow:hidden;border-radius:12px;transition:background-color .3s ease;border:1px solid transparent}:host-context(.dark-mode) .chapter-list-container{border:1px solid var(--border-base)}.custom-scrollbar{flex:1;overflow-y:auto;scroll-behavior:smooth}.custom-scrollbar::-webkit-scrollbar{width:8px}.custom-scrollbar::-webkit-scrollbar-thumb{background-color:#cbd5e1;border-radius:4px}.custom-scrollbar::-webkit-scrollbar-track{background:var(--bg-secondary)}.chapter-row{display:flex;align-items:baseline;padding:14px 24px;cursor:pointer;transition:background-color .2s ease}.chapter-row:hover{background-color:var(--bg-secondary)}.chapter-row:hover .dots{border-bottom-color:var(--text-secondary)}.chapter-row:hover .title{color:var(--text-primary)}.chapter-row .title-container{display:flex;align-items:baseline;flex-grow:1;overflow:hidden}.chapter-row .title{flex-shrink:0;max-width:90%;line-height:1.5;color:var(--text-primary)}.chapter-row .dots{flex-grow:1;border-bottom:1px dotted var(--border-base);margin:0 10px;height:1px;opacity:.8;position:relative;top:-4px}.chapter-row .page{flex-shrink:0;font-size:.95rem;color:var(--text-secondary);font-variant-numeric:tabular-nums;font-weight:500;min-width:32px;text-align:right}.chapter-row.main-chapter{font-size:18px;font-weight:700;color:var(--text-primary);padding-top:20px;padding-bottom:12px;border-bottom:1px solid var(--border-base)}.chapter-row.main-chapter .title{letter-spacing:-.01em}.chapter-row.main-chapter .page{font-size:1rem;color:var(--text-primary)}.chapter-row.sub-chapter{font-size:15px;font-weight:400;color:var(--text-secondary);padding-left:52px;padding-top:10px;padding-bottom:10px}.chapter-row.sub-chapter:hover .title{color:var(--text-primary)}.chapter-row.sub-chapter .dots{border-bottom-color:var(--border-base)}.header-title{font-family:inherit;font-weight:800;letter-spacing:-.02em;color:var(--text-primary);font-size:1.25rem}.chapter-header{padding:18px;background:var(--bg-secondary);display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border-base)}.chapter-title{font-size:1.25rem;font-weight:700;color:var(--text-primary);margin:0}.close-btn{display:flex;align-items:center;justify-content:center;padding:8px;border:none;background:transparent;color:#9ca3af;border-radius:9999px;cursor:pointer;transition:background-color .2s ease,color .2s ease}.close-btn:hover{background-color:var(--border-base);color:var(--text-primary)}.close-btn svg{pointer-events:none}\n"] }]
795
+ }], propDecorators: { chapterData: [{
796
+ type: Input
797
+ }], chapterClick: [{
798
+ type: Output
799
+ }], closeDialog: [{
800
+ type: Output
801
+ }] } });
802
+
803
+ class ReaderMainComponent {
804
+ ngZone;
805
+ httpService;
806
+ sharedService;
807
+ epubService;
808
+ cdr;
809
+ route;
810
+ dialog;
811
+ messageService;
812
+ location;
813
+ platformId;
814
+ router;
815
+ overlay;
816
+ epubUrl = null;
817
+ epubBlob = null;
818
+ coverPage = null;
819
+ // Feature flags
820
+ enableDarkMode = true;
821
+ enableSearch = true;
822
+ enableHighlights = true;
823
+ enableBookmarks = true;
824
+ enableChapterList = true;
825
+ enableTextSelection = true;
826
+ enableFontSize = true;
827
+ enableNotes = true;
828
+ underlineRemoveRequest = new EventEmitter();
829
+ book;
830
+ bookId = 0;
831
+ rendition;
832
+ readPercentage = {
833
+ roudedOff: 0,
834
+ numberFormat: 0,
835
+ };
836
+ bookDwnldPerctge = 0;
837
+ showDwndLoader = true;
838
+ mainOvelay = false;
839
+ showHighlightOverlay = false;
840
+ showhightLightColor = false;
841
+ audioNarrations = false;
842
+ toggleDarkMode = false;
843
+ curentPageEndCfi;
844
+ chaptersData;
845
+ selectedData = {
846
+ selectedCfiRange: '',
847
+ selectedText: '',
848
+ };
849
+ fontSize = 100; //default font size is 100
850
+ highlightList = [];
851
+ underlineList = [];
852
+ searchData = [];
853
+ searchKeyword = '';
854
+ newSentence = '';
855
+ colourCodeList = ['#F44336', '#FFEB3B', '#4CAF50']; //When adding or removing new colour to the colour list it should be updated here
856
+ readCfi = '';
857
+ annotedList;
858
+ readPercentagegetbyApi;
859
+ freeSample = 'false';
860
+ showOverlay = false;
861
+ showBookPageLoader = true;
862
+ showPreviewModal = false;
863
+ hasShownPreviewMessage = false;
864
+ maxPreviewClicks = 5;
865
+ coverImg = null;
866
+ isMenuOpen = false;
867
+ selectionPosition = { top: 0, left: 0 };
868
+ onDestroy$ = new Subject();
869
+ readProgressSub;
870
+ overlayRef;
871
+ handleKeyUp(event) {
872
+ if (event.key === 'PrintScreen') {
873
+ navigator.clipboard.writeText('');
874
+ this.showOverlay = true;
875
+ setTimeout(() => {
876
+ this.showOverlay = false;
877
+ }, 1500);
878
+ }
879
+ }
880
+ onKeyDown(event) {
881
+ const blockedKeys = ['c', 'v', 'p', 's', 'u', 'i', 'j'];
882
+ if ((event.ctrlKey || event.metaKey) && blockedKeys.includes(event.key.toLowerCase())) {
883
+ event.preventDefault();
884
+ event.stopPropagation();
885
+ }
886
+ if (event.key === 'F12') {
887
+ event.preventDefault();
888
+ event.stopPropagation();
889
+ }
890
+ }
891
+ constructor(ngZone, httpService, sharedService, epubService, cdr, route, dialog, messageService, location, platformId, router, overlay) {
892
+ this.ngZone = ngZone;
893
+ this.httpService = httpService;
894
+ this.sharedService = sharedService;
895
+ this.epubService = epubService;
896
+ this.cdr = cdr;
897
+ this.route = route;
898
+ this.dialog = dialog;
899
+ this.messageService = messageService;
900
+ this.location = location;
901
+ this.platformId = platformId;
902
+ this.router = router;
903
+ this.overlay = overlay;
904
+ effect(() => {
905
+ this.searchKeyword = this.epubService.searchKey();
906
+ if (this.searchKeyword && this.searchKeyword.length >= 3) {
907
+ this.searchBook();
908
+ }
909
+ });
910
+ const queryParams = this.route.snapshot.queryParams;
911
+ this.bookId = queryParams['bookID'];
912
+ this.freeSample = queryParams['freeSample'] || 'false';
913
+ }
914
+ ngOnInit() {
915
+ this.coverImg = this.coverPage || this.route.snapshot.queryParams['coverImg'];
916
+ // Load DotLottie dynamically if needed
917
+ this.loadDotLottieIfNeeded();
918
+ if (this.bookId) {
919
+ this.initialization();
920
+ }
921
+ else if (this.epubUrl || this.epubBlob) {
922
+ this.getBookData();
923
+ }
924
+ window.print = () => { };
925
+ }
926
+ ngOnChanges(changes) {
927
+ if (changes['coverPage'] && changes['coverPage'].currentValue) {
928
+ this.coverImg = changes['coverPage'].currentValue;
929
+ }
930
+ if ((changes['epubUrl'] && changes['epubUrl'].currentValue) ||
931
+ (changes['epubBlob'] && changes['epubBlob'].currentValue)) {
932
+ this.getBookData();
933
+ }
934
+ }
935
+ loadDotLottieIfNeeded() {
936
+ if (typeof window.loadDotLottie === 'function') {
937
+ window.loadDotLottie();
938
+ }
939
+ }
940
+ /*--------------------------------------------------------- Initialization -----------------------------------------------------------------------------------------*/
941
+ /*
942
+ *@Desc : To get the initial data to load the book, highlights, underline
943
+ *@Author: Jaisankar
944
+ */
945
+ initialization() {
946
+ const allApis = [
947
+ this.httpService.getAnnotations({
948
+ keyword: '',
949
+ pageSize: 10,
950
+ currentPage: 1,
951
+ type: 3, // type 3 is underline
952
+ bookId: this.bookId,
953
+ }),
954
+ this.httpService.getReadProgress({
955
+ bookId: this.bookId,
956
+ }),
957
+ this.httpService.getAnnotations({
958
+ keyword: '',
959
+ pageSize: 10,
960
+ currentPage: 1,
961
+ type: 2, // type 2 is highlight
962
+ bookId: this.bookId,
963
+ }),
964
+ ];
965
+ forkJoin(allApis)
966
+ .pipe(takeUntil(this.onDestroy$))
967
+ .subscribe({
968
+ next: (value) => {
969
+ this.annotedList = value;
970
+ this.readCfi = this.annotedList[1].cfiLocation;
971
+ this.readPercentagegetbyApi = this.annotedList[1].readProgress;
972
+ this.highlightList = this.annotedList[2].highlightedNotes.items;
973
+ this.underlineList = this.annotedList[0].underlinedNotes.items;
974
+ // this.bookdetails();
975
+ this.getBookData();
976
+ },
977
+ error: () => {
978
+ // If annotations fail, we might still want to load the book if inputs are present
979
+ this.sharedService.showHide(false);
980
+ this.getBookData();
981
+ },
982
+ complete: () => { },
983
+ });
984
+ }
985
+ goBack() {
986
+ this.location.back();
987
+ }
988
+ audioNarration() {
989
+ console.log('Audio Narration clicked');
990
+ }
991
+ /*
992
+ *@Desc : To get the book data from api, initialize all event for the rendition and display the epub
993
+ *@Author: Jaisankar
994
+ */
995
+ getBookData() {
996
+ this.ngZone.run(() => {
997
+ this.sharedService.showHide(false);
998
+ });
999
+ if (this.epubBlob) {
1000
+ this.loadBookFromBlob(this.epubBlob);
1001
+ }
1002
+ else if (this.epubUrl) {
1003
+ if (typeof this.epubUrl === 'string' && this.epubUrl.trim() !== '') {
1004
+ this.loadBookFromUrl(this.epubUrl);
1005
+ }
1006
+ else {
1007
+ this.handleBookLoadError('Invalid EPUB URL.');
1008
+ }
1009
+ }
1010
+ else {
1011
+ console.error('No EPUB source provided (epubUrl or epubBlob).');
1012
+ // Do not show error to user immediately if waiting for input, unless we want to enforce it.
1013
+ // But if this is called, it usually expects data.
1014
+ this.handleBookLoadError('No book source found.');
1015
+ }
1016
+ }
1017
+ loadBookFromUrl(url) {
1018
+ try {
1019
+ this.book = ePub(url);
1020
+ this.renderBook();
1021
+ }
1022
+ catch (err) {
1023
+ console.error('Error initializing EPUB from URL:', err);
1024
+ this.handleBookLoadError('Failed to load book from URL.');
1025
+ }
1026
+ }
1027
+ loadBookFromBlob(blob) {
1028
+ if (blob.size === 0) {
1029
+ this.handleBookLoadError('The provided EPUB file is empty.');
1030
+ return;
1031
+ }
1032
+ const reader = new FileReader();
1033
+ reader.readAsArrayBuffer(blob);
1034
+ reader.onload = async () => {
1035
+ try {
1036
+ const arrayBuffer = reader.result;
1037
+ if (!arrayBuffer || arrayBuffer.byteLength === 0) {
1038
+ throw new Error('Empty array buffer');
1039
+ }
1040
+ this.book = ePub(arrayBuffer);
1041
+ this.renderBook();
1042
+ }
1043
+ catch (error) {
1044
+ console.error('Error reading blob:', error);
1045
+ this.handleBookLoadError('Failed to load the EPUB file.');
1046
+ }
1047
+ };
1048
+ reader.onerror = () => {
1049
+ this.handleBookLoadError('Error reading the file.');
1050
+ };
1051
+ }
1052
+ handleBookLoadError(message) {
1053
+ this.showBookPageLoader = false;
1054
+ this.showDwndLoader = false;
1055
+ this.messageService.add({
1056
+ key: 'notifyToast',
1057
+ severity: 'error',
1058
+ summary: 'Error',
1059
+ detail: message,
1060
+ });
1061
+ this.ngZone.run(() => {
1062
+ this.sharedService.showHide(false);
1063
+ });
1064
+ }
1065
+ renderBook() {
1066
+ const area = document.getElementById('reader');
1067
+ if (area) {
1068
+ area.innerHTML = ''; // Clear previous content
1069
+ }
1070
+ this.rendition = this.book.renderTo('reader', {
1071
+ //To render the Epub and pass the config to the library
1072
+ width: '100%', // or "100%", depending on your layout
1073
+ height: '100%', // optional, adjust based on your container
1074
+ allowScriptedContent: true, // Important
1075
+ spread: 'auto'
1076
+ });
1077
+ this.book.ready.then(() => {
1078
+ if (this.book.navigation.toc.length) {
1079
+ // Map through chapters and extract subchapter information
1080
+ const chapters = this.book.navigation.toc.map((spineItem) => ({
1081
+ title: spineItem.label || `Chapter ${spineItem.index + 1}`,
1082
+ href: this.book?.spine && spineItem?.href
1083
+ ? this.book.spine.get(spineItem.href)?.href
1084
+ : '',
1085
+ subchapters: (spineItem.subitems || []).map((subItem) => {
1086
+ const spineEntry = this.book.spine.get(subItem.href);
1087
+ return {
1088
+ title: subItem.label || `Subchapter`,
1089
+ href: spineEntry?.href || '', // fallback to empty string or some default
1090
+ };
1091
+ }),
1092
+ }));
1093
+ this.chaptersData = chapters;
1094
+ }
1095
+ this.book.locations.generate(1000).then(() => {
1096
+ // Calculate page numbers for chapters
1097
+ if (this.chaptersData) {
1098
+ const updateChapterPage = (chapter) => {
1099
+ const spineItem = this.book.spine.get(chapter.href);
1100
+ if (spineItem) {
1101
+ const cfi = spineItem.cfiFromElement(undefined);
1102
+ chapter.page = this.book.locations.locationFromCfi(cfi);
1103
+ }
1104
+ if (chapter.subchapters) {
1105
+ chapter.subchapters.forEach((sub) => {
1106
+ const subSpine = this.book.spine.get(sub.href);
1107
+ if (subSpine) {
1108
+ const subCfi = subSpine.cfiFromElement(undefined);
1109
+ sub.page = this.book.locations.locationFromCfi(subCfi);
1110
+ }
1111
+ });
1112
+ }
1113
+ };
1114
+ this.chaptersData.forEach(updateChapterPage);
1115
+ }
1116
+ this.rendition.on('locationChanged', (location) => {
1117
+ if (this.freeSample == 'false') {
1118
+ this.applyHighlights();
1119
+ this.applyUnderline();
1120
+ }
1121
+ this.rendition.themes.default({
1122
+ '::selection': {
1123
+ background: '#cceeff', // Soft green
1124
+ color: '#1b5e20', // Dark text
1125
+ },
1126
+ '*': {
1127
+ '-webkit-tap-highlight-color': 'transparent', // removes green tap
1128
+ '-webkit-user-select': 'text',
1129
+ 'user-select': 'text',
1130
+ '-webkit-touch-callout': 'none', // removes iOS popup menu
1131
+ },
1132
+ });
1133
+ this.curentPageEndCfi = location.start;
1134
+ //To get the location changed event for calculating read percentage and saving current read percentage
1135
+ const percentage = this.book.locations.percentageFromCfi(location.end);
1136
+ this.readPercentage.roudedOff =
1137
+ Math.floor(percentage * 100) || 0;
1138
+ this.readPercentage.numberFormat = percentage;
1139
+ });
1140
+ this.showDwndLoader = false;
1141
+ if (this.showBookPageLoader === true ||
1142
+ this.showBookPageLoader === undefined) {
1143
+ this.showBookPageLoader = true;
1144
+ }
1145
+ if (this.readCfi && this.readCfi?.length) {
1146
+ setTimeout(() => {
1147
+ this.showBookPageLoader = false;
1148
+ this.rendition.display(this.readCfi);
1149
+ }, 1500);
1150
+ this.ngZone.run(() => {
1151
+ this.sharedService.showHide(false);
1152
+ });
1153
+ }
1154
+ else {
1155
+ setTimeout(() => {
1156
+ this.showBookPageLoader = false;
1157
+ this.rendition.display();
1158
+ }, 1500);
1159
+ this.ngZone.run(() => {
1160
+ this.sharedService.showHide(false);
1161
+ });
1162
+ }
1163
+ });
1164
+ });
1165
+ //Event when new section is rendered
1166
+ this.rendition.on('rendered', (section) => {
1167
+ this.darkMode();
1168
+ });
1169
+ this.rendition.on('rendered', () => {
1170
+ this.rendition.views().forEach((view) => {
1171
+ let iframe = view.iframe;
1172
+ if (iframe) {
1173
+ const doc = iframe.contentDocument;
1174
+ // 🔒 Disable copy, drag, and right-click inside EPUB iframe
1175
+ ['copy', 'contextmenu', 'dragstart'].forEach((event) => doc.addEventListener(event, (e) => e.preventDefault()));
1176
+ // 🔒 Block all Ctrl/Cmd-based keyboard shortcuts (e.g., Ctrl+P, Ctrl+C, Cmd+S)
1177
+ doc.addEventListener('keydown', (e) => {
1178
+ if (e.ctrlKey || e.metaKey) {
1179
+ e.preventDefault();
1180
+ e.stopPropagation();
1181
+ }
1182
+ });
1183
+ // 🎨 Inject CSS to disable dragging and prevent printing from Chrome menu
1184
+ const style = doc.createElement('style');
1185
+ style.innerHTML = `
1186
+ /* Disable element dragging */
1187
+ * {
1188
+ -webkit-user-drag: none;
1189
+ user-drag: none;
1190
+ }
1191
+
1192
+ /* Block printing via @media print */
1193
+ @media print {
1194
+ body, html {
1195
+ display: none !important;
1196
+ background: white !important;
1197
+ }
1198
+
1199
+ body::before {
1200
+ content: "🚫 Printing is disabled.";
1201
+ display: block;
1202
+ font-size: 24px;
1203
+ text-align: center;
1204
+ color: red;
1205
+ margin-top: 100px;
1206
+ }
1207
+ }
1208
+ `;
1209
+ doc.head.appendChild(style);
1210
+ }
1211
+ });
1212
+ const iframe = document.querySelector('iframe');
1213
+ if (iframe) {
1214
+ const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
1215
+ if (iframeDoc) {
1216
+ if (this.freeSample === 'true') {
1217
+ // Disable all <a> tags inside iframe
1218
+ const links = iframeDoc.querySelectorAll('a');
1219
+ links.forEach((link) => {
1220
+ link.style.pointerEvents = 'none'; // Disable clicking
1221
+ link.style.color = 'gray'; // Optional: gray out links
1222
+ // Also prevent default on click as a fallback
1223
+ link.addEventListener('click', (e) => e.preventDefault());
1224
+ });
1225
+ }
1226
+ let scrollTimeout = null;
1227
+ iframeDoc.addEventListener('wheel', (event) => {
1228
+ event.preventDefault(); // Prevent default scroll
1229
+ if (scrollTimeout) {
1230
+ clearTimeout(scrollTimeout);
1231
+ }
1232
+ scrollTimeout = setTimeout(() => {
1233
+ if (event.deltaY > 0) {
1234
+ this.nextClick();
1235
+ }
1236
+ else {
1237
+ this.prevClick();
1238
+ }
1239
+ }, 150);
1240
+ }, { passive: false }); // Important!
1241
+ }
1242
+ }
1243
+ });
1244
+ //Event when selection is made the function will be called and the cfi and selected content will be saved
1245
+ this.rendition.on('selected', (cfiRange, contents) => {
1246
+ if (!this.enableTextSelection) {
1247
+ contents.window.getSelection().removeAllRanges();
1248
+ return;
1249
+ }
1250
+ const selection = contents.window.getSelection();
1251
+ const range = selection.getRangeAt(0);
1252
+ const rect = range.getBoundingClientRect();
1253
+ // Get iframe position
1254
+ const frameRect = contents.document.defaultView.frameElement.getBoundingClientRect();
1255
+ this.ngZone.run(() => {
1256
+ this.selectionPosition = {
1257
+ top: frameRect.top + rect.top - 60, // Position above selection
1258
+ left: frameRect.left + rect.left + (rect.width / 2)
1259
+ };
1260
+ this.handleSelection(cfiRange, selection.toString().trim());
1261
+ });
1262
+ });
1263
+ }
1264
+ onProgressChange(event) {
1265
+ if (this.freeSample === 'true' && this.hasShownPreviewMessage) {
1266
+ return;
1267
+ }
1268
+ const input = event.target;
1269
+ const percent = +input.value;
1270
+ if (this.freeSample === 'true') {
1271
+ if (percent > this.maxPreviewClicks) {
1272
+ this.hasShownPreviewMessage = true;
1273
+ this.showPreviewModal = true;
1274
+ return;
1275
+ }
1276
+ }
1277
+ // Update progress bar in real-time
1278
+ this.readPercentage.roudedOff = percent;
1279
+ // Also update EPUB location in real-time
1280
+ if (this.book && this.book.locations) {
1281
+ const cfi = this.book.locations.cfiFromPercentage(percent / 100);
1282
+ this.rendition.display(cfi); // this gets called during drag
1283
+ }
1284
+ }
1285
+ /*--------------------------------------------- End of Initialization ---------------------------------------------------------*/
1286
+ mainOverlay() {
1287
+ this.mainOvelay = !this.mainOvelay;
1288
+ }
1289
+ showHighlightOverlaymethod() {
1290
+ this.showHighlightOverlay = !this.showHighlightOverlay;
1291
+ }
1292
+ showhightLightColormethod() {
1293
+ this.showHighlightOverlay = !this.showHighlightOverlay;
1294
+ }
1295
+ handleCloseButtonClick() {
1296
+ if (this.mainOvelay) {
1297
+ this.mainOverlay();
1298
+ }
1299
+ else if (this.showHighlightOverlay) {
1300
+ this.showHighlightOverlaymethod();
1301
+ }
1302
+ else if (this.showhightLightColor) {
1303
+ this.showhightLightColormethod();
1304
+ }
1305
+ }
1306
+ /*
1307
+ *@Desc : To paginate to next page and apply hightlight and underline again
1308
+ *@Author: Jaisankar
1309
+ */
1310
+ nextClick() {
1311
+ if (this.freeSample === 'true' && this.hasShownPreviewMessage) {
1312
+ return;
1313
+ }
1314
+ this.rendition
1315
+ ?.next()
1316
+ .then(() => {
1317
+ this.applyHighlights();
1318
+ this.applyUnderline();
1319
+ })
1320
+ .catch((err) => console.error(err));
1321
+ if (this.freeSample === 'true') {
1322
+ if (this.readPercentage.roudedOff > this.maxPreviewClicks) {
1323
+ this.hasShownPreviewMessage = true;
1324
+ this.showPreviewModal = true;
1325
+ return;
1326
+ }
1327
+ }
1328
+ }
1329
+ /*
1330
+ *@Desc : To paginate to previous page and apply hightlight and underline again
1331
+ *@Author: Jaisankar
1332
+ */
1333
+ prevClick() {
1334
+ this.rendition
1335
+ .prev()
1336
+ .then(() => {
1337
+ this.applyHighlights();
1338
+ this.applyUnderline();
1339
+ })
1340
+ .catch((err) => console.error(err));
1341
+ }
1342
+ /*
1343
+ *@Desc : To toggle between dark and light modes, native toggle feature of epub.js has bugs so custom method implemented
1344
+ *@Author: Jaisankar
1345
+ */
1346
+ darkMode() {
1347
+ const iframe = document.querySelector('iframe');
1348
+ const body = iframe?.contentDocument?.body;
1349
+ // Reset styles first
1350
+ body?.style?.removeProperty('background-color');
1351
+ body?.style?.removeProperty('color');
1352
+ if (this.toggleDarkMode) {
1353
+ document.body.classList.add('dark-mode');
1354
+ // Content colors for dark mode
1355
+ const darkBg = '#121212';
1356
+ const darkText = '#e0e0e0';
1357
+ const accentLink = '#32d74b';
1358
+ if (body) {
1359
+ body.style.setProperty('background-color', darkBg, 'important');
1360
+ body.style.setProperty('color', darkText, 'important');
1361
+ const allElements = body.querySelectorAll('div, section, article, p, li, h1, h2, h3, h4, h5, h6, span');
1362
+ allElements.forEach((el) => {
1363
+ el.style.setProperty('color', darkText, 'important');
1364
+ el.style.setProperty('background-color', 'transparent', 'important');
1365
+ });
1366
+ const links = body.querySelectorAll('a');
1367
+ links.forEach((a) => {
1368
+ a.style.setProperty('color', accentLink, 'important');
1369
+ });
1370
+ }
1371
+ }
1372
+ else {
1373
+ document.body.classList.remove('dark-mode');
1374
+ // Reset to light mode defaults
1375
+ if (body) {
1376
+ body.style.setProperty('background-color', '#ffffff', 'important');
1377
+ body.style.setProperty('color', '#1a1a1a', 'important');
1378
+ const allElements = body.querySelectorAll('div, section, article, p, li, h1, h2, h3, h4, h5, h6, span');
1379
+ allElements.forEach((el) => {
1380
+ el.style.removeProperty('color');
1381
+ el.style.removeProperty('background-color');
1382
+ });
1383
+ }
1384
+ }
1385
+ if (this.freeSample == 'false') {
1386
+ if (this.highlightList.length) {
1387
+ this.applyHighlights();
1388
+ }
1389
+ if (this.underlineList.length) {
1390
+ this.applyUnderline();
1391
+ }
1392
+ }
1393
+ }
1394
+ /*
1395
+ *@Desc : To toggle fontsizes, and reapply highlights
1396
+ *@Author: Jaisankar
1397
+ */
1398
+ changeFontSize(check) {
1399
+ if (check === '+') {
1400
+ if (this.fontSize < 200) {
1401
+ // Check if we can increase the font size
1402
+ this.fontSize = Math.min(this.fontSize + 10, 200); // Limit max size (200%)
1403
+ }
1404
+ this.rendition.themes.fontSize(this.fontSize.toString() + '%');
1405
+ if (this.highlightList.length) {
1406
+ this.applyHighlights();
1407
+ }
1408
+ if (this.underlineList.length) {
1409
+ this.applyUnderline();
1410
+ }
1411
+ }
1412
+ else {
1413
+ if (this.fontSize > 50) {
1414
+ // Check if we can decrease the font size
1415
+ this.fontSize = Math.max(this.fontSize - 10, 50); // Limit min size (50%)
1416
+ }
1417
+ this.rendition.themes.fontSize(this.fontSize.toString() + '%');
1418
+ if (this.highlightList.length) {
1419
+ this.applyHighlights();
1420
+ }
1421
+ if (this.underlineList.length) {
1422
+ this.applyUnderline();
1423
+ }
1424
+ }
1425
+ }
1426
+ /*
1427
+ *@Desc : To save the cfi of the selected text for highlight and underline
1428
+ *@Author: Jaisankar
1429
+ */
1430
+ handleSelection(cfiRange, selectedText) {
1431
+ this.selectedData.selectedCfiRange = cfiRange;
1432
+ this.selectedData.selectedText = selectedText;
1433
+ this.showHighlightOverlay = true;
1434
+ this.isMenuOpen = false; // Hide main menu if selection happens
1435
+ if (this.mainOvelay) {
1436
+ this.mainOvelay = !this.mainOvelay;
1437
+ }
1438
+ this.cdr.detectChanges();
1439
+ }
1440
+ /*
1441
+ *@Desc : To show chapter popup using custom implementation
1442
+ *@Author: Jaisankar
1443
+ */
1444
+ showChapterList = false;
1445
+ showChapterPopup() {
1446
+ this.showChapterList = true;
1447
+ }
1448
+ onChapterSelect(href) {
1449
+ this.showChapterList = false;
1450
+ if (href) {
1451
+ this.rendition.display(href);
1452
+ }
1453
+ }
1454
+ /*
1455
+ *@Desc : To open add notes popup
1456
+ *@Author: Jaisankar
1457
+ */
1458
+ addNotes() {
1459
+ this.epubService.setNote(this.selectedData.selectedText);
1460
+ const notedetails = {
1461
+ bookId: this.bookId,
1462
+ cfiLocation: this.selectedData.selectedCfiRange,
1463
+ };
1464
+ const dialog = this.dialog.open(AddNotesComponent, {
1465
+ closeButton: true,
1466
+ windowClass: 'addandeditNotepopup',
1467
+ data: {
1468
+ notedetails: notedetails,
1469
+ addNote: true,
1470
+ notetoAddorEdit: this.selectedData.selectedText,
1471
+ },
1472
+ });
1473
+ }
1474
+ /*
1475
+ *@Desc : To first get all items in spine that is sections of epub (not chapters)
1476
+ then load that section and find the matching keywords,
1477
+ then create a span with highlight class replace this span to the sentence where the keyword is found
1478
+ return the new sentence, cfi and old sentance as an object
1479
+ *@Author: Jaisankar
1480
+ */
1481
+ searchBook() {
1482
+ from(this.book.spine.spineItems)
1483
+ .pipe(mergeMap((item) => from(item.load(this.book.load.bind(this.book))).pipe(mergeMap(() => from(item.find(this.searchKeyword))), // Find after loading text
1484
+ map((result) => {
1485
+ // Create a case-insensitive regular expression for the keyword
1486
+ const regex = new RegExp(this.searchKeyword, 'gi');
1487
+ // Replace occurrences of the keyword with the highlighted version
1488
+ this.newSentence = result.excerpt.replace(regex, (match) => {
1489
+ // Wrap each match dynamically, preserving the original case
1490
+ return `<span class="highlight-key">${match}</span>`;
1491
+ });
1492
+ // Unload after processing
1493
+ item.unload();
1494
+ // Create the result object
1495
+ const newReturnObj = {
1496
+ cfi: result.cfi,
1497
+ highlightedSentence: this.newSentence,
1498
+ originalSentence: result.excerpt,
1499
+ };
1500
+ return newReturnObj; // Return the processed result
1501
+ }))), toArray() // Collect all results into a single array
1502
+ )
1503
+ .subscribe({
1504
+ next: (results) => {
1505
+ this.epubService.updateSearchData(results.flat()); //flat to flatten the array ex [a, [b,c] ,d ] output is [a,b,c,d]
1506
+ this.sharedService.showHide(false); //stop loader after the search result is saved
1507
+ },
1508
+ error: (err) => {
1509
+ console.error('Error:', err);
1510
+ },
1511
+ });
1512
+ }
1513
+ /*--------------------------------------------------------- Annotations Start -----------------------------------------------------------------------------------------*/
1514
+ /*--------------------------------------------------------- Underline Section -----------------------------------------------------------------------------------------*/
1515
+ /*
1516
+ *@Desc : Add underline to underline and apply underline
1517
+ *@Author: Jaisankar
1518
+ */
1519
+ addUnderline() {
1520
+ this.underlineList.push({
1521
+ typeId: 3,
1522
+ id: 0,
1523
+ userId: 0,
1524
+ libraryId: 0,
1525
+ bookId: 0,
1526
+ studentId: 0,
1527
+ cfiLocation: this.selectedData.selectedCfiRange,
1528
+ annotatedNotes: this.selectedData.selectedText,
1529
+ customNotes: null,
1530
+ colorCode: '',
1531
+ notesCapturedDate: '',
1532
+ createdBy: 0,
1533
+ });
1534
+ const payload = {
1535
+ typeId: 3, // type 4 is bookmark
1536
+ id: 0, // 0 for new highlight
1537
+ bookId: this.bookId,
1538
+ cfiLocation: this.selectedData.selectedCfiRange,
1539
+ annotatedNotes: this.selectedData.selectedText,
1540
+ customNotes: '',
1541
+ colorCode: '',
1542
+ };
1543
+ if (this.underlineList.length) {
1544
+ this.applyUnderline();
1545
+ }
1546
+ if (this.bookId) {
1547
+ this.httpService
1548
+ .saveAnnotations(payload)
1549
+ .pipe(takeUntil(this.onDestroy$))
1550
+ .subscribe({
1551
+ next: (res) => { },
1552
+ error: () => {
1553
+ this.sharedService.showHide(false);
1554
+ },
1555
+ complete: () => {
1556
+ this.getunderlineList();
1557
+ this.sharedService.showHide(false);
1558
+ },
1559
+ });
1560
+ }
1561
+ }
1562
+ /*
1563
+ *@Desc : To remove all underlines first and add underline class and then apply style to the class
1564
+ *@Author: Jaisankar
1565
+ */
1566
+ async applyUnderline() {
1567
+ for (const underline of this.underlineList) {
1568
+ await this.removeUnderline(underline.cfiLocation);
1569
+ }
1570
+ for (const underline of this.underlineList) {
1571
+ await this.addUnderlineClass(underline.cfiLocation);
1572
+ }
1573
+ await this.addStyleToUnderline();
1574
+ }
1575
+ /*
1576
+ *@Desc : To add the highlight class
1577
+ *@Author: Jaisankar
1578
+ */
1579
+ addUnderlineClass(cfi = null) {
1580
+ if (cfi?.length) {
1581
+ this.rendition?.annotations?.underline(cfi || this.selectedData.selectedCfiRange, { class: 'custom-underline' }, (e) => {
1582
+ const target = e.target;
1583
+ const cfiLocation = target.getAttribute('data-epubcfi') || target.closest('[data-epubcfi]')?.getAttribute('data-epubcfi');
1584
+ if (cfiLocation) {
1585
+ const dialog = this.dialog.open(ConfirmPopupComponent, {
1586
+ closeButton: false,
1587
+ data: {
1588
+ heading: 'Remove Underline?',
1589
+ message: 'Are you sure you want to remove this underline?',
1590
+ btnOneTxt: 'Cancel',
1591
+ btnTwoTxt: 'Remove',
1592
+ },
1593
+ });
1594
+ dialog.afterClosed$.subscribe((res) => {
1595
+ if (res) {
1596
+ if (res === true) {
1597
+ const annotation = this.underlineList.find((annotation) => annotation.cfiLocation === cfiLocation);
1598
+ if (annotation) {
1599
+ this.removeUnderline(cfiLocation);
1600
+ this.deleteAnnotations(3, annotation.id);
1601
+ }
1602
+ }
1603
+ }
1604
+ });
1605
+ }
1606
+ else {
1607
+ console.log('CFI Location not found', target); // Log target to help debug if needed
1608
+ }
1609
+ }, 'underline');
1610
+ }
1611
+ }
1612
+ /*
1613
+ *@Desc : To remove the highlight class
1614
+ *@Author: Jaisankar
1615
+ */
1616
+ removeUnderline(cfi) {
1617
+ try {
1618
+ this.rendition?.annotations?.remove(cfi, 'underline');
1619
+ }
1620
+ catch (error) {
1621
+ console.error('Error removing underline:', error);
1622
+ }
1623
+ }
1624
+ /*
1625
+ *@Desc : To add colour to the highlights
1626
+ *@Author: Jaisankar
1627
+ */
1628
+ addStyleToUnderline() {
1629
+ // Collect all documents (main + iframes)
1630
+ const docs = [document];
1631
+ this.rendition?.views().forEach((view) => {
1632
+ if (view.iframe?.contentDocument) {
1633
+ docs.push(view.iframe.contentDocument);
1634
+ }
1635
+ });
1636
+ const isDark = this.toggleDarkMode;
1637
+ const strokeColor = isDark ? '#FFFFFF' : '#000000';
1638
+ docs.forEach(doc => {
1639
+ // Target elements with standard and custom underline classes
1640
+ const underlineGroups = doc.querySelectorAll('.epubjs-underline, .underline, .custom-underline');
1641
+ underlineGroups.forEach((group) => {
1642
+ // SVG rects are often used as hit areas
1643
+ const rects = group.querySelectorAll('rect');
1644
+ rects.forEach((rect) => {
1645
+ rect.style.fill = 'transparent';
1646
+ rect.style.stroke = 'none';
1647
+ rect.style.pointerEvents = 'auto'; // Ensure it's clickable
1648
+ });
1649
+ // SVG lines are the actual underline
1650
+ const lines = group.querySelectorAll('line');
1651
+ lines.forEach((line) => {
1652
+ line.style.stroke = strokeColor;
1653
+ line.style.strokeWidth = '2';
1654
+ line.style.pointerEvents = 'auto';
1655
+ });
1656
+ });
1657
+ });
1658
+ }
1659
+ /*
1660
+ *@Desc : To get underline list
1661
+ *@Author: Jaisankar
1662
+ */
1663
+ getunderlineList() {
1664
+ const payload = {
1665
+ keyword: '',
1666
+ pageSize: 1000,
1667
+ currentPage: 1,
1668
+ id: '',
1669
+ type: 3, // type 3 is bookmark
1670
+ bookId: this.bookId,
1671
+ };
1672
+ this.httpService
1673
+ .getAnnotations(payload)
1674
+ .pipe(takeUntil(this.onDestroy$))
1675
+ .subscribe({
1676
+ next: (res) => {
1677
+ this.underlineList = res.underlinedNotes.items;
1678
+ if (this.underlineList.length) {
1679
+ this.applyUnderline();
1680
+ }
1681
+ },
1682
+ error: () => {
1683
+ this.sharedService.showHide(false);
1684
+ },
1685
+ complete: () => {
1686
+ this.sharedService.showHide(false);
1687
+ },
1688
+ });
1689
+ }
1690
+ /*--------------------------------------------------------- Highlight Section -----------------------------------------------------------------------------------------*/
1691
+ /*
1692
+ *@Desc : To add highlight and save to db, to add a new colour code it needs to be added to color code array
1693
+ "colorCode": "#F44336" - Red
1694
+ "colorCode": "#FFEB3B" - Yellow
1695
+ "colorCode": "#4CAF50" - Green
1696
+ *@Author: Jaisankar
1697
+ */
1698
+ addHighlight(colourCode) {
1699
+ //push the highlight to the array in FE
1700
+ this.highlightList.push({
1701
+ typeId: 2,
1702
+ id: 0,
1703
+ userId: 0,
1704
+ libraryId: 0,
1705
+ bookId: 0,
1706
+ studentId: 0,
1707
+ cfiLocation: this.selectedData.selectedCfiRange,
1708
+ annotatedNotes: this.selectedData.selectedText,
1709
+ customNotes: null,
1710
+ colorCode: colourCode,
1711
+ notesCapturedDate: '',
1712
+ createdBy: 0,
1713
+ });
1714
+ const payload = {
1715
+ typeId: 2, // type 4 is bookmark
1716
+ id: 0, // 0 for new highlight
1717
+ bookId: this.bookId,
1718
+ cfiLocation: this.selectedData.selectedCfiRange,
1719
+ annotatedNotes: this.selectedData.selectedText,
1720
+ customNotes: '',
1721
+ colorCode: colourCode,
1722
+ };
1723
+ if (this.highlightList.length) {
1724
+ this.applyHighlights();
1725
+ }
1726
+ if (this.bookId) {
1727
+ this.httpService
1728
+ .saveAnnotations(payload)
1729
+ .pipe(takeUntil(this.onDestroy$))
1730
+ .subscribe({
1731
+ next: (res) => { },
1732
+ error: () => {
1733
+ this.sharedService.showHide(false);
1734
+ },
1735
+ complete: () => {
1736
+ this.getHighlightlist();
1737
+ this.sharedService.showHide(false);
1738
+ },
1739
+ });
1740
+ }
1741
+ }
1742
+ /*
1743
+ *@Desc : To Remove all highlight first the add the class for highlight and then style based on class
1744
+ *@Author: Jaisankar
1745
+ */
1746
+ async applyHighlights() {
1747
+ for (const highlight of this.highlightList) {
1748
+ await this.removeHighlight(highlight.cfiLocation);
1749
+ }
1750
+ for (const highlight of this.highlightList) {
1751
+ await this.addHighlightClass(highlight.cfiLocation, highlight.colorCode);
1752
+ }
1753
+ await this.colourCodeList.forEach((colourCode) => {
1754
+ this.addColorToHighLights(colourCode);
1755
+ });
1756
+ }
1757
+ /*
1758
+ *@Desc : To Remove all highlights
1759
+ *@Author: Jaisankar
1760
+ */
1761
+ removeHighlight(cfi) {
1762
+ try {
1763
+ this.rendition?.annotations?.remove(cfi, 'highlight');
1764
+ }
1765
+ catch (error) {
1766
+ console.error('Error removing highlight:', error);
1767
+ }
1768
+ }
1769
+ /*
1770
+ *@Desc : To add the highlight class
1771
+ *@Author: Jaisankar
1772
+ */
1773
+ addHighlightClass(cfi = null, colourCode = null) {
1774
+ if (cfi?.length) {
1775
+ this.rendition?.annotations?.highlight(cfi || this.selectedData.selectedCfiRange, { class: `highlight-${colourCode}` }, (e) => {
1776
+ const cfiLocation = e.target?.getAttribute('data-epubcfi');
1777
+ if (cfiLocation) {
1778
+ const targetCfiLocation = e.target?.getAttribute('data-epubcfi');
1779
+ if (targetCfiLocation) {
1780
+ const dialog = this.dialog.open(ConfirmPopupComponent, {
1781
+ closeButton: false,
1782
+ data: {
1783
+ heading: 'Remove Highlight?',
1784
+ message: 'Are you sure you want to remove this highlight?',
1785
+ btnOneTxt: 'Cancel',
1786
+ btnTwoTxt: 'Remove',
1787
+ },
1788
+ });
1789
+ dialog.afterClosed$.subscribe((res) => {
1790
+ if (res) {
1791
+ if (res === true) {
1792
+ const annotation = this.highlightList.find((annotation) => annotation.cfiLocation === targetCfiLocation);
1793
+ if (annotation) {
1794
+ this.removeHighlight(targetCfiLocation);
1795
+ this.deleteAnnotations(2, annotation.id);
1796
+ }
1797
+ }
1798
+ }
1799
+ });
1800
+ }
1801
+ }
1802
+ else {
1803
+ console.log('CFI Location not found');
1804
+ }
1805
+ }, 'highlight');
1806
+ }
1807
+ }
1808
+ /*
1809
+ *@Desc : To add colour to the highlights
1810
+ *@Author: Jaisankar
1811
+ */
1812
+ addColorToHighLights(colourCode) {
1813
+ const docs = [document];
1814
+ this.rendition?.views().forEach((view) => {
1815
+ if (view.iframe?.contentDocument) {
1816
+ docs.push(view.iframe.contentDocument);
1817
+ }
1818
+ });
1819
+ docs.forEach(doc => {
1820
+ const rectElements = doc.querySelectorAll(`[data-class="highlight-${colourCode}"] rect`);
1821
+ rectElements.forEach((rect) => {
1822
+ rect.style.fill = colourCode;
1823
+ rect.style.opacity = '0.5';
1824
+ });
1825
+ });
1826
+ }
1827
+ /*
1828
+ *@Desc : To get highlight list
1829
+ *@Author: Jaisankar
1830
+ */
1831
+ getHighlightlist() {
1832
+ const payload = {
1833
+ keyword: '',
1834
+ pageSize: 100,
1835
+ currentPage: 1,
1836
+ id: '',
1837
+ type: 2, // type 2 is bookmark
1838
+ bookId: this.bookId,
1839
+ };
1840
+ this.httpService
1841
+ .getAnnotations(payload)
1842
+ .pipe(takeUntil(this.onDestroy$))
1843
+ .subscribe({
1844
+ next: (res) => {
1845
+ this.highlightList = res.highlightedNotes.items;
1846
+ if (this.bookId) {
1847
+ if (this.highlightList.length) {
1848
+ this.applyHighlights();
1849
+ }
1850
+ }
1851
+ },
1852
+ error: () => {
1853
+ this.sharedService.showHide(false);
1854
+ },
1855
+ complete: () => {
1856
+ this.sharedService.showHide(false);
1857
+ },
1858
+ });
1859
+ }
1860
+ /*--------------------------------------------------------- Bookmark Section -----------------------------------------------------------------------------------------*/
1861
+ /*
1862
+ *@Desc : To add bookmark and update in db
1863
+ *@Author: Jaisankar
1864
+ */
1865
+ addBookMark() {
1866
+ this.sharedService.showHide(true);
1867
+ let percentage = this.book.locations.percentageFromCfi(this.curentPageEndCfi);
1868
+ percentage = Math.floor(percentage * 100) || 0;
1869
+ this.book
1870
+ .getRange(this.curentPageEndCfi)
1871
+ .then((range) => {
1872
+ if (range) {
1873
+ const payload = {
1874
+ typeId: 4, // type 4 is bookmark
1875
+ id: 0, // 0 for new bookmark
1876
+ bookId: this.bookId,
1877
+ cfiLocation: this.curentPageEndCfi,
1878
+ annotatedNotes: range.startContainer?.textContent
1879
+ ?.trim()
1880
+ .substring(0, 100),
1881
+ customNotes: percentage,
1882
+ colorCode: this.getRandomHexColor,
1883
+ };
1884
+ this.httpService
1885
+ .saveAnnotations(payload)
1886
+ .pipe(takeUntil(this.onDestroy$))
1887
+ .subscribe({
1888
+ next: (res) => {
1889
+ this.messageService.add({
1890
+ key: 'notifyToast',
1891
+ severity: 'success', // Use 'warn' for orange color
1892
+ summary: 'Success',
1893
+ detail: res.message,
1894
+ });
1895
+ },
1896
+ error: () => {
1897
+ this.sharedService.showHide(false);
1898
+ },
1899
+ complete: () => {
1900
+ this.sharedService.showHide(false);
1901
+ },
1902
+ });
1903
+ }
1904
+ })
1905
+ .catch((error) => {
1906
+ console.error('Error');
1907
+ });
1908
+ }
1909
+ /*
1910
+ *@Desc : To get Bookmark list
1911
+ *@Author: Jaisankar
1912
+ */
1913
+ getBookMarkList() {
1914
+ const payload = {
1915
+ keyword: '',
1916
+ pageSize: 1000,
1917
+ currentPage: 1,
1918
+ id: '',
1919
+ type: 4, // type 4 is bookmark
1920
+ bookId: this.bookId,
1921
+ };
1922
+ const dialog = this.dialog.open(EpubCommonPopupComponent, {
1923
+ closeButton: true,
1924
+ windowClass: 'highlightandNotes',
1925
+ data: {
1926
+ template: 4,
1927
+ bookId: this.bookId,
1928
+ },
1929
+ });
1930
+ dialog.afterClosed$.subscribe((res) => {
1931
+ if (res?.length) {
1932
+ this.rendition.display(res);
1933
+ }
1934
+ });
1935
+ }
1936
+ /*--------------------------------------------------------- Annotations End -----------------------------------------------------------------------------------------*/
1937
+ /*
1938
+ *@Desc : To save read progress
1939
+ *@Author: Jaisankar
1940
+ */
1941
+ saveReadProgreess() {
1942
+ const payload = {
1943
+ bookId: +this.bookId,
1944
+ pageNumber: 0,
1945
+ cfiLocation: this.curentPageEndCfi,
1946
+ readProgress: this.readPercentage.numberFormat,
1947
+ };
1948
+ if (this.bookId) {
1949
+ this.readProgressSub = this.httpService
1950
+ .saveReadProgress(payload)
1951
+ .subscribe({
1952
+ next: () => { },
1953
+ error: () => {
1954
+ this.sharedService.showHide(false);
1955
+ },
1956
+ complete: () => {
1957
+ this.sharedService.showHide(false);
1958
+ },
1959
+ });
1960
+ }
1961
+ }
1962
+ /*
1963
+ *@Desc : To open All common popup based on template
1964
+ *@Author: Aboobacker
1965
+ */
1966
+ openCommonPopup(data) {
1967
+ if (data == 1) {
1968
+ const dialog = this.dialog.open(EpubCommonPopupComponent, {
1969
+ closeButton: true,
1970
+ windowClass: 'highlightandNotes',
1971
+ data: {
1972
+ template: 2,
1973
+ bookId: this.bookId,
1974
+ selectedWord: this.selectedData.selectedText,
1975
+ },
1976
+ });
1977
+ dialog.afterClosed$.subscribe((res) => {
1978
+ if (res) {
1979
+ this.rendition.display(res);
1980
+ }
1981
+ this.highlightList.forEach((highlight) => {
1982
+ this.removeHighlight(highlight.cfiLocation);
1983
+ });
1984
+ this.getHighlightlist();
1985
+ });
1986
+ }
1987
+ if (data == 2) {
1988
+ const dialog = this.dialog.open(EpubCommonPopupComponent, {
1989
+ closeButton: true,
1990
+ windowClass: 'highlightandNotes',
1991
+ data: {
1992
+ template: 3,
1993
+ bookId: this.bookId,
1994
+ },
1995
+ });
1996
+ dialog.afterClosed$.subscribe((res) => {
1997
+ if (res) {
1998
+ this.rendition.display(res);
1999
+ }
2000
+ });
2001
+ }
2002
+ if (data == 3) {
2003
+ const dialog = this.dialog.open(EpubCommonPopupComponent, {
2004
+ closeButton: true,
2005
+ windowClass: 'highlightandNotes',
2006
+ data: {
2007
+ template: 4,
2008
+ bookId: this.bookId,
2009
+ },
2010
+ });
2011
+ }
2012
+ if (data == 4) {
2013
+ const dialog = this.dialog.open(EpubCommonPopupComponent, {
2014
+ closeButton: true,
2015
+ windowClass: 'highlightandNotes',
2016
+ data: {
2017
+ template: 5,
2018
+ bookId: this.bookId,
2019
+ selectedText: this.selectedData.selectedText,
2020
+ },
2021
+ });
2022
+ }
2023
+ }
2024
+ /*
2025
+ *@Desc : To delete Annotations
2026
+ *@Author: Aboobacker
2027
+ */
2028
+ deleteAnnotations(typeId, id) {
2029
+ const payload = {
2030
+ typeId: typeId,
2031
+ id: id,
2032
+ };
2033
+ if (this.bookId) {
2034
+ this.httpService
2035
+ .deleteNotes(payload)
2036
+ .pipe(takeUntil(this.onDestroy$))
2037
+ .subscribe({
2038
+ next: (res) => { },
2039
+ error: (err) => {
2040
+ console.error('Error dlt Annotations:', err);
2041
+ },
2042
+ complete: () => {
2043
+ if (typeId === 2) {
2044
+ this.getHighlightlist();
2045
+ }
2046
+ else {
2047
+ this.getunderlineList();
2048
+ }
2049
+ },
2050
+ });
2051
+ }
2052
+ }
2053
+ /*
2054
+ *@Desc : To add note in a page
2055
+ *@Author: Aboobacker
2056
+ */
2057
+ addNoteToPage() {
2058
+ const notedetails = {
2059
+ bookId: this.bookId,
2060
+ cfiLocation: this.curentPageEndCfi,
2061
+ };
2062
+ const dialog = this.dialog.open(AddNotesComponent, {
2063
+ closeButton: true,
2064
+ windowClass: 'addandeditNotepopup',
2065
+ data: {
2066
+ notedetails: notedetails,
2067
+ addNote: true,
2068
+ notetoAddorEdit: '',
2069
+ },
2070
+ });
2071
+ }
2072
+ /*
2073
+ *@Desc : To generate random hexcode for bookmark
2074
+ *@Author: Jaisankar
2075
+ */
2076
+ get getRandomHexColor() {
2077
+ const randomColor = Math.floor(Math.random() * 16777215).toString(16);
2078
+ return `#${randomColor.padStart(6, '0')}`;
2079
+ }
2080
+ toggleAllMenus() {
2081
+ if (this.isMenuOpen || this.showHighlightOverlay || this.showhightLightColor) {
2082
+ this.isMenuOpen = false;
2083
+ this.showHighlightOverlay = false;
2084
+ this.showhightLightColor = false;
2085
+ }
2086
+ else {
2087
+ this.isMenuOpen = !this.isMenuOpen;
2088
+ }
2089
+ }
2090
+ ngOnDestroy() {
2091
+ if (this.rendition) {
2092
+ this.rendition.destroy();
2093
+ }
2094
+ if (isPlatformBrowser(this.platformId)) {
2095
+ document.body.classList.remove('dark-mode');
2096
+ }
2097
+ if (this.freeSample === 'false') {
2098
+ this.saveReadProgreess();
2099
+ }
2100
+ this.onDestroy$.next();
2101
+ this.onDestroy$.complete();
2102
+ }
2103
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ReaderMainComponent, deps: [{ token: i0.NgZone }, { token: HttpApiService }, { token: SharedService }, { token: EpubReaderService }, { token: i0.ChangeDetectorRef }, { token: i3.ActivatedRoute }, { token: CustomDialogService }, { token: i4.MessageService }, { token: i1$2.Location }, { token: PLATFORM_ID }, { token: i3.Router }, { token: i1$1.Overlay }], target: i0.ɵɵFactoryTarget.Component });
2104
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: ReaderMainComponent, isStandalone: false, selector: "epub-flow", inputs: { epubUrl: "epubUrl", epubBlob: "epubBlob", coverPage: "coverPage", enableDarkMode: "enableDarkMode", enableSearch: "enableSearch", enableHighlights: "enableHighlights", enableBookmarks: "enableBookmarks", enableChapterList: "enableChapterList", enableTextSelection: "enableTextSelection", enableFontSize: "enableFontSize", enableNotes: "enableNotes" }, outputs: { underlineRemoveRequest: "underlineRemoveRequest" }, host: { listeners: { "document:keyup": "handleKeyUp($event)", "document:keydown": "onKeyDown($event)" } }, usesOnChanges: true, ngImport: i0, template: "<div id=\"reader-container\" [hidden]=\"showBookPageLoader || showDwndLoader\">\n <div id=\"reader-content\" style=\"overflow: hidden;\">\n <div id=\"reader\"></div>\n </div>\n\n <div id=\"reader-footer\">\n <div class=\"footer-container\">\n <!-- Left: Previous Button -->\n <button class=\"nav-btn prev-btn\" (click)=\"prevClick()\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M15 18L9 12L15 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </button>\n\n <!-- Center: Progress Bar -->\n <div class=\"progress-section\">\n <div class=\"progress-label\">Read Progress</div>\n <div class=\"progress-bar-container\">\n <div class=\"progress-track\">\n <div class=\"progress-fill\" [style.width.%]=\"readPercentage.roudedOff\"></div>\n </div>\n <input type=\"range\" class=\"progress-slider\" min=\"0\" max=\"100\" [value]=\"readPercentage.roudedOff\"\n (input)=\"onProgressChange($event)\" />\n <span class=\"progress-text\">{{ readPercentage.roudedOff }}%</span>\n </div>\n </div>\n\n <!-- Right: Menu Toggle & Next Button -->\n <div class=\"right-controls\">\n <!-- Menu Toggle Button (Green) -->\n <div class=\"menu-container relative\">\n <!-- The Menu Popup -->\n @if (isMenuOpen) {\n <div class=\"menu-popup fade-in\">\n <ul class=\"menu-list\">\n <!-- Dark Mode -->\n <!-- Dark Mode -->\n @if (enableDarkMode) {\n <li (click)=\"toggleDarkMode = !toggleDarkMode; darkMode()\">\n <div class=\"menu-item\">\n <span class=\"icon moon-icon\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M13.5 10C12.7 10.3 11.9 10.5 11 10.5C7.4 10.5 4.5 7.6 4.5 4C4.5 3.1 4.7 2.3 5 1.5C2.7 2.5 1 4.8 1 7.5C1 11.1 3.9 14 7.5 14C10.2 14 12.5 12.3 13.5 10Z\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n <span>{{ toggleDarkMode ? 'Light Mode' : 'Dark Mode' }}</span>\n </div>\n </li>\n }\n\n <!-- Highlights & Notes -->\n <!-- Highlights & Notes -->\n @if (enableHighlights || enableNotes) {\n <li (click)=\"openCommonPopup(1); isMenuOpen = false\">\n <div class=\"menu-item\">\n <span class=\"icon notes-icon\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 4H14M2 8H14M2 12H8\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </span>\n <span>Highlights & Notes</span>\n </div>\n </li>\n }\n\n <!-- Search Book -->\n <!-- Search Book -->\n @if (enableSearch) {\n <li (click)=\"openCommonPopup(2); isMenuOpen = false\">\n <div class=\"menu-item\">\n <span class=\"icon search-icon\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M14 14L10.5 10.5M10.5 10.5C11.4 9.6 12 8.4 12 7C12 4.2 9.8 2 7 2C4.2 2 2 4.2 2 7C2 9.8 4.2 12 7 12C8.4 12 9.6 11.4 10.5 10.5Z\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n <span>Search Book</span>\n </div>\n </li>\n }\n\n <!-- Bookmark List -->\n <!-- Bookmark List -->\n @if (enableBookmarks) {\n <li (click)=\"getBookMarkList(); isMenuOpen = false\">\n <div class=\"menu-item\">\n <span class=\"icon bookmark-list-icon\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M12 1.33H4C3.26 1.33 2.66 1.93 2.66 2.66V13.33C2.66 14.06 3.26 14.66 4 14.66H12C12.73 14.66 13.33 14.06 13.33 13.33V2.66C13.33 1.93 12.73 1.33 12 1.33ZM6 2.66H7.33V6L6.66 5.5L6 6V2.66ZM12 13.33H4V2.66H4.66V8.66L6.66 7.16L8.66 8.66V2.66H12V13.33Z\"\n fill=\"currentColor\" />\n </svg>\n </span>\n <span>Bookmark List</span>\n </div>\n </li>\n }\n\n <!-- Chapters List -->\n <!-- Chapters List -->\n @if (enableChapterList) {\n <li (click)=\"showChapterPopup(); isMenuOpen = false\">\n <div class=\"menu-item\">\n <span class=\"icon chapter-icon\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M12 15.33H2.66C1.93 15.33 1.33 14.73 1.33 14V4.66H2.66V14H12V15.33ZM11 8.66H7V10H11V8.66ZM10 0.66H5.33C4.6 0.66 4 1.26 4 2L4 11.33C4 12.06 4.6 12.66 5.33 12.66H12.66C13.4 12.66 14 12.06 14 11.33V4.66L10 0.66ZM12.66 11.33H5.33V2H9.44L12.66 5.22V11.33Z\"\n fill=\"currentColor\" />\n </svg>\n </span>\n <span>Chapters List</span>\n </div>\n </li>\n }\n\n <!-- Divider Row for Quick Actions -->\n <!-- Divider Row for Quick Actions -->\n <li class=\"quick-actions-row\">\n @if (enableFontSize) {\n <div class=\"quick-action-btn\" (click)=\"changeFontSize('+')\">\n <span class=\"text-sm font-bold\">A+</span>\n </div>\n <div class=\"quick-action-btn\" (click)=\"changeFontSize('-')\">\n <span class=\"text-sm font-bold\">A-</span>\n </div>\n }\n @if (enableNotes) {\n <div class=\"quick-action-btn\" (click)=\"addNoteToPage(); isMenuOpen = false\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M3 17.25V21H6.75L17.81 9.94L14.06 6.19L3 17.25ZM20.71 7.04C21.1 6.65 21.1 6.02 20.71 5.63L18.37 3.29C17.98 2.9 17.35 2.9 16.96 3.29L15.13 5.12L18.88 8.87L20.71 7.04Z\"\n fill=\"currentColor\" />\n </svg>\n </div>\n }\n @if (enableBookmarks) {\n <div class=\"quick-action-btn\" (click)=\"addBookMark(); isMenuOpen = false\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M17 3H7C5.9 3 5 3.9 5 5V21L12 18L19 21V5C19 3.9 18.1 3 17 3Z\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </div>\n }\n </li>\n </ul>\n </div>\n }\n\n <!-- Selection Menu Inside Footer Hub -->\n @if (freeSample === 'false') {\n @if (showHighlightOverlay) {\n <div class=\"custom-selection-toolbar selection-menu fade-in\">\n @if (enableNotes) {\n <div class=\"menu-item\" (click)=\"addNotes(); showHighlightOverlay = false\">\n <span class=\"menu-text\">Add Notes</span>\n </div>\n }\n @if (enableHighlights) {\n <div class=\"menu-item\" (click)=\"showhightLightColor = true; showHighlightOverlay = false\" title=\"Highlight\">\n <span class=\"menu-text\">Highlights</span>\n </div>\n }\n @if (enableHighlights) {\n <div class=\"menu-item\" (click)=\"addUnderline(); showHighlightOverlay = false\">\n <span class=\"menu-text\">Underline</span>\n </div>\n }\n @if (enableNotes || enableHighlights) {\n <!-- Definition often goes with these tools -->\n <div class=\"menu-item border-none\" (click)=\"openCommonPopup(4); showHighlightOverlay = false\">\n <span class=\"menu-text\">Definition</span>\n </div>\n }\n </div>\n }\n\n @if (showhightLightColor) {\n <div class=\"custom-selection-toolbar color-picker fade-in\">\n @for (color of colourCodeList; track color) {\n <div class=\"color-circle\" [style.background]=\"color\"\n (click)=\"addHighlight(color); showhightLightColor = false\"></div>\n }\n <button class=\"toolbar-btn close-colors\" (click)=\"showhightLightColor = false; showHighlightOverlay = true\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M19 12H5\"></path>\n </svg>\n </button>\n </div>\n }\n }\n\n <!-- The Green Toggle Button -->\n <button class=\"menu-toggle-btn\" (click)=\"toggleAllMenus()\"\n [class.open]=\"isMenuOpen || showHighlightOverlay || showhightLightColor\">\n <!-- X Icon when open, Burger/Menu when closed -->\n @if (isMenuOpen || showHighlightOverlay || showhightLightColor) {\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n } @else {\n <!-- Menu Grid Icon -->\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 6H20M4 12H20M4 18H20\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n }\n </button>\n </div>\n\n <!-- Next Button -->\n <button class=\"nav-btn next-btn\" (click)=\"nextClick()\" [class.ml-4]=\"true\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M9 18L15 12L9 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n </div>\n </div>\n</div>\n\n\n@if (showBookPageLoader) {\n<div class=\"fixed inset-0 z-[45] flex items-center justify-center bg-premium-loader fade-in\">\n <div class=\"text-center\">\n <div class=\"mb-8 relative inline-block\">\n <!-- Decorative Backdrop for Book -->\n <div class=\"absolute inset-0 bg-black/5 blur-2xl rounded-full transform scale-110\"></div>\n\n <!-- Larger, Premium Book Image -->\n <img [src]=\"coverImg ?? ''\" alt=\"Loading\"\n class=\"w-56 h-80 object-cover rounded-xl shadow-premium border-[6px] border-white relative z-10 animate-pulse\" />\n </div>\n\n <!-- Enhanced Loading Text Container -->\n <div class=\"flex flex-col items-center space-y-3\">\n <div class=\"flex items-center space-x-2 text-gray-400\">\n <span class=\"text-base font-medium\">Please wait a moment</span>\n <div class=\"flex space-x-1.5\">\n <div class=\"w-1.5 h-1.5 bg-accent-color rounded-full animate-bounce\"></div>\n <div class=\"w-1.5 h-1.5 bg-accent-color rounded-full animate-bounce [animation-delay:-0.15s]\"></div>\n <div class=\"w-1.5 h-1.5 bg-accent-color rounded-full animate-bounce [animation-delay:-0.3s]\"></div>\n </div>\n </div>\n </div>\n </div>\n</div>\n}\n@if (showOverlay) {\n<div class=\"screenshot-overlay\"></div>\n}\n\n<!-- Full-screen Preview End Modal -->\n@if (showPreviewModal) {\n<div class=\"fixed inset-0 z-40 bg-white dark:bg-gray-900 flex flex-col items-center justify-center text-center px-4\">\n <h2\n class=\"text-[1.375rem] sm:text-2xl font-medium leading-snug text-center text-gray-800 dark:text-white mb-6 max-w-xs sm:max-w-sm mx-auto\">\n You\u2019ve reached the end of the sample for\n </h2>\n\n <img [src]=\"coverImg ?? ''\" alt=\"Book Cover\" class=\"w-40 h-auto rounded-lg shadow-lg mb-6\" />\n</div>\n}\n\n\n<!-- Custom Chapter List Modal -->\n@if (showChapterList) {\n<div class=\"custom-modal-overlay fade-in\">\n <div class=\"custom-modal-content\">\n <app-chapterlist [chapterData]=\"chaptersData\" (chapterClick)=\"onChapterSelect($event)\"\n (closeDialog)=\"showChapterList = false\">\n </app-chapterlist>\n </div>\n</div>\n}", styles: [":host{--bg-primary: #ffffff;--bg-secondary: #f8fafc;--text-primary: #0f172a;--text-secondary: #475569;--border-base: #edf2f7;--shadow-base: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);--accent-color: #34C759;--modal-bg: #ffffff;--modal-text: #0f172a}:host-context(.dark-mode){--bg-primary: rgb(32 33 32);--bg-secondary: rgb(42 43 42);--text-primary: #f8fafc;--text-secondary: #94a3b8;--border-base: #2d3748;--shadow-base: 0 10px 15px -3px rgba(0, 0, 0, .5);--accent-color: #32d74b;--modal-bg: rgb(32 33 32);--modal-text: #f9fafb}#reader{width:100%;height:100%}.no-select{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:default}.screenshot-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#fff;z-index:9999}img,[draggable=true]{-webkit-user-drag:none}::ng-deep *{-webkit-user-drag:none}::ng-deep .textLayer span{-webkit-user-drag:none;pointer-events:auto}.fade-in{animation:fadeIn .5s ease-in forwards}.fade-out{animation:fadeOut .5s ease-out forwards}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.7}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@media print{#viewer{display:none!important}body:before{content:\"Printing is disabled.\";font-size:24px;color:red;text-align:center;display:block;margin-top:100px}}.progress-container{position:relative;height:4px}.range-overlay{position:absolute;top:0;left:0;width:100%;z-index:10;background:transparent;cursor:pointer;-webkit-appearance:none;appearance:none;touch-action:none}.range-overlay::-webkit-slider-thumb{-webkit-appearance:none;height:14px;width:14px;margin-top:-5px;border-radius:50%;background:#0ea951;border:2px solid #ffffff;box-shadow:0 1px 3px #0003;transition:background .2s ease,transform .2s ease}.range-overlay::-webkit-slider-thumb:hover{background:#0cb14f;transform:scale(1.1)}.range-overlay::-webkit-slider-thumb:active{background:#0ba046;transform:scale(1.15)}.range-overlay::-webkit-slider-runnable-track{background:transparent;height:4px}.range-overlay::-moz-range-thumb{height:16px;width:16px;border-radius:50%;background:#0ea951;border:2px solid white}.range-overlay::-moz-range-track{background:transparent;height:4px}.range-overlay::-ms-thumb{height:16px;width:16px;border-radius:50%;background:#0ea951;border:2px solid white}.range-overlay::-ms-track{background:transparent;height:4px;border-color:transparent;color:transparent}#reader-container:after{content:\"\";position:absolute;top:13%;bottom:10%;left:50%;transform:translate(-50%);width:2px;background:linear-gradient(to bottom,#06030300,#0d080833,#0000);box-shadow:0 0 10px #0000001a;z-index:1}@media (max-width: 767px){#reader-container:after{display:none!important}}@media (max-width: 991px){#reader-container:after{display:none!important}}::ng-deep nav{box-shadow:none!important}::ng-deep .highlightandNotes .ngneat-dialog-content{width:510px!important;padding:24px}::ng-deep .highlightandNotes .ngneat-close-dialog{top:1.2rem}::ng-deep .highlightandNotes.dark-mode .ngneat-dialog-content{background-color:#333;color:#fff!important}::ng-deep .highlightandNotes.dark-mode .ngneat-close-dialog{color:#fff!important}::ng-deep .highlightandNotes.dark-mode .ngneat-dialog-content{color:#fff!important}::ng-deep .highlightandNotes.dark-mode .text-\\[\\#272C47\\]{color:#fff!important}::ng-deep .highlightandNotes.dark-mode .text-black{color:#fff!important}::ng-deep .addandeditNotepopup .ngneat-dialog-content{width:690px!important}::ng-deep .crop-popup .ngneat-dialog-content{width:420px!important}:host,app-notes{background-color:var(--bg-primary);display:flex;width:100%;justify-content:center;transition:background-color .3s ease}#reader-container{width:100%;height:98vh;background-color:var(--bg-primary);padding-top:0;display:flex;flex-direction:column;overflow:hidden;transition:background-color .3s ease}#reader-content{flex:1;overflow:hidden;position:relative}#reader-footer{position:relative;width:60vw;margin:0 auto;padding:10px 38px 20px;background:var(--bg-primary);z-index:50;transition:background-color .3s ease}#reader-footer .footer-container{display:flex;justify-content:space-between;align-items:flex-end;gap:20px}#reader-footer .nav-btn{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:50%;cursor:pointer;transition:background .2s;background:transparent;border:none;color:var(--text-primary)}#reader-footer .nav-btn:hover{background:#f5f5f5}#reader-footer .nav-btn svg{width:20px;height:20px}#reader-footer .progress-section{flex:1;display:flex;flex-direction:column;align-items:center;max-width:600px;margin:0 auto;padding-bottom:5px}#reader-footer .progress-section .progress-label{font-size:11px;font-weight:500;text-transform:uppercase;letter-spacing:.5px;color:#666;margin-bottom:6px}#reader-footer .progress-section .progress-bar-container{position:relative;width:100%;height:14px;display:flex;align-items:center}#reader-footer .progress-section .progress-bar-container .progress-track{flex:1;height:4px;background:#e7ebeb;border-radius:2px;overflow:hidden;position:relative}#reader-footer .progress-section .progress-bar-container .progress-fill{height:100%;background:#34c759;transition:width .2s ease-out;border-radius:2px}#reader-footer .progress-section .progress-bar-container .progress-slider{position:absolute;width:100%;height:100%;opacity:0;cursor:pointer;top:0;left:0;z-index:2}#reader-footer .progress-section .progress-bar-container .progress-text{margin-left:12px;font-size:12px;font-weight:600;min-width:35px;color:var(--text-primary)}#reader-footer .right-controls{display:flex;align-items:center;gap:16px}#reader-footer .menu-toggle-btn{width:48px;height:48px;border-radius:50%;background:#34c759;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 10px #34c7594d;border:none}#reader-footer .menu-toggle-btn.open{background:#34c759;transform:rotate(90deg)}#reader-footer .menu-toggle-btn:hover{transform:scale(1.05);background:#2ebd50}#reader-footer .menu-popup{position:absolute;bottom:70px;right:0;width:280px;background:var(--bg-primary);border-radius:16px;box-shadow:var(--shadow-base);padding:8px 0;z-index:100;border:1px solid var(--border-base);transform-origin:bottom right}#reader-footer .menu-popup .menu-list{list-style:none;padding:0;margin:0}#reader-footer .menu-popup .menu-list li{padding:0 8px;cursor:pointer}#reader-footer .menu-popup .menu-list .menu-item{display:flex;align-items:center;padding:12px;font-size:14px;font-weight:500;color:var(--text-primary);border-radius:8px;transition:background .1s}#reader-footer .menu-popup .menu-list .menu-item:hover{background:var(--bg-secondary)}#reader-footer .menu-popup .menu-list .menu-item .icon{width:32px;display:flex;align-items:center;justify-content:flex-start;color:var(--text-secondary)}#reader-footer .menu-popup .menu-list .menu-item .icon svg{width:18px;height:18px}#reader-footer .menu-popup .quick-actions-row{display:flex;justify-content:space-between;padding:12px 16px;margin-top:8px;border-top:1px solid #f0f0f0;gap:8px}#reader-footer .menu-popup .quick-actions-row .quick-action-btn{flex:1;height:40px;display:flex;align-items:center;justify-content:center;border-radius:8px;background:var(--bg-secondary);cursor:pointer;color:var(--text-primary);transition:all .2s}#reader-footer .menu-popup .quick-actions-row .quick-action-btn:hover{background:var(--border-base)}.calibre{padding:0!important}.hightlight-overlay ::ng-deep .p-overlay{top:-232px!important}app-chapterlist ::ng-deep #reader-content{padding:0!important}.custom-modal-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:#0006;backdrop-filter:blur(4px);z-index:200;display:flex;align-items:center;justify-content:center}.custom-modal-content{background:var(--modal-bg);color:var(--modal-text);width:90%;max-width:500px;border-radius:12px;box-shadow:var(--shadow-base);overflow:hidden;animation:modalEnter .2s ease-out;transition:background-color .3s ease,color .3s ease}@keyframes modalEnter{0%{opacity:0;transform:scale(.95) translateY(10px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes bounce{0%,to{transform:translateY(0)}50%{transform:translateY(-4px)}}.animate-bounce{animation:bounce .6s infinite}.fixed{position:fixed}.inset-0{inset:0}.z-40{z-index:40}.z-45{z-index:45}.flex{display:flex}.items-center{align-items:center}.justify-center{justify-content:center}.text-center{text-align:center}.bg-white{background-color:#fff}.bg-white\\/95{background-color:#fffffff2!important}.bg-black\\/5{background-color:#0000000d}.bg-accent-color{background-color:var(--accent-color)}.bg-premium-loader{background:var(--bg-primary)}.blur-2xl{filter:blur(40px)}.transform{--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-110{--tw-scale-x: 1.1;--tw-scale-y: 1.1}.relative{position:relative}.absolute{position:absolute}.inline-block{display:inline-block}.z-10{z-index:10}.mb-8{margin-bottom:2rem}.space-y-3>*+*{margin-top:.75rem}.space-x-1\\.5>*+*{margin-left:.375rem}.tracking-tight{letter-spacing:-.025em}.text-gray-800{color:#1f2937}.border-\\[6px\\]{border-width:6px}.backdrop-blur-sm{backdrop-filter:blur(4px)}.w-full{width:100%}.px-4{padding-left:1rem;padding-right:1rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mt-\\[-1\\.5rem\\]{margin-top:-1.5rem}.ml-1{margin-left:.25rem}.space-x-1>*+*{margin-left:.25rem}.space-x-2>*+*{margin-left:.5rem}.inline-flex{display:inline-flex}.text-gray-700{color:#374151}.text-gray-400{color:#9ca3af}.text-lg{font-size:1.125rem}.text-xl{font-size:1.25rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.w-40{width:10rem}.h-56{height:14rem}.w-56{width:14rem}.h-80{height:20rem}.w-32{width:8rem}.h-44{height:11rem}.object-cover{object-fit:cover}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.shadow-2xl{box-shadow:0 25px 50px -12px #00000040}.shadow-premium{box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a,0 0 0 1px #0000000d}.mx-auto{margin-left:auto;margin-right:auto}.border-4{border-width:4px}.border-white{border-color:#fff}.border-gray-50{border-color:#f9fafb}.w-1\\.5{width:.375rem}.h-1\\.5{height:.375rem}.bg-gray-400{background-color:#9ca3af}.rounded-full{border-radius:9999px}.custom-selection-toolbar{position:absolute;bottom:70px;right:0;z-index:100;width:280px;background:var(--bg-primary);border-radius:16px;box-shadow:var(--shadow-base);display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--border-base);padding:8px 0;transform-origin:bottom right}.custom-selection-toolbar .menu-item{padding:12px 20px;border-bottom:1px solid #f1f5f9;cursor:pointer;transition:all .2s ease;display:flex;align-items:center}.custom-selection-toolbar .menu-item:hover{background:#f8fafc}.custom-selection-toolbar .menu-item:hover .menu-text{color:var(--accent-color)}.custom-selection-toolbar .menu-item.border-none{border-bottom:none}.custom-selection-toolbar .menu-item .menu-text{font-size:15px;font-weight:500;color:#334155}.custom-selection-toolbar.color-picker{width:auto;flex-direction:row;padding:8px 12px;gap:12px;align-items:center}.custom-selection-toolbar.color-picker .color-circle{width:24px;height:24px;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0000001a;transition:transform .2s ease}.custom-selection-toolbar.color-picker .color-circle:hover{transform:scale(1.2)}.custom-selection-toolbar.color-picker .toolbar-btn{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:50%;border:none;background:#f1f5f9;color:#64748b;cursor:pointer;margin-left:4px}.custom-selection-toolbar.color-picker .toolbar-btn:hover{background:#e2e8f0}:host-context(.dark-mode) .custom-selection-toolbar{background:#1e293b;border-color:#334155}:host-context(.dark-mode) .custom-selection-toolbar .menu-item{border-bottom-color:#334155}:host-context(.dark-mode) .custom-selection-toolbar .menu-item:hover{background:#0f172a}:host-context(.dark-mode) .custom-selection-toolbar .menu-item .menu-text{color:#f1f5f9}:host-context(.dark-mode) .custom-selection-toolbar.color-picker .toolbar-btn{background:#334155;color:#f1f5f9}\n"], dependencies: [{ kind: "component", type: ChapterlistComponent, selector: "app-chapterlist", inputs: ["chapterData"], outputs: ["chapterClick", "closeDialog"] }] });
2105
+ }
2106
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ReaderMainComponent, decorators: [{
2107
+ type: Component,
2108
+ args: [{ selector: 'epub-flow', standalone: false, template: "<div id=\"reader-container\" [hidden]=\"showBookPageLoader || showDwndLoader\">\n <div id=\"reader-content\" style=\"overflow: hidden;\">\n <div id=\"reader\"></div>\n </div>\n\n <div id=\"reader-footer\">\n <div class=\"footer-container\">\n <!-- Left: Previous Button -->\n <button class=\"nav-btn prev-btn\" (click)=\"prevClick()\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M15 18L9 12L15 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </button>\n\n <!-- Center: Progress Bar -->\n <div class=\"progress-section\">\n <div class=\"progress-label\">Read Progress</div>\n <div class=\"progress-bar-container\">\n <div class=\"progress-track\">\n <div class=\"progress-fill\" [style.width.%]=\"readPercentage.roudedOff\"></div>\n </div>\n <input type=\"range\" class=\"progress-slider\" min=\"0\" max=\"100\" [value]=\"readPercentage.roudedOff\"\n (input)=\"onProgressChange($event)\" />\n <span class=\"progress-text\">{{ readPercentage.roudedOff }}%</span>\n </div>\n </div>\n\n <!-- Right: Menu Toggle & Next Button -->\n <div class=\"right-controls\">\n <!-- Menu Toggle Button (Green) -->\n <div class=\"menu-container relative\">\n <!-- The Menu Popup -->\n @if (isMenuOpen) {\n <div class=\"menu-popup fade-in\">\n <ul class=\"menu-list\">\n <!-- Dark Mode -->\n <!-- Dark Mode -->\n @if (enableDarkMode) {\n <li (click)=\"toggleDarkMode = !toggleDarkMode; darkMode()\">\n <div class=\"menu-item\">\n <span class=\"icon moon-icon\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M13.5 10C12.7 10.3 11.9 10.5 11 10.5C7.4 10.5 4.5 7.6 4.5 4C4.5 3.1 4.7 2.3 5 1.5C2.7 2.5 1 4.8 1 7.5C1 11.1 3.9 14 7.5 14C10.2 14 12.5 12.3 13.5 10Z\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n <span>{{ toggleDarkMode ? 'Light Mode' : 'Dark Mode' }}</span>\n </div>\n </li>\n }\n\n <!-- Highlights & Notes -->\n <!-- Highlights & Notes -->\n @if (enableHighlights || enableNotes) {\n <li (click)=\"openCommonPopup(1); isMenuOpen = false\">\n <div class=\"menu-item\">\n <span class=\"icon notes-icon\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 4H14M2 8H14M2 12H8\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </span>\n <span>Highlights & Notes</span>\n </div>\n </li>\n }\n\n <!-- Search Book -->\n <!-- Search Book -->\n @if (enableSearch) {\n <li (click)=\"openCommonPopup(2); isMenuOpen = false\">\n <div class=\"menu-item\">\n <span class=\"icon search-icon\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M14 14L10.5 10.5M10.5 10.5C11.4 9.6 12 8.4 12 7C12 4.2 9.8 2 7 2C4.2 2 2 4.2 2 7C2 9.8 4.2 12 7 12C8.4 12 9.6 11.4 10.5 10.5Z\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n <span>Search Book</span>\n </div>\n </li>\n }\n\n <!-- Bookmark List -->\n <!-- Bookmark List -->\n @if (enableBookmarks) {\n <li (click)=\"getBookMarkList(); isMenuOpen = false\">\n <div class=\"menu-item\">\n <span class=\"icon bookmark-list-icon\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M12 1.33H4C3.26 1.33 2.66 1.93 2.66 2.66V13.33C2.66 14.06 3.26 14.66 4 14.66H12C12.73 14.66 13.33 14.06 13.33 13.33V2.66C13.33 1.93 12.73 1.33 12 1.33ZM6 2.66H7.33V6L6.66 5.5L6 6V2.66ZM12 13.33H4V2.66H4.66V8.66L6.66 7.16L8.66 8.66V2.66H12V13.33Z\"\n fill=\"currentColor\" />\n </svg>\n </span>\n <span>Bookmark List</span>\n </div>\n </li>\n }\n\n <!-- Chapters List -->\n <!-- Chapters List -->\n @if (enableChapterList) {\n <li (click)=\"showChapterPopup(); isMenuOpen = false\">\n <div class=\"menu-item\">\n <span class=\"icon chapter-icon\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M12 15.33H2.66C1.93 15.33 1.33 14.73 1.33 14V4.66H2.66V14H12V15.33ZM11 8.66H7V10H11V8.66ZM10 0.66H5.33C4.6 0.66 4 1.26 4 2L4 11.33C4 12.06 4.6 12.66 5.33 12.66H12.66C13.4 12.66 14 12.06 14 11.33V4.66L10 0.66ZM12.66 11.33H5.33V2H9.44L12.66 5.22V11.33Z\"\n fill=\"currentColor\" />\n </svg>\n </span>\n <span>Chapters List</span>\n </div>\n </li>\n }\n\n <!-- Divider Row for Quick Actions -->\n <!-- Divider Row for Quick Actions -->\n <li class=\"quick-actions-row\">\n @if (enableFontSize) {\n <div class=\"quick-action-btn\" (click)=\"changeFontSize('+')\">\n <span class=\"text-sm font-bold\">A+</span>\n </div>\n <div class=\"quick-action-btn\" (click)=\"changeFontSize('-')\">\n <span class=\"text-sm font-bold\">A-</span>\n </div>\n }\n @if (enableNotes) {\n <div class=\"quick-action-btn\" (click)=\"addNoteToPage(); isMenuOpen = false\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M3 17.25V21H6.75L17.81 9.94L14.06 6.19L3 17.25ZM20.71 7.04C21.1 6.65 21.1 6.02 20.71 5.63L18.37 3.29C17.98 2.9 17.35 2.9 16.96 3.29L15.13 5.12L18.88 8.87L20.71 7.04Z\"\n fill=\"currentColor\" />\n </svg>\n </div>\n }\n @if (enableBookmarks) {\n <div class=\"quick-action-btn\" (click)=\"addBookMark(); isMenuOpen = false\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M17 3H7C5.9 3 5 3.9 5 5V21L12 18L19 21V5C19 3.9 18.1 3 17 3Z\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </div>\n }\n </li>\n </ul>\n </div>\n }\n\n <!-- Selection Menu Inside Footer Hub -->\n @if (freeSample === 'false') {\n @if (showHighlightOverlay) {\n <div class=\"custom-selection-toolbar selection-menu fade-in\">\n @if (enableNotes) {\n <div class=\"menu-item\" (click)=\"addNotes(); showHighlightOverlay = false\">\n <span class=\"menu-text\">Add Notes</span>\n </div>\n }\n @if (enableHighlights) {\n <div class=\"menu-item\" (click)=\"showhightLightColor = true; showHighlightOverlay = false\" title=\"Highlight\">\n <span class=\"menu-text\">Highlights</span>\n </div>\n }\n @if (enableHighlights) {\n <div class=\"menu-item\" (click)=\"addUnderline(); showHighlightOverlay = false\">\n <span class=\"menu-text\">Underline</span>\n </div>\n }\n @if (enableNotes || enableHighlights) {\n <!-- Definition often goes with these tools -->\n <div class=\"menu-item border-none\" (click)=\"openCommonPopup(4); showHighlightOverlay = false\">\n <span class=\"menu-text\">Definition</span>\n </div>\n }\n </div>\n }\n\n @if (showhightLightColor) {\n <div class=\"custom-selection-toolbar color-picker fade-in\">\n @for (color of colourCodeList; track color) {\n <div class=\"color-circle\" [style.background]=\"color\"\n (click)=\"addHighlight(color); showhightLightColor = false\"></div>\n }\n <button class=\"toolbar-btn close-colors\" (click)=\"showhightLightColor = false; showHighlightOverlay = true\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M19 12H5\"></path>\n </svg>\n </button>\n </div>\n }\n }\n\n <!-- The Green Toggle Button -->\n <button class=\"menu-toggle-btn\" (click)=\"toggleAllMenus()\"\n [class.open]=\"isMenuOpen || showHighlightOverlay || showhightLightColor\">\n <!-- X Icon when open, Burger/Menu when closed -->\n @if (isMenuOpen || showHighlightOverlay || showhightLightColor) {\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n } @else {\n <!-- Menu Grid Icon -->\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 6H20M4 12H20M4 18H20\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n }\n </button>\n </div>\n\n <!-- Next Button -->\n <button class=\"nav-btn next-btn\" (click)=\"nextClick()\" [class.ml-4]=\"true\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M9 18L15 12L9 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n </div>\n </div>\n</div>\n\n\n@if (showBookPageLoader) {\n<div class=\"fixed inset-0 z-[45] flex items-center justify-center bg-premium-loader fade-in\">\n <div class=\"text-center\">\n <div class=\"mb-8 relative inline-block\">\n <!-- Decorative Backdrop for Book -->\n <div class=\"absolute inset-0 bg-black/5 blur-2xl rounded-full transform scale-110\"></div>\n\n <!-- Larger, Premium Book Image -->\n <img [src]=\"coverImg ?? ''\" alt=\"Loading\"\n class=\"w-56 h-80 object-cover rounded-xl shadow-premium border-[6px] border-white relative z-10 animate-pulse\" />\n </div>\n\n <!-- Enhanced Loading Text Container -->\n <div class=\"flex flex-col items-center space-y-3\">\n <div class=\"flex items-center space-x-2 text-gray-400\">\n <span class=\"text-base font-medium\">Please wait a moment</span>\n <div class=\"flex space-x-1.5\">\n <div class=\"w-1.5 h-1.5 bg-accent-color rounded-full animate-bounce\"></div>\n <div class=\"w-1.5 h-1.5 bg-accent-color rounded-full animate-bounce [animation-delay:-0.15s]\"></div>\n <div class=\"w-1.5 h-1.5 bg-accent-color rounded-full animate-bounce [animation-delay:-0.3s]\"></div>\n </div>\n </div>\n </div>\n </div>\n</div>\n}\n@if (showOverlay) {\n<div class=\"screenshot-overlay\"></div>\n}\n\n<!-- Full-screen Preview End Modal -->\n@if (showPreviewModal) {\n<div class=\"fixed inset-0 z-40 bg-white dark:bg-gray-900 flex flex-col items-center justify-center text-center px-4\">\n <h2\n class=\"text-[1.375rem] sm:text-2xl font-medium leading-snug text-center text-gray-800 dark:text-white mb-6 max-w-xs sm:max-w-sm mx-auto\">\n You\u2019ve reached the end of the sample for\n </h2>\n\n <img [src]=\"coverImg ?? ''\" alt=\"Book Cover\" class=\"w-40 h-auto rounded-lg shadow-lg mb-6\" />\n</div>\n}\n\n\n<!-- Custom Chapter List Modal -->\n@if (showChapterList) {\n<div class=\"custom-modal-overlay fade-in\">\n <div class=\"custom-modal-content\">\n <app-chapterlist [chapterData]=\"chaptersData\" (chapterClick)=\"onChapterSelect($event)\"\n (closeDialog)=\"showChapterList = false\">\n </app-chapterlist>\n </div>\n</div>\n}", styles: [":host{--bg-primary: #ffffff;--bg-secondary: #f8fafc;--text-primary: #0f172a;--text-secondary: #475569;--border-base: #edf2f7;--shadow-base: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);--accent-color: #34C759;--modal-bg: #ffffff;--modal-text: #0f172a}:host-context(.dark-mode){--bg-primary: rgb(32 33 32);--bg-secondary: rgb(42 43 42);--text-primary: #f8fafc;--text-secondary: #94a3b8;--border-base: #2d3748;--shadow-base: 0 10px 15px -3px rgba(0, 0, 0, .5);--accent-color: #32d74b;--modal-bg: rgb(32 33 32);--modal-text: #f9fafb}#reader{width:100%;height:100%}.no-select{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:default}.screenshot-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#fff;z-index:9999}img,[draggable=true]{-webkit-user-drag:none}::ng-deep *{-webkit-user-drag:none}::ng-deep .textLayer span{-webkit-user-drag:none;pointer-events:auto}.fade-in{animation:fadeIn .5s ease-in forwards}.fade-out{animation:fadeOut .5s ease-out forwards}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.7}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@media print{#viewer{display:none!important}body:before{content:\"Printing is disabled.\";font-size:24px;color:red;text-align:center;display:block;margin-top:100px}}.progress-container{position:relative;height:4px}.range-overlay{position:absolute;top:0;left:0;width:100%;z-index:10;background:transparent;cursor:pointer;-webkit-appearance:none;appearance:none;touch-action:none}.range-overlay::-webkit-slider-thumb{-webkit-appearance:none;height:14px;width:14px;margin-top:-5px;border-radius:50%;background:#0ea951;border:2px solid #ffffff;box-shadow:0 1px 3px #0003;transition:background .2s ease,transform .2s ease}.range-overlay::-webkit-slider-thumb:hover{background:#0cb14f;transform:scale(1.1)}.range-overlay::-webkit-slider-thumb:active{background:#0ba046;transform:scale(1.15)}.range-overlay::-webkit-slider-runnable-track{background:transparent;height:4px}.range-overlay::-moz-range-thumb{height:16px;width:16px;border-radius:50%;background:#0ea951;border:2px solid white}.range-overlay::-moz-range-track{background:transparent;height:4px}.range-overlay::-ms-thumb{height:16px;width:16px;border-radius:50%;background:#0ea951;border:2px solid white}.range-overlay::-ms-track{background:transparent;height:4px;border-color:transparent;color:transparent}#reader-container:after{content:\"\";position:absolute;top:13%;bottom:10%;left:50%;transform:translate(-50%);width:2px;background:linear-gradient(to bottom,#06030300,#0d080833,#0000);box-shadow:0 0 10px #0000001a;z-index:1}@media (max-width: 767px){#reader-container:after{display:none!important}}@media (max-width: 991px){#reader-container:after{display:none!important}}::ng-deep nav{box-shadow:none!important}::ng-deep .highlightandNotes .ngneat-dialog-content{width:510px!important;padding:24px}::ng-deep .highlightandNotes .ngneat-close-dialog{top:1.2rem}::ng-deep .highlightandNotes.dark-mode .ngneat-dialog-content{background-color:#333;color:#fff!important}::ng-deep .highlightandNotes.dark-mode .ngneat-close-dialog{color:#fff!important}::ng-deep .highlightandNotes.dark-mode .ngneat-dialog-content{color:#fff!important}::ng-deep .highlightandNotes.dark-mode .text-\\[\\#272C47\\]{color:#fff!important}::ng-deep .highlightandNotes.dark-mode .text-black{color:#fff!important}::ng-deep .addandeditNotepopup .ngneat-dialog-content{width:690px!important}::ng-deep .crop-popup .ngneat-dialog-content{width:420px!important}:host,app-notes{background-color:var(--bg-primary);display:flex;width:100%;justify-content:center;transition:background-color .3s ease}#reader-container{width:100%;height:98vh;background-color:var(--bg-primary);padding-top:0;display:flex;flex-direction:column;overflow:hidden;transition:background-color .3s ease}#reader-content{flex:1;overflow:hidden;position:relative}#reader-footer{position:relative;width:60vw;margin:0 auto;padding:10px 38px 20px;background:var(--bg-primary);z-index:50;transition:background-color .3s ease}#reader-footer .footer-container{display:flex;justify-content:space-between;align-items:flex-end;gap:20px}#reader-footer .nav-btn{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:50%;cursor:pointer;transition:background .2s;background:transparent;border:none;color:var(--text-primary)}#reader-footer .nav-btn:hover{background:#f5f5f5}#reader-footer .nav-btn svg{width:20px;height:20px}#reader-footer .progress-section{flex:1;display:flex;flex-direction:column;align-items:center;max-width:600px;margin:0 auto;padding-bottom:5px}#reader-footer .progress-section .progress-label{font-size:11px;font-weight:500;text-transform:uppercase;letter-spacing:.5px;color:#666;margin-bottom:6px}#reader-footer .progress-section .progress-bar-container{position:relative;width:100%;height:14px;display:flex;align-items:center}#reader-footer .progress-section .progress-bar-container .progress-track{flex:1;height:4px;background:#e7ebeb;border-radius:2px;overflow:hidden;position:relative}#reader-footer .progress-section .progress-bar-container .progress-fill{height:100%;background:#34c759;transition:width .2s ease-out;border-radius:2px}#reader-footer .progress-section .progress-bar-container .progress-slider{position:absolute;width:100%;height:100%;opacity:0;cursor:pointer;top:0;left:0;z-index:2}#reader-footer .progress-section .progress-bar-container .progress-text{margin-left:12px;font-size:12px;font-weight:600;min-width:35px;color:var(--text-primary)}#reader-footer .right-controls{display:flex;align-items:center;gap:16px}#reader-footer .menu-toggle-btn{width:48px;height:48px;border-radius:50%;background:#34c759;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 10px #34c7594d;border:none}#reader-footer .menu-toggle-btn.open{background:#34c759;transform:rotate(90deg)}#reader-footer .menu-toggle-btn:hover{transform:scale(1.05);background:#2ebd50}#reader-footer .menu-popup{position:absolute;bottom:70px;right:0;width:280px;background:var(--bg-primary);border-radius:16px;box-shadow:var(--shadow-base);padding:8px 0;z-index:100;border:1px solid var(--border-base);transform-origin:bottom right}#reader-footer .menu-popup .menu-list{list-style:none;padding:0;margin:0}#reader-footer .menu-popup .menu-list li{padding:0 8px;cursor:pointer}#reader-footer .menu-popup .menu-list .menu-item{display:flex;align-items:center;padding:12px;font-size:14px;font-weight:500;color:var(--text-primary);border-radius:8px;transition:background .1s}#reader-footer .menu-popup .menu-list .menu-item:hover{background:var(--bg-secondary)}#reader-footer .menu-popup .menu-list .menu-item .icon{width:32px;display:flex;align-items:center;justify-content:flex-start;color:var(--text-secondary)}#reader-footer .menu-popup .menu-list .menu-item .icon svg{width:18px;height:18px}#reader-footer .menu-popup .quick-actions-row{display:flex;justify-content:space-between;padding:12px 16px;margin-top:8px;border-top:1px solid #f0f0f0;gap:8px}#reader-footer .menu-popup .quick-actions-row .quick-action-btn{flex:1;height:40px;display:flex;align-items:center;justify-content:center;border-radius:8px;background:var(--bg-secondary);cursor:pointer;color:var(--text-primary);transition:all .2s}#reader-footer .menu-popup .quick-actions-row .quick-action-btn:hover{background:var(--border-base)}.calibre{padding:0!important}.hightlight-overlay ::ng-deep .p-overlay{top:-232px!important}app-chapterlist ::ng-deep #reader-content{padding:0!important}.custom-modal-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:#0006;backdrop-filter:blur(4px);z-index:200;display:flex;align-items:center;justify-content:center}.custom-modal-content{background:var(--modal-bg);color:var(--modal-text);width:90%;max-width:500px;border-radius:12px;box-shadow:var(--shadow-base);overflow:hidden;animation:modalEnter .2s ease-out;transition:background-color .3s ease,color .3s ease}@keyframes modalEnter{0%{opacity:0;transform:scale(.95) translateY(10px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes bounce{0%,to{transform:translateY(0)}50%{transform:translateY(-4px)}}.animate-bounce{animation:bounce .6s infinite}.fixed{position:fixed}.inset-0{inset:0}.z-40{z-index:40}.z-45{z-index:45}.flex{display:flex}.items-center{align-items:center}.justify-center{justify-content:center}.text-center{text-align:center}.bg-white{background-color:#fff}.bg-white\\/95{background-color:#fffffff2!important}.bg-black\\/5{background-color:#0000000d}.bg-accent-color{background-color:var(--accent-color)}.bg-premium-loader{background:var(--bg-primary)}.blur-2xl{filter:blur(40px)}.transform{--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-110{--tw-scale-x: 1.1;--tw-scale-y: 1.1}.relative{position:relative}.absolute{position:absolute}.inline-block{display:inline-block}.z-10{z-index:10}.mb-8{margin-bottom:2rem}.space-y-3>*+*{margin-top:.75rem}.space-x-1\\.5>*+*{margin-left:.375rem}.tracking-tight{letter-spacing:-.025em}.text-gray-800{color:#1f2937}.border-\\[6px\\]{border-width:6px}.backdrop-blur-sm{backdrop-filter:blur(4px)}.w-full{width:100%}.px-4{padding-left:1rem;padding-right:1rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mt-\\[-1\\.5rem\\]{margin-top:-1.5rem}.ml-1{margin-left:.25rem}.space-x-1>*+*{margin-left:.25rem}.space-x-2>*+*{margin-left:.5rem}.inline-flex{display:inline-flex}.text-gray-700{color:#374151}.text-gray-400{color:#9ca3af}.text-lg{font-size:1.125rem}.text-xl{font-size:1.25rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.w-40{width:10rem}.h-56{height:14rem}.w-56{width:14rem}.h-80{height:20rem}.w-32{width:8rem}.h-44{height:11rem}.object-cover{object-fit:cover}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.shadow-2xl{box-shadow:0 25px 50px -12px #00000040}.shadow-premium{box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a,0 0 0 1px #0000000d}.mx-auto{margin-left:auto;margin-right:auto}.border-4{border-width:4px}.border-white{border-color:#fff}.border-gray-50{border-color:#f9fafb}.w-1\\.5{width:.375rem}.h-1\\.5{height:.375rem}.bg-gray-400{background-color:#9ca3af}.rounded-full{border-radius:9999px}.custom-selection-toolbar{position:absolute;bottom:70px;right:0;z-index:100;width:280px;background:var(--bg-primary);border-radius:16px;box-shadow:var(--shadow-base);display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--border-base);padding:8px 0;transform-origin:bottom right}.custom-selection-toolbar .menu-item{padding:12px 20px;border-bottom:1px solid #f1f5f9;cursor:pointer;transition:all .2s ease;display:flex;align-items:center}.custom-selection-toolbar .menu-item:hover{background:#f8fafc}.custom-selection-toolbar .menu-item:hover .menu-text{color:var(--accent-color)}.custom-selection-toolbar .menu-item.border-none{border-bottom:none}.custom-selection-toolbar .menu-item .menu-text{font-size:15px;font-weight:500;color:#334155}.custom-selection-toolbar.color-picker{width:auto;flex-direction:row;padding:8px 12px;gap:12px;align-items:center}.custom-selection-toolbar.color-picker .color-circle{width:24px;height:24px;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0000001a;transition:transform .2s ease}.custom-selection-toolbar.color-picker .color-circle:hover{transform:scale(1.2)}.custom-selection-toolbar.color-picker .toolbar-btn{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:50%;border:none;background:#f1f5f9;color:#64748b;cursor:pointer;margin-left:4px}.custom-selection-toolbar.color-picker .toolbar-btn:hover{background:#e2e8f0}:host-context(.dark-mode) .custom-selection-toolbar{background:#1e293b;border-color:#334155}:host-context(.dark-mode) .custom-selection-toolbar .menu-item{border-bottom-color:#334155}:host-context(.dark-mode) .custom-selection-toolbar .menu-item:hover{background:#0f172a}:host-context(.dark-mode) .custom-selection-toolbar .menu-item .menu-text{color:#f1f5f9}:host-context(.dark-mode) .custom-selection-toolbar.color-picker .toolbar-btn{background:#334155;color:#f1f5f9}\n"] }]
2109
+ }], ctorParameters: () => [{ type: i0.NgZone }, { type: HttpApiService }, { type: SharedService }, { type: EpubReaderService }, { type: i0.ChangeDetectorRef }, { type: i3.ActivatedRoute }, { type: CustomDialogService }, { type: i4.MessageService }, { type: i1$2.Location }, { type: Object, decorators: [{
2110
+ type: Inject,
2111
+ args: [PLATFORM_ID]
2112
+ }] }, { type: i3.Router }, { type: i1$1.Overlay }], propDecorators: { epubUrl: [{
2113
+ type: Input
2114
+ }], epubBlob: [{
2115
+ type: Input
2116
+ }], coverPage: [{
2117
+ type: Input
2118
+ }], enableDarkMode: [{
2119
+ type: Input
2120
+ }], enableSearch: [{
2121
+ type: Input
2122
+ }], enableHighlights: [{
2123
+ type: Input
2124
+ }], enableBookmarks: [{
2125
+ type: Input
2126
+ }], enableChapterList: [{
2127
+ type: Input
2128
+ }], enableTextSelection: [{
2129
+ type: Input
2130
+ }], enableFontSize: [{
2131
+ type: Input
2132
+ }], enableNotes: [{
2133
+ type: Input
2134
+ }], underlineRemoveRequest: [{
2135
+ type: Output
2136
+ }], handleKeyUp: [{
2137
+ type: HostListener,
2138
+ args: ['document:keyup', ['$event']]
2139
+ }], onKeyDown: [{
2140
+ type: HostListener,
2141
+ args: ['document:keydown', ['$event']]
2142
+ }] } });
2143
+
2144
+ class EpubLoaderComponent {
2145
+ loadPercentage = input(null);
2146
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EpubLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2147
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.17", type: EpubLoaderComponent, isStandalone: false, selector: "app-epub-loader", inputs: { loadPercentage: { classPropertyName: "loadPercentage", publicName: "loadPercentage", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"page-loader\">\n <div class=\"flex flex-col w-full loader-container\">\n <div class=\"text-black text-2xl font-bold mb-2\">Reader Loading</div>\n <p-progressBar\n [value]=\"loadPercentage()\"\n class=\"reader-progress w-full\"\n />\n <div class=\"text-percentage text-base font-normal inline-flex m-auto mt-3\">\n {{ loadPercentage() ? loadPercentage() : '0' }}\n </div>\n </div>\n</div>\n", styles: [".page-loader{position:fixed;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;top:0;background:#fff;z-index:99}.page-loader .loader-container{max-width:80%}@media (min-width: 768px){.page-loader .loader-container{max-width:410px}}\n"], dependencies: [{ kind: "component", type: i1$4.ProgressBar, selector: "p-progressBar", inputs: ["value", "showValue", "styleClass", "style", "unit", "mode", "color"] }] });
2148
+ }
2149
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EpubLoaderComponent, decorators: [{
2150
+ type: Component,
2151
+ args: [{ selector: 'app-epub-loader', standalone: false, template: "<div class=\"page-loader\">\n <div class=\"flex flex-col w-full loader-container\">\n <div class=\"text-black text-2xl font-bold mb-2\">Reader Loading</div>\n <p-progressBar\n [value]=\"loadPercentage()\"\n class=\"reader-progress w-full\"\n />\n <div class=\"text-percentage text-base font-normal inline-flex m-auto mt-3\">\n {{ loadPercentage() ? loadPercentage() : '0' }}\n </div>\n </div>\n</div>\n", styles: [".page-loader{position:fixed;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;top:0;background:#fff;z-index:99}.page-loader .loader-container{max-width:80%}@media (min-width: 768px){.page-loader .loader-container{max-width:410px}}\n"] }]
2152
+ }] });
2153
+
2154
+ const routes = [
2155
+ {
2156
+ path: 'reader',
2157
+ component: ReaderMainComponent,
2158
+ pathMatch: 'full',
2159
+ },
2160
+ {
2161
+ path: 'chapter-list',
2162
+ component: ChapterlistComponent,
2163
+ },
2164
+ ];
2165
+ class EpubReaderModule {
2166
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EpubReaderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2167
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.17", ngImport: i0, type: EpubReaderModule, declarations: [ReaderMainComponent,
2168
+ AddNotesComponent,
2169
+ EpubCommonPopupComponent,
2170
+ ChapterlistComponent,
2171
+ EpubLoaderComponent], imports: [CommonModule, i3.RouterModule, NgxEditorModule,
2172
+ FormsModule,
2173
+ ReactiveFormsModule,
2174
+ TabViewModule,
2175
+ ProgressBarModule,
2176
+ TableModule,
2177
+ ButtonModule,
2178
+ NoDataFoundComponent,
2179
+ HttpClientModule,
2180
+ SkeletonLoaderComponent], exports: [ReaderMainComponent,
2181
+ AddNotesComponent,
2182
+ EpubCommonPopupComponent,
2183
+ ChapterlistComponent,
2184
+ EpubLoaderComponent,
2185
+ SkeletonLoaderComponent] });
2186
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EpubReaderModule, providers: [
2187
+ CustomDialogService,
2188
+ EpubReaderService
2189
+ ], imports: [CommonModule,
2190
+ RouterModule.forChild(routes),
2191
+ NgxEditorModule,
2192
+ FormsModule,
2193
+ ReactiveFormsModule,
2194
+ TabViewModule,
2195
+ ProgressBarModule,
2196
+ TableModule,
2197
+ ButtonModule,
2198
+ NoDataFoundComponent,
2199
+ HttpClientModule,
2200
+ SkeletonLoaderComponent] });
2201
+ }
2202
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EpubReaderModule, decorators: [{
2203
+ type: NgModule,
2204
+ args: [{
2205
+ declarations: [
2206
+ ReaderMainComponent,
2207
+ AddNotesComponent,
2208
+ EpubCommonPopupComponent,
2209
+ ChapterlistComponent,
2210
+ EpubLoaderComponent
2211
+ ],
2212
+ imports: [
2213
+ CommonModule,
2214
+ RouterModule.forChild(routes),
2215
+ NgxEditorModule,
2216
+ FormsModule,
2217
+ ReactiveFormsModule,
2218
+ TabViewModule,
2219
+ ProgressBarModule,
2220
+ TableModule,
2221
+ ButtonModule,
2222
+ NoDataFoundComponent,
2223
+ HttpClientModule,
2224
+ SkeletonLoaderComponent,
2225
+ ],
2226
+ providers: [
2227
+ CustomDialogService,
2228
+ EpubReaderService
2229
+ ],
2230
+ exports: [
2231
+ ReaderMainComponent,
2232
+ AddNotesComponent,
2233
+ EpubCommonPopupComponent,
2234
+ ChapterlistComponent,
2235
+ EpubLoaderComponent,
2236
+ SkeletonLoaderComponent
2237
+ ],
2238
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
2239
+ }]
2240
+ }] });
2241
+
2242
+ // Export other components if needed
2243
+
2244
+ /**
2245
+ * Generated bundle index. Do not edit.
2246
+ */
2247
+
2248
+ export { AddNotesComponent, ChapterlistComponent, EpubCommonPopupComponent, EpubLoaderComponent, EpubReaderModule, EpubReaderService, ReaderMainComponent, SkeletonLoaderComponent };
2249
+ //# sourceMappingURL=epub-flow.mjs.map