PageSourceSearch

https://laguna22.ru/js/readmore.js?v=20260902d

js laguna22.ru collected 2026-09-28 22:45:35 UTC 11,260 bytes, 341 lines download raw bytes

vendor: 11,260 bytes, lines 1-341
1/*!
2 * @preserve
3 *
4 * Readmore.js jQuery plugin
5 * Author: @jed_foster
6 * Project home: http://jedfoster.github.io/Readmore.js
7 * Licensed under the MIT license
8 *
9 * Debounce function from http://davidwalsh.name/javascript-debounce-function
10 */
11
12/* global jQuery */
13
14(function(factory) {
15    if (typeof define === 'function' && define.amd) {
16        // AMD
17        define(['jquery'], factory);
18    } else if (typeof exports === 'object') {
19        // CommonJS
20        module.exports = factory(require('jquery'));
21    } else {
22        // Browser globals
23        factory(jQuery);
24    }
25}(function($) {
26    'use strict';
27
28    var readmore = 'readmore',
29        defaults = {
30            speed: 100,
31            collapsedHeight: 200,
32            heightMargin: 16,
33            moreLink: '<a href="#">Read More</a>',
34            lessLink: '<a href="#">Close</a>',
35            embedCSS: true,
36            blockCSS: 'display: block; width: 100%;',
37            startOpen: false,
38
39            // callbacks
40            blockProcessed: function() {},
41            beforeToggle: function() {},
42            afterToggle: function() {}
43        },
44        cssEmbedded = {},
45        uniqueIdCounter = 0;
46
47    function debounce(func, wait, immediate) {
48        var timeout;
49
50        return function() {
51            var context = this, args = arguments;
52            var later = function() {
53                timeout = null;
54                if (! immediate) {
55                    func.apply(context, args);
56                }
57            };
58            var callNow = immediate && !timeout;
59
60            clearTimeout(timeout);
61            timeout = setTimeout(later, wait);
62
63            if (callNow) {
64                func.apply(context, args);
65            }
66        };
67    }
68
69    function uniqueId(prefix) {
70        var id = ++uniqueIdCounter;
71
72        return String(prefix == null ? 'rmjs-' : prefix) + id;
73    }
74
75    function setBoxHeights(element) {
76        var el = element.clone().css({
77                height: 'auto',
78                width: element.width(),
79                maxHeight: 'none',
80                overflow: 'hidden'
81            }).insertAfter(element),
82            expandedHeight = el.outerHeight(),
83            cssMaxHeight = parseInt(el.css({maxHeight: ''}).css('max-height').replace(/[^-\d\.]/g, ''), 10),
84            defaultHeight = element.data('defaultHeight');
85
86        el.remove();
87
88        var collapsedHeight = cssMaxHeight || element.data('collapsedHeight') || defaultHeight;
89
90        // Store our measurements.
91        element.data({
92                expandedHeight: expandedHeight,
93                maxHeight: cssMaxHeight,
94                collapsedHeight: collapsedHeight
95            })
96            // and disable any `max-height` property set in CSS
97            .css({
98                maxHeight: 'none'
99            });
100    }
101
102    var resizeBoxes = debounce(function() {
103        $('[data-readmore]').each(function() {
104            var current = $(this),
105                isExpanded = (current.attr('aria-expanded') === 'true');
106
107            setBoxHeights(current);
108
109            current.css({
110                height: current.data( (isExpanded ? 'expandedHeight' : 'collapsedHeight') )
111            });
112        });
113    }, 100);
114
115    function embedCSS(options) {
116        if (! cssEmbedded[options.selector]) {
117            var styles = ' ';
118
119            if (options.embedCSS && options.blockCSS !== '') {
120                styles += options.selector + ' + [data-readmore-toggle], ' +
121                    options.selector + '[data-readmore]{' +
122                    options.blockCSS +
123                    '}';
124            }
125
126            // Include the transition CSS even if embedCSS is false
127            styles += options.selector + '[data-readmore]{' +
128                'transition: height ' + options.speed + 'ms;' +
129                'overflow: hidden;' +
130                '}';
131
132            (function(d, u) {
133                var css = d.createElement('style');
134                css.type = 'text/css';
135
136                if (css.styleSheet) {
137                    css.styleSheet.cssText = u;
138                }
139                else {
140                    css.appendChild(d.createTextNode(u));
141                }
142
143                d.getElementsByTagName('head')[0].appendChild(css);
144            }(document, styles));
145
146            cssEmbedded[options.selector] = true;
147        }
148    }
149
150    function Readmore(element, options) {
151        this.element = element;
152
153        this.options = $.extend({}, defaults, options);
154
155        embedCSS(this.options);
156
157        this._defaults = defaults;
158        this._name = readmore;
159
160        this.init();
161
162        // IE8 chokes on `window.addEventListener`, so need to test for support.
163        if (window.addEventListener) {
164            // Need to resize boxes when the page has fully loaded.
165            window.addEventListener('load', resizeBoxes);
166            window.addEventListener('resize', resizeBoxes);
167        }
168        else {
169            window.attachEvent('load', resizeBoxes);
170            window.attachEvent('resize', resizeBoxes);
171        }
172    }
173
174
175    Readmore.prototype = {
176        init: function() {
177            var current = $(this.element);
178
179            current.data({
180                defaultHeight: this.options.collapsedHeight,
181                heightMargin: this.options.heightMargin
182            });
183
184            setBoxHeights(current);
185
186            var collapsedHeight = current.data('collapsedHeight'),
187                heightMargin = current.data('heightMargin');
188
189            if (current.outerHeight(true) <= collapsedHeight + heightMargin) {
190                // The block is shorter than the limit, so there's no need to truncate it.
191                if (this.options.blockProcessed && typeof this.options.blockProcessed === 'function') {
192                    this.options.blockProcessed(current, false);
193                }
194                return true;
195            }
196            else {
197                var id = current.attr('id') || uniqueId(),
198                    useLink = this.options.startOpen ? this.options.lessLink : this.options.moreLink;
199
200                current.attr({
201                    'data-readmore': '',
202                    'aria-expanded': this.options.startOpen,
203                    'id': id
204                });
205
206                current.after($(useLink)
207                    .on('click', (function(_this) {
208                        return function(event) {
209                            _this.toggle(this, current[0], event);
210                        };
211                    })(this))
212                    .attr({
213                        'data-readmore-toggle': id,
214                        'aria-controls': id
215                    }));
216
217                if (! this.options.startOpen) {
218                    current.css({
219                        height: collapsedHeight
220                    });
221                }
222
223                if (this.options.blockProcessed && typeof this.options.blockProcessed === 'function') {
224                    this.options.blockProcessed(current, true);
225                }
226            }
227        },
228
229        toggle: function(trigger, element, event) {
230            if (event) {
231                event.preventDefault();
232            }
233
234            if (! trigger) {
235                trigger = $('[aria-controls="' + this.element.id + '"]')[0];
236            }
237
238            if (! element) {
239                element = this.element;
240            }
241
242            var $element = $(element),
243                newHeight = '',
244                newLink = '',
245                expanded = false,
246                collapsedHeight = $element.data('collapsedHeight');
247
248            if ($element.height() <= collapsedHeight) {
249                newHeight = $element.data('expandedHeight') + 'px';
250                newLink = 'lessLink';
251                expanded = true;
252            }
253            else {
254                newHeight = collapsedHeight;
255                newLink = 'moreLink';
256            }
257
258            // Fire beforeToggle callback
259            // Since we determined the new "expanded" state above we're now out of sync
260            // with our true current state, so we need to flip the value of `expanded`
261            if (this.options.beforeToggle && typeof this.options.beforeToggle === 'function') {
262                this.options.beforeToggle(trigger, $element, ! expanded);
263            }
264
265            $element.css({'height': newHeight});
266
267            // Fire afterToggle callback
268            $element.on('transitionend', (function(_this) {
269                return function() {
270                    if (_this.options.afterToggle && typeof _this.options.afterToggle === 'function') {
271                        _this.options.afterToggle(trigger, $element, expanded);
272                    }
273
274                    $(this).attr({
275                        'aria-expanded': expanded
276                    }).off('transitionend');
277                }
278            })(this));
279
280            $(trigger).replaceWith($(this.options[newLink])
281                .on('click', (function(_this) {
282                    return function(event) {
283                        _this.toggle(this, element, event);
284                    };
285                })(this))
286                .attr({
287                    'data-readmore-toggle': $element.attr('id'),
288                    'aria-controls': $element.attr('id')
289                }));
290        },
291
292        destroy: function() {
293            $(this.element).each(function() {
294                var current = $(this);
295
296                current.attr({
297                        'data-readmore': null,
298                        'aria-expanded': null
299                    })
300                    .css({
301                        maxHeight: '',
302                        height: ''
303                    })
304                    .next('[data-readmore-toggle]')
305                    .remove();
306
307                current.removeData();
308            });
309        }
310    };
311
312
313    $.fn.readmore = function(options) {
314        var args = arguments,
315            selector = this.selector;
316
317        options = options || {};
318
319        if (typeof options === 'object') {
320            return this.each(function() {
321                if ($.data(this, 'plugin_' + readmore)) {
322                    var instance = $.data(this, 'plugin_' + readmore);
323                    instance.destroy.apply(instance);
324                }
325
326                options.selector = selector;
327
328                $.data(this, 'plugin_' + readmore, new Readmore(this, options));
329            });
330        }
331        else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
332            return this.each(function () {
333                var instance = $.data(this, 'plugin_' + readmore);
334                if (instance instanceof Readmore && typeof instance[options] === 'function') {
335                    instance[options].apply(instance, Array.prototype.slice.call(args, 1));
336                }
337            });
338        }
339    };
340
341}));

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.