ellipsis.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.html.ellipsis"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.html.ellipsis"] = true;
  8. dojo.provide("dojox.html.ellipsis");
  9. /*=====
  10. dojox.html.ellipsis = {
  11. // summary: offers cross-browser support for text-overflow: ellipsis
  12. //
  13. // description: Add "dojoxEllipsis" on any node that you want to ellipsis-ize. In order to function properly,
  14. // the node with the dojoxEllipsis class set on it should be a child of a node with a defined width.
  15. // It should also be a block-level element (i.e. <div>) - it will not work on td elements.
  16. // NOTE: When using the dojoxEllipsis class within tables, the table needs to have the table-layout: fixed style
  17. }
  18. =====*/
  19. (function(d){
  20. if(d.isMoz){ //TODO: feature detect text-overflow in computed style?
  21. // The delay (in ms) to wait so that we don't keep querying when many
  22. // changes happen at once - set config "dojoxFFEllipsisDelay" if you
  23. // want a different value
  24. var delay = 1;
  25. if("dojoxFFEllipsisDelay" in d.config){
  26. delay = Number(d.config.dojoxFFEllipsisDelay);
  27. if(isNaN(delay)){
  28. delay = 1;
  29. }
  30. }
  31. try{
  32. var createXULEllipsis = (function(){
  33. // Create our stub XUL elements for cloning later
  34. // NOTE: this no longer works as of FF 4.0:
  35. // https://developer.mozilla.org/En/Firefox_4_for_developers#Remote_XUL_support_removed
  36. var sNS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
  37. var xml = document.createElementNS(sNS, 'window');
  38. var label = document.createElementNS(sNS, 'description');
  39. label.setAttribute('crop', 'end');
  40. xml.appendChild(label);
  41. return function(/* Node */ n){
  42. // Summary:
  43. // Given a node, it creates the XUL and sets its
  44. // content so that it will have an ellipsis
  45. var x = xml.cloneNode(true);
  46. x.firstChild.setAttribute('value', n.textContent);
  47. n.innerHTML = '';
  48. n.appendChild(x);
  49. };
  50. })();
  51. }catch(e){}
  52. // Create our iframe elements for cloning later
  53. var create = d.create;
  54. var dd = d.doc;
  55. var dp = d.place;
  56. var iFrame = create("iframe", {className: "dojoxEllipsisIFrame",
  57. src: "javascript:'<html><head><script>if(\"loadFirebugConsole\" in window){window.loadFirebugConsole();}</script></head><body></body></html>'"});
  58. var rollRange = function(/* W3C Range */ r, /* int? */ cnt){
  59. // Summary:
  60. // Rolls the given range back one character from the end
  61. //
  62. // r: W3C Range
  63. // The range to roll back
  64. // cnt: int?
  65. // An optional number of times to roll back (defaults 1)
  66. if(r.collapsed){
  67. // Do nothing - we are already collapsed
  68. return;
  69. }
  70. if(cnt > 0){
  71. do{
  72. rollRange(r);
  73. cnt--;
  74. }while(cnt);
  75. return;
  76. }
  77. if(r.endContainer.nodeType == 3 && r.endOffset > 0){
  78. r.setEnd(r.endContainer, r.endOffset - 1);
  79. }else if(r.endContainer.nodeType == 3){
  80. r.setEndBefore(r.endContainer);
  81. rollRange(r);
  82. return;
  83. }else if(r.endOffset && r.endContainer.childNodes.length >= r.endOffset){
  84. var nCont = r.endContainer.childNodes[r.endOffset - 1];
  85. if(nCont.nodeType == 3){
  86. r.setEnd(nCont, nCont.length - 1);
  87. }else if(nCont.childNodes.length){
  88. r.setEnd(nCont, nCont.childNodes.length);
  89. rollRange(r);
  90. return;
  91. }else{
  92. r.setEndBefore(nCont);
  93. rollRange(r);
  94. return;
  95. }
  96. }else{
  97. r.setEndBefore(r.endContainer);
  98. rollRange(r);
  99. return;
  100. }
  101. };
  102. var createIFrameEllipsis = function(/* Node */ n){
  103. // Summary:
  104. // Given a node, it creates an iframe and and ellipsis div and
  105. // sets up the connections so that they will work correctly.
  106. // This function is used when createXULEllipsis is not able
  107. // to be used (because there is markup within the node) - it's
  108. // a bit slower, but does the trick
  109. var c = create("div", {className: "dojoxEllipsisContainer"});
  110. var e = create("div", {className: "dojoxEllipsisShown", style: {display: "none"}});
  111. n.parentNode.replaceChild(c, n);
  112. c.appendChild(n);
  113. c.appendChild(e);
  114. var i = iFrame.cloneNode(true);
  115. var ns = n.style;
  116. var es = e.style;
  117. var ranges;
  118. var resizeNode = function(){
  119. ns.display = "";
  120. es.display = "none";
  121. if(n.scrollWidth <= n.offsetWidth){ return; }
  122. var r = dd.createRange();
  123. r.selectNodeContents(n);
  124. ns.display = "none";
  125. es.display = "";
  126. var done = false;
  127. do{
  128. var numRolls = 1;
  129. dp(r.cloneContents(), e, "only");
  130. var sw = e.scrollWidth, ow = e.offsetWidth;
  131. done = (sw <= ow);
  132. var pct = (1 - ((ow * 1) / sw));
  133. if(pct > 0){
  134. numRolls = Math.max(Math.round(e.textContent.length * pct) - 1, 1);
  135. }
  136. rollRange(r, numRolls);
  137. }while(!r.collapsed && !done);
  138. };
  139. i.onload = function(){
  140. i.contentWindow.onresize = resizeNode;
  141. resizeNode();
  142. };
  143. c.appendChild(i);
  144. };
  145. // Function for updating the ellipsis
  146. var hc = d.hasClass;
  147. var doc = d.doc;
  148. var s, fn, opt;
  149. if(doc.querySelectorAll){
  150. s = doc;
  151. fn = "querySelectorAll";
  152. opt = ".dojoxEllipsis";
  153. }else if(doc.getElementsByClassName){
  154. s = doc;
  155. fn = "getElementsByClassName";
  156. opt = "dojoxEllipsis";
  157. }else{
  158. s = d;
  159. fn = "query";
  160. opt = ".dojoxEllipsis";
  161. }
  162. fx = function(){
  163. d.forEach(s[fn].apply(s, [opt]), function(n){
  164. if(!n || n._djx_ellipsis_done){ return; }
  165. n._djx_ellipsis_done = true;
  166. if(createXULEllipsis && n.textContent == n.innerHTML && !hc(n, "dojoxEllipsisSelectable")){
  167. // We can do the faster XUL version, instead of calculating
  168. createXULEllipsis(n);
  169. }else{
  170. createIFrameEllipsis(n);
  171. }
  172. });
  173. };
  174. d.addOnLoad(function(){
  175. // Apply our initial stuff
  176. var t = null;
  177. var c = null;
  178. var connFx = function(){
  179. if(c){
  180. // disconnect us - so we don't fire anymore
  181. d.disconnect(c);
  182. c = null;
  183. }
  184. if(t){ clearTimeout(t); }
  185. t = setTimeout(function(){
  186. t = null;
  187. fx();
  188. // Connect to the modified function so that we can catch
  189. // our next change
  190. c = d.connect(d.body(), "DOMSubtreeModified", connFx);
  191. }, delay);
  192. };
  193. connFx();
  194. });
  195. }
  196. })(dojo);
  197. }