PageSourceSearch

https://www.visitfuerteventura.com/themes/custom/hic_front_theme/js/truncate/truncate.js?v=0.1

js visitfuerteventura.com collected 2026-10-02 11:35:32 UTC 14,992 bytes, 539 lines download raw bytes

1(function(module, $, undefined) {
2
3 var BLOCK_TAGS = ['table', 'thead', 'tbody', 'tfoot', 'tr', 'col', 'colgroup',
4   'object', 'embed', 'param', 'ol', 'ul', 'dl', 'blockquote', 'select',
5   'optgroup', 'option', 'textarea', 'script', 'style'];
6
7 /*
8  * Trim function. Trim only end of string whitespaces
9  * 
10  * text - String to trim
11  * 
12  * Returns text without end whitespaces
13  */
14 function trimRight(text) {
15  return text.replace(/\s*$/, "");
16 }
17
18 function setText(element, text) {
19  if (element.innerText) {
20   element.innerText = text;
21  } else if (element.nodeValue) {
22   element.nodeValue = text;
23  } else if (element.textContent) {
24   element.textContent = text;
25  } else {
26   return false;
27  }
28 }
29
30 /*
31  * Truncate the nearest sibling node. If no valid immediate sibling is found,
32  * traverse one level up to a cousin node.
33  * 
34  * $element - The jQuery node to truncate. $rootNode - The jQuery root node to
35  * measure the truncated height. $clipNode - The jQuery node to insert right
36  * after the truncation point. options - An object containing: ellipsis - The
37  * ellipsis string to append at the end of the truncation. maxHeight - The
38  * maximum height for the root node (in px). position - The position of the
39  * truncation ("start", "middle", "end").
40  * 
41  * Returns true if truncation happened, false otherwise.
42  */
43 function truncateNearestSibling($element, $rootNode, $clipNode, options) {
44  var $parent = $element.parent();
45  var $prevSibling;
46
47  $element.remove();
48
49  // Take into account length of $clipNode element previous inserted.
50  var clipLength = $clipNode ? $clipNode.length : 0;
51
52  if ($parent.contents().length > clipLength) {
53
54   // Valid previous sibling element (sharing same parent node) exists,
55   // so attempt to truncate it.
56   $prevSibling = $parent.contents().eq(-1 - clipLength);
57   return truncateTextContent($prevSibling, $rootNode, $clipNode, options);
58
59  } else {
60
61   // No previous sibling element (sharing same parent node) exists.
62   // Therefore, search parent's sibling.
63
64   var $parentSibling = $parent.prev();
65   $prevSibling = $parentSibling.contents().eq(-1);
66
67   if ($prevSibling.length) {
68
69    // Because traversal is in-order so the algorithm already checked that
70    // this point meets the height requirement. As such, it's safe to truncate
71    // here.
72    setText($prevSibling[0], $prevSibling.text() + options.ellipsis);
73    $parent.remove();
74
75    if ($clipNode.length) {
76     $parentSibling.append($clipNode);
77    }
78    return true;
79   }
80  }
81
82  return false;
83 }
84
85 /* Truncates, at the beginning, the text content of a node using binary search */
86 function truncateTextStart($element, $rootNode, $clipNode, options) {
87  var element = $element[0];
88  var original = $element.text();
89
90  var maxChunk = '';
91  var mid, chunk;
92  var low = 0;
93  var high = original.length;
94
95  while (low <= high) {
96   mid = low + ((high - low) >> 1); // Integer division
97
98   chunk = options.ellipsis
99     + trimRight(original.substr(mid - 1, original.length));
100   setText(element, chunk);
101
102   if ($rootNode.height() > options.maxHeight) {
103    // too big, reduce the chunk
104    low = mid + 1;
105   } else {
106    // chunk valid, try to get a bigger chunk
107    high = mid - 1;
108    maxChunk = maxChunk.length > chunk.length ? maxChunk : chunk;
109   }
110  }
111
112  if (maxChunk.length > 0) {
113   setText(element, maxChunk);
114   return true;
115  } else {
116   return truncateNearestSibling($element, $rootNode, $clipNode, options);
117  }
118 }
119
120 /* Truncates, at the end, the text content of a node using binary search */
121 function truncateTextEnd($element, $rootNode, $clipNode, options) {
122  var element = $element[0];
123  var original = $element.text();
124
125  var maxChunk = '';
126  var mid, chunk;
127  var low = 0;
128  var high = original.length;
129
130  // Binary Search
131  while (low <= high) {
132   mid = low + ((high - low) >> 1); // Integer division
133
134   chunk = trimRight(original.substr(0, mid + 1)) + options.ellipsis;
135   setText(element, chunk);
136
137   if ($rootNode.height() > options.maxHeight) {
138    // too big, reduce the chunk
139    high = mid - 1;
140   } else {
141    // chunk valid, try to get a bigger chunk
142    low = mid + 1;
143    maxChunk = maxChunk.length > chunk.length ? maxChunk : chunk;
144   }
145  }
146
147  if (maxChunk.length > 0) {
148   setText(element, maxChunk);
149   return true;
150  } else {
151   return truncateNearestSibling($element, $rootNode, $clipNode, options);
152  }
153 }
154
155 /* Truncates, at the middle, the text content of a node using binary search */
156 function truncateTextMiddle($element, $rootNode, $clipNode, options) {
157  var element = $element[0];
158  var original = $element.text();
159
160  var maxChunk = '';
161  var low = 0;
162  var len = original.length;
163  var high = len >> 1;
164  var mid, chunk;
165
166  while (low <= high) {
167   mid = low + ((high - low) >> 1); // Integer division
168
169   chunk = trimRight(original.substr(0, mid)) + options.ellipsis
170     + original.substr(len - mid, len - mid);
171   setText(element, chunk);
172
173   if ($rootNode.height() > options.maxHeight) {
174    // too big, reduce the chunk
175    high = mid - 1;
176   } else {
177    // chunk valid, try to get a bigger chunk
178    low = mid + 1;
179
180    maxChunk = maxChunk.length > chunk.length ? maxChunk : chunk;
181   }
182  }
183
184  if (maxChunk.length > 0) {
185   setText(element, maxChunk);
186   return true;
187  } else {
188   return truncateNearestSibling($element, $rootNode, $clipNode, options);
189  }
190 }
191
192 /*
193  * Truncates the text content of a node using binary search. If no valid
194  * truncation point is found, attempt to truncate its nearest sibling.
195  * 
196  * $textNode - The jQuery node to truncate. $rootNode - The jQuery root node to
197  * measure the truncated height. $clipNode - The jQuery node to insert right
198  * after the truncation point. options - An object containing: ellipsis - The
199  * ellipsis string to append at the end of the truncation. maxHeight - The
200  * maximum height for the root node (in px). position - The position of the
201  * truncation ("start", "middle", "end").
202  * 
203  * Returns true if truncation happened, false otherwise.
204  */
205 function truncateTextContent($element, $rootNode, $clipNode, options) { // jshint
206  // ignore:line
207  if (options.position === "end") {
208   return truncateTextEnd($element, $rootNode, $clipNode, options);
209  } else if (options.position === "start") {
210   return truncateTextStart($element, $rootNode, $clipNode, options);
211  } else {
212   return truncateTextMiddle($element, $rootNode, $clipNode, options);
213  }
214 }
215
216 /*
217  * Recursively truncates a nested node. Traverses the children node tree in
218  * reverse order.
219  */
220 function truncateNestedNodeStart($element, $rootNode, $clipNode, options) {
221  var element = $element[0];
222
223  var $children = $element.contents();
224  var $child, child;
225
226  var length = $children.length;
227  var index = length - 1;
228  var truncated = false;
229
230  $element.empty();
231
232  for (; index >= 0 && !truncated; index--) {
233
234   $child = $children.eq(index);
235   child = $child[0];
236
237   if (child.nodeType === 8) { // comment node
238    continue;
239   }
240
241   element.insertBefore(child, element.firstChild);
242
243   if ($clipNode.length) {
244    if ($.inArray(element.tagName.toLowerCase(), BLOCK_TAGS) >= 0) {
245     // Certain elements like <li> should not be appended to.
246     $element.after($clipNode);
247    } else {
248     $element.append($clipNode);
249    }
250   }
251
252   if ($rootNode.height() > options.maxHeight) {
253    if (child.nodeType === 3) { // text node
254     truncated = truncateTextContent($child, $rootNode, $clipNode, options);
255    } else {
256     truncated = truncateNestedNode($child, $rootNode, $clipNode, options);
257    }
258   }
259
260   if (!truncated && $clipNode.length) {
261    $clipNode.remove();
262   }
263
264  }
265
266  return truncated;
267 }
268
269 /*
270  * Recursively truncates a nested node. Traverses the children node tree
271  * in-order.
272  */
273 function truncateNestedNodeEnd($element, $rootNode, $clipNode, options) {
274  var element = $element[0];
275
276  var $children = $element.contents();
277  var $child, child;
278
279  var index = 0;
280  var length = $children.length;
281  var truncated = false;
282
283  $element.empty();
284
285  for (; index < length && !truncated; index++) {
286
287   $child = $children.eq(index);
288   child = $child[0];
289
290   if (child.nodeType === 8) { // comment node
291    continue;
292   }
293
294   element.appendChild(child);
295
296   if ($clipNode.length) {
297    if ($.inArray(element.tagName.toLowerCase(), BLOCK_TAGS) >= 0) {
298     // Certain elements like <li> should not be appended to.
299     $element.after($clipNode);
300    } else {
301     $element.append($clipNode);
302    }
303   }
304
305   if ($rootNode.height() > options.maxHeight) {
306    if (child.nodeType === 3) { // text node
307     truncated = truncateTextContent($child, $rootNode, $clipNode, options);
308    } else {
309     truncated = truncateNestedNode($child, $rootNode, $clipNode, options);
310    }
311   }
312
313   if (!truncated && $clipNode.length) {
314    $clipNode.remove();
315   }
316
317  }
318
319  return truncated;
320 }
321
322 /*
323  * Recursively truncates a nested node.
324  * 
325  * $element - The jQuery nested node to truncate. $rootNode - The jQuery root
326  * node to measure the truncated height. $clipNode - The jQuery node to insert
327  * right after the truncation point. options - An object containing: ellipsis -
328  * The ellipsis string to append at the end of the truncation. maxHeight - The
329  * maximum height for the root node (in px). position - The position of the
330  * truncation ("start", "middle", "end").
331  * 
332  * Returns true if truncation happened, false otherwise.
333  */
334 function truncateNestedNode($element, $rootNode, $clipNode, options) { // jshint
335  // ignore:line
336  if (options.position === "end") {
337   return truncateNestedNodeEnd($element, $rootNode, $clipNode, options);
338  } else if (options.position === "start") {
339   return truncateNestedNodeStart($element, $rootNode, $clipNode, options);
340  } else {
341   // TODO: Truncate middle for nested = HARDCORE
342   return truncateNestedNodeEnd($element, $rootNode, $clipNode, options);
343  }
344 }
345
346 /*
347  * Public: Creates an instance of Truncate.
348  * 
349  * element - A DOM element to be truncated. options - An Object literal
350  * containing setup options.
351  * 
352  * Examples:
353  * 
354  * var element = document.createElement('span'); element.innerHTML = 'This is<br>odd.';
355  * var truncated = new Truncate(element, { lines: 1, lineHeight: 16, ellipsis: '… ',
356  * showMore: '<a class="show-more">Show More</a>', showLess: '<a
357  * class="show-less">Show Less</a>', position: "start" }); // Update HTML
358  * truncated.update('This is not very odd.'); // Undo truncation
359  * truncated.expand(); // Redo truncation truncated.collapse();
360  */
361 function Truncate(element, options) {
362  this.element = element;
363  this.$element = $(element);
364
365  this._name = 'truncate';
366  this._defaults = {
367   lines: 1,
368   ellipsis: '…',
369   showMore: '',
370   showLess: '',
371   position: 'end',
372   lineHeight: 'auto'
373  };
374
375  this.options = $.extend({}, this._defaults, options);
376
377  if (this.options.lineHeight === 'auto') {
378   var lineHeightCss = this.$element.css('line-height'), lineHeight = 18; // for
379   // Normal
380   // we
381   // return
382   // the
383   // default
384   // value:
385   // 18px
386
387   if (lineHeightCss !== "normal") {
388    lineHeight = parseInt(lineHeightCss, 10);
389   }
390
391   this.options.lineHeight = lineHeight;
392  }
393
394  if (this.options.maxHeight === undefined) {
395   this.options.maxHeight = parseInt(this.options.lines, 10)
396     * parseInt(this.options.lineHeight, 10);
397  }
398
399  if (this.options.position !== 'start' && this.options.position !== 'middle'
400    && this.options.position !== 'end') {
401   this.options.position = 'end';
402  }
403
404  this.$clipNode = $($.parseHTML(this.options.showMore), this.$element);
405
406  this.original = this.cached = element.innerHTML;
407
408  this.isTruncated = false; // True if the original content overflows the
409  // container.
410  this.isCollapsed = true; // True if the container is currently collapsed.
411
412  this.update();
413 }
414
415 Truncate.prototype = {
416
417  /*
418   * Public: Updates the inner HTML of the element and re-truncates. Will not
419   * perform an updade if the container is currently expanded, instead it will
420   * wait until the next time .collapse() is called.
421   * 
422   * html - The new HTML.
423   * 
424   * Returns nothing.
425   */
426  update: function(html) {
427   var wasExpanded = !this.isCollapsed;
428
429   // Update HTML if provided, otherwise use the current html and restore
430   // the truncated content to the original if it's currently present.
431   if (typeof html !== 'undefined') {
432    this.original = this.element.innerHTML = html;
433   } else if (this.isCollapsed && this.element.innerHTML === this.cached) {
434    this.element.innerHTML = this.original;
435   }
436
437   // Wrap the contents in order to ignore container's margin/padding.
438   var $wrap = this.$element.wrapInner('<div/>').children();
439   $wrap.css({
440    border: 'none',
441    margin: 0,
442    padding: 0,
443    width: 'auto',
444    height: 'auto',
445    'word-wrap': 'break-word'
446   });
447
448   this.isTruncated = false;
449   // Check if already meets height requirement
450   if ($wrap.height() > this.options.maxHeight) {
451    this.isTruncated = truncateNestedNode($wrap, $wrap, this.$clipNode,
452      this.options);
453
454    if (this.isExplicitlyCollapsed) {
455     this.isCollapsed = true;
456     wasExpanded = false;
457    }
458   } else {
459    this.isCollapsed = false;
460   }
461
462   // Restore the wrapped contents
463   $wrap.replaceWith($wrap.contents());
464
465   // Cache the truncated content
466   this.cached = this.element.innerHTML;
467
468   // If the container was expanded when .update() was called then restore
469   // it to it's previous state.
470   if (wasExpanded) {
471    this.element.innerHTML = this.original;
472   }
473  },
474
475  /*
476   * Public: Expands the element to show content in full.
477   * 
478   * Returns nothing.
479   */
480  expand: function() {
481   var includeShowLess = true;
482
483   if (this.isExplicitlyCollapsed) {
484    this.isExplicitlyCollapsed = false;
485    includeShowLess = false;
486   }
487
488   if (!this.isCollapsed) {
489    return;
490   }
491
492   this.isCollapsed = false;
493
494   this.element.innerHTML = this.isTruncated ? this.original
495     + (includeShowLess ? this.options.showLess : "") : this.original;
496  },
497
498  /*
499   * Public: Collapses the element to the truncated state. Uses the cached HTML
500   * from .update() by default.
501   * 
502   * retruncate - True to retruncate original HTML, otherwise use cached HTML.
503   * 
504   * Returns nothing.
505   */
506  collapse: function(retruncate) {
507   this.isExplicitlyCollapsed = true;
508
509   if (this.isCollapsed) {
510    return;
511   }
512
513   this.isCollapsed = true;
514
515   retruncate = retruncate || false;
516   if (retruncate) {
517    this.update();
518   } else {
519    this.element.innerHTML = this.cached;
520   }
521  }
522 };
523
524 // Lightweight plugin wrapper preventing multiple instantiations
525 $.fn.truncate = function(options) {
526  var args = $.makeArray(arguments).slice(1);
527  return this.each(function() {
528   var truncate = $.data(this, 'jquery-truncate');
529   if (!truncate) {
530    $.data(this, 'jquery-truncate', new Truncate(this, options));
531   } else if (typeof truncate[options] === 'function') {
532    truncate[options].apply(truncate, args);
533   }
534  });
535 };
536
537 module.Truncate = Truncate;
538
539})(this, jQuery);

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.