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