forked from jsillitoe/jquery-condense-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
jquery.condense.js
196 lines (164 loc) · 6.79 KB
/
jquery.condense.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/**
* Condense 0.1 - Condense and expand text heavy elements
*
* (c) 2008 Joseph Sillitoe
* Dual licensed under the MIT License (MIT-LICENSE) and GPL License,version 2 (GPL-LICENSE).
*/
/*
* jQuery plugin
*
* usage:
*
* $(document).ready(function(){
* $('#example1').condense();
* });
*
* Options:
* condensedLength: Target length of condensed element. Default: 200
* minTrail: Minimun length of the trailing text. Default: 20
* delim: Delimiter used for finding the break point. Default: " " - {space}
* moreText: Text used for the more control. Default: [more]
* lessText: Text used for the less control. Default: [less]
* ellipsis: Text added to condensed element. Default: ( ... )
* moreSpeed: Animation Speed for expanding. Default: "normal"
* lessSpeed: Animation Speed for condensing. Default: "normal"
* easing: Easing algorith. Default: "linear"
* expandedWidth: Width of the expanded text (optional)
*/
(function($) {
// plugin definition
$.fn.condense = function(options) {
$.metadata ? debug('metadata plugin detected') : debug('metadata plugin not present');//detect the metadata plugin?
var opts = $.extend({}, $.fn.condense.defaults, options); // build main options before element iteration
// iterate each matched element
return this.each(function() {
$this = $(this);
// support metadata plugin (v2.0)
var o = $.metadata ? $.extend({}, opts, $this.metadata()) : opts; // build element specific options
debug('Condensing ['+$this.text().length+']: '+$this.text());
var clone = cloneCondensed($this,o);
if (clone){
// id attribute switch. make sure that the visible elem keeps the original id (if set).
$this.attr('id') ? $this.attr('id','condensed_'+$this.attr('id')) : false;
var controlMore = " <span class='condense_control condense_control_more' style='cursor:pointer;'>"+o.moreText+"</span>";
var controlLess = " <span class='condense_control condense_control_less' style='cursor:pointer;'>"+o.lessText+"</span>";
clone.append(o.ellipsis + controlMore);
$this.after(clone).hide().append(controlLess);
$('.condense_control_more',clone).click(function(){
debug('moreControl clicked.');
triggerExpand($(this),o)
});
$('.condense_control_less',$this).click(function(){
debug('lessControl clicked.');
triggerCondense($(this),o)
});
}
});
};
function cloneCondensed(elem, opts){
// Try to clone and condense the element. if not possible because of the length/minTrail options, return false.
// also, dont count tag declarations as part of the text length.
// check the length of the text first, return false if too short.
if ($.trim(elem.text()).length <= opts.condensedLength + opts.minTrail){
debug('element too short: skipping.');
return false;
}
var fullbody = $.trim(elem.html());
var fulltext = $.trim(elem.text());
var delim = opts.delim;
var clone = elem.clone();
var delta = 0;
do {
// find the location of the next potential break-point.
var loc = findDelimiterLocation(fullbody, opts.delim, (opts.condensedLength + delta));
//set the html of the clone to the substring html of the original
clone.html($.trim(fullbody.substring(0,(loc+1))));
var cloneTextLength = clone.text().length;
var cloneHtmlLength = clone.html().length;
delta = clone.html().length - cloneTextLength;
debug ("condensing... [html-length:"+cloneHtmlLength+" text-length:"+cloneTextLength+" delta: "+delta+" break-point: "+loc+"]");
//is the length of the clone text long enough?
}while(delta && clone.text().length < opts.condensedLength )
// after skipping ahead to the delimiter, do we still have enough trailing text?
if ((fulltext.length - cloneTextLength) < opts.minTrail){
debug('not enough trailing text: skipping.');
return false;
}
debug('clone condensed. [text-length:'+cloneTextLength+']');
return clone;
}
function findDelimiterLocation(html, delim, startpos){
// find the location inside the html of the delimiter, starting at the specified length.
var foundDelim = false;
var loc = startpos;
do {
var loc = html.indexOf(delim, loc);
if (loc < 0){
debug ("No delimiter found.");
return html.length;
} // if there is no delimiter found, just return the length of the entire html string.
foundDelim = true;
while (isInsideTag(html, loc)) {
// if we are inside a tag, this delim doesn't count. keep looking...
loc++;
foundDelim = false;
}
}while(!foundDelim)
debug ("Delimiter found in html at: "+loc);
return loc;
}
function isInsideTag(html, loc){
return (html.indexOf('>',loc) < html.indexOf('<',loc));
}
function triggerCondense(control, opts){
debug('Condense Trigger: '+control.html());
var orig = control.parent(); // The original element will be the control's immediate parent.
var condensed = orig.next(); // The condensed element will be the original immediate next sibling.
condensed.show();
var con_w = condensed.width();
var con_h = condensed.height();
condensed.hide(); //briefly flashed the condensed element so we can get the target width/height
var orig_w = orig.width();
var orig_h = orig.height();
orig.animate({height:con_h, width:con_w, opacity: 1}, opts.lessSpeed, opts.easing,
function(){
orig.height(orig_h).width(orig_w).hide();
condensed.show();
});
}
function triggerExpand(control, opts){
debug('Expand Trigger: '+control.html());
var condensed = control.parent(); // The condensed element will be the control's immediate parent.
var orig = condensed.prev(); // The original element will be the condensed immediate previous sibling.
if (opts.expandedWidth) {
orig.width(opts.expandedWidth);
}
orig.show();
var orig_w = orig.width();
var orig_h = orig.height();
orig.width(condensed.width()+"px").height(condensed.height()+"px");
condensed.hide();
orig.animate({height:orig_h, width:orig_w, opacity: 1}, opts.moreSpeed, opts.easing);
if(condensed.attr('id')){
var idAttr = condensed.attr('id');
condensed.attr('id','condensed_'+idAttr);
orig.attr('id',idAttr);
}
}
/**
* private function for debugging
*/
function debug($obj) {if (window.console && window.console.log){window.console.log($obj);}};
// plugin defaults
$.fn.condense.defaults = {
condensedLength: 200,
minTrail: 20,
delim: " ",
moreText: "[more]",
lessText: "[less]",
ellipsis: " ( ... )",
moreSpeed: "normal",
lessSpeed: "normal",
easing: "linear"
};
})(jQuery);