Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
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 = "&nbsp;"
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 &nbsp; to _really_ force the issue
346
				newP.innerHTML = noWhiteSpaceInEmptyP ? "" : "&nbsp;";
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=="&nbsp;"){ return true }
379
				//return node.innerHTML.match(/^(<br\ ?\/?>| |\&nbsp\;)$/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
}