2150 |
mathias |
1 |
if(!dojo._hasResource["dijit.InlineEditBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
|
|
|
2 |
dojo._hasResource["dijit.InlineEditBox"] = true;
|
|
|
3 |
dojo.provide("dijit.InlineEditBox");
|
|
|
4 |
|
|
|
5 |
dojo.require("dojo.i18n");
|
|
|
6 |
|
|
|
7 |
dojo.require("dijit._Widget");
|
|
|
8 |
dojo.require("dijit._Container");
|
|
|
9 |
dojo.require("dijit.form.Button");
|
|
|
10 |
dojo.require("dijit.form.TextBox");
|
|
|
11 |
|
|
|
12 |
dojo.requireLocalization("dijit", "common", null, "ko,zh,ja,zh-tw,ru,it,hu,fr,pt,ROOT,pl,es,de,cs");
|
|
|
13 |
|
|
|
14 |
dojo.declare("dijit.InlineEditBox",
|
|
|
15 |
dijit._Widget,
|
|
|
16 |
{
|
|
|
17 |
// summary: An element with in-line edit capabilitites
|
|
|
18 |
//
|
|
|
19 |
// description:
|
|
|
20 |
// Behavior for an existing node (<p>, <div>, <span>, etc.) so that
|
|
|
21 |
// when you click it, an editor shows up in place of the original
|
|
|
22 |
// text. Optionally, Save and Cancel button are displayed below the edit widget.
|
|
|
23 |
// When Save is clicked, the text is pulled from the edit
|
|
|
24 |
// widget and redisplayed and the edit widget is again hidden.
|
|
|
25 |
// By default a plain Textarea widget is used as the editor (or for
|
|
|
26 |
// inline values a TextBox), but you can specify an editor such as
|
|
|
27 |
// dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
|
|
|
28 |
// An edit widget must support the following API to be used:
|
|
|
29 |
// String getDisplayedValue() OR String getValue()
|
|
|
30 |
// void setDisplayedValue(String) OR void setValue(String)
|
|
|
31 |
// void focus()
|
|
|
32 |
//
|
|
|
33 |
// editing: Boolean
|
|
|
34 |
// Is the node currently in edit mode?
|
|
|
35 |
editing: false,
|
|
|
36 |
|
|
|
37 |
// autoSave: Boolean
|
|
|
38 |
// Changing the value automatically saves it; don't have to push save button
|
|
|
39 |
// (and save button isn't even displayed)
|
|
|
40 |
autoSave: true,
|
|
|
41 |
|
|
|
42 |
// buttonSave: String
|
|
|
43 |
// Save button label
|
|
|
44 |
buttonSave: "",
|
|
|
45 |
|
|
|
46 |
// buttonCancel: String
|
|
|
47 |
// Cancel button label
|
|
|
48 |
buttonCancel: "",
|
|
|
49 |
|
|
|
50 |
// renderAsHtml: Boolean
|
|
|
51 |
// Set this to true if the specified Editor's value should be interpreted as HTML
|
|
|
52 |
// rather than plain text (ie, dijit.Editor)
|
|
|
53 |
renderAsHtml: false,
|
|
|
54 |
|
|
|
55 |
// editor: String
|
|
|
56 |
// Class name for Editor widget
|
|
|
57 |
editor: "dijit.form.TextBox",
|
|
|
58 |
|
|
|
59 |
// editorParams: Object
|
|
|
60 |
// Set of parameters for editor, like {required: true}
|
|
|
61 |
editorParams: {},
|
|
|
62 |
|
|
|
63 |
onChange: function(value){
|
|
|
64 |
// summary: User should set this handler to be notified of changes to value
|
|
|
65 |
},
|
|
|
66 |
|
|
|
67 |
// width: String
|
|
|
68 |
// Width of editor. By default it's width=100% (ie, block mode)
|
|
|
69 |
width: "100%",
|
|
|
70 |
|
|
|
71 |
// value: String
|
|
|
72 |
// The display value of the widget in read-only mode
|
|
|
73 |
value: "",
|
|
|
74 |
|
|
|
75 |
// noValueIndicator: String
|
|
|
76 |
// The text that gets displayed when there is no value (so that the user has a place to click to edit)
|
|
|
77 |
noValueIndicator: "<span style='font-family: wingdings; text-decoration: underline;'> ✍ </span>",
|
|
|
78 |
|
|
|
79 |
postMixInProperties: function(){
|
|
|
80 |
this.inherited('postMixInProperties', arguments);
|
|
|
81 |
|
|
|
82 |
// save pointer to original source node, since Widget nulls-out srcNodeRef
|
|
|
83 |
this.displayNode = this.srcNodeRef;
|
|
|
84 |
|
|
|
85 |
// connect handlers to the display node
|
|
|
86 |
var events = {
|
|
|
87 |
ondijitclick: "_onClick",
|
|
|
88 |
onmouseover: "_onMouseOver",
|
|
|
89 |
onmouseout: "_onMouseOut",
|
|
|
90 |
onfocus: "_onMouseOver",
|
|
|
91 |
onblur: "_onMouseOut"
|
|
|
92 |
};
|
|
|
93 |
for(var name in events){
|
|
|
94 |
this.connect(this.displayNode, name, events[name]);
|
|
|
95 |
}
|
|
|
96 |
dijit.setWaiRole(this.displayNode, "button");
|
|
|
97 |
if(!this.displayNode.getAttribute("tabIndex")){
|
|
|
98 |
this.displayNode.setAttribute("tabIndex", 0);
|
|
|
99 |
}
|
|
|
100 |
|
|
|
101 |
if(!this.value){
|
|
|
102 |
this.value = this.displayNode.innerHTML;
|
|
|
103 |
}
|
|
|
104 |
this._setDisplayValue(this.value); // if blank, change to icon for "input needed"
|
|
|
105 |
},
|
|
|
106 |
|
|
|
107 |
_onMouseOver: function(){
|
|
|
108 |
dojo.addClass(this.displayNode, this.disabled ? "dijitDisabledClickableRegion" : "dijitClickableRegion");
|
|
|
109 |
},
|
|
|
110 |
|
|
|
111 |
_onMouseOut: function(){
|
|
|
112 |
dojo.removeClass(this.displayNode, this.disabled ? "dijitDisabledClickableRegion" : "dijitClickableRegion");
|
|
|
113 |
},
|
|
|
114 |
|
|
|
115 |
_onClick: function(/*Event*/ e){
|
|
|
116 |
if(this.disabled){ return; }
|
|
|
117 |
if(e){ dojo.stopEvent(e); }
|
|
|
118 |
this._onMouseOut();
|
|
|
119 |
|
|
|
120 |
// Since FF gets upset if you move a node while in an event handler for that node...
|
|
|
121 |
setTimeout(dojo.hitch(this, "_edit"), 0);
|
|
|
122 |
},
|
|
|
123 |
|
|
|
124 |
_edit: function(){
|
|
|
125 |
// summary: display the editor widget in place of the original (read only) markup
|
|
|
126 |
|
|
|
127 |
this.editing = true;
|
|
|
128 |
|
|
|
129 |
var editValue =
|
|
|
130 |
(this.renderAsHtml ?
|
|
|
131 |
this.value :
|
|
|
132 |
this.value.replace(/\s*\r?\n\s*/g,"").replace(/<br\/?>/gi, "\n").replace(/>/g,">").replace(/</g,"<").replace(/&/g,"&"));
|
|
|
133 |
|
|
|
134 |
// Placeholder for edit widget
|
|
|
135 |
// Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
|
|
|
136 |
// when Calendar dropdown appears, which happens automatically on focus.
|
|
|
137 |
var placeholder = document.createElement("span");
|
|
|
138 |
dojo.place(placeholder, this.domNode, "before");
|
|
|
139 |
|
|
|
140 |
var ew = this.editWidget = new dijit._InlineEditor({
|
|
|
141 |
value: dojo.trim(editValue),
|
|
|
142 |
autoSave: this.autoSave,
|
|
|
143 |
buttonSave: this.buttonSave,
|
|
|
144 |
buttonCancel: this.buttonCancel,
|
|
|
145 |
renderAsHtml: this.renderAsHtml,
|
|
|
146 |
editor: this.editor,
|
|
|
147 |
editorParams: this.editorParams,
|
|
|
148 |
style: dojo.getComputedStyle(this.displayNode),
|
|
|
149 |
save: dojo.hitch(this, "save"),
|
|
|
150 |
cancel: dojo.hitch(this, "cancel"),
|
|
|
151 |
width: this.width
|
|
|
152 |
}, placeholder);
|
|
|
153 |
|
|
|
154 |
// to avoid screen jitter, we first create the editor with position:absolute, visibility:hidden,
|
|
|
155 |
// and then when it's finished rendering, we switch from display mode to editor
|
|
|
156 |
var ews = ew.domNode.style;
|
|
|
157 |
this.displayNode.style.display="none";
|
|
|
158 |
ews.position = "static";
|
|
|
159 |
ews.visibility = "visible";
|
|
|
160 |
|
|
|
161 |
// Replace the display widget with edit widget, leaving them both displayed for a brief time so that
|
|
|
162 |
// focus can be shifted without incident. (browser may needs some time to render the editor.)
|
|
|
163 |
this.domNode = ew.domNode;
|
|
|
164 |
setTimeout(function(){
|
|
|
165 |
ew.focus();
|
|
|
166 |
}, 100);
|
|
|
167 |
},
|
|
|
168 |
|
|
|
169 |
_showText: function(/*Boolean*/ focus){
|
|
|
170 |
// summary: revert to display mode, and optionally focus on display node
|
|
|
171 |
|
|
|
172 |
// display the read-only text and then quickly hide the editor (to avoid screen jitter)
|
|
|
173 |
this.displayNode.style.display="";
|
|
|
174 |
var ews = this.editWidget.domNode.style;
|
|
|
175 |
ews.position="absolute";
|
|
|
176 |
ews.visibility="hidden";
|
|
|
177 |
|
|
|
178 |
this.domNode = this.displayNode;
|
|
|
179 |
|
|
|
180 |
// give the browser some time to render the display node and then shift focus to it
|
|
|
181 |
// and hide the edit widget
|
|
|
182 |
var _this = this;
|
|
|
183 |
setTimeout(function(){
|
|
|
184 |
if(focus){
|
|
|
185 |
dijit.focus(_this.displayNode);
|
|
|
186 |
}
|
|
|
187 |
_this.editWidget.destroy();
|
|
|
188 |
delete _this.editWidget;
|
|
|
189 |
}, 100);
|
|
|
190 |
},
|
|
|
191 |
|
|
|
192 |
save: function(/*Boolean*/ focus){
|
|
|
193 |
// summary:
|
|
|
194 |
// Save the contents of the editor and revert to display mode.
|
|
|
195 |
// focus: Boolean
|
|
|
196 |
// Focus on the display mode text
|
|
|
197 |
this.editing = false;
|
|
|
198 |
|
|
|
199 |
this.value = this.editWidget.getValue() + "";
|
|
|
200 |
if(this.renderAsHtml){
|
|
|
201 |
this.value = this.value.replace(/&/gm, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, """)
|
|
|
202 |
.replace("\n", "<br>");
|
|
|
203 |
}
|
|
|
204 |
this._setDisplayValue(this.value);
|
|
|
205 |
|
|
|
206 |
// tell the world that we have changed
|
|
|
207 |
this.onChange(this.value);
|
|
|
208 |
|
|
|
209 |
this._showText(focus);
|
|
|
210 |
},
|
|
|
211 |
|
|
|
212 |
_setDisplayValue: function(/*String*/ val){
|
|
|
213 |
// summary: inserts specified HTML value into this node, or an "input needed" character if node is blank
|
|
|
214 |
this.displayNode.innerHTML = val || this.noValueIndicator;
|
|
|
215 |
},
|
|
|
216 |
|
|
|
217 |
cancel: function(/*Boolean*/ focus){
|
|
|
218 |
// summary:
|
|
|
219 |
// Revert to display mode, discarding any changes made in the editor
|
|
|
220 |
this.editing = false;
|
|
|
221 |
this._showText(focus);
|
|
|
222 |
}
|
|
|
223 |
});
|
|
|
224 |
|
|
|
225 |
dojo.declare(
|
|
|
226 |
"dijit._InlineEditor",
|
|
|
227 |
[dijit._Widget, dijit._Templated],
|
|
|
228 |
{
|
|
|
229 |
// summary:
|
|
|
230 |
// internal widget used by InlineEditBox, displayed when in editing mode
|
|
|
231 |
// to display the editor and maybe save/cancel buttons. Calling code should
|
|
|
232 |
// connect to save/cancel methods to detect when editing is finished
|
|
|
233 |
//
|
|
|
234 |
// Has mainly the same parameters as InlineEditBox, plus these values:
|
|
|
235 |
//
|
|
|
236 |
// style: Object
|
|
|
237 |
// Set of CSS attributes of display node, to replicate in editor
|
|
|
238 |
//
|
|
|
239 |
// value: String
|
|
|
240 |
// Value as an HTML string or plain text string, depending on renderAsHTML flag
|
|
|
241 |
|
|
|
242 |
templateString:"<fieldset dojoAttachPoint=\"editNode\" waiRole=\"presentation\" style=\"position: absolute; visibility:hidden\" class=\"dijitReset dijitInline\"\n\tdojoAttachEvent=\"onkeypress: _onKeyPress\" \n\t><input dojoAttachPoint=\"editorPlaceholder\"\n\t/><span dojoAttachPoint=\"buttonContainer\"\n\t\t><button class='saveButton' dojoAttachPoint=\"saveButton\" dojoType=\"dijit.form.Button\" dojoAttachEvent=\"onClick:save\">${buttonSave}</button\n\t\t><button class='cancelButton' dojoAttachPoint=\"cancelButton\" dojoType=\"dijit.form.Button\" dojoAttachEvent=\"onClick:cancel\">${buttonCancel}</button\n\t></span\n></fieldset>\n",
|
|
|
243 |
widgetsInTemplate: true,
|
|
|
244 |
|
|
|
245 |
postMixInProperties: function(){
|
|
|
246 |
this.inherited('postMixInProperties', arguments);
|
|
|
247 |
this.messages = dojo.i18n.getLocalization("dijit", "common", this.lang);
|
|
|
248 |
dojo.forEach(["buttonSave", "buttonCancel"], function(prop){
|
|
|
249 |
if(!this[prop]){ this[prop] = this.messages[prop]; }
|
|
|
250 |
}, this);
|
|
|
251 |
},
|
|
|
252 |
|
|
|
253 |
postCreate: function(){
|
|
|
254 |
// Create edit widget in place in the template
|
|
|
255 |
var cls = dojo.getObject(this.editor);
|
|
|
256 |
var ew = this.editWidget = new cls(this.editorParams, this.editorPlaceholder);
|
|
|
257 |
|
|
|
258 |
// Copy the style from the source
|
|
|
259 |
// Don't copy ALL properties though, just the necessary/applicable ones
|
|
|
260 |
var srcStyle = this.style;
|
|
|
261 |
dojo.forEach(["fontWeight","fontFamily","fontSize","fontStyle"], function(prop){
|
|
|
262 |
ew.focusNode.style[prop]=srcStyle[prop];
|
|
|
263 |
}, this);
|
|
|
264 |
dojo.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], function(prop){
|
|
|
265 |
this.domNode.style[prop]=srcStyle[prop];
|
|
|
266 |
}, this);
|
|
|
267 |
if(this.width=="100%"){
|
|
|
268 |
// block mode
|
|
|
269 |
ew.domNode.style.width = "100%"; // because display: block doesn't work for table widgets
|
|
|
270 |
this.domNode.style.display="block";
|
|
|
271 |
}else{
|
|
|
272 |
// inline-block mode
|
|
|
273 |
ew.domNode.style.width = this.width + (Number(this.width)==this.width ? "px" : "");
|
|
|
274 |
}
|
|
|
275 |
|
|
|
276 |
this.connect(this.editWidget, "onChange", "_onChange");
|
|
|
277 |
|
|
|
278 |
// setting the value of the edit widget will cause a possibly asynchronous onChange() call.
|
|
|
279 |
// we need to ignore it, since we are only interested in when the user changes the value.
|
|
|
280 |
this._ignoreNextOnChange = true;
|
|
|
281 |
(this.editWidget.setDisplayedValue||this.editWidget.setValue).call(this.editWidget, this.value);
|
|
|
282 |
|
|
|
283 |
this._initialText = this.getValue();
|
|
|
284 |
|
|
|
285 |
if(this.autoSave){
|
|
|
286 |
this.buttonContainer.style.display="none";
|
|
|
287 |
}
|
|
|
288 |
},
|
|
|
289 |
|
|
|
290 |
destroy: function(){
|
|
|
291 |
this.editWidget.destroy();
|
|
|
292 |
this.inherited(arguments);
|
|
|
293 |
},
|
|
|
294 |
|
|
|
295 |
getValue: function(){
|
|
|
296 |
var ew = this.editWidget;
|
|
|
297 |
return ew.getDisplayedValue ? ew.getDisplayedValue() : ew.getValue();
|
|
|
298 |
},
|
|
|
299 |
|
|
|
300 |
_onKeyPress: function(e){
|
|
|
301 |
// summary: Callback when keypress in the edit box (see template).
|
|
|
302 |
// description:
|
|
|
303 |
// For autoSave widgets, if Esc/Enter, call cancel/save.
|
|
|
304 |
// For non-autoSave widgets, enable save button if the text value is
|
|
|
305 |
// different than the original value.
|
|
|
306 |
if(this._exitInProgress){
|
|
|
307 |
return;
|
|
|
308 |
}
|
|
|
309 |
if(this.autoSave){
|
|
|
310 |
// If Enter/Esc pressed, treat as save/cancel.
|
|
|
311 |
if(e.keyCode == dojo.keys.ESCAPE){
|
|
|
312 |
dojo.stopEvent(e);
|
|
|
313 |
this._exitInProgress = true;
|
|
|
314 |
this.cancel(true);
|
|
|
315 |
}else if(e.keyCode == dojo.keys.ENTER){
|
|
|
316 |
dojo.stopEvent(e);
|
|
|
317 |
this._exitInProgress = true;
|
|
|
318 |
this.save(true);
|
|
|
319 |
}
|
|
|
320 |
}else{
|
|
|
321 |
var _this = this;
|
|
|
322 |
// Delay before calling getValue().
|
|
|
323 |
// The delay gives the browser a chance to update the Textarea.
|
|
|
324 |
setTimeout(
|
|
|
325 |
function(){
|
|
|
326 |
_this.saveButton.setDisabled(_this.getValue() == _this._initialText);
|
|
|
327 |
}, 100);
|
|
|
328 |
}
|
|
|
329 |
},
|
|
|
330 |
|
|
|
331 |
_onBlur: function(){
|
|
|
332 |
// summary:
|
|
|
333 |
// Called when focus moves outside the editor
|
|
|
334 |
if(this._exitInProgress){
|
|
|
335 |
// when user clicks the "save" button, focus is shifted back to display text, causing this
|
|
|
336 |
// function to be called, but in that case don't do anything
|
|
|
337 |
return;
|
|
|
338 |
}
|
|
|
339 |
if(this.autoSave){
|
|
|
340 |
this._exitInProgress = true;
|
|
|
341 |
if(this.getValue() == this._initialText){
|
|
|
342 |
this.cancel(false);
|
|
|
343 |
}else{
|
|
|
344 |
this.save(false);
|
|
|
345 |
}
|
|
|
346 |
}
|
|
|
347 |
},
|
|
|
348 |
|
|
|
349 |
enableSave: function(){
|
|
|
350 |
// summary: User replacable function returning a Boolean to indicate
|
|
|
351 |
// if the Save button should be enabled or not - usually due to invalid conditions
|
|
|
352 |
return this.editWidget.isValid ? this.editWidget.isValid() : true; // Boolean
|
|
|
353 |
},
|
|
|
354 |
|
|
|
355 |
_onChange: function(){
|
|
|
356 |
// summary:
|
|
|
357 |
// Called when the underlying widget fires an onChange event,
|
|
|
358 |
// which means that the user has finished entering the value
|
|
|
359 |
|
|
|
360 |
if(this._ignoreNextOnChange){
|
|
|
361 |
delete this._ignoreNextOnChange;
|
|
|
362 |
return;
|
|
|
363 |
}
|
|
|
364 |
if(this._exitInProgress){
|
|
|
365 |
// TODO: the onChange event might happen after the return key for an async widget
|
|
|
366 |
// like FilteringSelect. Shouldn't be deleting the edit widget on end-of-edit
|
|
|
367 |
return;
|
|
|
368 |
}
|
|
|
369 |
if(this.autoSave){
|
|
|
370 |
this._exitInProgress = true;
|
|
|
371 |
this.save(true);
|
|
|
372 |
}else{
|
|
|
373 |
// in case the keypress event didn't get through (old problem with Textarea that has been fixed
|
|
|
374 |
// in theory) or if the keypress event comes too quickly and the value inside the Textarea hasn't
|
|
|
375 |
// been updated yet)
|
|
|
376 |
this.saveButton.setDisabled((this.getValue() == this._initialText) || !this.enableSave());
|
|
|
377 |
}
|
|
|
378 |
},
|
|
|
379 |
|
|
|
380 |
enableSave: function(){
|
|
|
381 |
// summary: User replacable function returning a Boolean to indicate
|
|
|
382 |
// if the Save button should be enabled or not - usually due to invalid conditions
|
|
|
383 |
return this.editWidget.isValid ? this.editWidget.isValid() : true;
|
|
|
384 |
},
|
|
|
385 |
|
|
|
386 |
focus: function(){
|
|
|
387 |
this.editWidget.focus();
|
|
|
388 |
dijit.selectInputText(this.editWidget.focusNode);
|
|
|
389 |
}
|
|
|
390 |
});
|
|
|
391 |
|
|
|
392 |
dijit.selectInputText = function(/*DomNode*/element){
|
|
|
393 |
// summary: select all the text in an input element
|
|
|
394 |
|
|
|
395 |
// TODO: use functions in _editor/selection.js?
|
|
|
396 |
var _window = dojo.global;
|
|
|
397 |
var _document = dojo.doc;
|
|
|
398 |
element = dojo.byId(element);
|
|
|
399 |
if(_document["selection"] && dojo.body()["createTextRange"]){ // IE
|
|
|
400 |
if(element.createTextRange){
|
|
|
401 |
var range = element.createTextRange();
|
|
|
402 |
range.moveStart("character", 0);
|
|
|
403 |
range.moveEnd("character", element.value.length);
|
|
|
404 |
range.select();
|
|
|
405 |
}
|
|
|
406 |
}else if(_window["getSelection"]){
|
|
|
407 |
var selection = _window.getSelection();
|
|
|
408 |
// FIXME: does this work on Safari?
|
|
|
409 |
if(element.setSelectionRange){
|
|
|
410 |
element.setSelectionRange(0, element.value.length);
|
|
|
411 |
}
|
|
|
412 |
}
|
|
|
413 |
element.focus();
|
|
|
414 |
};
|
|
|
415 |
|
|
|
416 |
|
|
|
417 |
}
|