PageSourceSearch

https://showcase.kumparan.com/addons/default/themes/showcase/js/jquery.dotdotdot.js

js kumparan.com collected 2026-09-24 08:54:57 UTC 20,651 bytes, 654 lines download raw bytes

1/*
2 *  jQuery dotdotdot 1.8.3
3 *
4 *  Copyright (c) Fred Heusschen
5 *  www.frebsite.nl
6 *
7 *  Plugin website:
8 *  dotdotdot.frebsite.nl
9 *
10 *  Licensed under the MIT license.
11 *  http://en.wikipedia.org/wiki/MIT_License
12 */
13
14(function($, undef) {
15    'use strict';
16    if ($.fn.dotdotdot) {
17        return;
18    }
19
20    $.fn.dotdotdot = function(o) {
21        if (this.length === 0) {
22            $.fn.dotdotdot.debug('No element found for "' + this.selector + '".');
23            return this;
24        }
25        if (this.length > 1) {
26            return this.each(
27                function() {
28                    $(this).dotdotdot(o);
29                }
30            );
31        }
32
33        var $dot = this;
34        var orgContent = $dot.contents();
35
36        if ($dot.data('dotdotdot')) {
37            $dot.trigger('destroy.dot');
38        }
39
40        $dot.data('dotdotdot-style', $dot.attr('style') || '');
41        $dot.css('word-wrap', 'break-word');
42        if ($dot.css('white-space') === 'nowrap') {
43            $dot.css('white-space', 'normal');
44        }
45        $dot.bind_events = function() {
46            $dot.bind(
47                'update.dot',
48                function(e, c) {
49                    $dot.removeClass("is-truncated");
50                    e.preventDefault();
51                    e.stopPropagation();
52
53                    switch (typeof opts.height) {
54                        case 'number':
55                            opts.maxHeight = opts.height;
56                            break;
57
58                        case 'function':
59                            opts.maxHeight = opts.height.call($dot[0]);
60                            break;
61
62                        default:
63                            opts.maxHeight = getTrueInnerHeight($dot);
64                            break;
65                    }
66
67                    opts.maxHeight += opts.tolerance;
68
69                    if (typeof c != 'undefined') {
70                        if (typeof c == 'string' || ('nodeType' in c && c.nodeType === 1)) {
71                            c = $('<div />').append(c).contents();
72                        }
73                        if (c instanceof $) {
74                            orgContent = c;
75                        }
76                    }
77
78                    $inr = $dot.wrapInner('<div class="dotdotdot" />').children();
79                    $inr.contents()
80                        .detach()
81                        .end()
82                        .append(orgContent.clone(true))
83                        .find('br')
84                        .replaceWith('  <br />  ')
85                        .end()
86                        .css({
87                            'height': 'auto',
88                            'width': 'auto',
89                            'border': 'none',
90                            'padding': 0,
91                            'margin': 0
92                        });
93
94                    var after = false,
95                        trunc = false;
96
97                    if (conf.afterElement) {
98                        after = conf.afterElement.clone(true);
99                        after.show();
100                        conf.afterElement.detach();
101                    }
102
103                    if (test($inr, opts)) {
104                        if (opts.wrap == 'children') {
105                            trunc = children($inr, opts, after);
106                        } else {
107                            trunc = ellipsis($inr, $dot, $inr, opts, after);
108                        }
109                    }
110                    $inr.replaceWith($inr.contents());
111                    $inr = null;
112
113                    if ($.isFunction(opts.callback)) {
114                        opts.callback.call($dot[0], trunc, orgContent);
115                    }
116
117                    conf.isTruncated = trunc;
118                    if($dot.parent('.with-line').length){
119                        $dot.css('display', 'unset');
120                    }
121                    return trunc;
122                }
123
124            ).bind(
125                'isTruncated.dot',
126                function(e, fn) {
127                    e.preventDefault();
128                    e.stopPropagation();
129
130                    if (typeof fn == 'function') {
131                        fn.call($dot[0], conf.isTruncated);
132                    }
133                    return conf.isTruncated;
134                }
135
136            ).bind(
137                'originalContent.dot',
138                function(e, fn) {
139                    e.preventDefault();
140                    e.stopPropagation();
141
142                    if (typeof fn == 'function') {
143                        fn.call($dot[0], orgContent);
144                    }
145                    return orgContent;
146                }
147
148            ).bind(
149                'destroy.dot',
150                function(e) {
151                    e.preventDefault();
152                    e.stopPropagation();
153
154                    $dot.unwatch()
155                        .unbind_events()
156                        .contents()
157                        .detach()
158                        .end()
159                        .append(orgContent)
160                        .attr('style', $dot.data('dotdotdot-style') || '')
161                        .removeClass('is-truncated')
162                        .data('dotdotdot', false);
163                }
164            );
165            return $dot;
166        }; //   /bind_events
167
168        $dot.unbind_events = function() {
169            $dot.unbind('.dot');
170            return $dot;
171        }; //   /unbind_events
172
173        $dot.watch = function() {
174            $dot.unwatch();
175            if (opts.watch == 'window') {
176                var $window = $(window),
177                    _wWidth = $window.width(),
178                    _wHeight = $window.height();
179
180                $window.bind(
181                    'resize.dot' + conf.dotId,
182                    function() {
183                        if (_wWidth != $window.width() || _wHeight != $window.height() || !opts.windowResizeFix) {
184                            _wWidth = $window.width();
185                            _wHeight = $window.height();
186
187                            if (watchInt) {
188                                clearInterval(watchInt);
189                            }
190                            watchInt = setTimeout(
191                                function() {
192                                    $dot.trigger('update.dot');
193                                }, 100
194                            );
195                        }
196                    }
197                );
198            } else {
199                watchOrg = getSizes($dot);
200                watchInt = setInterval(
201                    function() {
202                        if ($dot.is(':visible')) {
203                            var watchNew = getSizes($dot);
204                            if (watchOrg.width != watchNew.width ||
205                                watchOrg.height != watchNew.height) {
206                                $dot.trigger('update.dot');
207                                watchOrg = watchNew;
208                            }
209                        }
210                    }, 500
211                );
212            }
213            return $dot;
214        };
215        $dot.unwatch = function() {
216            $(window).unbind('resize.dot' + conf.dotId);
217            if (watchInt) {
218                clearInterval(watchInt);
219            }
220            return $dot;
221        };
222
223        var opts = $.extend(true, {}, $.fn.dotdotdot.defaults, o),
224            conf = {},
225            watchOrg = {},
226            watchInt = null,
227            $inr = null;
228
229
230        if (!(opts.lastCharacter.remove instanceof Array)) {
231            opts.lastCharacter.remove = $.fn.dotdotdot.defaultArrays.lastCharacter.remove;
232        }
233        if (!(opts.lastCharacter.noEllipsis instanceof Array)) {
234            opts.lastCharacter.noEllipsis = $.fn.dotdotdot.defaultArrays.lastCharacter.noEllipsis;
235        }
236
237
238        conf.afterElement = getElement(opts.after, $dot);
239        conf.isTruncated = false;
240        conf.dotId = dotId++;
241
242
243        $dot.data('dotdotdot', true)
244            .bind_events()
245            .trigger('update.dot');
246
247        if (opts.watch) {
248            $dot.watch();
249        }
250
251        return $dot;
252    };
253
254
255    //  public
256    $.fn.dotdotdot.defaults = {
257        'ellipsis': '... ',
258        'wrap': 'word',
259        'fallbackToLetter': true,
260        'lastCharacter': {},
261        'tolerance': 0,
262        'callback': null,
263        'after': null,
264        'height': null,
265        'watch': false,
266        'windowResizeFix': true,
267        'maxLength': null
268    };
269    $.fn.dotdotdot.defaultArrays = {
270        'lastCharacter': {
271            'remove': [' ', '\u3000', ',', ';', '.', '!', '?'],
272            'noEllipsis': []
273        }
274    };
275    $.fn.dotdotdot.debug = function(msg) {};
276
277
278    //  private
279    var dotId = 1;
280
281    function children($elem, o, after) {
282        var $elements = $elem.children(),
283            isTruncated = false;
284
285        $elem.empty();
286
287        for (var a = 0, l = $elements.length; a < l; a++) {
288            var $e = $elements.eq(a);
289            $elem.append($e);
290            if (after) {
291                $elem.append(after);
292            }
293            if (test($elem, o)) {
294                $e.remove();
295                isTruncated = true;
296                break;
297            } else {
298                if (after) {
299                    after.detach();
300                }
301            }
302        }
303        return isTruncated;
304    }
305
306    function ellipsis($elem, $d, $i, o, after) {
307        var isTruncated = false;
308
309        //  Don't put the ellipsis directly inside these elements
310        var notx = 'a, table, thead, tbody, tfoot, tr, col, colgroup, object, embed, param, ol, ul, dl, blockquote, select, optgroup, option, textarea, script, style';
311
312        //  Don't remove these elements even if they are after the ellipsis
313        var noty = 'script, .dotdotdot-keep';
314
315        $elem
316            .contents()
317            .detach()
318            .each(
319                function() {
320
321                    var e = this,
322                        $e = $(e);
323
324                    if (typeof e == 'undefined') {
325                        return true;
326                    } else if ($e.is(noty)) {
327                        $elem.append($e);
328                    } else if (isTruncated) {
329                        return true;
330                    } else {
331                        $elem.append($e);
332                        if (after && !$e.is(o.after) && !$e.find(o.after).length) {
333                            $elem[$elem.is(notx) ? 'after' : 'append'](after);
334                        }
335                        if (test($i, o)) {
336                            if (e.nodeType == 3) // node is TEXT
337                            {
338                                isTruncated = ellipsisElement($e, $d, $i, o, after);
339                            } else {
340                                isTruncated = ellipsis($e, $d, $i, o, after);
341                            }
342                        }
343
344                        if (!isTruncated) {
345                            if (after) {
346                                after.detach();
347                            }
348                        }
349                    }
350                }
351            );
352        $d.addClass("is-truncated");
353        return isTruncated;
354    }
355
356    function ellipsisElement($e, $d, $i, o, after) {
357        var e = $e[0];
358
359        if (!e) {
360            return false;
361        }
362
363        var txt = getTextContent(e),
364            space = (txt.indexOf(' ') !== -1) ? ' ' : '\u3000',
365            separator = (o.wrap == 'letter') ? '' : space,
366            textArr = txt.split(separator),
367            position = -1,
368            midPos = -1,
369            startPos = 0,
370            endPos = textArr.length - 1;
371
372
373        //  Only one word
374        if (o.fallbackToLetter && startPos === 0 && endPos === 0) {
375            separator = '';
376            textArr = txt.split(separator);
377            endPos = textArr.length - 1;
378        }
379
380        if (o.maxLength) {
381            txt = addEllipsis(txt.trim().substr(0, o.maxLength), o);
382            setTextContent(e, txt);
383        } else {
384
385            while (startPos <= endPos && !(startPos === 0 && endPos === 0)) {
386                var m = Math.floor((startPos + endPos) / 2);
387                if (m == midPos) {
388                    break;
389                }
390                midPos = m;
391
392                setTextContent(e, textArr.slice(0, midPos + 1).join(separator) + o.ellipsis);
393
394                $i.children().each(function() {
395                    $(this).toggle().toggle();
396                });
397
398                if (!test($i, o)) {
399                    position = midPos;
400                    startPos = midPos;
401                } else {
402                    endPos = midPos;
403
404                    //  Fallback to letter
405                    if (o.fallbackToLetter && startPos === 0 && endPos === 0) {
406                        separator = '';
407                        textArr = textArr[0].split(separator);
408                        position = -1;
409                        midPos = -1;
410                        startPos = 0;
411                        endPos = textArr.length - 1;
412                    }
413                }
414            }
415
416            if (position != -1 && !(textArr.length === 1 && textArr[0].length === 0)) {
417                txt = addEllipsis(textArr.slice(0, position + 1).join(separator), o);
418                setTextContent(e, txt);
419            } else {
420                var $w = $e.parent();
421                $e.detach();
422
423                var afterLength = (after && after.closest($w).length) ? after.length : 0;
424
425                if ($w.contents().length > afterLength) {
426                    e = findLastTextNode($w.contents().eq(-1 - afterLength), $d);
427                } else {
428                    e = findLastTextNode($w, $d, true);
429                    if (!afterLength) {
430                        $w.detach();
431                    }
432                }
433                if (e) {
434                    txt = addEllipsis(getTextContent(e), o);
435                    setTextContent(e, txt);
436                    if (afterLength && after) {
437                        var $parent = after.parent();
438
439                        $(e).parent().append(after);
440
441                        if (!$.trim($parent.html())) {
442                            $parent.remove();
443                        }
444                    }
445                }
446            }
447        }
448
449        return true;
450    }
451
452    function test($i, o) {
453        return ($i.innerHeight() > o.maxHeight || (o.maxLength && $i.text().trim().length > o.maxLength));
454    }
455
456    function addEllipsis(txt, o) {
457        while ($.inArray(txt.slice(-1), o.lastCharacter.remove) > -1) {
458            txt = txt.slice(0, -1);
459        }
460        if ($.inArray(txt.slice(-1), o.lastCharacter.noEllipsis) < 0) {
461            txt += o.ellipsis;
462        }
463        return txt;
464    }
465
466    function getSizes($d) {
467        return {
468            'width': $d.innerWidth(),
469            'height': $d.innerHeight()
470        };
471    }
472
473    function setTextContent(e, content) {
474        if (e.innerText) {
475            e.innerText = content;
476        } else if (e.nodeValue) {
477            e.nodeValue = content;
478        } else if (e.textContent) {
479            e.textContent = content;
480        }
481
482    }
483
484    function getTextContent(e) {
485        if (e.innerText) {
486            return e.innerText;
487        } else if (e.nodeValue) {
488            return e.nodeValue;
489        } else if (e.textContent) {
490            return e.textContent;
491        } else {
492            return "";
493        }
494    }
495
496    function getPrevNode(n) {
497        do {
498            n = n.previousSibling;
499        }
500        while (n && n.nodeType !== 1 && n.nodeType !== 3);
501
502        return n;
503    }
504
505    function findLastTextNode($el, $top, excludeCurrent) {
506        var e = $el && $el[0],
507            p;
508        if (e) {
509            if (!excludeCurrent) {
510                if (e.nodeType === 3) {
511                    return e;
512                }
513                if ($.trim($el.text())) {
514                    return findLastTextNode($el.contents().last(), $top);
515                }
516            }
517            p = getPrevNode(e);
518            while (!p) {
519                $el = $el.parent();
520                if ($el.is($top) || !$el.length) {
521                    return false;
522                }
523                p = getPrevNode($el[0]);
524            }
525            if (p) {
526                return findLastTextNode($(p), $top);
527            }
528        }
529        return false;
530    }
531
532    function getElement(e, $i) {
533        if (!e) {
534            return false;
535        }
536        if (typeof e === 'string') {
537            e = $(e, $i);
538            return (e.length) ?
539                e :
540                false;
541        }
542        return !e.jquery ?
543            false :
544            e;
545    }
546
547    function getTrueInnerHeight($el) {
548        var h = $el.innerHeight(),
549            a = ['paddingTop', 'paddingBottom'];
550
551        for (var z = 0, l = a.length; z < l; z++) {
552            var m = parseInt($el.css(a[z]), 10);
553            if (isNaN(m)) {
554                m = 0;
555            }
556            h -= m;
557        }
558        return h;
559    }
560
561
562    //  override jQuery.html
563    var _orgHtml = $.fn.html;
564    $.fn.html = function(str) {
565        if (str != undef && !$.isFunction(str) && this.data('dotdotdot')) {
566            return this.trigger('update', [str]);
567        }
568        return _orgHtml.apply(this, arguments);
569    };
570
571
572    //  override jQuery.text
573    var _orgText = $.fn.text;
574    $.fn.text = function(str) {
575        if (str != undef && !$.isFunction(str) && this.data('dotdotdot')) {
576            str = $('<div />').text(str).html();
577            return this.trigger('update', [str]);
578        }
579        return _orgText.apply(this, arguments);
580    };
581
582
583})(jQuery);
584
585/*
586
587## Automatic parsing for CSS classes
588Contributed by [Ramil Valitov](https://github.com/rvalitov)
589
590### The idea
591You can add one or several CSS classes to HTML elements to automatically invoke "jQuery.dotdotdot functionality" and some extra features. It allows to use jQuery.dotdotdot only by adding appropriate CSS classes without JS programming.
592
593### Available classes and their description
594* dot-ellipsis - automatically invoke jQuery.dotdotdot to this element. This class must be included if you plan to use other classes below.
595* dot-resize-update - automatically update if window resize event occurs. It's equivalent to option `watch:'window'`.
596* dot-timer-update - automatically update at regular intervals using setInterval. It's equivalent to option `watch:true`.
597* dot-load-update - automatically update after the window has beem completely rendered. Can be useful if your content is generated dynamically using using JS and, hence, jQuery.dotdotdot can't correctly detect the height of the element before it's rendered completely.
598* dot-height-XXX - available height of content area in pixels, where XXX is a number, e.g. can be `dot-height-35` if you want to set maximum height for 35 pixels. It's equivalent to option `height:'XXX'`.
599
600### Usage examples
601*Adding jQuery.dotdotdot to element*
602
603    <div class="dot-ellipsis">
604    <p>Lorem Ipsum is simply dummy text.</p>
605    </div>
606
607*Adding jQuery.dotdotdot to element with update on window resize*
608
609    <div class="dot-ellipsis dot-resize-update">
610    <p>Lorem Ipsum is simply dummy text.</p>
611    </div>
612
613*Adding jQuery.dotdotdot to element with predefined height of 50px*
614
615    <div class="dot-ellipsis dot-height-50">
616    <p>Lorem Ipsum is simply dummy text.</p>
617    </div>
618
619*/
620
621jQuery(document).ready(function($) {
622    //We only invoke jQuery.dotdotdot on elements that have dot-ellipsis class
623    $(".dot-ellipsis").each(function() {
624        //Checking if update on window resize required
625        var watch_window = $(this).hasClass("dot-resize-update");
626
627        //Checking if update on timer required
628        var watch_timer = $(this).hasClass("dot-timer-update");
629
630        //Checking if height set
631        var height = 0;
632        var classList = $(this).attr('class').split(/\s+/);
633        $.each(classList, function(index, item) {
634            var matchResult = item.match(/^dot-height-(\d+)$/);
635            if (matchResult !== null)
636                height = Number(matchResult[1]);
637        });
638
639        //Invoking jQuery.dotdotdot
640        var x = {};
641        if (watch_timer)
642            x.watch = true;
643        if (watch_window)
644            x.watch = 'window';
645        if (height > 0)
646            x.height = height;
647        $(this).dotdotdot(x);
648    });
649});
650
651//Updating elements (if any) on window.load event
652jQuery(window).on('load', function() {
653    jQuery(".dot-ellipsis.dot-load-update").trigger("update.dot");
654});

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.