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");
5 dojo.require("dojo.i18n");
7 dojo.require("dijit._Widget");
8 dojo.require("dijit._Container");
9 dojo.require("dijit.form.Button");
10 dojo.require("dijit.form.TextBox");
12 dojo.requireLocalization("dijit", "common", null, "zh,pt,da,tr,ru,de,sv,ja,he,fi,nb,el,ar,ROOT,pt-pt,cs,fr,es,ko,nl,zh-tw,pl,it,hu");
14 dojo.declare("dijit.InlineEditBox",
17 // summary: An element with in-line edit capabilitites
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)
34 // Is the node currently in edit mode?
38 // Changing the value automatically saves it; don't have to push save button
39 // (and save button isn't even displayed)
46 // buttonCancel: String
47 // Cancel button label
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)
56 // Class name for Editor widget
57 editor: "dijit.form.TextBox",
59 // editorParams: Object
60 // Set of parameters for editor, like {required: true}
63 onChange: function(value){
64 // summary: User should set this handler to be notified of changes to value
68 // Width of editor. By default it's width=100% (ie, block mode)
72 // The display value of the widget in read-only mode
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>",
79 postMixInProperties: function(){
80 this.inherited('postMixInProperties', arguments);
82 // save pointer to original source node, since Widget nulls-out srcNodeRef
83 this.displayNode = this.srcNodeRef;
85 // connect handlers to the display node
87 ondijitclick: "_onClick",
88 onmouseover: "_onMouseOver",
89 onmouseout: "_onMouseOut",
90 onfocus: "_onMouseOver",
93 for(var name in events){
94 this.connect(this.displayNode, name, events[name]);
96 dijit.setWaiRole(this.displayNode, "button");
97 if(!this.displayNode.getAttribute("tabIndex")){
98 this.displayNode.setAttribute("tabIndex", 0);
101 this.setValue(this.value || this.displayNode.innerHTML);
104 setDisabled: function(/*Boolean*/ disabled){
106 // Set disabled state of widget.
108 this.disabled = disabled;
109 dijit.setWaiState(this.focusNode || this.domNode, "disabled", disabled);
112 _onMouseOver: function(){
113 dojo.addClass(this.displayNode, this.disabled ? "dijitDisabledClickableRegion" : "dijitClickableRegion");
116 _onMouseOut: function(){
117 dojo.removeClass(this.displayNode, this.disabled ? "dijitDisabledClickableRegion" : "dijitClickableRegion");
120 _onClick: function(/*Event*/ e){
121 if(this.disabled){ return; }
122 if(e){ dojo.stopEvent(e); }
125 // Since FF gets upset if you move a node while in an event handler for that node...
126 setTimeout(dojo.hitch(this, "_edit"), 0);
130 // summary: display the editor widget in place of the original (read only) markup
137 this.value.replace(/\s*\r?\n\s*/g,"").replace(/<br\/?>/gi, "\n").replace(/>/g,">").replace(/</g,"<").replace(/&/g,"&"));
139 // Placeholder for edit widget
140 // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
141 // when Calendar dropdown appears, which happens automatically on focus.
142 var placeholder = dojo.doc.createElement("span");
143 dojo.place(placeholder, this.domNode, "before");
145 var ew = this.editWidget = new dijit._InlineEditor({
146 value: dojo.trim(editValue),
147 autoSave: this.autoSave,
148 buttonSave: this.buttonSave,
149 buttonCancel: this.buttonCancel,
150 renderAsHtml: this.renderAsHtml,
152 editorParams: this.editorParams,
153 style: dojo.getComputedStyle(this.displayNode),
154 save: dojo.hitch(this, "save"),
155 cancel: dojo.hitch(this, "cancel"),
159 // to avoid screen jitter, we first create the editor with position:absolute, visibility:hidden,
160 // and then when it's finished rendering, we switch from display mode to editor
161 var ews = ew.domNode.style;
162 this.displayNode.style.display="none";
163 ews.position = "static";
164 ews.visibility = "visible";
166 // Replace the display widget with edit widget, leaving them both displayed for a brief time so that
167 // focus can be shifted without incident. (browser may needs some time to render the editor.)
168 this.domNode = ew.domNode;
169 setTimeout(function(){
174 _showText: function(/*Boolean*/ focus){
175 // summary: revert to display mode, and optionally focus on display node
177 // display the read-only text and then quickly hide the editor (to avoid screen jitter)
178 this.displayNode.style.display="";
179 var ew = this.editWidget;
180 var ews = ew.domNode.style;
181 ews.position="absolute";
182 ews.visibility="hidden";
184 this.domNode = this.displayNode;
187 dijit.focus(this.displayNode);
189 ews.display = "none";
190 // give the browser some time to render the display node and then shift focus to it
191 // and hide the edit widget before garbage collecting the edit widget
192 setTimeout(function(){
196 // messing with the DOM tab order can cause IE to focus the body - so restore
197 dijit.focus(dijit.getFocus());
199 }, 1000); // no hurry - wait for things to quiesce
202 save: function(/*Boolean*/ focus){
204 // Save the contents of the editor and revert to display mode.
206 // Focus on the display mode text
207 this.editing = false;
209 var value = this.editWidget.getValue() + "";
210 if(!this.renderAsHtml){
211 value = value.replace(/&/gm, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, """)
212 .replace(/\n/g, "<br>");
214 this.setValue(value);
216 // tell the world that we have changed
217 this.onChange(value);
219 this._showText(focus);
222 setValue: function(/*String*/ val){
223 // summary: inserts specified HTML value into this node, or an "input needed" character if node is blank
225 this.displayNode.innerHTML = dojo.trim(val) || this.noValueIndicator;
228 getValue: function(){
232 cancel: function(/*Boolean*/ focus){
234 // Revert to display mode, discarding any changes made in the editor
235 this.editing = false;
236 this._showText(focus);
241 "dijit._InlineEditor",
242 [dijit._Widget, dijit._Templated],
245 // internal widget used by InlineEditBox, displayed when in editing mode
246 // to display the editor and maybe save/cancel buttons. Calling code should
247 // connect to save/cancel methods to detect when editing is finished
249 // Has mainly the same parameters as InlineEditBox, plus these values:
252 // Set of CSS attributes of display node, to replicate in editor
255 // Value as an HTML string or plain text string, depending on renderAsHTML flag
257 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\" disabled=\"true\">${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",
258 widgetsInTemplate: true,
260 postMixInProperties: function(){
261 this.inherited('postMixInProperties', arguments);
262 this.messages = dojo.i18n.getLocalization("dijit", "common", this.lang);
263 dojo.forEach(["buttonSave", "buttonCancel"], function(prop){
264 if(!this[prop]){ this[prop] = this.messages[prop]; }
268 postCreate: function(){
269 // Create edit widget in place in the template
270 var cls = dojo.getObject(this.editor);
271 var ew = this.editWidget = new cls(this.editorParams, this.editorPlaceholder);
273 // Copy the style from the source
274 // Don't copy ALL properties though, just the necessary/applicable ones
275 var srcStyle = this.style;
276 dojo.forEach(["fontWeight","fontFamily","fontSize","fontStyle"], function(prop){
277 ew.focusNode.style[prop]=srcStyle[prop];
279 dojo.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], function(prop){
280 this.domNode.style[prop]=srcStyle[prop];
282 if(this.width=="100%"){
284 ew.domNode.style.width = "100%"; // because display: block doesn't work for table widgets
285 this.domNode.style.display="block";
288 ew.domNode.style.width = this.width + (Number(this.width)==this.width ? "px" : "");
291 this.connect(ew, "onChange", "_onChange");
293 // Monitor keypress on the edit widget. Note that edit widgets do a stopEvent() on ESC key (to
294 // prevent Dialog from closing when the user just wants to revert the value in the edit widget),
295 // so this is the only way we can see the key press event.
296 this.connect(ew.focusNode || ew.domNode, "onkeypress", "_onKeyPress");
298 // priorityChange=false will prevent bogus onChange event
299 (this.editWidget.setDisplayedValue||this.editWidget.setValue).call(this.editWidget, this.value, false);
301 this._initialText = this.getValue();
304 this.buttonContainer.style.display="none";
309 this.editWidget.destroy();
310 this.inherited(arguments);
313 getValue: function(){
314 var ew = this.editWidget;
315 return ew.getDisplayedValue ? ew.getDisplayedValue() : ew.getValue();
318 _onKeyPress: function(e){
319 // summary: Callback when keypress in the edit box (see template).
321 // For autoSave widgets, if Esc/Enter, call cancel/save.
322 // For non-autoSave widgets, enable save button if the text value is
323 // different than the original value.
324 if(this._exitInProgress){
328 if(e.altKey || e.ctrlKey){ return; }
329 // If Enter/Esc pressed, treat as save/cancel.
330 if(e.keyCode == dojo.keys.ESCAPE){
332 this._exitInProgress = true;
334 }else if(e.keyCode == dojo.keys.ENTER){
336 this._exitInProgress = true;
338 }else if(e.keyCode == dojo.keys.TAB){
339 this._exitInProgress = true;
340 // allow the TAB to change focus before we mess with the DOM: #6227
341 // Expounding by request:
342 // The current focus is on the edit widget input field.
343 // save() will hide and destroy this widget.
344 // We want the focus to jump from the currently hidden
345 // displayNode, but since it's hidden, it's impossible to
346 // unhide it, focus it, and then have the browser focus
347 // away from it to the next focusable element since each
348 // of these events is asynchronous and the focus-to-next-element
349 // is already queued.
350 // So we allow the browser time to unqueue the move-focus event
351 // before we do all the hide/show stuff.
352 setTimeout(dojo.hitch(this, "save", false), 0);
356 // Delay before calling getValue().
357 // The delay gives the browser a chance to update the Textarea.
360 _this.saveButton.setAttribute("disabled", _this.getValue() == _this._initialText);
367 // Called when focus moves outside the editor
368 this.inherited(arguments);
369 if(this._exitInProgress){
370 // when user clicks the "save" button, focus is shifted back to display text, causing this
371 // function to be called, but in that case don't do anything
375 this._exitInProgress = true;
376 if(this.getValue() == this._initialText){
384 enableSave: function(){
385 // summary: User replacable function returning a Boolean to indicate
386 // if the Save button should be enabled or not - usually due to invalid conditions
387 return this.editWidget.isValid ? this.editWidget.isValid() : true; // Boolean
390 _onChange: function(){
392 // Called when the underlying widget fires an onChange event,
393 // which means that the user has finished entering the value
394 if(this._exitInProgress){
395 // TODO: the onChange event might happen after the return key for an async widget
396 // like FilteringSelect. Shouldn't be deleting the edit widget on end-of-edit
400 this._exitInProgress = true;
403 // in case the keypress event didn't get through (old problem with Textarea that has been fixed
404 // in theory) or if the keypress event comes too quickly and the value inside the Textarea hasn't
406 this.saveButton.setAttribute("disabled", (this.getValue() == this._initialText) || !this.enableSave());
410 enableSave: function(){
411 // summary: User replacable function returning a Boolean to indicate
412 // if the Save button should be enabled or not - usually due to invalid conditions
413 return this.editWidget.isValid ? this.editWidget.isValid() : true;
417 this.editWidget.focus();
418 dijit.selectInputText(this.editWidget.focusNode);