if (!dojo._hasResource["dijit.InlineEditBox"]) { // _hasResource checks added
// by build. Do not use
// _hasResource directly in
// your code.
dojo._hasResource["dijit.InlineEditBox"] = true;
dojo.provide("dijit.InlineEditBox");
dojo.require("dojo.i18n");
dojo.require("dijit._Widget");
dojo.require("dijit._Container");
dojo.require("dijit.form.Button");
dojo.require("dijit.form.TextBox");
dojo.requireLocalization("dijit", "common", null,
"ko,zh,ja,zh-tw,ru,it,hu,fr,pt,ROOT,pl,es,de,cs");
dojo.declare("dijit.InlineEditBox", dijit._Widget, {
// summary: An element with in-line edit capabilitites
//
// description:
// Behavior for an existing node (
,
, , etc.) so that
// when you click it, an editor shows up in place of the original
// text. Optionally, Save and Cancel button are displayed below the edit
// widget.
// When Save is clicked, the text is pulled from the edit
// widget and redisplayed and the edit widget is again hidden.
// By default a plain Textarea widget is used as the editor (or for
// inline values a TextBox), but you can specify an editor such as
// dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
// An edit widget must support the following API to be used:
// String getDisplayedValue() OR String getValue()
// void setDisplayedValue(String) OR void setValue(String)
// void focus()
//
// editing: Boolean
// Is the node currently in edit mode?
editing : false,
// autoSave: Boolean
// Changing the value automatically saves it; don't have to push save
// button
// (and save button isn't even displayed)
autoSave : true,
// buttonSave: String
// Save button label
buttonSave : "",
// buttonCancel: String
// Cancel button label
buttonCancel : "",
// renderAsHtml: Boolean
// Set this to true if the specified Editor's value should be
// interpreted as HTML
// rather than plain text (ie, dijit.Editor)
renderAsHtml : false,
// editor: String
// Class name for Editor widget
editor : "dijit.form.TextBox",
// editorParams: Object
// Set of parameters for editor, like {required: true}
editorParams : {},
onChange : function(value) {
// summary: User should set this handler to be notified of changes
// to value
},
// width: String
// Width of editor. By default it's width=100% (ie, block mode)
width : "100%",
// value: String
// The display value of the widget in read-only mode
value : "",
// noValueIndicator: String
// The text that gets displayed when there is no value (so that the user
// has a place to click to edit)
noValueIndicator : " ✍ ",
postMixInProperties : function() {
this.inherited('postMixInProperties', arguments);
// save pointer to original source node, since Widget nulls-out
// srcNodeRef
this.displayNode = this.srcNodeRef;
// connect handlers to the display node
var events = {
ondijitclick : "_onClick",
onmouseover : "_onMouseOver",
onmouseout : "_onMouseOut",
onfocus : "_onMouseOver",
onblur : "_onMouseOut"
};
for (var name in events) {
this.connect(this.displayNode, name, events[name]);
}
dijit.setWaiRole(this.displayNode, "button");
if (!this.displayNode.getAttribute("tabIndex")) {
this.displayNode.setAttribute("tabIndex", 0);
}
if (!this.value) {
this.value = this.displayNode.innerHTML;
}
this._setDisplayValue(this.value); // if blank, change to icon for
// "input needed"
},
_onMouseOver : function() {
dojo.addClass(this.displayNode, this.disabled
? "dijitDisabledClickableRegion"
: "dijitClickableRegion");
},
_onMouseOut : function() {
dojo.removeClass(this.displayNode, this.disabled
? "dijitDisabledClickableRegion"
: "dijitClickableRegion");
},
_onClick : function(/* Event */e) {
if (this.disabled) {
return;
}
if (e) {
dojo.stopEvent(e);
}
this._onMouseOut();
// Since FF gets upset if you move a node while in an event handler
// for that node...
setTimeout(dojo.hitch(this, "_edit"), 0);
},
_edit : function() {
// summary: display the editor widget in place of the original (read
// only) markup
this.editing = true;
var editValue = (this.renderAsHtml ? this.value : this.value
.replace(/\s*\r?\n\s*/g, "").replace(/ /gi, "\n")
.replace(/>/g, ">").replace(/</g, "<").replace(
/&/g, "&"));
// Placeholder for edit widget
// Put place holder (and eventually editWidget) before the display
// node so that it's positioned correctly
// when Calendar dropdown appears, which happens automatically on
// focus.
var placeholder = document.createElement("span");
dojo.place(placeholder, this.domNode, "before");
var ew = this.editWidget = new dijit._InlineEditor({
value : dojo.trim(editValue),
autoSave : this.autoSave,
buttonSave : this.buttonSave,
buttonCancel : this.buttonCancel,
renderAsHtml : this.renderAsHtml,
editor : this.editor,
editorParams : this.editorParams,
style : dojo.getComputedStyle(this.displayNode),
save : dojo.hitch(this, "save"),
cancel : dojo.hitch(this, "cancel"),
width : this.width
}, placeholder);
// to avoid screen jitter, we first create the editor with
// position:absolute, visibility:hidden,
// and then when it's finished rendering, we switch from display
// mode to editor
var ews = ew.domNode.style;
this.displayNode.style.display = "none";
ews.position = "static";
ews.visibility = "visible";
// Replace the display widget with edit widget, leaving them both
// displayed for a brief time so that
// focus can be shifted without incident. (browser may needs some
// time to render the editor.)
this.domNode = ew.domNode;
setTimeout(function() {
ew.focus();
}, 100);
},
_showText : function(/* Boolean */focus) {
// summary: revert to display mode, and optionally focus on display
// node
// display the read-only text and then quickly hide the editor (to
// avoid screen jitter)
this.displayNode.style.display = "";
var ews = this.editWidget.domNode.style;
ews.position = "absolute";
ews.visibility = "hidden";
this.domNode = this.displayNode;
// give the browser some time to render the display node and then
// shift focus to it
// and hide the edit widget
var _this = this;
setTimeout(function() {
if (focus) {
dijit.focus(_this.displayNode);
}
_this.editWidget.destroy();
delete _this.editWidget;
}, 100);
},
save : function(/* Boolean */focus) {
// summary:
// Save the contents of the editor and revert to display mode.
// focus: Boolean
// Focus on the display mode text
this.editing = false;
this.value = this.editWidget.getValue() + "";
if (this.renderAsHtml) {
this.value = this.value.replace(/&/gm, "&").replace(//gm, ">").replace(/"/gm, """)
.replace("\n", " ");
}
this._setDisplayValue(this.value);
// tell the world that we have changed
this.onChange(this.value);
this._showText(focus);
},
_setDisplayValue : function(/* String */val) {
// summary: inserts specified HTML value into this node, or an
// "input needed" character if node is blank
this.displayNode.innerHTML = val || this.noValueIndicator;
},
cancel : function(/* Boolean */focus) {
// summary:
// Revert to display mode, discarding any changes made in the editor
this.editing = false;
this._showText(focus);
}
});
dojo.declare("dijit._InlineEditor", [dijit._Widget, dijit._Templated], {
// summary:
// internal widget used by InlineEditBox, displayed when in editing mode
// to display the editor and maybe save/cancel buttons. Calling code
// should
// connect to save/cancel methods to detect when editing is finished
//
// Has mainly the same parameters as InlineEditBox, plus these values:
//
// style: Object
// Set of CSS attributes of display node, to replicate in editor
//
// value: String
// Value as an HTML string or plain text string, depending on
// renderAsHTML flag
templateString : "\n",
widgetsInTemplate : true,
postMixInProperties : function() {
this.inherited('postMixInProperties', arguments);
this.messages = dojo.i18n.getLocalization("dijit", "common",
this.lang);
dojo.forEach(["buttonSave", "buttonCancel"], function(prop) {
if (!this[prop]) {
this[prop] = this.messages[prop];
}
}, this);
},
postCreate : function() {
// Create edit widget in place in the template
var cls = dojo.getObject(this.editor);
var ew = this.editWidget = new cls(this.editorParams,
this.editorPlaceholder);
// Copy the style from the source
// Don't copy ALL properties though, just the necessary/applicable
// ones
var srcStyle = this.style;
dojo.forEach(["fontWeight", "fontFamily", "fontSize", "fontStyle"],
function(prop) {
ew.focusNode.style[prop] = srcStyle[prop];
}, this);
dojo.forEach(["marginTop", "marginBottom", "marginLeft",
"marginRight"], function(prop) {
this.domNode.style[prop] = srcStyle[prop];
}, this);
if (this.width == "100%") {
// block mode
ew.domNode.style.width = "100%"; // because display: block
// doesn't work for table
// widgets
this.domNode.style.display = "block";
} else {
// inline-block mode
ew.domNode.style.width = this.width
+ (Number(this.width) == this.width ? "px" : "");
}
this.connect(this.editWidget, "onChange", "_onChange");
// setting the value of the edit widget will cause a possibly
// asynchronous onChange() call.
// we need to ignore it, since we are only interested in when the
// user changes the value.
this._ignoreNextOnChange = true;
(this.editWidget.setDisplayedValue || this.editWidget.setValue)
.call(this.editWidget, this.value);
this._initialText = this.getValue();
if (this.autoSave) {
this.buttonContainer.style.display = "none";
}
},
destroy : function() {
this.editWidget.destroy();
this.inherited(arguments);
},
getValue : function() {
var ew = this.editWidget;
return ew.getDisplayedValue ? ew.getDisplayedValue() : ew
.getValue();
},
_onKeyPress : function(e) {
// summary: Callback when keypress in the edit box (see template).
// description:
// For autoSave widgets, if Esc/Enter, call cancel/save.
// For non-autoSave widgets, enable save button if the text value is
// different than the original value.
if (this._exitInProgress) {
return;
}
if (this.autoSave) {
// If Enter/Esc pressed, treat as save/cancel.
if (e.keyCode == dojo.keys.ESCAPE) {
dojo.stopEvent(e);
this._exitInProgress = true;
this.cancel(true);
} else if (e.keyCode == dojo.keys.ENTER) {
dojo.stopEvent(e);
this._exitInProgress = true;
this.save(true);
}
} else {
var _this = this;
// Delay before calling getValue().
// The delay gives the browser a chance to update the Textarea.
setTimeout(function() {
_this.saveButton
.setDisabled(_this.getValue() == _this._initialText);
}, 100);
}
},
_onBlur : function() {
// summary:
// Called when focus moves outside the editor
if (this._exitInProgress) {
// when user clicks the "save" button, focus is shifted back to
// display text, causing this
// function to be called, but in that case don't do anything
return;
}
if (this.autoSave) {
this._exitInProgress = true;
if (this.getValue() == this._initialText) {
this.cancel(false);
} else {
this.save(false);
}
}
},
enableSave : function() {
// summary: User replacable function returning a Boolean to indicate
// if the Save button should be enabled or not - usually due to
// invalid conditions
return this.editWidget.isValid ? this.editWidget.isValid() : true; // Boolean
},
_onChange : function() {
// summary:
// Called when the underlying widget fires an onChange event,
// which means that the user has finished entering the value
if (this._ignoreNextOnChange) {
delete this._ignoreNextOnChange;
return;
}
if (this._exitInProgress) {
// TODO: the onChange event might happen after the return key
// for an async widget
// like FilteringSelect. Shouldn't be deleting the edit widget
// on end-of-edit
return;
}
if (this.autoSave) {
this._exitInProgress = true;
this.save(true);
} else {
// in case the keypress event didn't get through (old problem
// with Textarea that has been fixed
// in theory) or if the keypress event comes too quickly and the
// value inside the Textarea hasn't
// been updated yet)
this.saveButton
.setDisabled((this.getValue() == this._initialText)
|| !this.enableSave());
}
},
enableSave : function() {
// summary: User replacable function returning a Boolean to indicate
// if the Save button should be enabled or not - usually due to
// invalid conditions
return this.editWidget.isValid ? this.editWidget.isValid() : true;
},
focus : function() {
this.editWidget.focus();
dijit.selectInputText(this.editWidget.focusNode);
}
});
dijit.selectInputText = function(/* DomNode */element) {
// summary: select all the text in an input element
// TODO: use functions in _editor/selection.js?
var _window = dojo.global;
var _document = dojo.doc;
element = dojo.byId(element);
if (_document["selection"] && dojo.body()["createTextRange"]) { // IE
if (element.createTextRange) {
var range = element.createTextRange();
range.moveStart("character", 0);
range.moveEnd("character", element.value.length);
range.select();
}
} else if (_window["getSelection"]) {
var selection = _window.getSelection();
// FIXME: does this work on Safari?
if (element.setSelectionRange) {
element.setSelectionRange(0, element.value.length);
}
}
element.focus();
};
}