New file |
0,0 → 1,417 |
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 (<p>, <div>, <span>, 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: "<span style='font-family: wingdings; text-decoration: underline;'> ✍ </span>", |
|
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(/<br\/?>/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(/"/gm, """) |
.replace("\n", "<br>"); |
} |
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:"<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", |
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(); |
}; |
|
|
} |