]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dijit/_editor/plugins/EnterKeyHandling.js
Comment class stub
[eow] / static / dojo-release-1.1.1 / dijit / _editor / plugins / EnterKeyHandling.js
1 if(!dojo._hasResource["dijit._editor.plugins.EnterKeyHandling"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dijit._editor.plugins.EnterKeyHandling"] = true;
3 dojo.provide("dijit._editor.plugins.EnterKeyHandling");
4
5 dojo.declare("dijit._editor.plugins.EnterKeyHandling", dijit._editor._Plugin, {
6         // summary: this plugin tries to handle enter key events to make all 
7         //              browsers have identical behaviors.
8
9         // blockNodeForEnter: String
10         //              this property decides the behavior of Enter key. It can be either P,
11         //              DIV, BR, or empty (which means disable this feature). Anything else
12         //              will trigger errors.
13         blockNodeForEnter: 'P',
14         constructor: function(args){
15                 if(args){
16                         dojo.mixin(this,args);
17                 }
18         },
19         setEditor: function(editor){
20                 this.editor = editor;
21                 if(this.blockNodeForEnter == 'BR'){
22                         if(dojo.isIE){
23                                 editor.contentDomPreFilters.push(dojo.hitch(this, "regularPsToSingleLinePs"));
24                                 editor.contentDomPostFilters.push(dojo.hitch(this, "singleLinePsToRegularPs"));
25                                 editor.onLoadDeferred.addCallback(dojo.hitch(this, "_fixNewLineBehaviorForIE"));
26                         }else{
27                                 editor.onLoadDeferred.addCallback(dojo.hitch(this,function(d){
28                                         try{
29                                                 this.editor.document.execCommand("insertBrOnReturn", false, true);
30                                         }catch(e){}
31                                         return d;
32                                 }));
33                         }
34                 }else if(this.blockNodeForEnter){
35                         //add enter key handler
36                         // FIXME: need to port to the new event code!!
37                         dojo['require']('dijit._editor.range');
38                         var h = dojo.hitch(this,this.handleEnterKey);
39                         editor.addKeyHandler(13, 0, h); //enter
40                         editor.addKeyHandler(13, 2, h); //shift+enter
41                         this.connect(this.editor,'onKeyPressed','onKeyPressed');
42                 }
43         },
44         connect: function(o,f,tf){
45                 if(!this._connects){
46                         this._connects=[];
47                 }
48                 this._connects.push(dojo.connect(o,f,this,tf));
49         },
50         destroy: function(){
51                 dojo.forEach(this._connects,dojo.disconnect);
52                 this._connects=[];
53         },
54         onKeyPressed: function(e){
55                 if(this._checkListLater){
56                         if(dojo.withGlobal(this.editor.window, 'isCollapsed', dijit)){
57                                 if(!dojo.withGlobal(this.editor.window, 'hasAncestorElement', dijit._editor.selection, ['LI'])){
58                                         //circulate the undo detection code by calling RichText::execCommand directly
59                                         dijit._editor.RichText.prototype.execCommand.apply(this.editor, ['formatblock',this.blockNodeForEnter]);
60                                         //set the innerHTML of the new block node
61                                         var block = dojo.withGlobal(this.editor.window, 'getAncestorElement', dijit._editor.selection, [this.blockNodeForEnter]);
62                                         if(block){
63                                                 block.innerHTML=this.bogusHtmlContent;
64                                                 if(dojo.isIE){
65                                                         //the following won't work, it will move the caret to the last list item in the previous list
66 //                                                      var newrange = dijit.range.create();
67 //                                                      newrange.setStart(block.firstChild,0);
68 //                                                      var selection = dijit.range.getSelection(this.editor.window)
69 //                                                      selection.removeAllRanges();
70 //                                                      selection.addRange(newrange);
71                                                         //move to the start by move backward one char
72                                                         var r = this.editor.document.selection.createRange();
73                                                         r.move('character',-1);
74                                                         r.select();
75                                                 }
76                                         }else{
77                                                 alert('onKeyPressed: Can not find the new block node'); //FIXME
78                                         }
79                                 }
80                         }
81                         this._checkListLater = false;
82                 }else if(this._pressedEnterInBlock){
83                         //the new created is the original current P, so we have previousSibling below
84                         this.removeTrailingBr(this._pressedEnterInBlock.previousSibling);
85                         delete this._pressedEnterInBlock;
86                 }
87         },
88         bogusHtmlContent: ' ',
89         blockNodes: /^(?:H1|H2|H3|H4|H5|H6|LI)$/,
90         handleEnterKey: function(e){
91                 // summary: manually handle enter key event to make the behavior consistant across
92                 //      all supported browsers. See property blockNodeForEnter for available options
93                 if(!this.blockNodeForEnter){ return true; } //let browser handle this
94                 var selection, range, newrange;
95                 if(e.shiftKey  //shift+enter always generates <br>
96                         || this.blockNodeForEnter=='BR'){
97                         var parent = dojo.withGlobal(this.editor.window, "getParentElement", dijit._editor.selection);
98                         var header = dijit.range.getAncestor(parent,this.editor.blockNodes);
99                         if(header){
100                                 if(header.tagName=='LI'){
101                                         return true; //let brower handle
102                                 }
103                                 selection = dijit.range.getSelection(this.editor.window);
104                                 range = selection.getRangeAt(0);
105                                 if(!range.collapsed){
106                                         range.deleteContents();
107                                 }
108                                 if(dijit.range.atBeginningOfContainer(header, range.startContainer, range.startOffset)){
109                                         dojo.place(this.editor.document.createElement('br'), header, "before");
110                                 }else if(dijit.range.atEndOfContainer(header, range.startContainer, range.startOffset)){
111                                         dojo.place(this.editor.document.createElement('br'), header, "after");
112                                         newrange = dijit.range.create();
113                                         newrange.setStartAfter(header);
114
115                                         selection.removeAllRanges();
116                                         selection.addRange(newrange);
117                                 }else{
118                                         return true; //let brower handle
119                                 }
120                         }else{
121                                 //don't change this: do not call this.execCommand, as that may have other logic in subclass
122                                 // FIXME
123                                 dijit._editor.RichText.prototype.execCommand.call(this.editor, 'inserthtml', '<br>');
124                         }
125                         return false;
126                 }
127                 var _letBrowserHandle = true;
128                 //blockNodeForEnter is either P or DIV
129                 //first remove selection
130                 selection = dijit.range.getSelection(this.editor.window);
131                 range = selection.getRangeAt(0);
132                 if(!range.collapsed){
133                         range.deleteContents();
134                 }
135
136                 var block = dijit.range.getBlockAncestor(range.endContainer, null, this.editor.editNode);
137
138                 if((this._checkListLater = (block.blockNode && block.blockNode.tagName == 'LI'))){
139                         return true;
140                 }
141
142                 //text node directly under body, let's wrap them in a node
143                 if(!block.blockNode){
144                         this.editor.document.execCommand('formatblock', false, this.blockNodeForEnter);
145                         //get the newly created block node
146                         // FIXME
147                         block = {blockNode:dojo.withGlobal(this.editor.window, "getAncestorElement", dijit._editor.selection, [this.blockNodeForEnter]),
148                                         blockContainer: this.editor.editNode};
149                         if(block.blockNode){
150                                 if(!(block.blockNode.textContent || block.blockNode.innerHTML).replace(/^\s+|\s+$/g, "").length){
151                                         this.removeTrailingBr(block.blockNode);
152                                         return false;
153                                 }
154                         }else{
155                                 block.blockNode = this.editor.editNode;
156                         }
157                         selection = dijit.range.getSelection(this.editor.window);
158                         range = selection.getRangeAt(0);
159                 }
160                 var newblock = this.editor.document.createElement(this.blockNodeForEnter);
161                 newblock.innerHTML=this.bogusHtmlContent;
162                 this.removeTrailingBr(block.blockNode);
163                 if(dijit.range.atEndOfContainer(block.blockNode, range.endContainer, range.endOffset)){
164                         if(block.blockNode === block.blockContainer){
165                                 block.blockNode.appendChild(newblock);
166                         }else{
167                                 dojo.place(newblock, block.blockNode, "after");
168                         }
169                         _letBrowserHandle = false;
170                         //lets move caret to the newly created block
171                         newrange = dijit.range.create();
172                         newrange.setStart(newblock,0);
173                         selection.removeAllRanges();
174                         selection.addRange(newrange);
175                         if(this.editor.height){
176                                 newblock.scrollIntoView(false);
177                         }
178                 }else if(dijit.range.atBeginningOfContainer(block.blockNode,
179                                 range.startContainer, range.startOffset)){
180                         dojo.place(newblock, block.blockNode, block.blockNode === block.blockContainer ? "first" : "before");
181                         if(newblock.nextSibling && this.editor.height){
182                                 //browser does not scroll the caret position into view, do it manually
183                                 newblock.nextSibling.scrollIntoView(false);
184                         }
185                         _letBrowserHandle = false;
186                 }else{ //press enter in the middle of P
187                         if(dojo.isMoz){
188                                 //press enter in middle of P may leave a trailing <br/>, let's remove it later
189                                 this._pressedEnterInBlock = block.blockNode;
190                         }
191                 }
192                 return _letBrowserHandle;
193         },
194         removeTrailingBr: function(container){
195                 var para = /P|DIV|LI/i.test(container.tagName) ?
196                         container : dijit._editor.selection.getParentOfType(container,['P','DIV','LI']);
197
198                 if(!para){ return; }
199                 if(para.lastChild){
200                         if((para.childNodes.length > 1 && para.lastChild.nodeType == 3 && /^[\s\xAD]*$/.test(para.lastChild.nodeValue)) ||
201                                 (para.lastChild && para.lastChild.tagName=='BR')){
202
203                                 dojo._destroyElement(para.lastChild);
204                         }
205                 }
206                 if(!para.childNodes.length){
207                         para.innerHTML=this.bogusHtmlContent;
208                 }
209         },
210         _fixNewLineBehaviorForIE: function(d){
211                 if(this.editor.document.__INSERTED_EDITIOR_NEWLINE_CSS === undefined){
212                         var lineFixingStyles = "p{margin:0 !important;}";
213                         var insertCssText = function(
214                                 /*String*/ cssStr,
215                                 /*Document*/ doc,
216                                 /*String*/ URI)
217                         {
218                                 //      summary:
219                                 //              Attempt to insert CSS rules into the document through inserting a
220                                 //              style element
221
222                                 // DomNode Style  = insertCssText(String ".dojoMenu {color: green;}"[, DomDoc document, dojo.uri.Uri Url ])
223                                 if(!cssStr){
224                                         return null; // HTMLStyleElement
225                                 }
226                                 if(!doc){ doc = document; }
227 //                                      if(URI){// fix paths in cssStr
228 //                                              cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
229 //                                      }
230                                 var style = doc.createElement("style");
231                                 style.setAttribute("type", "text/css");
232                                 // IE is b0rken enough to require that we add the element to the doc
233                                 // before changing it's properties
234                                 var head = doc.getElementsByTagName("head")[0];
235                                 if(!head){ // must have a head tag
236                                         console.debug("No head tag in document, aborting styles");
237                                         return null;    //      HTMLStyleElement
238                                 }else{
239                                         head.appendChild(style);
240                                 }
241                                 if(style.styleSheet){// IE
242                                         var setFunc = function(){
243                                                 try{
244                                                         style.styleSheet.cssText = cssStr;
245                                                 }catch(e){ console.debug(e); }
246                                         };
247                                         if(style.styleSheet.disabled){
248                                                 setTimeout(setFunc, 10);
249                                         }else{
250                                                 setFunc();
251                                         }
252                                 }else{ // w3c
253                                         var cssText = doc.createTextNode(cssStr);
254                                         style.appendChild(cssText);
255                                 }
256                                 return style;   //      HTMLStyleElement
257                         }
258                         insertCssText(lineFixingStyles, this.editor.document);
259                         this.editor.document.__INSERTED_EDITIOR_NEWLINE_CSS = true;
260                         // this.regularPsToSingleLinePs(this.editNode);
261                         return d;
262                 }
263                 return null;
264         },
265         regularPsToSingleLinePs: function(element, noWhiteSpaceInEmptyP){
266                 function wrapLinesInPs(el){
267                   // move "lines" of top-level text nodes into ps
268                         function wrapNodes(nodes){
269                                 // nodes are assumed to all be siblings
270                                 var newP = nodes[0].ownerDocument.createElement('p'); // FIXME: not very idiomatic
271                                 nodes[0].parentNode.insertBefore(newP, nodes[0]);
272                                 dojo.forEach(nodes, function(node){
273                                         newP.appendChild(node);
274                                 });
275                         }
276
277                         var currentNodeIndex = 0;
278                         var nodesInLine = [];
279                         var currentNode;
280                         while(currentNodeIndex < el.childNodes.length){
281                                 currentNode = el.childNodes[currentNodeIndex];
282                                 if( (currentNode.nodeName!='BR') &&
283                                         (currentNode.nodeType==1) &&
284                                         (dojo.style(currentNode, "display")!="block")
285                                 ){
286                                         nodesInLine.push(currentNode);
287                                 }else{
288                                         // hit line delimiter; process nodesInLine if there are any
289                                         var nextCurrentNode = currentNode.nextSibling;
290                                         if(nodesInLine.length){
291                                                 wrapNodes(nodesInLine);
292                                                 currentNodeIndex = (currentNodeIndex+1)-nodesInLine.length;
293                                                 if(currentNode.nodeName=="BR"){
294                                                         dojo._destroyElement(currentNode);
295                                                 }
296                                         }
297                                         nodesInLine = [];
298                                 }
299                                 currentNodeIndex++;
300                         }
301                         if(nodesInLine.length){ wrapNodes(nodesInLine); }
302                 }
303
304                 function splitP(el){
305                         // split a paragraph into seperate paragraphs at BRs
306                         var currentNode = null;
307                         var trailingNodes = [];
308                         var lastNodeIndex = el.childNodes.length-1;
309                         for(var i=lastNodeIndex; i>=0; i--){
310                                 currentNode = el.childNodes[i];
311                                 if(currentNode.nodeName=="BR"){
312                                         var newP = currentNode.ownerDocument.createElement('p');
313                                         dojo.place(newP, el, "after");
314                                         if (trailingNodes.length==0 && i != lastNodeIndex) {
315                                                 newP.innerHTML = "&nbsp;"
316                                         }
317                                         dojo.forEach(trailingNodes, function(node){
318                                                 newP.appendChild(node);
319                                         });
320                                         dojo._destroyElement(currentNode);
321                                         trailingNodes = [];
322                                 }else{
323                                         trailingNodes.unshift(currentNode);
324                                 }
325                         }
326                 }
327
328                 var pList = [];
329                 var ps = element.getElementsByTagName('p');
330                 dojo.forEach(ps, function(p){ pList.push(p); });
331                 dojo.forEach(pList, function(p){
332                         if(     (p.previousSibling) &&
333                                 (p.previousSibling.nodeName == 'P' || dojo.style(p.previousSibling, 'display') != 'block')
334                         ){
335                                 var newP = p.parentNode.insertBefore(this.document.createElement('p'), p);
336                                 // this is essential to prevent IE from losing the P.
337                                 // if it's going to be innerHTML'd later we need
338                                 // to add the &nbsp; to _really_ force the issue
339                                 newP.innerHTML = noWhiteSpaceInEmptyP ? "" : "&nbsp;";
340                         }
341                         splitP(p);
342           },this.editor);
343                 wrapLinesInPs(element);
344                 return element;
345         },
346
347         singleLinePsToRegularPs: function(element){
348                 function getParagraphParents(node){
349                         var ps = node.getElementsByTagName('p');
350                         var parents = [];
351                         for(var i=0; i<ps.length; i++){
352                                 var p = ps[i];
353                                 var knownParent = false;
354                                 for(var k=0; k < parents.length; k++){
355                                         if(parents[k] === p.parentNode){
356                                                 knownParent = true;
357                                                 break;
358                                         }
359                                 }
360                                 if(!knownParent){
361                                         parents.push(p.parentNode);
362                                 }
363                         }
364                         return parents;
365                 }
366
367                 function isParagraphDelimiter(node){
368                         if(node.nodeType != 1 || node.tagName != 'P'){
369                                 return dojo.style(node, 'display') == 'block';
370                         }else{
371                                 if(!node.childNodes.length || node.innerHTML=="&nbsp;"){ return true; }
372                                 //return node.innerHTML.match(/^(<br\ ?\/?>| |\&nbsp\;)$/i);
373                         }
374                         return false;
375                 }
376
377                 var paragraphContainers = getParagraphParents(element);
378                 for(var i=0; i<paragraphContainers.length; i++){
379                         var container = paragraphContainers[i];
380                         var firstPInBlock = null;
381                         var node = container.firstChild;
382                         var deleteNode = null;
383                         while(node){
384                                 if(node.nodeType != "1" || node.tagName != 'P'){
385                                         firstPInBlock = null;
386                                 }else if (isParagraphDelimiter(node)){
387                                         deleteNode = node;
388                                         firstPInBlock = null;
389                                 }else{
390                                         if(firstPInBlock == null){
391                                                 firstPInBlock = node;
392                                         }else{
393                                                 if( (!firstPInBlock.lastChild || firstPInBlock.lastChild.nodeName != 'BR') &&
394                                                         (node.firstChild) &&
395                                                         (node.firstChild.nodeName != 'BR')
396                                                 ){
397                                                         firstPInBlock.appendChild(this.editor.document.createElement('br'));
398                                                 }
399                                                 while(node.firstChild){
400                                                         firstPInBlock.appendChild(node.firstChild);
401                                                 }
402                                                 deleteNode = node;
403                                         }
404                                 }
405                                 node = node.nextSibling;
406                                 if(deleteNode){
407                                         dojo._destroyElement(deleteNode);
408                                         deleteNode = null;
409                                 }
410                         }
411                 }
412                 return element;
413         }
414 });
415
416 }