summaryrefslogtreecommitdiff
path: root/js/dojo/dojox/html/ellipsis.js
blob: f67063710aee12cea395dd8c6309625a11711264 (plain)
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
//>>built
define("dojox/html/ellipsis",["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/array", "dojo/_base/Color", "dojo/colors"], function(d){
	/*=====
	dojox.html.ellipsis = {
		// summary: offers cross-browser support for text-overflow: ellipsis
		//
		// description: Add "dojoxEllipsis" on any node that you want to ellipsis-ize. In order to function properly,
		//	the node with the dojoxEllipsis class set on it should be a child of a node with a defined width.
		//	It should also be a block-level element (i.e. <div>) - it will not work on td elements.
		//	NOTE: When using the dojoxEllipsis class within tables, the table needs to have the table-layout: fixed style
	}
	=====*/
	
	if(d.isFF < 7){ //TODO: feature detect text-overflow in computed style?
		// The delay (in ms) to wait so that we don't keep querying when many
		// changes happen at once - set config "dojoxFFEllipsisDelay" if you
		// want a different value
		var delay = 1;
		if("dojoxFFEllipsisDelay" in d.config){
			delay = Number(d.config.dojoxFFEllipsisDelay);
			if(isNaN(delay)){
				delay = 1;
			}
		}
		try{
			var createXULEllipsis = (function(){
				// Create our stub XUL elements for cloning later
				// NOTE: this no longer works as of FF 4.0:
				// https://developer.mozilla.org/En/Firefox_4_for_developers#Remote_XUL_support_removed
				var sNS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
				var xml = document.createElementNS(sNS, 'window');
				var label = document.createElementNS(sNS, 'description');
				label.setAttribute('crop', 'end');
				xml.appendChild(label);

				return function(/* Node */ n){
					// Summary:
					//		Given a node, it creates the XUL and sets its
					//		content so that it will have an ellipsis
					var x = xml.cloneNode(true);
					x.firstChild.setAttribute('value', n.textContent);
					n.innerHTML = '';
					n.appendChild(x);
				};
			})();
		}catch(e){}
		
		// Create our iframe elements for cloning later
		var create = d.create;
		var dd = d.doc;
		var dp = d.place;
		var iFrame = create("iframe", {className: "dojoxEllipsisIFrame",
					src: "javascript:'<html><head><script>if(\"loadFirebugConsole\" in window){window.loadFirebugConsole();}</script></head><body></body></html>'"});
		var rollRange = function(/* W3C Range */ r, /* int? */ cnt){
			// Summary:
			//		Rolls the given range back one character from the end
			//
			//	r: W3C Range
			//		The range to roll back
			//	cnt: int?
			//		An optional number of times to roll back (defaults 1)
			if(r.collapsed){
				// Do nothing - we are already collapsed
				return;
			}
			if(cnt > 0){
				do{
					rollRange(r);
					cnt--;
				}while(cnt);
				return;
			}
			if(r.endContainer.nodeType == 3 && r.endOffset > 0){
				r.setEnd(r.endContainer, r.endOffset - 1);
			}else if(r.endContainer.nodeType == 3){
				r.setEndBefore(r.endContainer);
				rollRange(r);
				return;
			}else if(r.endOffset && r.endContainer.childNodes.length >= r.endOffset){
				var nCont = r.endContainer.childNodes[r.endOffset - 1];
				if(nCont.nodeType == 3){
					r.setEnd(nCont, nCont.length - 1);
				}else if(nCont.childNodes.length){
					r.setEnd(nCont, nCont.childNodes.length);
					rollRange(r);
					return;
				}else{
					r.setEndBefore(nCont);
					rollRange(r);
					return;
				}
			}else{
				r.setEndBefore(r.endContainer);
				rollRange(r);
				return;
			}
		};
		var createIFrameEllipsis = function(/* Node */ n){
			// Summary:
			//		Given a node, it creates an iframe and and ellipsis div and
			//		sets up the connections so that they will work correctly.
			//		This function is used when createXULEllipsis is not able
			//		to be used (because there is markup within the node) - it's
			//		a bit slower, but does the trick
			var c = create("div", {className: "dojoxEllipsisContainer"});
			var e = create("div", {className: "dojoxEllipsisShown", style: {display: "none"}});
			n.parentNode.replaceChild(c, n);
			c.appendChild(n);
			c.appendChild(e);
			var i = iFrame.cloneNode(true);
			var ns = n.style;
			var es = e.style;
			var ranges;
			var resizeNode = function(){
				ns.display = "";
				es.display = "none";
				if(n.scrollWidth <= n.offsetWidth){ return; }
				var r = dd.createRange();
				r.selectNodeContents(n);
				ns.display = "none";
				es.display = "";
				var done = false;
				do{
					var numRolls = 1;
					dp(r.cloneContents(), e, "only");
					var sw = e.scrollWidth, ow = e.offsetWidth;
					done = (sw <= ow);
					var pct = (1 - ((ow * 1) / sw));
					if(pct > 0){
						numRolls = Math.max(Math.round(e.textContent.length * pct) - 1, 1);
					}
					rollRange(r, numRolls);
				}while(!r.collapsed && !done);
			};
			i.onload = function(){
				i.contentWindow.onresize = resizeNode;
				resizeNode();
			};
			c.appendChild(i);
		};

		// Function for updating the ellipsis
		var hc = d.hasClass;
		var doc = d.doc;
		var s, fn, opt;
		if(doc.querySelectorAll){
			s = doc;
			fn = "querySelectorAll";
			opt = ".dojoxEllipsis";
		}else if(doc.getElementsByClassName){
			s = doc;
			fn = "getElementsByClassName";
			opt = "dojoxEllipsis";
		}else{
			s = d;
			fn = "query";
			opt = ".dojoxEllipsis";
		}
		fx = function(){
			d.forEach(s[fn].apply(s, [opt]), function(n){
				if(!n || n._djx_ellipsis_done){ return; }
				n._djx_ellipsis_done = true;
				if(createXULEllipsis && n.textContent == n.innerHTML && !hc(n, "dojoxEllipsisSelectable")){
					// We can do the faster XUL version, instead of calculating
					createXULEllipsis(n);
				}else{
					createIFrameEllipsis(n);
				}
			});
		};
		
		d.addOnLoad(function(){
			// Apply our initial stuff
			var t = null;
			var c = null;
			var connFx = function(){
				if(c){
					// disconnect us - so we don't fire anymore
					d.disconnect(c);
					c = null;
				}
				if(t){ clearTimeout(t); }
				t = setTimeout(function(){
					t = null;
					fx();
					// Connect to the modified function so that we can catch
					// our next change
					c = d.connect(d.body(), "DOMSubtreeModified", connFx);
				}, delay);
			};
			connFx();
		});
	}
});