2150 |
mathias |
1 |
if(!dojo._hasResource["dijit._editor.RichText"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
|
|
|
2 |
dojo._hasResource["dijit._editor.RichText"] = true;
|
|
|
3 |
dojo.provide("dijit._editor.RichText");
|
|
|
4 |
|
|
|
5 |
dojo.require("dijit._Widget");
|
|
|
6 |
dojo.require("dijit._editor.selection");
|
|
|
7 |
dojo.require("dojo.i18n");
|
|
|
8 |
dojo.requireLocalization("dijit", "Textarea", null, "ROOT");
|
|
|
9 |
|
|
|
10 |
// used to restore content when user leaves this page then comes back
|
|
|
11 |
// but do not try doing document.write if we are using xd loading.
|
|
|
12 |
// document.write will only work if RichText.js is included in the dojo.js
|
|
|
13 |
// file. If it is included in dojo.js and you want to allow rich text saving
|
|
|
14 |
// for back/forward actions, then set djConfig.allowXdRichTextSave = true.
|
|
|
15 |
if(!djConfig["useXDomain"] || djConfig["allowXdRichTextSave"]){
|
|
|
16 |
if(dojo._postLoad){
|
|
|
17 |
(function(){
|
|
|
18 |
var savetextarea = dojo.doc.createElement('textarea');
|
|
|
19 |
savetextarea.id = "dijit._editor.RichText.savedContent";
|
|
|
20 |
var s = savetextarea.style;
|
|
|
21 |
s.display='none';
|
|
|
22 |
s.position='absolute';
|
|
|
23 |
s.top="-100px";
|
|
|
24 |
s.left="-100px"
|
|
|
25 |
s.height="3px";
|
|
|
26 |
s.width="3px";
|
|
|
27 |
dojo.body().appendChild(savetextarea);
|
|
|
28 |
})();
|
|
|
29 |
}else{
|
|
|
30 |
//dojo.body() is not available before onLoad is fired
|
|
|
31 |
try {
|
|
|
32 |
dojo.doc.write('<textarea id="dijit._editor.RichText.savedContent" ' +
|
|
|
33 |
'style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>');
|
|
|
34 |
}catch(e){ }
|
|
|
35 |
}
|
|
|
36 |
}
|
|
|
37 |
dojo.declare("dijit._editor.RichText", [ dijit._Widget ], {
|
|
|
38 |
constructor: function(){
|
|
|
39 |
// summary:
|
|
|
40 |
// dijit._editor.RichText is the core of the WYSIWYG editor in dojo, which
|
|
|
41 |
// provides the basic editing features. It also encapsulates the differences
|
|
|
42 |
// of different js engines for various browsers
|
|
|
43 |
//
|
|
|
44 |
// contentPreFilters: Array
|
|
|
45 |
// pre content filter function register array.
|
|
|
46 |
// these filters will be executed before the actual
|
|
|
47 |
// editing area get the html content
|
|
|
48 |
this.contentPreFilters = [];
|
|
|
49 |
|
|
|
50 |
// contentPostFilters: Array
|
|
|
51 |
// post content filter function register array.
|
|
|
52 |
// these will be used on the resulting html
|
|
|
53 |
// from contentDomPostFilters. The resuling
|
|
|
54 |
// content is the final html (returned by getValue())
|
|
|
55 |
this.contentPostFilters = [];
|
|
|
56 |
|
|
|
57 |
// contentDomPreFilters: Array
|
|
|
58 |
// pre content dom filter function register array.
|
|
|
59 |
// these filters are applied after the result from
|
|
|
60 |
// contentPreFilters are set to the editing area
|
|
|
61 |
this.contentDomPreFilters = [];
|
|
|
62 |
|
|
|
63 |
// contentDomPostFilters: Array
|
|
|
64 |
// post content dom filter function register array.
|
|
|
65 |
// these filters are executed on the editing area dom
|
|
|
66 |
// the result from these will be passed to contentPostFilters
|
|
|
67 |
this.contentDomPostFilters = [];
|
|
|
68 |
|
|
|
69 |
// editingAreaStyleSheets: Array
|
|
|
70 |
// array to store all the stylesheets applied to the editing area
|
|
|
71 |
this.editingAreaStyleSheets=[];
|
|
|
72 |
|
|
|
73 |
this._keyHandlers = {};
|
|
|
74 |
this.contentPreFilters.push(dojo.hitch(this, "_preFixUrlAttributes"));
|
|
|
75 |
if(dojo.isMoz){
|
|
|
76 |
this.contentPreFilters.push(this._fixContentForMoz);
|
|
|
77 |
}
|
|
|
78 |
//this.contentDomPostFilters.push(this._postDomFixUrlAttributes);
|
|
|
79 |
|
|
|
80 |
this.onLoadDeferred = new dojo.Deferred();
|
|
|
81 |
},
|
|
|
82 |
|
|
|
83 |
// inheritWidth: Boolean
|
|
|
84 |
// whether to inherit the parent's width or simply use 100%
|
|
|
85 |
inheritWidth: false,
|
|
|
86 |
|
|
|
87 |
// focusOnLoad: Boolean
|
|
|
88 |
// whether focusing into this instance of richtext when page onload
|
|
|
89 |
focusOnLoad: false,
|
|
|
90 |
|
|
|
91 |
// name: String
|
|
|
92 |
// If a save name is specified the content is saved and restored when the user
|
|
|
93 |
// leave this page can come back, or if the editor is not properly closed after
|
|
|
94 |
// editing has started.
|
|
|
95 |
name: "",
|
|
|
96 |
|
|
|
97 |
// styleSheets: String
|
|
|
98 |
// semicolon (";") separated list of css files for the editing area
|
|
|
99 |
styleSheets: "",
|
|
|
100 |
|
|
|
101 |
// _content: String
|
|
|
102 |
// temporary content storage
|
|
|
103 |
_content: "",
|
|
|
104 |
|
|
|
105 |
// height: String
|
|
|
106 |
// set height to fix the editor at a specific height, with scrolling.
|
|
|
107 |
// By default, this is 300px. If you want to have the editor always
|
|
|
108 |
// resizes to accommodate the content, use AlwaysShowToolbar plugin
|
|
|
109 |
// and set height=""
|
|
|
110 |
height: "300px",
|
|
|
111 |
|
|
|
112 |
// minHeight: String
|
|
|
113 |
// The minimum height that the editor should have
|
|
|
114 |
minHeight: "1em",
|
|
|
115 |
|
|
|
116 |
// isClosed: Boolean
|
|
|
117 |
isClosed: true,
|
|
|
118 |
|
|
|
119 |
// isLoaded: Boolean
|
|
|
120 |
isLoaded: false,
|
|
|
121 |
|
|
|
122 |
// _SEPARATOR: String
|
|
|
123 |
// used to concat contents from multiple textareas into a single string
|
|
|
124 |
_SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@",
|
|
|
125 |
|
|
|
126 |
// onLoadDeferred: dojo.Deferred
|
|
|
127 |
// deferred which is fired when the editor finishes loading
|
|
|
128 |
onLoadDeferred: null,
|
|
|
129 |
|
|
|
130 |
postCreate: function(){
|
|
|
131 |
// summary: init
|
|
|
132 |
dojo.publish("dijit._editor.RichText::init", [this]);
|
|
|
133 |
this.open();
|
|
|
134 |
this.setupDefaultShortcuts();
|
|
|
135 |
},
|
|
|
136 |
|
|
|
137 |
setupDefaultShortcuts: function(){
|
|
|
138 |
// summary: add some default key handlers
|
|
|
139 |
// description:
|
|
|
140 |
// Overwrite this to setup your own handlers. The default
|
|
|
141 |
// implementation does not use Editor commands, but directly
|
|
|
142 |
// executes the builtin commands within the underlying browser
|
|
|
143 |
// support.
|
|
|
144 |
var ctrl = this.KEY_CTRL;
|
|
|
145 |
var exec = function(cmd, arg){
|
|
|
146 |
return arguments.length == 1 ? function(){ this.execCommand(cmd); } :
|
|
|
147 |
function(){ this.execCommand(cmd, arg); }
|
|
|
148 |
}
|
|
|
149 |
this.addKeyHandler("b", ctrl, exec("bold"));
|
|
|
150 |
this.addKeyHandler("i", ctrl, exec("italic"));
|
|
|
151 |
this.addKeyHandler("u", ctrl, exec("underline"));
|
|
|
152 |
this.addKeyHandler("a", ctrl, exec("selectall"));
|
|
|
153 |
this.addKeyHandler("s", ctrl, function () { this.save(true); });
|
|
|
154 |
|
|
|
155 |
this.addKeyHandler("1", ctrl, exec("formatblock", "h1"));
|
|
|
156 |
this.addKeyHandler("2", ctrl, exec("formatblock", "h2"));
|
|
|
157 |
this.addKeyHandler("3", ctrl, exec("formatblock", "h3"));
|
|
|
158 |
this.addKeyHandler("4", ctrl, exec("formatblock", "h4"));
|
|
|
159 |
|
|
|
160 |
this.addKeyHandler("\\", ctrl, exec("insertunorderedlist"));
|
|
|
161 |
if(!dojo.isIE){
|
|
|
162 |
this.addKeyHandler("Z", ctrl, exec("redo"));
|
|
|
163 |
}
|
|
|
164 |
},
|
|
|
165 |
|
|
|
166 |
// events: Array
|
|
|
167 |
// events which should be connected to the underlying editing area
|
|
|
168 |
events: ["onKeyPress", "onKeyDown", "onKeyUp", "onClick"],
|
|
|
169 |
|
|
|
170 |
// events: Array
|
|
|
171 |
// events which should be connected to the underlying editing
|
|
|
172 |
// area, events in this array will be addListener with
|
|
|
173 |
// capture=true
|
|
|
174 |
captureEvents: [],
|
|
|
175 |
|
|
|
176 |
_editorCommandsLocalized: false,
|
|
|
177 |
_localizeEditorCommands: function(){
|
|
|
178 |
if(this._editorCommandsLocalized){
|
|
|
179 |
return;
|
|
|
180 |
}
|
|
|
181 |
this._editorCommandsLocalized = true;
|
|
|
182 |
|
|
|
183 |
//in IE, names for blockformat is locale dependent, so we cache the values here
|
|
|
184 |
|
|
|
185 |
//if the normal way fails, we try the hard way to get the list
|
|
|
186 |
|
|
|
187 |
//do not use _cacheLocalBlockFormatNames here, as it will
|
|
|
188 |
//trigger security warning in IE7
|
|
|
189 |
|
|
|
190 |
//in the array below, ul can not come directly after ol,
|
|
|
191 |
//otherwise the queryCommandValue returns Normal for it
|
|
|
192 |
var formats = ['p', 'pre', 'address', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'div', 'ul'];
|
|
|
193 |
var localhtml = "", format, i=0;
|
|
|
194 |
while((format=formats[i++])){
|
|
|
195 |
if(format.charAt(1) != 'l'){
|
|
|
196 |
localhtml += "<"+format+"><span>content</span></"+format+">";
|
|
|
197 |
}else{
|
|
|
198 |
localhtml += "<"+format+"><li>content</li></"+format+">";
|
|
|
199 |
}
|
|
|
200 |
}
|
|
|
201 |
//queryCommandValue returns empty if we hide editNode, so move it out of screen temporary
|
|
|
202 |
var div=document.createElement('div');
|
|
|
203 |
div.style.position = "absolute";
|
|
|
204 |
div.style.left = "-2000px";
|
|
|
205 |
div.style.top = "-2000px";
|
|
|
206 |
document.body.appendChild(div);
|
|
|
207 |
div.innerHTML = localhtml;
|
|
|
208 |
var node = div.firstChild;
|
|
|
209 |
while(node){
|
|
|
210 |
dijit._editor.selection.selectElement(node.firstChild);
|
|
|
211 |
dojo.withGlobal(this.window, "selectElement", dijit._editor.selection, [node.firstChild]);
|
|
|
212 |
var nativename = node.tagName.toLowerCase();
|
|
|
213 |
this._local2NativeFormatNames[nativename] = document.queryCommandValue("formatblock");//this.queryCommandValue("formatblock");
|
|
|
214 |
this._native2LocalFormatNames[this._local2NativeFormatNames[nativename]] = nativename;
|
|
|
215 |
node = node.nextSibling;
|
|
|
216 |
}
|
|
|
217 |
document.body.removeChild(div);
|
|
|
218 |
},
|
|
|
219 |
|
|
|
220 |
open: function(/*DomNode?*/element){
|
|
|
221 |
// summary:
|
|
|
222 |
// Transforms the node referenced in this.domNode into a rich text editing
|
|
|
223 |
// node. This will result in the creation and replacement with an <iframe>
|
|
|
224 |
// if designMode(FF)/contentEditable(IE) is used.
|
|
|
225 |
|
|
|
226 |
if((!this.onLoadDeferred)||(this.onLoadDeferred.fired >= 0)){
|
|
|
227 |
this.onLoadDeferred = new dojo.Deferred();
|
|
|
228 |
}
|
|
|
229 |
|
|
|
230 |
if(!this.isClosed){ this.close(); }
|
|
|
231 |
dojo.publish("dijit._editor.RichText::open", [ this ]);
|
|
|
232 |
|
|
|
233 |
this._content = "";
|
|
|
234 |
if((arguments.length == 1)&&(element["nodeName"])){ this.domNode = element; } // else unchanged
|
|
|
235 |
|
|
|
236 |
if( (this.domNode["nodeName"])&&
|
|
|
237 |
(this.domNode.nodeName.toLowerCase() == "textarea")){
|
|
|
238 |
// if we were created from a textarea, then we need to create a
|
|
|
239 |
// new editing harness node.
|
|
|
240 |
this.textarea = this.domNode;
|
|
|
241 |
this.name=this.textarea.name;
|
|
|
242 |
var html = this._preFilterContent(this.textarea.value);
|
|
|
243 |
this.domNode = dojo.doc.createElement("div");
|
|
|
244 |
this.domNode.setAttribute('widgetId',this.id);
|
|
|
245 |
this.textarea.removeAttribute('widgetId');
|
|
|
246 |
this.domNode.cssText = this.textarea.cssText;
|
|
|
247 |
this.domNode.className += " "+this.textarea.className;
|
|
|
248 |
dojo.place(this.domNode, this.textarea, "before");
|
|
|
249 |
var tmpFunc = dojo.hitch(this, function(){
|
|
|
250 |
//some browsers refuse to submit display=none textarea, so
|
|
|
251 |
//move the textarea out of screen instead
|
|
|
252 |
with(this.textarea.style){
|
|
|
253 |
display = "block";
|
|
|
254 |
position = "absolute";
|
|
|
255 |
left = top = "-1000px";
|
|
|
256 |
|
|
|
257 |
if(dojo.isIE){ //nasty IE bug: abnormal formatting if overflow is not hidden
|
|
|
258 |
this.__overflow = overflow;
|
|
|
259 |
overflow = "hidden";
|
|
|
260 |
}
|
|
|
261 |
}
|
|
|
262 |
});
|
|
|
263 |
if(dojo.isIE){
|
|
|
264 |
setTimeout(tmpFunc, 10);
|
|
|
265 |
}else{
|
|
|
266 |
tmpFunc();
|
|
|
267 |
}
|
|
|
268 |
|
|
|
269 |
// this.domNode.innerHTML = html;
|
|
|
270 |
|
|
|
271 |
// if(this.textarea.form){
|
|
|
272 |
// // FIXME: port: this used to be before advice!!!
|
|
|
273 |
// dojo.connect(this.textarea.form, "onsubmit", this, function(){
|
|
|
274 |
// // FIXME: should we be calling close() here instead?
|
|
|
275 |
// this.textarea.value = this.getValue();
|
|
|
276 |
// });
|
|
|
277 |
// }
|
|
|
278 |
}else{
|
|
|
279 |
var html = this._preFilterContent(this.getNodeChildrenHtml(this.domNode));
|
|
|
280 |
this.domNode.innerHTML = '';
|
|
|
281 |
}
|
|
|
282 |
if(html == ""){ html = " "; }
|
|
|
283 |
|
|
|
284 |
var content = dojo.contentBox(this.domNode);
|
|
|
285 |
// var content = dojo.contentBox(this.srcNodeRef);
|
|
|
286 |
this._oldHeight = content.h;
|
|
|
287 |
this._oldWidth = content.w;
|
|
|
288 |
|
|
|
289 |
this.savedContent = html;
|
|
|
290 |
|
|
|
291 |
// If we're a list item we have to put in a blank line to force the
|
|
|
292 |
// bullet to nicely align at the top of text
|
|
|
293 |
if( (this.domNode["nodeName"]) &&
|
|
|
294 |
(this.domNode.nodeName == "LI") ){
|
|
|
295 |
this.domNode.innerHTML = " <br>";
|
|
|
296 |
}
|
|
|
297 |
|
|
|
298 |
this.editingArea = dojo.doc.createElement("div");
|
|
|
299 |
this.domNode.appendChild(this.editingArea);
|
|
|
300 |
|
|
|
301 |
if(this.name != "" && (!djConfig["useXDomain"] || djConfig["allowXdRichTextSave"])){
|
|
|
302 |
var saveTextarea = dojo.byId("dijit._editor.RichText.savedContent");
|
|
|
303 |
if(saveTextarea.value != ""){
|
|
|
304 |
var datas = saveTextarea.value.split(this._SEPARATOR), i=0, dat;
|
|
|
305 |
while((dat=datas[i++])){
|
|
|
306 |
var data = dat.split(":");
|
|
|
307 |
if(data[0] == this.name){
|
|
|
308 |
html = data[1];
|
|
|
309 |
datas.splice(i, 1);
|
|
|
310 |
break;
|
|
|
311 |
}
|
|
|
312 |
}
|
|
|
313 |
}
|
|
|
314 |
|
|
|
315 |
// FIXME: need to do something different for Opera/Safari
|
|
|
316 |
dojo.connect(window, "onbeforeunload", this, "_saveContent");
|
|
|
317 |
// dojo.connect(window, "onunload", this, "_saveContent");
|
|
|
318 |
}
|
|
|
319 |
|
|
|
320 |
this.isClosed = false;
|
|
|
321 |
// Safari's selections go all out of whack if we do it inline,
|
|
|
322 |
// so for now IE is our only hero
|
|
|
323 |
//if (typeof document.body.contentEditable != "undefined") {
|
|
|
324 |
if(dojo.isIE || dojo.isSafari || dojo.isOpera){ // contentEditable, easy
|
|
|
325 |
var ifr = this.iframe = dojo.doc.createElement('iframe');
|
|
|
326 |
ifr.src = 'javascript:void(0)';
|
|
|
327 |
this.editorObject = ifr;
|
|
|
328 |
ifr.style.border = "none";
|
|
|
329 |
ifr.style.width = "100%";
|
|
|
330 |
ifr.frameBorder = 0;
|
|
|
331 |
// ifr.style.scrolling = this.height ? "auto" : "vertical";
|
|
|
332 |
this.editingArea.appendChild(ifr);
|
|
|
333 |
this.window = ifr.contentWindow;
|
|
|
334 |
this.document = this.window.document;
|
|
|
335 |
this.document.open();
|
|
|
336 |
this.document.write(this._getIframeDocTxt(html));
|
|
|
337 |
this.document.close();
|
|
|
338 |
|
|
|
339 |
if(dojo.isIE >= 7){
|
|
|
340 |
if(this.height){
|
|
|
341 |
ifr.style.height = this.height;
|
|
|
342 |
}
|
|
|
343 |
if(this.minHeight){
|
|
|
344 |
ifr.style.minHeight = this.minHeight;
|
|
|
345 |
}
|
|
|
346 |
}else{
|
|
|
347 |
ifr.style.height = this.height ? this.height : this.minHeight;
|
|
|
348 |
}
|
|
|
349 |
|
|
|
350 |
if(dojo.isIE){
|
|
|
351 |
this._localizeEditorCommands();
|
|
|
352 |
}
|
|
|
353 |
|
|
|
354 |
this.onLoad();
|
|
|
355 |
}else{ // designMode in iframe
|
|
|
356 |
this._drawIframe(html);
|
|
|
357 |
}
|
|
|
358 |
|
|
|
359 |
// TODO: this is a guess at the default line-height, kinda works
|
|
|
360 |
if(this.domNode.nodeName == "LI"){ this.domNode.lastChild.style.marginTop = "-1.2em"; }
|
|
|
361 |
this.domNode.className += " RichTextEditable";
|
|
|
362 |
},
|
|
|
363 |
|
|
|
364 |
//static cache variables shared among all instance of this class
|
|
|
365 |
_local2NativeFormatNames: {},
|
|
|
366 |
_native2LocalFormatNames: {},
|
|
|
367 |
_localizedIframeTitles: null,
|
|
|
368 |
|
|
|
369 |
_getIframeDocTxt: function(/* String */ html){
|
|
|
370 |
var _cs = dojo.getComputedStyle(this.domNode);
|
|
|
371 |
if(!this.height && !dojo.isMoz){
|
|
|
372 |
html="<div>"+html+"</div>";
|
|
|
373 |
}
|
|
|
374 |
var font = [ _cs.fontWeight, _cs.fontSize, _cs.fontFamily ].join(" ");
|
|
|
375 |
|
|
|
376 |
// line height is tricky - applying a units value will mess things up.
|
|
|
377 |
// if we can't get a non-units value, bail out.
|
|
|
378 |
var lineHeight = _cs.lineHeight;
|
|
|
379 |
if(lineHeight.indexOf("px") >= 0){
|
|
|
380 |
lineHeight = parseFloat(lineHeight)/parseFloat(_cs.fontSize);
|
|
|
381 |
// console.debug(lineHeight);
|
|
|
382 |
}else if(lineHeight.indexOf("em")>=0){
|
|
|
383 |
lineHeight = parseFloat(lineHeight);
|
|
|
384 |
}else{
|
|
|
385 |
lineHeight = "1.0";
|
|
|
386 |
}
|
|
|
387 |
return [
|
|
|
388 |
this.isLeftToRight() ? "<html><head>" : "<html dir='rtl'><head>",
|
|
|
389 |
(dojo.isMoz ? "<title>" + this._localizedIframeTitles.iframeEditTitle + "</title>" : ""),
|
|
|
390 |
"<style>",
|
|
|
391 |
"body,html {",
|
|
|
392 |
" background:transparent;",
|
|
|
393 |
" padding: 0;",
|
|
|
394 |
" margin: 0;",
|
|
|
395 |
"}",
|
|
|
396 |
// TODO: left positioning will cause contents to disappear out of view
|
|
|
397 |
// if it gets too wide for the visible area
|
|
|
398 |
"body{",
|
|
|
399 |
" top:0px; left:0px; right:0px;",
|
|
|
400 |
((this.height||dojo.isOpera) ? "" : "position: fixed;"),
|
|
|
401 |
" font:", font, ";",
|
|
|
402 |
// FIXME: IE 6 won't understand min-height?
|
|
|
403 |
" min-height:", this.minHeight, ";",
|
|
|
404 |
" line-height:", lineHeight,
|
|
|
405 |
"}",
|
|
|
406 |
"p{ margin: 1em 0 !important; }",
|
|
|
407 |
(this.height ?
|
|
|
408 |
"" : "body,html{overflow-y:hidden;/*for IE*/} body > div {overflow-x:auto;/*for FF to show vertical scrollbar*/}"
|
|
|
409 |
),
|
|
|
410 |
"li > ul:-moz-first-node, li > ol:-moz-first-node{ padding-top: 1.2em; } ",
|
|
|
411 |
"li{ min-height:1.2em; }",
|
|
|
412 |
"</style>",
|
|
|
413 |
this._applyEditingAreaStyleSheets(),
|
|
|
414 |
"</head><body>"+html+"</body></html>"
|
|
|
415 |
].join(""); // String
|
|
|
416 |
},
|
|
|
417 |
|
|
|
418 |
_drawIframe: function(/*String*/html){
|
|
|
419 |
// summary:
|
|
|
420 |
// Draws an iFrame using the existing one if one exists.
|
|
|
421 |
// Used by Mozilla, Safari, and Opera
|
|
|
422 |
|
|
|
423 |
if(!this.iframe){
|
|
|
424 |
var ifr = this.iframe = dojo.doc.createElement("iframe");
|
|
|
425 |
// this.iframe.src = "about:blank";
|
|
|
426 |
// document.body.appendChild(this.iframe);
|
|
|
427 |
// console.debug(this.iframe.contentDocument.open());
|
|
|
428 |
// dojo.body().appendChild(this.iframe);
|
|
|
429 |
var ifrs = ifr.style;
|
|
|
430 |
// ifrs.border = "1px solid black";
|
|
|
431 |
ifrs.border = "none";
|
|
|
432 |
ifrs.lineHeight = "0"; // squash line height
|
|
|
433 |
ifrs.verticalAlign = "bottom";
|
|
|
434 |
// ifrs.scrolling = this.height ? "auto" : "vertical";
|
|
|
435 |
this.editorObject = this.iframe;
|
|
|
436 |
// get screen reader text for mozilla here, too
|
|
|
437 |
this._localizedIframeTitles = dojo.i18n.getLocalization("dijit", "Textarea");
|
|
|
438 |
// need to find any associated label element and update iframe document title
|
|
|
439 |
var label=dojo.query('label[for="'+this.id+'"]');
|
|
|
440 |
if(label.length){
|
|
|
441 |
this._localizedIframeTitles.iframeEditTitle = label[0].innerHTML + " " + this._localizedIframeTitles.iframeEditTitle;
|
|
|
442 |
}
|
|
|
443 |
}
|
|
|
444 |
// opera likes this to be outside the with block
|
|
|
445 |
// this.iframe.src = "javascript:void(0)";//dojo.uri.dojoUri("src/widget/templates/richtextframe.html") + ((dojo.doc.domain != currentDomain) ? ("#"+dojo.doc.domain) : "");
|
|
|
446 |
this.iframe.style.width = this.inheritWidth ? this._oldWidth : "100%";
|
|
|
447 |
|
|
|
448 |
if(this.height){
|
|
|
449 |
this.iframe.style.height = this.height;
|
|
|
450 |
}else{
|
|
|
451 |
this.iframe.height = this._oldHeight;
|
|
|
452 |
}
|
|
|
453 |
|
|
|
454 |
if(this.textarea){
|
|
|
455 |
var tmpContent = this.srcNodeRef;
|
|
|
456 |
}else{
|
|
|
457 |
var tmpContent = dojo.doc.createElement('div');
|
|
|
458 |
tmpContent.style.display="none";
|
|
|
459 |
tmpContent.innerHTML = html;
|
|
|
460 |
//append tmpContent to under the current domNode so that the margin
|
|
|
461 |
//calculation below is correct
|
|
|
462 |
this.editingArea.appendChild(tmpContent);
|
|
|
463 |
}
|
|
|
464 |
|
|
|
465 |
this.editingArea.appendChild(this.iframe);
|
|
|
466 |
|
|
|
467 |
//do we want to show the content before the editing area finish loading here?
|
|
|
468 |
//if external style sheets are used for the editing area, the appearance now
|
|
|
469 |
//and after loading of the editing area won't be the same (and padding/margin
|
|
|
470 |
//calculation above may not be accurate)
|
|
|
471 |
// tmpContent.style.display = "none";
|
|
|
472 |
// this.editingArea.appendChild(this.iframe);
|
|
|
473 |
|
|
|
474 |
var _iframeInitialized = false;
|
|
|
475 |
// console.debug(this.iframe);
|
|
|
476 |
// var contentDoc = this.iframe.contentWindow.document;
|
|
|
477 |
|
|
|
478 |
|
|
|
479 |
// note that on Safari lower than 420+, we have to get the iframe
|
|
|
480 |
// by ID in order to get something w/ a contentDocument property
|
|
|
481 |
|
|
|
482 |
var contentDoc = this.iframe.contentDocument;
|
|
|
483 |
contentDoc.open();
|
|
|
484 |
contentDoc.write(this._getIframeDocTxt(html));
|
|
|
485 |
contentDoc.close();
|
|
|
486 |
|
|
|
487 |
// now we wait for onload. Janky hack!
|
|
|
488 |
var ifrFunc = dojo.hitch(this, function(){
|
|
|
489 |
if(!_iframeInitialized){
|
|
|
490 |
_iframeInitialized = true;
|
|
|
491 |
}else{ return; }
|
|
|
492 |
if(!this.editNode){
|
|
|
493 |
try{
|
|
|
494 |
if(this.iframe.contentWindow){
|
|
|
495 |
this.window = this.iframe.contentWindow;
|
|
|
496 |
this.document = this.iframe.contentWindow.document
|
|
|
497 |
}else if(this.iframe.contentDocument){
|
|
|
498 |
// for opera
|
|
|
499 |
this.window = this.iframe.contentDocument.window;
|
|
|
500 |
this.document = this.iframe.contentDocument;
|
|
|
501 |
}
|
|
|
502 |
if(!this.document.body){
|
|
|
503 |
throw 'Error';
|
|
|
504 |
}
|
|
|
505 |
}catch(e){
|
|
|
506 |
setTimeout(ifrFunc,500);
|
|
|
507 |
_iframeInitialized = false;
|
|
|
508 |
return;
|
|
|
509 |
}
|
|
|
510 |
|
|
|
511 |
dojo._destroyElement(tmpContent);
|
|
|
512 |
this.document.designMode = "on";
|
|
|
513 |
// try{
|
|
|
514 |
// this.document.designMode = "on";
|
|
|
515 |
// }catch(e){
|
|
|
516 |
// this._tryDesignModeOnClick=true;
|
|
|
517 |
// }
|
|
|
518 |
|
|
|
519 |
this.onLoad();
|
|
|
520 |
}else{
|
|
|
521 |
dojo._destroyElement(tmpContent);
|
|
|
522 |
this.editNode.innerHTML = html;
|
|
|
523 |
this.onDisplayChanged();
|
|
|
524 |
}
|
|
|
525 |
this._preDomFilterContent(this.editNode);
|
|
|
526 |
});
|
|
|
527 |
|
|
|
528 |
ifrFunc();
|
|
|
529 |
},
|
|
|
530 |
|
|
|
531 |
_applyEditingAreaStyleSheets: function(){
|
|
|
532 |
// summary:
|
|
|
533 |
// apply the specified css files in styleSheets
|
|
|
534 |
var files = [];
|
|
|
535 |
if(this.styleSheets){
|
|
|
536 |
files = this.styleSheets.split(';');
|
|
|
537 |
this.styleSheets = '';
|
|
|
538 |
}
|
|
|
539 |
|
|
|
540 |
//empty this.editingAreaStyleSheets here, as it will be filled in addStyleSheet
|
|
|
541 |
files = files.concat(this.editingAreaStyleSheets);
|
|
|
542 |
this.editingAreaStyleSheets = [];
|
|
|
543 |
|
|
|
544 |
var text='', i=0, url;
|
|
|
545 |
while((url=files[i++])){
|
|
|
546 |
var abstring = (new dojo._Url(dojo.global.location, url)).toString();
|
|
|
547 |
this.editingAreaStyleSheets.push(abstring);
|
|
|
548 |
text += '<link rel="stylesheet" type="text/css" href="'+abstring+'"/>'
|
|
|
549 |
}
|
|
|
550 |
return text;
|
|
|
551 |
},
|
|
|
552 |
|
|
|
553 |
addStyleSheet: function(/*dojo._Url*/uri){
|
|
|
554 |
// summary:
|
|
|
555 |
// add an external stylesheet for the editing area
|
|
|
556 |
// uri: a dojo.uri.Uri pointing to the url of the external css file
|
|
|
557 |
var url=uri.toString();
|
|
|
558 |
|
|
|
559 |
//if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
|
|
|
560 |
if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){
|
|
|
561 |
url = (new dojo._Url(dojo.global.location, url)).toString();
|
|
|
562 |
}
|
|
|
563 |
|
|
|
564 |
if(dojo.indexOf(this.editingAreaStyleSheets, url) > -1){
|
|
|
565 |
console.debug("dijit._editor.RichText.addStyleSheet: Style sheet "+url+" is already applied to the editing area!");
|
|
|
566 |
return;
|
|
|
567 |
}
|
|
|
568 |
|
|
|
569 |
this.editingAreaStyleSheets.push(url);
|
|
|
570 |
if(this.document.createStyleSheet){ //IE
|
|
|
571 |
this.document.createStyleSheet(url);
|
|
|
572 |
}else{ //other browser
|
|
|
573 |
var head = this.document.getElementsByTagName("head")[0];
|
|
|
574 |
var stylesheet = this.document.createElement("link");
|
|
|
575 |
with(stylesheet){
|
|
|
576 |
rel="stylesheet";
|
|
|
577 |
type="text/css";
|
|
|
578 |
href=url;
|
|
|
579 |
}
|
|
|
580 |
head.appendChild(stylesheet);
|
|
|
581 |
}
|
|
|
582 |
},
|
|
|
583 |
|
|
|
584 |
removeStyleSheet: function(/*dojo._Url*/uri){
|
|
|
585 |
// summary:
|
|
|
586 |
// remove an external stylesheet for the editing area
|
|
|
587 |
var url=uri.toString();
|
|
|
588 |
//if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
|
|
|
589 |
if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){
|
|
|
590 |
url = (new dojo._Url(dojo.global.location, url)).toString();
|
|
|
591 |
}
|
|
|
592 |
var index = dojo.indexOf(this.editingAreaStyleSheets, url);
|
|
|
593 |
if(index == -1){
|
|
|
594 |
console.debug("dijit._editor.RichText.removeStyleSheet: Style sheet "+url+" is not applied to the editing area so it can not be removed!");
|
|
|
595 |
return;
|
|
|
596 |
}
|
|
|
597 |
delete this.editingAreaStyleSheets[index];
|
|
|
598 |
dojo.withGlobal(this.window,'query', dojo, ['link:[href="'+url+'"]']).orphan()
|
|
|
599 |
},
|
|
|
600 |
|
|
|
601 |
disabled: false,
|
|
|
602 |
_mozSettingProps: ['styleWithCSS','insertBrOnReturn'],
|
|
|
603 |
setDisabled: function(/*Boolean*/ disabled){
|
|
|
604 |
if(dojo.isIE || dojo.isSafari || dojo.isOpera){
|
|
|
605 |
this.editNode.contentEditable=!disabled;
|
|
|
606 |
}else{ //moz
|
|
|
607 |
if(disabled){
|
|
|
608 |
this._mozSettings=[false,this.blockNodeForEnter==='BR'];
|
|
|
609 |
}
|
|
|
610 |
this.document.designMode=(disabled?'off':'on');
|
|
|
611 |
if(!disabled){
|
|
|
612 |
dojo.forEach(this._mozSettingProps, function(s,i){
|
|
|
613 |
this.document.execCommand(s,false,this._mozSettings[i]);
|
|
|
614 |
},this);
|
|
|
615 |
}
|
|
|
616 |
// this.document.execCommand('contentReadOnly', false, disabled);
|
|
|
617 |
// if(disabled){
|
|
|
618 |
// this.blur(); //to remove the blinking caret
|
|
|
619 |
// }
|
|
|
620 |
//
|
|
|
621 |
}
|
|
|
622 |
this.disabled=disabled;
|
|
|
623 |
},
|
|
|
624 |
|
|
|
625 |
/* Event handlers
|
|
|
626 |
*****************/
|
|
|
627 |
|
|
|
628 |
_isResized: function(){ return false; },
|
|
|
629 |
|
|
|
630 |
onLoad: function(/* Event */ e){
|
|
|
631 |
// summary: handler after the content of the document finishes loading
|
|
|
632 |
this.isLoaded = true;
|
|
|
633 |
if(this.height || dojo.isMoz){
|
|
|
634 |
this.editNode=this.document.body;
|
|
|
635 |
}else{
|
|
|
636 |
this.editNode=this.document.body.firstChild;
|
|
|
637 |
}
|
|
|
638 |
this.editNode.contentEditable = true; //should do no harm in FF
|
|
|
639 |
this._preDomFilterContent(this.editNode);
|
|
|
640 |
|
|
|
641 |
var events=this.events.concat(this.captureEvents),i=0,et;
|
|
|
642 |
while((et=events[i++])){
|
|
|
643 |
this.connect(this.document, et.toLowerCase(), et);
|
|
|
644 |
}
|
|
|
645 |
if(!dojo.isIE){
|
|
|
646 |
try{ // sanity check for Mozilla
|
|
|
647 |
// this.document.execCommand("useCSS", false, true); // old moz call
|
|
|
648 |
this.document.execCommand("styleWithCSS", false, false); // new moz call
|
|
|
649 |
//this.document.execCommand("insertBrOnReturn", false, false); // new moz call
|
|
|
650 |
}catch(e2){ }
|
|
|
651 |
// FIXME: when scrollbars appear/disappear this needs to be fired
|
|
|
652 |
}else{ // IE contentEditable
|
|
|
653 |
// give the node Layout on IE
|
|
|
654 |
this.editNode.style.zoom = 1.0;
|
|
|
655 |
}
|
|
|
656 |
|
|
|
657 |
if(this.focusOnLoad){
|
|
|
658 |
this.focus();
|
|
|
659 |
}
|
|
|
660 |
|
|
|
661 |
this.onDisplayChanged(e);
|
|
|
662 |
if(this.onLoadDeferred){
|
|
|
663 |
this.onLoadDeferred.callback(true);
|
|
|
664 |
}
|
|
|
665 |
},
|
|
|
666 |
|
|
|
667 |
onKeyDown: function(/* Event */ e){
|
|
|
668 |
// summary: Fired on keydown
|
|
|
669 |
|
|
|
670 |
// console.info("onkeydown:", e.keyCode);
|
|
|
671 |
|
|
|
672 |
// we need this event at the moment to get the events from control keys
|
|
|
673 |
// such as the backspace. It might be possible to add this to Dojo, so that
|
|
|
674 |
// keyPress events can be emulated by the keyDown and keyUp detection.
|
|
|
675 |
if(dojo.isIE){
|
|
|
676 |
if(e.keyCode === dojo.keys.BACKSPACE && this.document.selection.type === "Control"){
|
|
|
677 |
// IE has a bug where if a non-text object is selected in the editor,
|
|
|
678 |
// hitting backspace would act as if the browser's back button was
|
|
|
679 |
// clicked instead of deleting the object. see #1069
|
|
|
680 |
dojo.stopEvent(e);
|
|
|
681 |
this.execCommand("delete");
|
|
|
682 |
}else if( (65 <= e.keyCode&&e.keyCode <= 90) ||
|
|
|
683 |
(e.keyCode>=37&&e.keyCode<=40) // FIXME: get this from connect() instead!
|
|
|
684 |
){ //arrow keys
|
|
|
685 |
e.charCode = e.keyCode;
|
|
|
686 |
this.onKeyPress(e);
|
|
|
687 |
}
|
|
|
688 |
}
|
|
|
689 |
else if (dojo.isMoz){
|
|
|
690 |
if(e.keyCode == dojo.keys.TAB && !e.shiftKey && !e.ctrlKey && !e.altKey && this.iframe){
|
|
|
691 |
// update iframe document title for screen reader
|
|
|
692 |
this.iframe.contentDocument.title = this._localizedIframeTitles.iframeFocusTitle;
|
|
|
693 |
|
|
|
694 |
// Place focus on the iframe. A subsequent tab or shift tab will put focus
|
|
|
695 |
// on the correct control.
|
|
|
696 |
this.iframe.focus(); // this.focus(); won't work
|
|
|
697 |
dojo.stopEvent(e);
|
|
|
698 |
}else if (e.keyCode == dojo.keys.TAB && e.shiftKey){
|
|
|
699 |
// if there is a toolbar, set focus to it, otherwise ignore
|
|
|
700 |
if (this.toolbar){
|
|
|
701 |
this.toolbar.focus();
|
|
|
702 |
}
|
|
|
703 |
dojo.stopEvent(e);
|
|
|
704 |
}
|
|
|
705 |
}
|
|
|
706 |
},
|
|
|
707 |
|
|
|
708 |
onKeyUp: function(e){
|
|
|
709 |
// summary: Fired on keyup
|
|
|
710 |
return;
|
|
|
711 |
},
|
|
|
712 |
|
|
|
713 |
KEY_CTRL: 1,
|
|
|
714 |
KEY_SHIFT: 2,
|
|
|
715 |
|
|
|
716 |
onKeyPress: function(e){
|
|
|
717 |
// summary: Fired on keypress
|
|
|
718 |
|
|
|
719 |
// console.info("onkeypress:", e.keyCode);
|
|
|
720 |
|
|
|
721 |
// handle the various key events
|
|
|
722 |
var modifiers = e.ctrlKey ? this.KEY_CTRL : 0 | e.shiftKey?this.KEY_SHIFT : 0;
|
|
|
723 |
|
|
|
724 |
var key = e.keyChar||e.keyCode;
|
|
|
725 |
if(this._keyHandlers[key]){
|
|
|
726 |
// console.debug("char:", e.key);
|
|
|
727 |
var handlers = this._keyHandlers[key], i = 0, h;
|
|
|
728 |
while((h = handlers[i++])){
|
|
|
729 |
if(modifiers == h.modifiers){
|
|
|
730 |
if(!h.handler.apply(this,arguments)){
|
|
|
731 |
e.preventDefault();
|
|
|
732 |
}
|
|
|
733 |
break;
|
|
|
734 |
}
|
|
|
735 |
}
|
|
|
736 |
}
|
|
|
737 |
|
|
|
738 |
// function call after the character has been inserted
|
|
|
739 |
setTimeout(dojo.hitch(this, function(){
|
|
|
740 |
this.onKeyPressed(e);
|
|
|
741 |
}), 1);
|
|
|
742 |
},
|
|
|
743 |
|
|
|
744 |
addKeyHandler: function(/*String*/key, /*Int*/modifiers, /*Function*/handler){
|
|
|
745 |
// summary: add a handler for a keyboard shortcut
|
|
|
746 |
if(!dojo.isArray(this._keyHandlers[key])){ this._keyHandlers[key] = []; }
|
|
|
747 |
this._keyHandlers[key].push({
|
|
|
748 |
modifiers: modifiers || 0,
|
|
|
749 |
handler: handler
|
|
|
750 |
});
|
|
|
751 |
},
|
|
|
752 |
|
|
|
753 |
onKeyPressed: function(/*Event*/e){
|
|
|
754 |
this.onDisplayChanged(/*e*/); // can't pass in e
|
|
|
755 |
},
|
|
|
756 |
|
|
|
757 |
onClick: function(/*Event*/e){
|
|
|
758 |
// console.debug('onClick',this._tryDesignModeOnClick);
|
|
|
759 |
// if(this._tryDesignModeOnClick){
|
|
|
760 |
// try{
|
|
|
761 |
// this.document.designMode='on';
|
|
|
762 |
// this._tryDesignModeOnClick=false;
|
|
|
763 |
// }catch(e){}
|
|
|
764 |
// }
|
|
|
765 |
this.onDisplayChanged(e); },
|
|
|
766 |
_onBlur: function(e){
|
|
|
767 |
var _c=this.getValue(true);
|
|
|
768 |
if(_c!=this.savedContent){
|
|
|
769 |
this.onChange(_c);
|
|
|
770 |
this.savedContent=_c;
|
|
|
771 |
}
|
|
|
772 |
if (dojo.isMoz && this.iframe){
|
|
|
773 |
this.iframe.contentDocument.title = this._localizedIframeTitles.iframeEditTitle;
|
|
|
774 |
}
|
|
|
775 |
// console.info('_onBlur')
|
|
|
776 |
},
|
|
|
777 |
_initialFocus: true,
|
|
|
778 |
_onFocus: function(/*Event*/e){
|
|
|
779 |
// console.info('_onFocus')
|
|
|
780 |
// summary: Fired on focus
|
|
|
781 |
if( (dojo.isMoz)&&(this._initialFocus) ){
|
|
|
782 |
this._initialFocus = false;
|
|
|
783 |
if(this.editNode.innerHTML.replace(/^\s+|\s+$/g, "") == " "){
|
|
|
784 |
this.placeCursorAtStart();
|
|
|
785 |
// this.execCommand("selectall");
|
|
|
786 |
// this.window.getSelection().collapseToStart();
|
|
|
787 |
}
|
|
|
788 |
}
|
|
|
789 |
},
|
|
|
790 |
|
|
|
791 |
blur: function(){
|
|
|
792 |
// summary: remove focus from this instance
|
|
|
793 |
if(this.iframe){
|
|
|
794 |
this.window.blur();
|
|
|
795 |
}else if(this.editNode){
|
|
|
796 |
this.editNode.blur();
|
|
|
797 |
}
|
|
|
798 |
},
|
|
|
799 |
|
|
|
800 |
focus: function(){
|
|
|
801 |
// summary: move focus to this instance
|
|
|
802 |
if(this.iframe && !dojo.isIE){
|
|
|
803 |
dijit.focus(this.iframe);
|
|
|
804 |
}else if(this.editNode && this.editNode.focus){
|
|
|
805 |
// editNode may be hidden in display:none div, lets just punt in this case
|
|
|
806 |
dijit.focus(this.editNode);
|
|
|
807 |
}else{
|
|
|
808 |
console.debug("Have no idea how to focus into the editor!");
|
|
|
809 |
}
|
|
|
810 |
},
|
|
|
811 |
|
|
|
812 |
// _lastUpdate: 0,
|
|
|
813 |
updateInterval: 200,
|
|
|
814 |
_updateTimer: null,
|
|
|
815 |
onDisplayChanged: function(/*Event*/e){
|
|
|
816 |
// summary:
|
|
|
817 |
// This event will be fired everytime the display context
|
|
|
818 |
// changes and the result needs to be reflected in the UI.
|
|
|
819 |
// description:
|
|
|
820 |
// If you don't want to have update too often,
|
|
|
821 |
// onNormalizedDisplayChanged should be used instead
|
|
|
822 |
|
|
|
823 |
// var _t=new Date();
|
|
|
824 |
if(!this._updateTimer){
|
|
|
825 |
// this._lastUpdate=_t;
|
|
|
826 |
if(this._updateTimer){
|
|
|
827 |
clearTimeout(this._updateTimer);
|
|
|
828 |
}
|
|
|
829 |
this._updateTimer=setTimeout(dojo.hitch(this,this.onNormalizedDisplayChanged),this.updateInterval);
|
|
|
830 |
}
|
|
|
831 |
},
|
|
|
832 |
onNormalizedDisplayChanged: function(){
|
|
|
833 |
// summary:
|
|
|
834 |
// This event is fired every updateInterval ms or more
|
|
|
835 |
// description:
|
|
|
836 |
// If something needs to happen immidiately after a
|
|
|
837 |
// user change, please use onDisplayChanged instead
|
|
|
838 |
this._updateTimer=null;
|
|
|
839 |
},
|
|
|
840 |
onChange: function(newContent){
|
|
|
841 |
// summary:
|
|
|
842 |
// this is fired if and only if the editor loses focus and
|
|
|
843 |
// the content is changed
|
|
|
844 |
|
|
|
845 |
// console.log('onChange',newContent);
|
|
|
846 |
},
|
|
|
847 |
_normalizeCommand: function(/*String*/cmd){
|
|
|
848 |
// summary:
|
|
|
849 |
// Used as the advice function by dojo.connect to map our
|
|
|
850 |
// normalized set of commands to those supported by the target
|
|
|
851 |
// browser
|
|
|
852 |
|
|
|
853 |
var command = cmd.toLowerCase();
|
|
|
854 |
if(command == "formatblock"){
|
|
|
855 |
if(dojo.isSafari){ command = "heading"; }
|
|
|
856 |
}else if(command == "hilitecolor" && !dojo.isMoz){
|
|
|
857 |
command = "backcolor";
|
|
|
858 |
}
|
|
|
859 |
|
|
|
860 |
return command;
|
|
|
861 |
},
|
|
|
862 |
|
|
|
863 |
queryCommandAvailable: function(/*String*/command){
|
|
|
864 |
// summary:
|
|
|
865 |
// Tests whether a command is supported by the host. Clients SHOULD check
|
|
|
866 |
// whether a command is supported before attempting to use it, behaviour
|
|
|
867 |
// for unsupported commands is undefined.
|
|
|
868 |
// command: The command to test for
|
|
|
869 |
var ie = 1;
|
|
|
870 |
var mozilla = 1 << 1;
|
|
|
871 |
var safari = 1 << 2;
|
|
|
872 |
var opera = 1 << 3;
|
|
|
873 |
var safari420 = 1 << 4;
|
|
|
874 |
|
|
|
875 |
var gt420 = dojo.isSafari;
|
|
|
876 |
|
|
|
877 |
function isSupportedBy(browsers){
|
|
|
878 |
return {
|
|
|
879 |
ie: Boolean(browsers & ie),
|
|
|
880 |
mozilla: Boolean(browsers & mozilla),
|
|
|
881 |
safari: Boolean(browsers & safari),
|
|
|
882 |
safari420: Boolean(browsers & safari420),
|
|
|
883 |
opera: Boolean(browsers & opera)
|
|
|
884 |
}
|
|
|
885 |
}
|
|
|
886 |
|
|
|
887 |
var supportedBy = null;
|
|
|
888 |
|
|
|
889 |
switch(command.toLowerCase()){
|
|
|
890 |
case "bold": case "italic": case "underline":
|
|
|
891 |
case "subscript": case "superscript":
|
|
|
892 |
case "fontname": case "fontsize":
|
|
|
893 |
case "forecolor": case "hilitecolor":
|
|
|
894 |
case "justifycenter": case "justifyfull": case "justifyleft":
|
|
|
895 |
case "justifyright": case "delete": case "selectall":
|
|
|
896 |
supportedBy = isSupportedBy(mozilla | ie | safari | opera);
|
|
|
897 |
break;
|
|
|
898 |
|
|
|
899 |
case "createlink": case "unlink": case "removeformat":
|
|
|
900 |
case "inserthorizontalrule": case "insertimage":
|
|
|
901 |
case "insertorderedlist": case "insertunorderedlist":
|
|
|
902 |
case "indent": case "outdent": case "formatblock":
|
|
|
903 |
case "inserthtml": case "undo": case "redo": case "strikethrough":
|
|
|
904 |
supportedBy = isSupportedBy(mozilla | ie | opera | safari420);
|
|
|
905 |
break;
|
|
|
906 |
|
|
|
907 |
case "blockdirltr": case "blockdirrtl":
|
|
|
908 |
case "dirltr": case "dirrtl":
|
|
|
909 |
case "inlinedirltr": case "inlinedirrtl":
|
|
|
910 |
supportedBy = isSupportedBy(ie);
|
|
|
911 |
break;
|
|
|
912 |
case "cut": case "copy": case "paste":
|
|
|
913 |
supportedBy = isSupportedBy( ie | mozilla | safari420);
|
|
|
914 |
break;
|
|
|
915 |
|
|
|
916 |
case "inserttable":
|
|
|
917 |
supportedBy = isSupportedBy(mozilla | ie);
|
|
|
918 |
break;
|
|
|
919 |
|
|
|
920 |
case "insertcell": case "insertcol": case "insertrow":
|
|
|
921 |
case "deletecells": case "deletecols": case "deleterows":
|
|
|
922 |
case "mergecells": case "splitcell":
|
|
|
923 |
supportedBy = isSupportedBy(ie | mozilla);
|
|
|
924 |
break;
|
|
|
925 |
|
|
|
926 |
default: return false;
|
|
|
927 |
}
|
|
|
928 |
|
|
|
929 |
return (dojo.isIE && supportedBy.ie) ||
|
|
|
930 |
(dojo.isMoz && supportedBy.mozilla) ||
|
|
|
931 |
(dojo.isSafari && supportedBy.safari) ||
|
|
|
932 |
(gt420 && supportedBy.safari420) ||
|
|
|
933 |
(dojo.isOpera && supportedBy.opera); // Boolean return true if the command is supported, false otherwise
|
|
|
934 |
},
|
|
|
935 |
|
|
|
936 |
execCommand: function(/*String*/command, argument){
|
|
|
937 |
// summary: Executes a command in the Rich Text area
|
|
|
938 |
// command: The command to execute
|
|
|
939 |
// argument: An optional argument to the command
|
|
|
940 |
var returnValue;
|
|
|
941 |
|
|
|
942 |
//focus() is required for IE to work
|
|
|
943 |
//In addition, focus() makes sure after the execution of
|
|
|
944 |
//the command, the editor receives the focus as expected
|
|
|
945 |
this.focus();
|
|
|
946 |
|
|
|
947 |
command = this._normalizeCommand(command);
|
|
|
948 |
if(argument != undefined){
|
|
|
949 |
if(command == "heading"){
|
|
|
950 |
throw new Error("unimplemented");
|
|
|
951 |
}else if((command == "formatblock") && dojo.isIE){
|
|
|
952 |
argument = '<'+argument+'>';
|
|
|
953 |
}
|
|
|
954 |
}
|
|
|
955 |
if(command == "inserthtml"){
|
|
|
956 |
//TODO: we shall probably call _preDomFilterContent here as well
|
|
|
957 |
argument=this._preFilterContent(argument);
|
|
|
958 |
if(dojo.isIE){
|
|
|
959 |
var insertRange = this.document.selection.createRange();
|
|
|
960 |
insertRange.pasteHTML(argument);
|
|
|
961 |
insertRange.select();
|
|
|
962 |
//insertRange.collapse(true);
|
|
|
963 |
returnValue=true;
|
|
|
964 |
}else if(dojo.isMoz && !argument.length){
|
|
|
965 |
//mozilla can not inserthtml an empty html to delete current selection
|
|
|
966 |
//so we delete the selection instead in this case
|
|
|
967 |
dojo.withGlobal(this.window,'remove',dijit._editor.selection); // FIXME
|
|
|
968 |
returnValue=true;
|
|
|
969 |
}else{
|
|
|
970 |
returnValue=this.document.execCommand(command, false, argument);
|
|
|
971 |
}
|
|
|
972 |
}else if(
|
|
|
973 |
(command == "unlink")&&
|
|
|
974 |
(this.queryCommandEnabled("unlink"))&&
|
|
|
975 |
(dojo.isMoz || dojo.isSafari)
|
|
|
976 |
){
|
|
|
977 |
// fix up unlink in Mozilla to unlink the link and not just the selection
|
|
|
978 |
|
|
|
979 |
// grab selection
|
|
|
980 |
// Mozilla gets upset if we just store the range so we have to
|
|
|
981 |
// get the basic properties and recreate to save the selection
|
|
|
982 |
var selection = this.window.getSelection();
|
|
|
983 |
// var selectionRange = selection.getRangeAt(0);
|
|
|
984 |
// var selectionStartContainer = selectionRange.startContainer;
|
|
|
985 |
// var selectionStartOffset = selectionRange.startOffset;
|
|
|
986 |
// var selectionEndContainer = selectionRange.endContainer;
|
|
|
987 |
// var selectionEndOffset = selectionRange.endOffset;
|
|
|
988 |
|
|
|
989 |
// select our link and unlink
|
|
|
990 |
var a = dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, ['a']);
|
|
|
991 |
dojo.withGlobal(this.window, "selectElement", dijit._editor.selection, [a]);
|
|
|
992 |
|
|
|
993 |
returnValue=this.document.execCommand("unlink", false, null);
|
|
|
994 |
}else if((command == "hilitecolor")&&(dojo.isMoz)){
|
|
|
995 |
// // mozilla doesn't support hilitecolor properly when useCSS is
|
|
|
996 |
// // set to false (bugzilla #279330)
|
|
|
997 |
|
|
|
998 |
this.document.execCommand("styleWithCSS", false, true);
|
|
|
999 |
returnValue = this.document.execCommand(command, false, argument);
|
|
|
1000 |
this.document.execCommand("styleWithCSS", false, false);
|
|
|
1001 |
|
|
|
1002 |
}else if((dojo.isIE)&&( (command == "backcolor")||(command == "forecolor") )){
|
|
|
1003 |
// Tested under IE 6 XP2, no problem here, comment out
|
|
|
1004 |
// IE weirdly collapses ranges when we exec these commands, so prevent it
|
|
|
1005 |
// var tr = this.document.selection.createRange();
|
|
|
1006 |
argument = arguments.length > 1 ? argument : null;
|
|
|
1007 |
returnValue = this.document.execCommand(command, false, argument);
|
|
|
1008 |
|
|
|
1009 |
// timeout is workaround for weird IE behavior were the text
|
|
|
1010 |
// selection gets correctly re-created, but subsequent input
|
|
|
1011 |
// apparently isn't bound to it
|
|
|
1012 |
// setTimeout(function(){tr.select();}, 1);
|
|
|
1013 |
}else{
|
|
|
1014 |
argument = arguments.length > 1 ? argument : null;
|
|
|
1015 |
// if(dojo.isMoz){
|
|
|
1016 |
// this.document = this.iframe.contentWindow.document
|
|
|
1017 |
// }
|
|
|
1018 |
|
|
|
1019 |
if(argument || command!="createlink"){
|
|
|
1020 |
returnValue = this.document.execCommand(command, false, argument);
|
|
|
1021 |
}
|
|
|
1022 |
}
|
|
|
1023 |
|
|
|
1024 |
this.onDisplayChanged();
|
|
|
1025 |
return returnValue;
|
|
|
1026 |
},
|
|
|
1027 |
|
|
|
1028 |
queryCommandEnabled: function(/*String*/command){
|
|
|
1029 |
// summary: check whether a command is enabled or not
|
|
|
1030 |
command = this._normalizeCommand(command);
|
|
|
1031 |
if(dojo.isMoz || dojo.isSafari){
|
|
|
1032 |
if(command == "unlink"){ // mozilla returns true always
|
|
|
1033 |
// console.debug(dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, ['a']));
|
|
|
1034 |
return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, ['a']);
|
|
|
1035 |
}else if (command == "inserttable"){
|
|
|
1036 |
return true;
|
|
|
1037 |
}
|
|
|
1038 |
}
|
|
|
1039 |
//see #4109
|
|
|
1040 |
if(dojo.isSafari)
|
|
|
1041 |
if(command == "copy"){
|
|
|
1042 |
command="cut";
|
|
|
1043 |
}else if(command == "paste"){
|
|
|
1044 |
return true;
|
|
|
1045 |
}
|
|
|
1046 |
|
|
|
1047 |
// return this.document.queryCommandEnabled(command);
|
|
|
1048 |
var elem = (dojo.isIE) ? this.document.selection.createRange() : this.document;
|
|
|
1049 |
return elem.queryCommandEnabled(command);
|
|
|
1050 |
},
|
|
|
1051 |
|
|
|
1052 |
queryCommandState: function(command){
|
|
|
1053 |
// summary: check the state of a given command
|
|
|
1054 |
command = this._normalizeCommand(command);
|
|
|
1055 |
return this.document.queryCommandState(command);
|
|
|
1056 |
},
|
|
|
1057 |
|
|
|
1058 |
queryCommandValue: function(command){
|
|
|
1059 |
// summary: check the value of a given command
|
|
|
1060 |
command = this._normalizeCommand(command);
|
|
|
1061 |
if(dojo.isIE && command == "formatblock"){
|
|
|
1062 |
return this._local2NativeFormatNames[this.document.queryCommandValue(command)];
|
|
|
1063 |
}
|
|
|
1064 |
return this.document.queryCommandValue(command);
|
|
|
1065 |
},
|
|
|
1066 |
|
|
|
1067 |
// Misc.
|
|
|
1068 |
|
|
|
1069 |
placeCursorAtStart: function(){
|
|
|
1070 |
// summary:
|
|
|
1071 |
// place the cursor at the start of the editing area
|
|
|
1072 |
this.focus();
|
|
|
1073 |
|
|
|
1074 |
//see comments in placeCursorAtEnd
|
|
|
1075 |
var isvalid=false;
|
|
|
1076 |
if(dojo.isMoz){
|
|
|
1077 |
var first=this.editNode.firstChild;
|
|
|
1078 |
while(first){
|
|
|
1079 |
if(first.nodeType == 3){
|
|
|
1080 |
if(first.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
|
|
|
1081 |
isvalid=true;
|
|
|
1082 |
dojo.withGlobal(this.window, "selectElement", dijit._editor.selection, [first]);
|
|
|
1083 |
break;
|
|
|
1084 |
}
|
|
|
1085 |
}else if(first.nodeType == 1){
|
|
|
1086 |
isvalid=true;
|
|
|
1087 |
dojo.withGlobal(this.window, "selectElementChildren",dijit._editor.selection, [first]);
|
|
|
1088 |
break;
|
|
|
1089 |
}
|
|
|
1090 |
first = first.nextSibling;
|
|
|
1091 |
}
|
|
|
1092 |
}else{
|
|
|
1093 |
isvalid=true;
|
|
|
1094 |
dojo.withGlobal(this.window, "selectElementChildren",dijit._editor.selection, [this.editNode]);
|
|
|
1095 |
}
|
|
|
1096 |
if(isvalid){
|
|
|
1097 |
dojo.withGlobal(this.window, "collapse", dijit._editor.selection, [true]);
|
|
|
1098 |
}
|
|
|
1099 |
},
|
|
|
1100 |
|
|
|
1101 |
placeCursorAtEnd: function(){
|
|
|
1102 |
// summary:
|
|
|
1103 |
// place the cursor at the end of the editing area
|
|
|
1104 |
this.focus();
|
|
|
1105 |
|
|
|
1106 |
//In mozilla, if last child is not a text node, we have to use selectElementChildren on this.editNode.lastChild
|
|
|
1107 |
//otherwise the cursor would be placed at the end of the closing tag of this.editNode.lastChild
|
|
|
1108 |
var isvalid=false;
|
|
|
1109 |
if(dojo.isMoz){
|
|
|
1110 |
var last=this.editNode.lastChild;
|
|
|
1111 |
while(last){
|
|
|
1112 |
if(last.nodeType == 3){
|
|
|
1113 |
if(last.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
|
|
|
1114 |
isvalid=true;
|
|
|
1115 |
dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [last]);
|
|
|
1116 |
break;
|
|
|
1117 |
}
|
|
|
1118 |
}else if(last.nodeType == 1){
|
|
|
1119 |
isvalid=true;
|
|
|
1120 |
if(last.lastChild){
|
|
|
1121 |
dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [last.lastChild]);
|
|
|
1122 |
}else{
|
|
|
1123 |
dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [last]);
|
|
|
1124 |
}
|
|
|
1125 |
break;
|
|
|
1126 |
}
|
|
|
1127 |
last = last.previousSibling;
|
|
|
1128 |
}
|
|
|
1129 |
}else{
|
|
|
1130 |
isvalid=true;
|
|
|
1131 |
dojo.withGlobal(this.window, "selectElementChildren",dijit._editor.selection, [this.editNode]);
|
|
|
1132 |
}
|
|
|
1133 |
if(isvalid){
|
|
|
1134 |
dojo.withGlobal(this.window, "collapse", dijit._editor.selection, [false]);
|
|
|
1135 |
}
|
|
|
1136 |
},
|
|
|
1137 |
|
|
|
1138 |
getValue: function(/*Boolean?*/nonDestructive){
|
|
|
1139 |
// summary:
|
|
|
1140 |
// return the current content of the editing area (post filters are applied)
|
|
|
1141 |
if(this.textarea){
|
|
|
1142 |
if(this.isClosed || !this.isLoaded){
|
|
|
1143 |
return this.textarea.value;
|
|
|
1144 |
}
|
|
|
1145 |
}
|
|
|
1146 |
|
|
|
1147 |
return this._postFilterContent(null, nonDestructive);
|
|
|
1148 |
},
|
|
|
1149 |
|
|
|
1150 |
setValue: function(/*String*/html){
|
|
|
1151 |
// summary:
|
|
|
1152 |
// this function set the content. No undo history is preserved
|
|
|
1153 |
if(this.textarea && (this.isClosed || !this.isLoaded)){
|
|
|
1154 |
this.textarea.value=html;
|
|
|
1155 |
}else{
|
|
|
1156 |
html = this._preFilterContent(html);
|
|
|
1157 |
if(this.isClosed){
|
|
|
1158 |
this.domNode.innerHTML = html;
|
|
|
1159 |
this._preDomFilterContent(this.domNode);
|
|
|
1160 |
}else{
|
|
|
1161 |
this.editNode.innerHTML = html;
|
|
|
1162 |
this._preDomFilterContent(this.editNode);
|
|
|
1163 |
}
|
|
|
1164 |
}
|
|
|
1165 |
},
|
|
|
1166 |
|
|
|
1167 |
replaceValue: function(/*String*/html){
|
|
|
1168 |
// summary:
|
|
|
1169 |
// this function set the content while trying to maintain the undo stack
|
|
|
1170 |
// (now only works fine with Moz, this is identical to setValue in all
|
|
|
1171 |
// other browsers)
|
|
|
1172 |
if(this.isClosed){
|
|
|
1173 |
this.setValue(html);
|
|
|
1174 |
}else if(this.window && this.window.getSelection && !dojo.isMoz){ // Safari
|
|
|
1175 |
// look ma! it's a totally f'd browser!
|
|
|
1176 |
this.setValue(html);
|
|
|
1177 |
}else if(this.window && this.window.getSelection){ // Moz
|
|
|
1178 |
html = this._preFilterContent(html);
|
|
|
1179 |
this.execCommand("selectall");
|
|
|
1180 |
if(dojo.isMoz && !html){ html = " " }
|
|
|
1181 |
this.execCommand("inserthtml", html);
|
|
|
1182 |
this._preDomFilterContent(this.editNode);
|
|
|
1183 |
}else if(this.document && this.document.selection){//IE
|
|
|
1184 |
//In IE, when the first element is not a text node, say
|
|
|
1185 |
//an <a> tag, when replacing the content of the editing
|
|
|
1186 |
//area, the <a> tag will be around all the content
|
|
|
1187 |
//so for now, use setValue for IE too
|
|
|
1188 |
this.setValue(html);
|
|
|
1189 |
}
|
|
|
1190 |
},
|
|
|
1191 |
|
|
|
1192 |
_preFilterContent: function(/*String*/html){
|
|
|
1193 |
// summary:
|
|
|
1194 |
// filter the input before setting the content of the editing area
|
|
|
1195 |
var ec = html;
|
|
|
1196 |
dojo.forEach(this.contentPreFilters, function(ef){ if(ef){ ec = ef(ec); } });
|
|
|
1197 |
return ec;
|
|
|
1198 |
},
|
|
|
1199 |
_preDomFilterContent: function(/*DomNode*/dom){
|
|
|
1200 |
// summary:
|
|
|
1201 |
// filter the input
|
|
|
1202 |
dom = dom || this.editNode;
|
|
|
1203 |
dojo.forEach(this.contentDomPreFilters, function(ef){
|
|
|
1204 |
if(ef && dojo.isFunction(ef)){
|
|
|
1205 |
ef(dom);
|
|
|
1206 |
}
|
|
|
1207 |
}, this);
|
|
|
1208 |
},
|
|
|
1209 |
|
|
|
1210 |
_postFilterContent: function(/*DomNode|DomNode[]?*/dom,/*Boolean?*/nonDestructive){
|
|
|
1211 |
// summary:
|
|
|
1212 |
// filter the output after getting the content of the editing area
|
|
|
1213 |
dom = dom || this.editNode;
|
|
|
1214 |
if(this.contentDomPostFilters.length){
|
|
|
1215 |
if(nonDestructive && dom['cloneNode']){
|
|
|
1216 |
dom = dom.cloneNode(true);
|
|
|
1217 |
}
|
|
|
1218 |
dojo.forEach(this.contentDomPostFilters, function(ef){
|
|
|
1219 |
dom = ef(dom);
|
|
|
1220 |
});
|
|
|
1221 |
}
|
|
|
1222 |
var ec = this.getNodeChildrenHtml(dom);
|
|
|
1223 |
if(!ec.replace(/^(?:\s|\xA0)+/g, "").replace(/(?:\s|\xA0)+$/g,"").length){ ec = ""; }
|
|
|
1224 |
|
|
|
1225 |
// if(dojo.isIE){
|
|
|
1226 |
// //removing appended <P> </P> for IE
|
|
|
1227 |
// ec = ec.replace(/(?:<p> </p>[\n\r]*)+$/i,"");
|
|
|
1228 |
// }
|
|
|
1229 |
dojo.forEach(this.contentPostFilters, function(ef){
|
|
|
1230 |
ec = ef(ec);
|
|
|
1231 |
});
|
|
|
1232 |
|
|
|
1233 |
return ec;
|
|
|
1234 |
},
|
|
|
1235 |
|
|
|
1236 |
_saveContent: function(/*Event*/e){
|
|
|
1237 |
// summary:
|
|
|
1238 |
// Saves the content in an onunload event if the editor has not been closed
|
|
|
1239 |
var saveTextarea = dojo.byId("dijit._editor.RichText.savedContent");
|
|
|
1240 |
saveTextarea.value += this._SEPARATOR + this.name + ":" + this.getValue();
|
|
|
1241 |
},
|
|
|
1242 |
|
|
|
1243 |
|
|
|
1244 |
escapeXml: function(/*String*/str, /*Boolean*/noSingleQuotes){
|
|
|
1245 |
//summary:
|
|
|
1246 |
// Adds escape sequences for special characters in XML: &<>"'
|
|
|
1247 |
// Optionally skips escapes for single quotes
|
|
|
1248 |
str = str.replace(/&/gm, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, """);
|
|
|
1249 |
if(!noSingleQuotes){
|
|
|
1250 |
str = str.replace(/'/gm, "'");
|
|
|
1251 |
}
|
|
|
1252 |
return str; // string
|
|
|
1253 |
},
|
|
|
1254 |
|
|
|
1255 |
getNodeHtml: function(/* DomNode */node){
|
|
|
1256 |
switch(node.nodeType){
|
|
|
1257 |
case 1: //element node
|
|
|
1258 |
var output = '<'+node.tagName.toLowerCase();
|
|
|
1259 |
if(dojo.isMoz){
|
|
|
1260 |
if(node.getAttribute('type')=='_moz'){
|
|
|
1261 |
node.removeAttribute('type');
|
|
|
1262 |
}
|
|
|
1263 |
if(node.getAttribute('_moz_dirty') != undefined){
|
|
|
1264 |
node.removeAttribute('_moz_dirty');
|
|
|
1265 |
}
|
|
|
1266 |
}
|
|
|
1267 |
//store the list of attributes and sort it to have the
|
|
|
1268 |
//attributes appear in the dictionary order
|
|
|
1269 |
var attrarray = [];
|
|
|
1270 |
if(dojo.isIE){
|
|
|
1271 |
var s = node.outerHTML;
|
|
|
1272 |
s = s.substr(0,s.indexOf('>'));
|
|
|
1273 |
s = s.replace(/(?:['"])[^"']*\1/g, '');//to make the following regexp safe
|
|
|
1274 |
var reg = /([^\s=]+)=/g;
|
|
|
1275 |
var m, key;
|
|
|
1276 |
while((m = reg.exec(s)) != undefined){
|
|
|
1277 |
key=m[1];
|
|
|
1278 |
if(key.substr(0,3) != '_dj'){
|
|
|
1279 |
if(key == 'src' || key == 'href'){
|
|
|
1280 |
if(node.getAttribute('_djrealurl')){
|
|
|
1281 |
attrarray.push([key,node.getAttribute('_djrealurl')]);
|
|
|
1282 |
continue;
|
|
|
1283 |
}
|
|
|
1284 |
}
|
|
|
1285 |
if(key == 'class'){
|
|
|
1286 |
attrarray.push([key,node.className]);
|
|
|
1287 |
}else{
|
|
|
1288 |
attrarray.push([key,node.getAttribute(key)]);
|
|
|
1289 |
}
|
|
|
1290 |
}
|
|
|
1291 |
}
|
|
|
1292 |
}else{
|
|
|
1293 |
var attr, i=0, attrs = node.attributes;
|
|
|
1294 |
while((attr=attrs[i++])){
|
|
|
1295 |
//ignore all attributes starting with _dj which are
|
|
|
1296 |
//internal temporary attributes used by the editor
|
|
|
1297 |
if(attr.name.substr(0,3) != '_dj' /*&&
|
|
|
1298 |
(attr.specified == undefined || attr.specified)*/){
|
|
|
1299 |
var v = attr.value;
|
|
|
1300 |
if(attr.name == 'src' || attr.name == 'href'){
|
|
|
1301 |
if(node.getAttribute('_djrealurl')){
|
|
|
1302 |
v = node.getAttribute('_djrealurl');
|
|
|
1303 |
}
|
|
|
1304 |
}
|
|
|
1305 |
attrarray.push([attr.name,v]);
|
|
|
1306 |
}
|
|
|
1307 |
}
|
|
|
1308 |
}
|
|
|
1309 |
attrarray.sort(function(a,b){
|
|
|
1310 |
return a[0]<b[0]?-1:(a[0]==b[0]?0:1);
|
|
|
1311 |
});
|
|
|
1312 |
i=0;
|
|
|
1313 |
while((attr=attrarray[i++])){
|
|
|
1314 |
output += ' '+attr[0]+'="'+attr[1]+'"';
|
|
|
1315 |
}
|
|
|
1316 |
if(node.childNodes.length){
|
|
|
1317 |
output += '>' + this.getNodeChildrenHtml(node)+'</'+node.tagName.toLowerCase()+'>';
|
|
|
1318 |
}else{
|
|
|
1319 |
output += ' />';
|
|
|
1320 |
}
|
|
|
1321 |
break;
|
|
|
1322 |
case 3: //text
|
|
|
1323 |
// FIXME:
|
|
|
1324 |
var output = this.escapeXml(node.nodeValue,true);
|
|
|
1325 |
break;
|
|
|
1326 |
case 8: //comment
|
|
|
1327 |
// FIXME:
|
|
|
1328 |
var output = '<!--'+this.escapeXml(node.nodeValue,true)+'-->';
|
|
|
1329 |
break;
|
|
|
1330 |
default:
|
|
|
1331 |
var output = "Element not recognized - Type: " + node.nodeType + " Name: " + node.nodeName;
|
|
|
1332 |
}
|
|
|
1333 |
return output;
|
|
|
1334 |
},
|
|
|
1335 |
|
|
|
1336 |
getNodeChildrenHtml: function(/* DomNode */dom){
|
|
|
1337 |
// summary: Returns the html content of a DomNode and children
|
|
|
1338 |
var out = "";
|
|
|
1339 |
if(!dom){ return out; }
|
|
|
1340 |
var nodes = dom["childNodes"]||dom;
|
|
|
1341 |
var i=0;
|
|
|
1342 |
var node;
|
|
|
1343 |
while((node=nodes[i++])){
|
|
|
1344 |
out += this.getNodeHtml(node);
|
|
|
1345 |
}
|
|
|
1346 |
return out; // String
|
|
|
1347 |
},
|
|
|
1348 |
|
|
|
1349 |
close: function(/*Boolean*/save, /*Boolean*/force){
|
|
|
1350 |
// summary:
|
|
|
1351 |
// Kills the editor and optionally writes back the modified contents to the
|
|
|
1352 |
// element from which it originated.
|
|
|
1353 |
// save:
|
|
|
1354 |
// Whether or not to save the changes. If false, the changes are discarded.
|
|
|
1355 |
// force:
|
|
|
1356 |
if(this.isClosed){return false; }
|
|
|
1357 |
|
|
|
1358 |
if(!arguments.length){ save = true; }
|
|
|
1359 |
this._content = this.getValue();
|
|
|
1360 |
var changed = (this.savedContent != this._content);
|
|
|
1361 |
|
|
|
1362 |
// line height is squashed for iframes
|
|
|
1363 |
// FIXME: why was this here? if (this.iframe){ this.domNode.style.lineHeight = null; }
|
|
|
1364 |
|
|
|
1365 |
if(this.interval){ clearInterval(this.interval); }
|
|
|
1366 |
|
|
|
1367 |
if(this.textarea){
|
|
|
1368 |
with(this.textarea.style){
|
|
|
1369 |
position = "";
|
|
|
1370 |
left = top = "";
|
|
|
1371 |
if(dojo.isIE){
|
|
|
1372 |
overflow = this.__overflow;
|
|
|
1373 |
this.__overflow = null;
|
|
|
1374 |
}
|
|
|
1375 |
}
|
|
|
1376 |
if(save){
|
|
|
1377 |
this.textarea.value = this._content;
|
|
|
1378 |
}else{
|
|
|
1379 |
this.textarea.value = this.savedContent;
|
|
|
1380 |
}
|
|
|
1381 |
dojo._destroyElement(this.domNode);
|
|
|
1382 |
this.domNode = this.textarea;
|
|
|
1383 |
}else{
|
|
|
1384 |
if(save){
|
|
|
1385 |
//why we treat moz differently? comment out to fix #1061
|
|
|
1386 |
// if(dojo.isMoz){
|
|
|
1387 |
// var nc = dojo.doc.createElement("span");
|
|
|
1388 |
// this.domNode.appendChild(nc);
|
|
|
1389 |
// nc.innerHTML = this.editNode.innerHTML;
|
|
|
1390 |
// }else{
|
|
|
1391 |
// this.domNode.innerHTML = this._content;
|
|
|
1392 |
// }
|
|
|
1393 |
this.domNode.innerHTML = this._content;
|
|
|
1394 |
}else{
|
|
|
1395 |
this.domNode.innerHTML = this.savedContent;
|
|
|
1396 |
}
|
|
|
1397 |
}
|
|
|
1398 |
|
|
|
1399 |
dojo.removeClass(this.domNode, "RichTextEditable");
|
|
|
1400 |
this.isClosed = true;
|
|
|
1401 |
this.isLoaded = false;
|
|
|
1402 |
// FIXME: is this always the right thing to do?
|
|
|
1403 |
delete this.editNode;
|
|
|
1404 |
|
|
|
1405 |
if(this.window && this.window._frameElement){
|
|
|
1406 |
this.window._frameElement = null;
|
|
|
1407 |
}
|
|
|
1408 |
|
|
|
1409 |
this.window = null;
|
|
|
1410 |
this.document = null;
|
|
|
1411 |
this.editingArea = null;
|
|
|
1412 |
this.editorObject = null;
|
|
|
1413 |
|
|
|
1414 |
return changed; // Boolean: whether the content has been modified
|
|
|
1415 |
},
|
|
|
1416 |
|
|
|
1417 |
destroyRendering: function(){
|
|
|
1418 |
// summary: stub
|
|
|
1419 |
},
|
|
|
1420 |
|
|
|
1421 |
destroy: function(){
|
|
|
1422 |
this.destroyRendering();
|
|
|
1423 |
if(!this.isClosed){ this.close(false); }
|
|
|
1424 |
this.inherited("destroy",arguments);
|
|
|
1425 |
//dijit._editor.RichText.superclass.destroy.call(this);
|
|
|
1426 |
},
|
|
|
1427 |
|
|
|
1428 |
_fixContentForMoz: function(/* String */ html){
|
|
|
1429 |
// summary:
|
|
|
1430 |
// Moz can not handle strong/em tags correctly, convert them to b/i
|
|
|
1431 |
html = html.replace(/<(\/)?strong([ \>])/gi, '<$1b$2' );
|
|
|
1432 |
html = html.replace(/<(\/)?em([ \>])/gi, '<$1i$2' );
|
|
|
1433 |
return html; // String
|
|
|
1434 |
},
|
|
|
1435 |
|
|
|
1436 |
_srcInImgRegex : /(?:(<img(?=\s).*?\ssrc=)("|')(.*?)\2)|(?:(<img\s.*?src=)([^"'][^ >]+))/gi ,
|
|
|
1437 |
_hrefInARegex : /(?:(<a(?=\s).*?\shref=)("|')(.*?)\2)|(?:(<a\s.*?href=)([^"'][^ >]+))/gi ,
|
|
|
1438 |
|
|
|
1439 |
_preFixUrlAttributes: function(/* String */ html){
|
|
|
1440 |
html = html.replace(this._hrefInARegex, '$1$4$2$3$5$2 _djrealurl=$2$3$5$2') ;
|
|
|
1441 |
html = html.replace(this._srcInImgRegex, '$1$4$2$3$5$2 _djrealurl=$2$3$5$2') ;
|
|
|
1442 |
return html; // String
|
|
|
1443 |
}
|
|
|
1444 |
});
|
|
|
1445 |
|
|
|
1446 |
}
|