forked from zaheerm/flumotion-extra
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjquery.expander.js
152 lines (143 loc) · 5.83 KB
/
jquery.expander.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
* jQuery Expander plugin
* Version 0.4 (12/09/2008)
* @requires jQuery v1.1.1+
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(function($) {
$.fn.expander = function(options) {
var opts = $.extend({}, $.fn.expander.defaults, options);
var delayedCollapse;
return this.each(function() {
var $this = $(this);
var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
var cleanedTag, startTags, endTags;
var allText = $this.html();
var startText = allText.slice(0, o.slicePoint).replace(/\w+$/,'');
startTags = startText.match(/<\w[^>]*>/g);
if (startTags) {startText = allText.slice(0,o.slicePoint + startTags.join('').length).replace(/\w+$/,'');}
if (startText.lastIndexOf('<') > startText.lastIndexOf('>') ) {
startText = startText.slice(0,startText.lastIndexOf('<'));
}
var endText = allText.slice(startText.length);
// create necessary expand/collapse elements if they don't already exist
if (!$('span.details', this).length) {
// end script if text length isn't long enough.
if ( endText.replace(/\s+$/,'').split(' ').length < o.widow ) { return; }
// otherwise, continue...
if (endText.indexOf('</') > -1) {
endTags = endText.match(/<(\/)?[^>]*>/g);
for (var i=0; i < endTags.length; i++) {
if (endTags[i].indexOf('</') > -1) {
var startTag, startTagExists = false;
for (var j=0; j < i; j++) {
startTag = endTags[j].slice(0, endTags[j].indexOf(' ')).replace(/(\w)$/,'$1>');
if (startTag == rSlash(endTags[i])) {
startTagExists = true;
}
}
if (!startTagExists) {
startText = startText + endTags[i];
var matched = false;
for (var s=startTags.length - 1; s >= 0; s--) {
if (startTags[s].slice(0, startTags[s].indexOf(' ')).replace(/(\w)$/,'$1>') == rSlash(endTags[i])
&& matched == false) {
cleanedTag = cleanedTag ? startTags[s] + cleanedTag : startTags[s];
matched = true;
}
};
}
}
}
endText = cleanedTag && cleanedTag + endText || endText;
}
$this.html([
startText,
'<span class="read-more">',
o.expandPrefix,
'<a href="#">',
o.expandText,
'</a>',
'</span>',
'<span class="details">',
endText,
'</span>'
].join('')
);
}
var $thisDetails = $('span.details', this),
$readMore = $('span.read-more', this);
$thisDetails.hide();
$readMore.find('a').click(function() {
$readMore.hide();
if (o.expandEffect === 'show' && !o.expandSpeed) {
o.beforeExpand($this);
$thisDetails.show();
o.afterExpand($this);
delayCollapse(o, $thisDetails);
} else {
o.beforeExpand($this);
$thisDetails[o.expandEffect](o.expandSpeed, function() {
$thisDetails.css({zoom: ''});
o.afterExpand($this);
delayCollapse(o, $thisDetails);
});
}
return false;
});
if (o.userCollapse) {
$this
.find('span.details').append('<span class="re-collapse">' + o.userCollapsePrefix + '<a href="#">' + o.userCollapseText + '</a></span>');
$this.find('span.re-collapse a').click(function() {
clearTimeout(delayedCollapse);
var $detailsCollapsed = $(this).parents('span.details');
reCollapse($detailsCollapsed);
o.onCollapse($this, true);
return false;
});
}
});
function reCollapse(el) {
el.hide()
.prev('span.read-more').show();
}
function delayCollapse(option, $collapseEl) {
if (option.collapseTimer) {
delayedCollapse = setTimeout(function() {
reCollapse($collapseEl);
option.onCollapse($collapseEl.parent(), false);
},
option.collapseTimer
);
}
}
function rSlash(rString) {
return rString.replace(/\//,'');
}
};
// plugin defaults
$.fn.expander.defaults = {
slicePoint: 100, // the number of characters at which the contents will be sliced into two parts.
// Note: any tag names in the HTML that appear inside the sliced element before
// the slicePoint will be counted along with the text characters.
widow: 4, // a threshold of sorts for whether to initially hide/collapse part of the element's contents.
// If after slicing the contents in two there are fewer words in the second part than
// the value set by widow, we won't bother hiding/collapsing anything.
expandText: 'read more', // text displayed in a link instead of the hidden part of the element.
// clicking this will expand/show the hidden/collapsed text
expandPrefix: '… ',
collapseTimer: 0, // number of milliseconds after text has been expanded at which to collapse the text again
expandEffect: 'fadeIn',
expandSpeed: '', // speed in milliseconds of the animation effect for expanding the text
userCollapse: true, // allow the user to re-collapse the expanded text.
userCollapseText: '[collapse expanded text]', // text to use for the link to re-collapse the text
userCollapsePrefix: ' ',
beforeExpand: function($thisEl) {},
afterExpand: function($thisEl) {},
onCollapse: function($thisEl, byUser) {}
};
})(jQuery);