selection.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  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["dijit._editor.selection"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dijit._editor.selection"] = true;
  8. dojo.provide("dijit._editor.selection");
  9. dojo.getObject("_editor.selection", true, dijit);
  10. // FIXME:
  11. // all of these methods branch internally for IE. This is probably
  12. // sub-optimal in terms of runtime performance. We should investigate the
  13. // size difference for differentiating at definition time.
  14. dojo.mixin(dijit._editor.selection, {
  15. getType: function(){
  16. // summary:
  17. // Get the selection type (like dojo.doc.select.type in IE).
  18. if(dojo.isIE < 9){
  19. return dojo.doc.selection.type.toLowerCase();
  20. }else{
  21. var stype = "text";
  22. // Check if the actual selection is a CONTROL (IMG, TABLE, HR, etc...).
  23. var oSel;
  24. try{
  25. oSel = dojo.global.getSelection();
  26. }catch(e){ /*squelch*/ }
  27. if(oSel && oSel.rangeCount == 1){
  28. var oRange = oSel.getRangeAt(0);
  29. if( (oRange.startContainer == oRange.endContainer) &&
  30. ((oRange.endOffset - oRange.startOffset) == 1) &&
  31. (oRange.startContainer.nodeType != 3 /* text node*/)
  32. ){
  33. stype = "control";
  34. }
  35. }
  36. return stype; //String
  37. }
  38. },
  39. getSelectedText: function(){
  40. // summary:
  41. // Return the text (no html tags) included in the current selection or null if no text is selected
  42. if(dojo.isIE < 9){
  43. if(dijit._editor.selection.getType() == 'control'){
  44. return null;
  45. }
  46. return dojo.doc.selection.createRange().text;
  47. }else{
  48. var selection = dojo.global.getSelection();
  49. if(selection){
  50. return selection.toString(); //String
  51. }
  52. }
  53. return '';
  54. },
  55. getSelectedHtml: function(){
  56. // summary:
  57. // Return the html text of the current selection or null if unavailable
  58. if(dojo.isIE < 9){
  59. if(dijit._editor.selection.getType() == 'control'){
  60. return null;
  61. }
  62. return dojo.doc.selection.createRange().htmlText;
  63. }else{
  64. var selection = dojo.global.getSelection();
  65. if(selection && selection.rangeCount){
  66. var i;
  67. var html = "";
  68. for(i = 0; i < selection.rangeCount; i++){
  69. //Handle selections spanning ranges, such as Opera
  70. var frag = selection.getRangeAt(i).cloneContents();
  71. var div = dojo.doc.createElement("div");
  72. div.appendChild(frag);
  73. html += div.innerHTML;
  74. }
  75. return html; //String
  76. }
  77. return null;
  78. }
  79. },
  80. getSelectedElement: function(){
  81. // summary:
  82. // Retrieves the selected element (if any), just in the case that
  83. // a single element (object like and image or a table) is
  84. // selected.
  85. if(dijit._editor.selection.getType() == "control"){
  86. if(dojo.isIE < 9){
  87. var range = dojo.doc.selection.createRange();
  88. if(range && range.item){
  89. return dojo.doc.selection.createRange().item(0);
  90. }
  91. }else{
  92. var selection = dojo.global.getSelection();
  93. return selection.anchorNode.childNodes[ selection.anchorOffset ];
  94. }
  95. }
  96. return null;
  97. },
  98. getParentElement: function(){
  99. // summary:
  100. // Get the parent element of the current selection
  101. if(dijit._editor.selection.getType() == "control"){
  102. var p = this.getSelectedElement();
  103. if(p){ return p.parentNode; }
  104. }else{
  105. if(dojo.isIE < 9){
  106. var r = dojo.doc.selection.createRange();
  107. r.collapse(true);
  108. return r.parentElement();
  109. }else{
  110. var selection = dojo.global.getSelection();
  111. if(selection){
  112. var node = selection.anchorNode;
  113. while(node && (node.nodeType != 1)){ // not an element
  114. node = node.parentNode;
  115. }
  116. return node;
  117. }
  118. }
  119. }
  120. return null;
  121. },
  122. hasAncestorElement: function(/*String*/tagName /* ... */){
  123. // summary:
  124. // Check whether current selection has a parent element which is
  125. // of type tagName (or one of the other specified tagName)
  126. // tagName: String
  127. // The tag name to determine if it has an ancestor of.
  128. return this.getAncestorElement.apply(this, arguments) != null; //Boolean
  129. },
  130. getAncestorElement: function(/*String*/tagName /* ... */){
  131. // summary:
  132. // Return the parent element of the current selection which is of
  133. // type tagName (or one of the other specified tagName)
  134. // tagName: String
  135. // The tag name to determine if it has an ancestor of.
  136. var node = this.getSelectedElement() || this.getParentElement();
  137. return this.getParentOfType(node, arguments); //DOMNode
  138. },
  139. isTag: function(/*DomNode*/ node, /*String[]*/ tags){
  140. // summary:
  141. // Function to determine if a node is one of an array of tags.
  142. // node:
  143. // The node to inspect.
  144. // tags:
  145. // An array of tag name strings to check to see if the node matches.
  146. if(node && node.tagName){
  147. var _nlc = node.tagName.toLowerCase();
  148. for(var i=0; i<tags.length; i++){
  149. var _tlc = String(tags[i]).toLowerCase();
  150. if(_nlc == _tlc){
  151. return _tlc; // String
  152. }
  153. }
  154. }
  155. return "";
  156. },
  157. getParentOfType: function(/*DomNode*/ node, /*String[]*/ tags){
  158. // summary:
  159. // Function to locate a parent node that matches one of a set of tags
  160. // node:
  161. // The node to inspect.
  162. // tags:
  163. // An array of tag name strings to check to see if the node matches.
  164. while(node){
  165. if(this.isTag(node, tags).length){
  166. return node; // DOMNode
  167. }
  168. node = node.parentNode;
  169. }
  170. return null;
  171. },
  172. collapse: function(/*Boolean*/beginning){
  173. // summary:
  174. // Function to collapse (clear), the current selection
  175. // beginning: Boolean
  176. // Boolean to indicate whether to collapse the cursor to the beginning of the selection or end.
  177. if(window.getSelection){
  178. var selection = dojo.global.getSelection();
  179. if(selection.removeAllRanges){ // Mozilla
  180. if(beginning){
  181. selection.collapseToStart();
  182. }else{
  183. selection.collapseToEnd();
  184. }
  185. }else{ // Safari
  186. // pulled from WebCore/ecma/kjs_window.cpp, line 2536
  187. selection.collapse(beginning);
  188. }
  189. }else if(dojo.isIE){ // IE
  190. var range = dojo.doc.selection.createRange();
  191. range.collapse(beginning);
  192. range.select();
  193. }
  194. },
  195. remove: function(){
  196. // summary:
  197. // Function to delete the currently selected content from the document.
  198. var sel = dojo.doc.selection;
  199. if(dojo.isIE < 9){
  200. if(sel.type.toLowerCase() != "none"){
  201. sel.clear();
  202. }
  203. return sel; //Selection
  204. }else{
  205. sel = dojo.global.getSelection();
  206. sel.deleteFromDocument();
  207. return sel; //Selection
  208. }
  209. },
  210. selectElementChildren: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
  211. // summary:
  212. // clear previous selection and select the content of the node
  213. // (excluding the node itself)
  214. // element: DOMNode
  215. // The element you wish to select the children content of.
  216. // nochangefocus: Boolean
  217. // Boolean to indicate if the foxus should change or not.
  218. var win = dojo.global;
  219. var doc = dojo.doc;
  220. var range;
  221. element = dojo.byId(element);
  222. if(doc.selection && dojo.isIE < 9 && dojo.body().createTextRange){ // IE
  223. range = element.ownerDocument.body.createTextRange();
  224. range.moveToElementText(element);
  225. if(!nochangefocus){
  226. try{
  227. range.select(); // IE throws an exception here if the widget is hidden. See #5439
  228. }catch(e){ /* squelch */}
  229. }
  230. }else if(win.getSelection){
  231. var selection = dojo.global.getSelection();
  232. if(dojo.isOpera){
  233. //Opera's selectAllChildren doesn't seem to work right
  234. //against <body> nodes and possibly others ... so
  235. //we use the W3C range API
  236. if(selection.rangeCount){
  237. range = selection.getRangeAt(0);
  238. }else{
  239. range = doc.createRange();
  240. }
  241. range.setStart(element, 0);
  242. range.setEnd(element,(element.nodeType == 3)?element.length:element.childNodes.length);
  243. selection.addRange(range);
  244. }else{
  245. selection.selectAllChildren(element);
  246. }
  247. }
  248. },
  249. selectElement: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
  250. // summary:
  251. // clear previous selection and select element (including all its children)
  252. // element: DOMNode
  253. // The element to select.
  254. // nochangefocus: Boolean
  255. // Boolean indicating if the focus should be changed. IE only.
  256. var range;
  257. var doc = dojo.doc;
  258. var win = dojo.global;
  259. element = dojo.byId(element);
  260. if(dojo.isIE < 9 && dojo.body().createTextRange){
  261. try{
  262. var tg = element.tagName ? element.tagName.toLowerCase() : "";
  263. if(tg === "img" || tg === "table"){
  264. range = dojo.body().createControlRange();
  265. }else{
  266. range = dojo.body().createRange();
  267. }
  268. range.addElement(element);
  269. if(!nochangefocus){
  270. range.select();
  271. }
  272. }catch(e){
  273. this.selectElementChildren(element,nochangefocus);
  274. }
  275. }else if(dojo.global.getSelection){
  276. var selection = win.getSelection();
  277. range = doc.createRange();
  278. if(selection.removeAllRanges){ // Mozilla
  279. // FIXME: does this work on Safari?
  280. if(dojo.isOpera){
  281. //Opera works if you use the current range on
  282. //the selection if present.
  283. if(selection.getRangeAt(0)){
  284. range = selection.getRangeAt(0);
  285. }
  286. }
  287. range.selectNode(element);
  288. selection.removeAllRanges();
  289. selection.addRange(range);
  290. }
  291. }
  292. },
  293. inSelection: function(node){
  294. // summary:
  295. // This function determines if 'node' is
  296. // in the current selection.
  297. // tags:
  298. // public
  299. if(node){
  300. var newRange;
  301. var doc = dojo.doc;
  302. var range;
  303. if(dojo.global.getSelection){
  304. //WC3
  305. var sel = dojo.global.getSelection();
  306. if(sel && sel.rangeCount > 0){
  307. range = sel.getRangeAt(0);
  308. }
  309. if(range && range.compareBoundaryPoints && doc.createRange){
  310. try{
  311. newRange = doc.createRange();
  312. newRange.setStart(node, 0);
  313. if(range.compareBoundaryPoints(range.START_TO_END, newRange) === 1){
  314. return true;
  315. }
  316. }catch(e){ /* squelch */}
  317. }
  318. }else if(doc.selection){
  319. // Probably IE, so we can't use the range object as the pseudo
  320. // range doesn't implement the boundry checking, we have to
  321. // use IE specific crud.
  322. range = doc.selection.createRange();
  323. try{
  324. newRange = node.ownerDocument.body.createControlRange();
  325. if(newRange){
  326. newRange.addElement(node);
  327. }
  328. }catch(e1){
  329. try{
  330. newRange = node.ownerDocument.body.createTextRange();
  331. newRange.moveToElementText(node);
  332. }catch(e2){/* squelch */}
  333. }
  334. if(range && newRange){
  335. // We can finally compare similar to W3C
  336. if(range.compareEndPoints("EndToStart", newRange) === 1){
  337. return true;
  338. }
  339. }
  340. }
  341. }
  342. return false; // boolean
  343. }
  344. });
  345. }