PageSourceSearch

https://www.medioq.com/js/listing-description.js?v=20260916-3

js medioq.com collected 2026-10-02 06:57:51 UTC 6,412 bytes, 150 lines download raw bytes

1(function () {
2    'use strict';
3
4    var nextId = 0;
5
6    function trimPreviewEnd(element) {
7        while (element.lastChild) {
8            var child = element.lastChild;
9            if (child.nodeType === 1) {
10                if (child.tagName === 'IMG') return;
11                trimPreviewEnd(child);
12                if (child.textContent.trim() || child.querySelector('img')) return;
13            } else if (child.nodeType === 3 && child.textContent.trim()) {
14                return;
15            }
16            element.removeChild(child);
17        }
18    }
19
20    // Find the end of the eighth rendered text line, including lines in later
21    // paragraphs. Paragraph margins are spacing, not part of a text line.
22    function previewRange(content) {
23        var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);
24        var range = document.createRange();
25        var line = null;
26        var count = 0;
27        var node;
28        while ((node = walker.nextNode())) {
29            range.selectNodeContents(node);
30            var rects = Array.prototype.filter.call(range.getClientRects(), function (rect) {
31                return rect.width > 0 && rect.height > 0;
32            });
33            for (var i = 0; i < rects.length; i++) {
34                var rect = rects[i];
35                if (line && rect.top < line.bottom - 1 && rect.bottom > line.top + 1) {
36                    line.top = Math.min(line.top, rect.top);
37                    line.bottom = Math.max(line.bottom, rect.bottom);
38                    continue;
39                }
40                if (count === 8) {
41                    // Binary search only the text node that crosses the boundary.
42                    var low = 0;
43                    var high = node.length;
44                    while (low < high) {
45                        var middle = Math.ceil((low + high) / 2);
46                        range.setEnd(node, middle);
47                        var beyond = Array.prototype.some.call(range.getClientRects(), function (part) {
48                            return part.width > 0 && part.bottom > line.bottom + 0.5;
49                        });
50                        if (beyond) high = middle - 1;
51                        else low = middle;
52                    }
53                    range.setStart(content, 0);
54                    range.setEnd(node, low);
55                    return range;
56                }
57                line = { top: rect.top, bottom: rect.bottom };
58                count++;
59            }
60        }
61        return null;
62    }
63
64    function init(content) {
65        if (content.dataset.descriptionReady) return;
66        // The server supplies labels from the active site's translation config.
67        var copy = [content.getAttribute('data-description-more'), content.getAttribute('data-description-less')];
68        if (!copy[0] || !copy[1]) return;
69        content.dataset.descriptionReady = 'true';
70        content.classList.add('listing-description-content');
71        if (!content.id) {
72            var id;
73            do { id = 'listing-description-' + (++nextId); }
74            while (document.getElementById(id));
75            content.id = id;
76        }
77        var button = document.createElement('button');
78        button.type = 'button';
79        button.className = 'listing-description-toggle';
80        button.hidden = true;
81        button.setAttribute('aria-controls', content.id);
82        content.insertAdjacentElement('afterend', button);
83        var preview = content.cloneNode(false);
84        ['id', 'data-listing-description', 'data-description-ready', 'data-description-more', 'data-description-less', 'itemprop', 'itemscope', 'itemtype'].forEach(function (attribute) {
85            preview.removeAttribute(attribute);
86        });
87        preview.classList.add('listing-description-preview');
88        preview.hidden = true;
89        content.insertAdjacentElement('beforebegin', preview);
90        var expanded = false;
91        var long = false;
92
93        function render() {
94            var collapsed = long && !expanded;
95            content.hidden = collapsed;
96            preview.hidden = !collapsed;
97            button.hidden = !long;
98            button.textContent = expanded ? copy[1] : copy[0];
99            button.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
100        }
101
102        function update() {
103            // Measure the original at its real width. This synchronous swap is
104            // completed before paint; the full content remains untouched.
105            preview.hidden = true;
106            content.hidden = false;
107            var range = previewRange(content);
108            long = range !== null;
109            preview.textContent = '';
110            if (range) {
111                preview.appendChild(range.cloneContents());
112                preview.querySelectorAll('[id], [itemprop], [itemscope], [itemtype]').forEach(function (element) {
113                    ['id', 'itemprop', 'itemscope', 'itemtype'].forEach(function (attribute) {
114                        element.removeAttribute(attribute);
115                    });
116                });
117                // A boundary at the start of a paragraph can leave an empty
118                // paragraph or trailing BR. Neither belongs in the preview.
119                trimPreviewEnd(preview);
120            }
121            render();
122        }
123
124        button.addEventListener('click', function () {
125            expanded = !expanded;
126            render();
127            if (!expanded && button.getBoundingClientRect().top < 0) {
128                button.scrollIntoView({ block: 'nearest' });
129            }
130        });
131        update();
132        if (window.ResizeObserver) {
133            var parent = content.parentElement;
134            var width = parent.getBoundingClientRect().width;
135            new ResizeObserver(function () {
136                var newWidth = parent.getBoundingClientRect().width;
137                if (newWidth !== width) { width = newWidth; update(); }
138            }).observe(parent);
139        }
140        window.addEventListener('resize', update);
141        content.addEventListener('load', update, true);
142        if (document.fonts && document.fonts.ready) document.fonts.ready.then(update);
143    }
144
145    function start() {
146        document.querySelectorAll('[data-listing-description]').forEach(init);
147    }
148    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start);
149    else start();
150}());

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.