]> git.pond.sub.org Git - eow/blobdiff - static/dojo-release-1.1.1/dijit/_editor/plugins/EnterKeyHandling.js
add Dojo 1.1.1
[eow] / static / dojo-release-1.1.1 / dijit / _editor / plugins / EnterKeyHandling.js
diff --git a/static/dojo-release-1.1.1/dijit/_editor/plugins/EnterKeyHandling.js b/static/dojo-release-1.1.1/dijit/_editor/plugins/EnterKeyHandling.js
new file mode 100644 (file)
index 0000000..083743d
--- /dev/null
@@ -0,0 +1,416 @@
+if(!dojo._hasResource["dijit._editor.plugins.EnterKeyHandling"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dijit._editor.plugins.EnterKeyHandling"] = true;
+dojo.provide("dijit._editor.plugins.EnterKeyHandling");
+
+dojo.declare("dijit._editor.plugins.EnterKeyHandling", dijit._editor._Plugin, {
+       // summary: this plugin tries to handle enter key events to make all 
+       //              browsers have identical behaviors.
+
+       // blockNodeForEnter: String
+       //              this property decides the behavior of Enter key. It can be either P,
+       //              DIV, BR, or empty (which means disable this feature). Anything else
+       //              will trigger errors.
+       blockNodeForEnter: 'P',
+       constructor: function(args){
+               if(args){
+                       dojo.mixin(this,args);
+               }
+       },
+       setEditor: function(editor){
+               this.editor = editor;
+               if(this.blockNodeForEnter == 'BR'){
+                       if(dojo.isIE){
+                               editor.contentDomPreFilters.push(dojo.hitch(this, "regularPsToSingleLinePs"));
+                               editor.contentDomPostFilters.push(dojo.hitch(this, "singleLinePsToRegularPs"));
+                               editor.onLoadDeferred.addCallback(dojo.hitch(this, "_fixNewLineBehaviorForIE"));
+                       }else{
+                               editor.onLoadDeferred.addCallback(dojo.hitch(this,function(d){
+                                       try{
+                                               this.editor.document.execCommand("insertBrOnReturn", false, true);
+                                       }catch(e){}
+                                       return d;
+                               }));
+                       }
+               }else if(this.blockNodeForEnter){
+                       //add enter key handler
+                       // FIXME: need to port to the new event code!!
+                       dojo['require']('dijit._editor.range');
+                       var h = dojo.hitch(this,this.handleEnterKey);
+                       editor.addKeyHandler(13, 0, h); //enter
+                       editor.addKeyHandler(13, 2, h); //shift+enter
+                       this.connect(this.editor,'onKeyPressed','onKeyPressed');
+               }
+       },
+       connect: function(o,f,tf){
+               if(!this._connects){
+                       this._connects=[];
+               }
+               this._connects.push(dojo.connect(o,f,this,tf));
+       },
+       destroy: function(){
+               dojo.forEach(this._connects,dojo.disconnect);
+               this._connects=[];
+       },
+       onKeyPressed: function(e){
+               if(this._checkListLater){
+                       if(dojo.withGlobal(this.editor.window, 'isCollapsed', dijit)){
+                               if(!dojo.withGlobal(this.editor.window, 'hasAncestorElement', dijit._editor.selection, ['LI'])){
+                                       //circulate the undo detection code by calling RichText::execCommand directly
+                                       dijit._editor.RichText.prototype.execCommand.apply(this.editor, ['formatblock',this.blockNodeForEnter]);
+                                       //set the innerHTML of the new block node
+                                       var block = dojo.withGlobal(this.editor.window, 'getAncestorElement', dijit._editor.selection, [this.blockNodeForEnter]);
+                                       if(block){
+                                               block.innerHTML=this.bogusHtmlContent;
+                                               if(dojo.isIE){
+                                                       //the following won't work, it will move the caret to the last list item in the previous list
+//                                                     var newrange = dijit.range.create();
+//                                                     newrange.setStart(block.firstChild,0);
+//                                                     var selection = dijit.range.getSelection(this.editor.window)
+//                                                     selection.removeAllRanges();
+//                                                     selection.addRange(newrange);
+                                                       //move to the start by move backward one char
+                                                       var r = this.editor.document.selection.createRange();
+                                                       r.move('character',-1);
+                                                       r.select();
+                                               }
+                                       }else{
+                                               alert('onKeyPressed: Can not find the new block node'); //FIXME
+                                       }
+                               }
+                       }
+                       this._checkListLater = false;
+               }else if(this._pressedEnterInBlock){
+                       //the new created is the original current P, so we have previousSibling below
+                       this.removeTrailingBr(this._pressedEnterInBlock.previousSibling);
+                       delete this._pressedEnterInBlock;
+               }
+       },
+       bogusHtmlContent: ' ',
+       blockNodes: /^(?:H1|H2|H3|H4|H5|H6|LI)$/,
+       handleEnterKey: function(e){
+               // summary: manually handle enter key event to make the behavior consistant across
+               //      all supported browsers. See property blockNodeForEnter for available options
+               if(!this.blockNodeForEnter){ return true; } //let browser handle this
+               var selection, range, newrange;
+               if(e.shiftKey  //shift+enter always generates <br>
+                       || this.blockNodeForEnter=='BR'){
+                       var parent = dojo.withGlobal(this.editor.window, "getParentElement", dijit._editor.selection);
+                       var header = dijit.range.getAncestor(parent,this.editor.blockNodes);
+                       if(header){
+                               if(header.tagName=='LI'){
+                                       return true; //let brower handle
+                               }
+                               selection = dijit.range.getSelection(this.editor.window);
+                               range = selection.getRangeAt(0);
+                               if(!range.collapsed){
+                                       range.deleteContents();
+                               }
+                               if(dijit.range.atBeginningOfContainer(header, range.startContainer, range.startOffset)){
+                                       dojo.place(this.editor.document.createElement('br'), header, "before");
+                               }else if(dijit.range.atEndOfContainer(header, range.startContainer, range.startOffset)){
+                                       dojo.place(this.editor.document.createElement('br'), header, "after");
+                                       newrange = dijit.range.create();
+                                       newrange.setStartAfter(header);
+
+                                       selection.removeAllRanges();
+                                       selection.addRange(newrange);
+                               }else{
+                                       return true; //let brower handle
+                               }
+                       }else{
+                               //don't change this: do not call this.execCommand, as that may have other logic in subclass
+                               // FIXME
+                               dijit._editor.RichText.prototype.execCommand.call(this.editor, 'inserthtml', '<br>');
+                       }
+                       return false;
+               }
+               var _letBrowserHandle = true;
+               //blockNodeForEnter is either P or DIV
+               //first remove selection
+               selection = dijit.range.getSelection(this.editor.window);
+               range = selection.getRangeAt(0);
+               if(!range.collapsed){
+                       range.deleteContents();
+               }
+
+               var block = dijit.range.getBlockAncestor(range.endContainer, null, this.editor.editNode);
+
+               if((this._checkListLater = (block.blockNode && block.blockNode.tagName == 'LI'))){
+                       return true;
+               }
+
+               //text node directly under body, let's wrap them in a node
+               if(!block.blockNode){
+                       this.editor.document.execCommand('formatblock', false, this.blockNodeForEnter);
+                       //get the newly created block node
+                       // FIXME
+                       block = {blockNode:dojo.withGlobal(this.editor.window, "getAncestorElement", dijit._editor.selection, [this.blockNodeForEnter]),
+                                       blockContainer: this.editor.editNode};
+                       if(block.blockNode){
+                               if(!(block.blockNode.textContent || block.blockNode.innerHTML).replace(/^\s+|\s+$/g, "").length){
+                                       this.removeTrailingBr(block.blockNode);
+                                       return false;
+                               }
+                       }else{
+                               block.blockNode = this.editor.editNode;
+                       }
+                       selection = dijit.range.getSelection(this.editor.window);
+                       range = selection.getRangeAt(0);
+               }
+               var newblock = this.editor.document.createElement(this.blockNodeForEnter);
+               newblock.innerHTML=this.bogusHtmlContent;
+               this.removeTrailingBr(block.blockNode);
+               if(dijit.range.atEndOfContainer(block.blockNode, range.endContainer, range.endOffset)){
+                       if(block.blockNode === block.blockContainer){
+                               block.blockNode.appendChild(newblock);
+                       }else{
+                               dojo.place(newblock, block.blockNode, "after");
+                       }
+                       _letBrowserHandle = false;
+                       //lets move caret to the newly created block
+                       newrange = dijit.range.create();
+                       newrange.setStart(newblock,0);
+                       selection.removeAllRanges();
+                       selection.addRange(newrange);
+                       if(this.editor.height){
+                               newblock.scrollIntoView(false);
+                       }
+               }else if(dijit.range.atBeginningOfContainer(block.blockNode,
+                               range.startContainer, range.startOffset)){
+                       dojo.place(newblock, block.blockNode, block.blockNode === block.blockContainer ? "first" : "before");
+                       if(newblock.nextSibling && this.editor.height){
+                               //browser does not scroll the caret position into view, do it manually
+                               newblock.nextSibling.scrollIntoView(false);
+                       }
+                       _letBrowserHandle = false;
+               }else{ //press enter in the middle of P
+                       if(dojo.isMoz){
+                               //press enter in middle of P may leave a trailing <br/>, let's remove it later
+                               this._pressedEnterInBlock = block.blockNode;
+                       }
+               }
+               return _letBrowserHandle;
+       },
+       removeTrailingBr: function(container){
+               var para = /P|DIV|LI/i.test(container.tagName) ?
+                       container : dijit._editor.selection.getParentOfType(container,['P','DIV','LI']);
+
+               if(!para){ return; }
+               if(para.lastChild){
+                       if((para.childNodes.length > 1 && para.lastChild.nodeType == 3 && /^[\s\xAD]*$/.test(para.lastChild.nodeValue)) ||
+                               (para.lastChild && para.lastChild.tagName=='BR')){
+
+                               dojo._destroyElement(para.lastChild);
+                       }
+               }
+               if(!para.childNodes.length){
+                       para.innerHTML=this.bogusHtmlContent;
+               }
+       },
+       _fixNewLineBehaviorForIE: function(d){
+               if(this.editor.document.__INSERTED_EDITIOR_NEWLINE_CSS === undefined){
+                       var lineFixingStyles = "p{margin:0 !important;}";
+                       var insertCssText = function(
+                               /*String*/ cssStr,
+                               /*Document*/ doc,
+                               /*String*/ URI)
+                       {
+                               //      summary:
+                               //              Attempt to insert CSS rules into the document through inserting a
+                               //              style element
+
+                               // DomNode Style  = insertCssText(String ".dojoMenu {color: green;}"[, DomDoc document, dojo.uri.Uri Url ])
+                               if(!cssStr){
+                                       return null; // HTMLStyleElement
+                               }
+                               if(!doc){ doc = document; }
+//                                     if(URI){// fix paths in cssStr
+//                                             cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
+//                                     }
+                               var style = doc.createElement("style");
+                               style.setAttribute("type", "text/css");
+                               // IE is b0rken enough to require that we add the element to the doc
+                               // before changing it's properties
+                               var head = doc.getElementsByTagName("head")[0];
+                               if(!head){ // must have a head tag
+                                       console.debug("No head tag in document, aborting styles");
+                                       return null;    //      HTMLStyleElement
+                               }else{
+                                       head.appendChild(style);
+                               }
+                               if(style.styleSheet){// IE
+                                       var setFunc = function(){
+                                               try{
+                                                       style.styleSheet.cssText = cssStr;
+                                               }catch(e){ console.debug(e); }
+                                       };
+                                       if(style.styleSheet.disabled){
+                                               setTimeout(setFunc, 10);
+                                       }else{
+                                               setFunc();
+                                       }
+                               }else{ // w3c
+                                       var cssText = doc.createTextNode(cssStr);
+                                       style.appendChild(cssText);
+                               }
+                               return style;   //      HTMLStyleElement
+                       }
+                       insertCssText(lineFixingStyles, this.editor.document);
+                       this.editor.document.__INSERTED_EDITIOR_NEWLINE_CSS = true;
+                       // this.regularPsToSingleLinePs(this.editNode);
+                       return d;
+               }
+               return null;
+       },
+       regularPsToSingleLinePs: function(element, noWhiteSpaceInEmptyP){
+               function wrapLinesInPs(el){
+                 // move "lines" of top-level text nodes into ps
+                       function wrapNodes(nodes){
+                               // nodes are assumed to all be siblings
+                               var newP = nodes[0].ownerDocument.createElement('p'); // FIXME: not very idiomatic
+                               nodes[0].parentNode.insertBefore(newP, nodes[0]);
+                               dojo.forEach(nodes, function(node){
+                                       newP.appendChild(node);
+                               });
+                       }
+
+                       var currentNodeIndex = 0;
+                       var nodesInLine = [];
+                       var currentNode;
+                       while(currentNodeIndex < el.childNodes.length){
+                               currentNode = el.childNodes[currentNodeIndex];
+                               if( (currentNode.nodeName!='BR') &&
+                                       (currentNode.nodeType==1) &&
+                                       (dojo.style(currentNode, "display")!="block")
+                               ){
+                                       nodesInLine.push(currentNode);
+                               }else{
+                                       // hit line delimiter; process nodesInLine if there are any
+                                       var nextCurrentNode = currentNode.nextSibling;
+                                       if(nodesInLine.length){
+                                               wrapNodes(nodesInLine);
+                                               currentNodeIndex = (currentNodeIndex+1)-nodesInLine.length;
+                                               if(currentNode.nodeName=="BR"){
+                                                       dojo._destroyElement(currentNode);
+                                               }
+                                       }
+                                       nodesInLine = [];
+                               }
+                               currentNodeIndex++;
+                       }
+                       if(nodesInLine.length){ wrapNodes(nodesInLine); }
+               }
+
+               function splitP(el){
+                       // split a paragraph into seperate paragraphs at BRs
+                       var currentNode = null;
+                       var trailingNodes = [];
+                       var lastNodeIndex = el.childNodes.length-1;
+                       for(var i=lastNodeIndex; i>=0; i--){
+                               currentNode = el.childNodes[i];
+                               if(currentNode.nodeName=="BR"){
+                                       var newP = currentNode.ownerDocument.createElement('p');
+                                       dojo.place(newP, el, "after");
+                                       if (trailingNodes.length==0 && i != lastNodeIndex) {
+                                               newP.innerHTML = "&nbsp;"
+                                       }
+                                       dojo.forEach(trailingNodes, function(node){
+                                               newP.appendChild(node);
+                                       });
+                                       dojo._destroyElement(currentNode);
+                                       trailingNodes = [];
+                               }else{
+                                       trailingNodes.unshift(currentNode);
+                               }
+                       }
+               }
+
+               var pList = [];
+               var ps = element.getElementsByTagName('p');
+               dojo.forEach(ps, function(p){ pList.push(p); });
+               dojo.forEach(pList, function(p){
+                       if(     (p.previousSibling) &&
+                               (p.previousSibling.nodeName == 'P' || dojo.style(p.previousSibling, 'display') != 'block')
+                       ){
+                               var newP = p.parentNode.insertBefore(this.document.createElement('p'), p);
+                               // this is essential to prevent IE from losing the P.
+                               // if it's going to be innerHTML'd later we need
+                               // to add the &nbsp; to _really_ force the issue
+                               newP.innerHTML = noWhiteSpaceInEmptyP ? "" : "&nbsp;";
+                       }
+                       splitP(p);
+         },this.editor);
+               wrapLinesInPs(element);
+               return element;
+       },
+
+       singleLinePsToRegularPs: function(element){
+               function getParagraphParents(node){
+                       var ps = node.getElementsByTagName('p');
+                       var parents = [];
+                       for(var i=0; i<ps.length; i++){
+                               var p = ps[i];
+                               var knownParent = false;
+                               for(var k=0; k < parents.length; k++){
+                                       if(parents[k] === p.parentNode){
+                                               knownParent = true;
+                                               break;
+                                       }
+                               }
+                               if(!knownParent){
+                                       parents.push(p.parentNode);
+                               }
+                       }
+                       return parents;
+               }
+
+               function isParagraphDelimiter(node){
+                       if(node.nodeType != 1 || node.tagName != 'P'){
+                               return dojo.style(node, 'display') == 'block';
+                       }else{
+                               if(!node.childNodes.length || node.innerHTML=="&nbsp;"){ return true; }
+                               //return node.innerHTML.match(/^(<br\ ?\/?>| |\&nbsp\;)$/i);
+                       }
+                       return false;
+               }
+
+               var paragraphContainers = getParagraphParents(element);
+               for(var i=0; i<paragraphContainers.length; i++){
+                       var container = paragraphContainers[i];
+                       var firstPInBlock = null;
+                       var node = container.firstChild;
+                       var deleteNode = null;
+                       while(node){
+                               if(node.nodeType != "1" || node.tagName != 'P'){
+                                       firstPInBlock = null;
+                               }else if (isParagraphDelimiter(node)){
+                                       deleteNode = node;
+                                       firstPInBlock = null;
+                               }else{
+                                       if(firstPInBlock == null){
+                                               firstPInBlock = node;
+                                       }else{
+                                               if( (!firstPInBlock.lastChild || firstPInBlock.lastChild.nodeName != 'BR') &&
+                                                       (node.firstChild) &&
+                                                       (node.firstChild.nodeName != 'BR')
+                                               ){
+                                                       firstPInBlock.appendChild(this.editor.document.createElement('br'));
+                                               }
+                                               while(node.firstChild){
+                                                       firstPInBlock.appendChild(node.firstChild);
+                                               }
+                                               deleteNode = node;
+                                       }
+                               }
+                               node = node.nextSibling;
+                               if(deleteNode){
+                                       dojo._destroyElement(deleteNode);
+                                       deleteNode = null;
+                               }
+                       }
+               }
+               return element;
+       }
+});
+
+}