Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
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;'>&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
		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(/&gt;/g,">").replace(/&lt;/g,"<").replace(/&amp;/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, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;")
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
}