]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dijit/InlineEditBox.js
Comment class stub
[eow] / static / dojo-release-1.1.1 / dijit / InlineEditBox.js
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, "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");
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;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</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                 this.setValue(this.value || this.displayNode.innerHTML);
102         },
103
104         setDisabled: function(/*Boolean*/ disabled){
105                 // summary:
106                 //              Set disabled state of widget.
107
108                 this.disabled = disabled;
109                 dijit.setWaiState(this.focusNode || this.domNode, "disabled", disabled);
110         },
111
112         _onMouseOver: function(){
113                 dojo.addClass(this.displayNode, this.disabled ? "dijitDisabledClickableRegion" : "dijitClickableRegion");
114         },
115
116         _onMouseOut: function(){
117                 dojo.removeClass(this.displayNode, this.disabled ? "dijitDisabledClickableRegion" : "dijitClickableRegion");
118         },
119
120         _onClick: function(/*Event*/ e){
121                 if(this.disabled){ return; }
122                 if(e){ dojo.stopEvent(e); }
123                 this._onMouseOut();
124
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);
127         },
128
129         _edit: function(){
130                 // summary: display the editor widget in place of the original (read only) markup
131
132                 this.editing = true;
133
134                 var editValue = 
135                                 (this.renderAsHtml ?
136                                 this.value :
137                                 this.value.replace(/\s*\r?\n\s*/g,"").replace(/<br\/?>/gi, "\n").replace(/&gt;/g,">").replace(/&lt;/g,"<").replace(/&amp;/g,"&"));
138
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");
144
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,
151                         editor: this.editor,
152                         editorParams: this.editorParams,
153                         style: dojo.getComputedStyle(this.displayNode),
154                         save: dojo.hitch(this, "save"),
155                         cancel: dojo.hitch(this, "cancel"),
156                         width: this.width
157                 }, placeholder);
158
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";
165
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(){
170                         ew.focus();
171                 }, 100);
172         },
173
174         _showText: function(/*Boolean*/ focus){
175                 // summary: revert to display mode, and optionally focus on display node
176
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";
183
184                 this.domNode = this.displayNode;
185
186                 if(focus){
187                         dijit.focus(this.displayNode);
188                 }
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(){
193                         ew.destroy();
194                         delete ew;
195                         if(dojo.isIE){
196                                 // messing with the DOM tab order can cause IE to focus the body - so restore
197                                 dijit.focus(dijit.getFocus());
198                         }
199                 }, 1000); // no hurry - wait for things to quiesce
200         },
201
202         save: function(/*Boolean*/ focus){
203                 // summary:
204                 //              Save the contents of the editor and revert to display mode.
205                 // focus: Boolean
206                 //              Focus on the display mode text
207                 this.editing = false;
208
209                 var value = this.editWidget.getValue() + "";
210                 if(!this.renderAsHtml){
211                         value = value.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;")
212                                 .replace(/\n/g, "<br>");
213                 }
214                 this.setValue(value);
215
216                 // tell the world that we have changed
217                 this.onChange(value);
218
219                 this._showText(focus);  
220         },
221
222         setValue: function(/*String*/ val){
223                 // summary: inserts specified HTML value into this node, or an "input needed" character if node is blank
224                 this.value = val;
225                 this.displayNode.innerHTML = dojo.trim(val) || this.noValueIndicator;
226         },
227
228         getValue: function(){
229                 return this.value;
230         },
231
232         cancel: function(/*Boolean*/ focus){
233                 // summary:
234                 //              Revert to display mode, discarding any changes made in the editor
235                 this.editing = false;
236                 this._showText(focus);
237         }
238 });
239
240 dojo.declare(
241         "dijit._InlineEditor",
242          [dijit._Widget, dijit._Templated],
243 {
244         // summary:
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
248         //
249         //              Has mainly the same parameters as InlineEditBox, plus these values:
250         //
251         // style: Object
252         //              Set of CSS attributes of display node, to replicate in editor
253         //
254         // value: String
255         //              Value as an HTML string or plain text string, depending on renderAsHTML flag
256
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,
259
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]; }
265                 }, this);
266         },
267
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);
272
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];
278                 }, this);
279                 dojo.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], function(prop){
280                         this.domNode.style[prop]=srcStyle[prop];
281                 }, this);
282                 if(this.width=="100%"){
283                         // block mode
284                         ew.domNode.style.width = "100%";        // because display: block doesn't work for table widgets
285                         this.domNode.style.display="block";
286                 }else{
287                         // inline-block mode
288                         ew.domNode.style.width = this.width + (Number(this.width)==this.width ? "px" : "");                     
289                 }
290
291                 this.connect(ew, "onChange", "_onChange");
292
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");
297
298                 // priorityChange=false will prevent bogus onChange event
299                 (this.editWidget.setDisplayedValue||this.editWidget.setValue).call(this.editWidget, this.value, false);
300
301                 this._initialText = this.getValue();
302
303                 if(this.autoSave){
304                         this.buttonContainer.style.display="none";
305                 }
306         },
307
308         destroy: function(){
309                 this.editWidget.destroy();
310                 this.inherited(arguments);
311         },
312
313         getValue: function(){
314                 var ew = this.editWidget;
315                 return ew.getDisplayedValue ? ew.getDisplayedValue() : ew.getValue();
316         },
317
318         _onKeyPress: function(e){
319                 // summary: Callback when keypress in the edit box (see template).
320                 // description:
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){
325                         return;
326                 }
327                 if(this.autoSave){
328                         if(e.altKey || e.ctrlKey){ return; }
329                         // If Enter/Esc pressed, treat as save/cancel.
330                         if(e.keyCode == dojo.keys.ESCAPE){
331                                 dojo.stopEvent(e);
332                                 this._exitInProgress = true;
333                                 this.cancel(true);
334                         }else if(e.keyCode == dojo.keys.ENTER){
335                                 dojo.stopEvent(e);
336                                 this._exitInProgress = true;
337                                 this.save(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);
353                         }
354                 }else{
355                         var _this = this;
356                         // Delay before calling getValue().
357                         // The delay gives the browser a chance to update the Textarea.
358                         setTimeout(
359                                 function(){
360                                         _this.saveButton.setAttribute("disabled", _this.getValue() == _this._initialText);
361                                 }, 100);
362                 }
363         },
364
365         _onBlur: function(){
366                 // summary:
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
372                         return;
373                 }
374                 if(this.autoSave){
375                         this._exitInProgress = true;
376                         if(this.getValue() == this._initialText){
377                                 this.cancel(false);
378                         }else{
379                                 this.save(false);
380                         }
381                 }
382         },
383
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
388         },
389
390         _onChange: function(){
391                 // summary:
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
397                         return;
398                 }
399                 if(this.autoSave){
400                         this._exitInProgress = true;
401                         this.save(true);
402                 }else{
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
405                         // been updated yet)
406                         this.saveButton.setAttribute("disabled", (this.getValue() == this._initialText) || !this.enableSave());
407                 }
408         },
409         
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;
414         },
415
416         focus: function(){
417                 this.editWidget.focus();
418                 dijit.selectInputText(this.editWidget.focusNode);
419         }
420 });
421
422 }