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(); }; }