1 /**
  2  * monitor user's enter and shift enter keydown,modified from ckeditor
  3  * @author yiminghe@gmail.com
  4  */
  5 KISSY.add("editor/core/enterKey", function (S,Editor,Walker,ElementPath) {
  6     var UA = S.UA,
  7         headerTagRegex = /^h[1-6]$/,
  8         dtd = Editor.XHTML_DTD,
  9         Node = S.Node,
 10         Event = S.Event;
 11 
 12 
 13     function getRange(editor) {
 14         // Get the selection ranges.
 15         var ranges = editor.getSelection().getRanges();
 16         // Delete the contents of all ranges except the first one.
 17         for (var i = ranges.length - 1; i > 0; i--) {
 18             ranges[ i ].deleteContents();
 19         }
 20         // Return the first range.
 21         return ranges[ 0 ];
 22     }
 23 
 24     function enterBlock(editor) {
 25         // Get the range for the current selection.
 26         var range = getRange(editor);
 27         var doc = range.document;
 28         // Exit the list when we're inside an empty list item block. (#5376)
 29         if (range.checkStartOfBlock() && range.checkEndOfBlock()) {
 30             var path = new ElementPath(range.startContainer),
 31                 block = path.block;
 32             //只有两层?
 33             if (block &&
 34                 ( block.nodeName() == 'li' || block.parent().nodeName() == 'li' )
 35 
 36                 ) {
 37                 if (editor.hasCommand('outdent')) {
 38                     editor.execCommand("save");
 39                     editor.execCommand('outdent');
 40                     editor.execCommand("save");
 41                     return true;
 42                 } else {
 43                     return false;
 44                 }
 45             }
 46         }
 47 
 48         // Determine the block element to be used.
 49         var blockTag = "p";
 50 
 51         // Split the range.
 52         var splitInfo = range.splitBlock(blockTag);
 53 
 54         if (!splitInfo)
 55             return true;
 56 
 57         // Get the current blocks.
 58         var previousBlock = splitInfo.previousBlock,
 59             nextBlock = splitInfo.nextBlock;
 60 
 61         var isStartOfBlock = splitInfo.wasStartOfBlock,
 62             isEndOfBlock = splitInfo.wasEndOfBlock;
 63 
 64         var node;
 65 
 66         // If this is a block under a list item, split it as well. (#1647)
 67         if (nextBlock) {
 68             node = nextBlock.parent();
 69             if (node.nodeName() == 'li') {
 70                 nextBlock._4e_breakParent(node);
 71                 nextBlock._4e_move(nextBlock.next(), true);
 72             }
 73         }
 74         else if (previousBlock && ( node = previousBlock.parent() ) && node.nodeName() == 'li') {
 75             previousBlock._4e_breakParent(node);
 76             range.moveToElementEditablePosition(previousBlock.next());
 77             previousBlock._4e_move(previousBlock.prev());
 78         }
 79 
 80         // If we have both the previous and next blocks, it means that the
 81         // boundaries were on separated blocks, or none of them where on the
 82         // block limits (start/end).
 83         if (!isStartOfBlock && !isEndOfBlock) {
 84             // If the next block is an <li> with another list tree as the first
 85             // child, we'll need to append a filler (<br>/NBSP) or the list item
 86             // wouldn't be editable. (#1420)
 87             if (nextBlock.nodeName() == 'li' &&
 88                 ( node = nextBlock.first(Walker.invisible(true)) ) &&
 89                 S.inArray(node.nodeName(), ['ul', 'ol']))
 90                 (UA['ie'] ? new Node(doc.createTextNode('\xa0')) :
 91                     new Node(doc.createElement('br'))).insertBefore(node);
 92 
 93             // Move the selection to the end block.
 94             if (nextBlock)
 95                 range.moveToElementEditablePosition(nextBlock);
 96         }
 97         else {
 98             var newBlock;
 99 
100             if (previousBlock) {
101                 // Do not enter this block if it's a header tag, or we are in
102                 // a Shift+Enter (#77). Create a new block element instead
103                 // (later in the code).
104                 if (previousBlock.nodeName() == 'li' || !headerTagRegex.test(previousBlock.nodeName())) {
105                     // Otherwise, duplicate the previous block.
106                     newBlock = previousBlock.clone();
107                 }
108             }
109             else if (nextBlock)
110                 newBlock = nextBlock.clone();
111 
112             if (!newBlock)
113                 newBlock = new Node("<" + blockTag + ">", null, doc);
114 
115             // Recreate the inline elements tree, which was available
116             // before hitting enter, so the same styles will be available in
117             // the new block.
118             var elementPath = splitInfo.elementPath;
119             if (elementPath) {
120                 for (var i = 0, len = elementPath.elements.length; i < len; i++) {
121                     var element = elementPath.elements[ i ];
122 
123                     if (element.equals(elementPath.block) || element.equals(elementPath.blockLimit))
124                         break;
125                     //<li><strong>^</strong></li>
126                     if (dtd.$removeEmpty[ element.nodeName() ]) {
127                         element = element.clone();
128                         newBlock._4e_moveChildren(element);
129                         newBlock.append(element);
130                     }
131                 }
132             }
133 
134             if (!UA['ie'])
135                 newBlock._4e_appendBogus();
136 
137             range.insertNode(newBlock);
138 
139             // This is tricky, but to make the new block visible correctly
140             // we must select it.
141             // The previousBlock check has been included because it may be
142             // empty if we have fixed a block-less space (like ENTER into an
143             // empty table cell).
144             if (UA['ie'] && isStartOfBlock && ( !isEndOfBlock || !previousBlock[0].childNodes.length )) {
145                 // Move the selection to the new block.
146                 range.moveToElementEditablePosition(isEndOfBlock ? previousBlock : newBlock);
147                 range.select();
148             }
149 
150             // Move the selection to the new block.
151             range.moveToElementEditablePosition(isStartOfBlock && !isEndOfBlock ? nextBlock : newBlock);
152         }
153 
154         if (!UA['ie']) {
155             if (nextBlock) {
156                 // If we have split the block, adds a temporary span at the
157                 // range position and scroll relatively to it.
158                 var tmpNode = new Node(doc.createElement('span'));
159 
160                 // We need some content for Safari.
161                 tmpNode.html(' ');
162 
163                 range.insertNode(tmpNode);
164                 tmpNode.scrollIntoView(undefined,false);
165                 range.deleteContents();
166             }
167             else {
168                 // We may use the above scroll logic for the new block case
169                 // too, but it gives some weird result with Opera.
170                 newBlock.scrollIntoView(undefined,false);
171             }
172         }
173         range.select();
174         return true;
175     }
176 
177     function EnterKey(editor) {
178         var doc = editor.get("document")[0];
179         Event.on(doc, "keydown", function (ev) {
180             var keyCode = ev.keyCode;
181             if (keyCode === 13) {
182                 if (ev.shiftKey || ev.ctrlKey || ev.metaKey) {
183                 } else {
184                     editor.execCommand("save");
185                     var re = editor.execCommand("enterBlock");
186                     editor.execCommand("save");
187                     if (re !== false) {
188                         ev.preventDefault();
189                     }
190                 }
191             }
192         });
193     }
194 
195     return {
196         init:function (editor) {
197             editor.addCommand("enterBlock", {
198                 exec:enterBlock
199             });
200             editor.docReady(function () {
201                 EnterKey(editor);
202             });
203         }
204     };
205 }, {
206     requires:['./base','./walker','./elementPath']
207 });
208