PageSourceSearch

https://pixapa.com/assets/@ehyiah/ux-quill/modules/readtimeModule-YcYYac1.js

js pixapa.com collected 2026-10-08 20:23:02 UTC 4,154 bytes, 108 lines download raw bytes

1export default class ReadingTime {
2  quill;
3  wpm;
4  label;
5  suffix;
6  targetElement;
7  readTimeOk;
8  readTimeMedium;
9  toolbarContainer = null;
10  toolbarOriginalPaddingRight = null;
11  targetIsCustom = false;
12  constructor(quill, options) {
13    if (options === void 0) {
14      options = {};
15    }
16    this.quill = quill;
17    this.wpm = options.wpm || 200;
18    this.label = options.label || '⏱ Reading time: ~ ';
19    this.suffix = options.suffix || ' minute(s)';
20    this.readTimeOk = options.readTimeOk || 5;
21    this.readTimeMedium = options.readTimeMedium || 8;
22    if (options.target) {
23      const el = document.querySelector(options.target);
24      if (!el) {
25        throw new Error(`Cannot find target element: ${options.target}`);
26      }
27      this.targetElement = el;
28      this.targetIsCustom = true;
29    } else {
30      this.targetElement = document.createElement('div');
31      this.targetElement.classList.add('ql-reading-time');
32      this.targetElement.style.cssText = `
33                font-size: 12px;
34                font-weight: 500;
35                padding: 4px 10px;
36                border-radius: 6px;
37                background: #f5f5f5;
38                color: #2e7d32;
39                font-family: sans-serif;
40                transition: background 0.3s ease, color 0.3s ease;
41                position: absolute;
42                right: 10px;
43                top: 50%;
44                transform: translateY(-50%);
45                pointer-events: none;
46                box-sizing: border-box;
47                min-width: 48px; /* minimum to stabilise width */
48            `;
49      const toolbar = this.quill.getModule('toolbar');
50      if (toolbar?.container) {
51        const tb = toolbar.container;
52        if (window.getComputedStyle(tb).position === 'static') {
53          tb.style.position = 'relative';
54        }
55        tb.appendChild(this.targetElement);
56        this.toolbarContainer = tb;
57        this.toolbarOriginalPaddingRight = window.getComputedStyle(tb).paddingRight || '';
58      } else {
59        const wrapper = document.createElement('div');
60        wrapper.style.position = 'relative';
61        this.quill.container.parentNode?.insertBefore(wrapper, this.quill.container);
62        wrapper.appendChild(this.quill.container);
63        wrapper.appendChild(this.targetElement);
64        this.toolbarContainer = wrapper;
65        this.toolbarOriginalPaddingRight = window.getComputedStyle(wrapper).paddingRight || '';
66      }
67    }
68    this.updateReadingTime();
69    this.quill.on('text-change', () => this.updateReadingTime());
70    window.addEventListener('resize', this.onWindowResize);
71  }
72  onWindowResize = () => {
73    if (this.toolbarContainer && !this.targetIsCustom) {
74      const width = Math.ceil(this.targetElement.getBoundingClientRect().width);
75      this.toolbarContainer.style.paddingRight = `${width + 12}px`;
76    }
77  };
78  updateReadingTime() {
79    const text = this.quill.getText().trim();
80    const words = text.length > 0 ? text.split(/\s+/).length : 0;
81    const minutes = Math.max(1, Math.ceil(words / this.wpm));
82    if (minutes <= this.readTimeOk) {
83      this.setStyle('#2e7d32', '#e8f5e9');
84    } else if (minutes <= this.readTimeMedium) {
85      this.setStyle('#ef6c00', '#fff3e0');
86    } else {
87      this.setStyle('#c62828', '#ffebee');
88    }
89    this.targetElement.textContent = `${this.label}${minutes}${this.suffix}`;
90    if (this.toolbarContainer && !this.targetIsCustom) {
91      const width = Math.ceil(this.targetElement.getBoundingClientRect().width);
92      this.toolbarContainer.style.paddingRight = `${width + 12}px`; // 12px marge
93    }
94  }
95  setStyle(color, background) {
96    this.targetElement.style.color = color;
97    this.targetElement.style.background = background;
98  }
99  destroy() {
100    if (this.toolbarContainer && this.toolbarOriginalPaddingRight !== null && !this.targetIsCustom) {
101      this.toolbarContainer.style.paddingRight = this.toolbarOriginalPaddingRight;
102    }
103    if (!this.targetIsCustom && this.targetElement.parentNode) {
104      this.targetElement.parentNode.removeChild(this.targetElement);
105    }
106    window.removeEventListener('resize', this.onWindowResize);
107  }
108}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.