[483] | 1 | define([ |
---|
| 2 | "require", |
---|
| 3 | "dojo/_base/array", // array.forEach |
---|
| 4 | "dojo/aspect", |
---|
| 5 | "dojo/_base/declare", // declare |
---|
| 6 | "dojo/dom-attr", // domAttr.set domAttr.get |
---|
| 7 | "dojo/dom-class", // domClass.add domClass.remove domClass.toggle |
---|
| 8 | "dojo/dom-construct", // domConstruct.create domConstruct.destroy |
---|
| 9 | "dojo/dom-style", // domStyle.getComputedStyle domStyle.set domStyle.get |
---|
| 10 | "dojo/i18n", // i18n.getLocalization |
---|
| 11 | "dojo/_base/kernel", // kernel.deprecated |
---|
| 12 | "dojo/keys", // keys.ENTER keys.ESCAPE |
---|
| 13 | "dojo/_base/lang", // lang.getObject |
---|
| 14 | "dojo/on", |
---|
| 15 | "dojo/sniff", // has("ie") |
---|
| 16 | "dojo/when", |
---|
| 17 | "./a11yclick", |
---|
| 18 | "./focus", |
---|
| 19 | "./_Widget", |
---|
| 20 | "./_TemplatedMixin", |
---|
| 21 | "./_WidgetsInTemplateMixin", |
---|
| 22 | "./_Container", |
---|
| 23 | "./form/Button", |
---|
| 24 | "./form/_TextBoxMixin", |
---|
| 25 | "./form/TextBox", |
---|
| 26 | "dojo/text!./templates/InlineEditBox.html", |
---|
| 27 | "dojo/i18n!./nls/common" |
---|
| 28 | ], function(require, array, aspect, declare, domAttr, domClass, domConstruct, domStyle, i18n, kernel, keys, lang, on, has, when, a11yclick, fm, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, Button, _TextBoxMixin, TextBox, template){ |
---|
| 29 | |
---|
| 30 | // module: |
---|
| 31 | // dijit/InlineEditBox |
---|
| 32 | |
---|
| 33 | var InlineEditor = declare("dijit._InlineEditor", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], { |
---|
| 34 | // summary: |
---|
| 35 | // Internal widget used by InlineEditBox, displayed when in editing mode |
---|
| 36 | // to display the editor and maybe save/cancel buttons. Calling code should |
---|
| 37 | // connect to save/cancel methods to detect when editing is finished |
---|
| 38 | // |
---|
| 39 | // Has mainly the same parameters as InlineEditBox, plus these values: |
---|
| 40 | // |
---|
| 41 | // style: Object |
---|
| 42 | // Set of CSS attributes of display node, to replicate in editor |
---|
| 43 | // |
---|
| 44 | // value: String |
---|
| 45 | // Value as an HTML string or plain text string, depending on renderAsHTML flag |
---|
| 46 | |
---|
| 47 | templateString: template, |
---|
| 48 | |
---|
| 49 | contextRequire: require, |
---|
| 50 | |
---|
| 51 | postMixInProperties: function(){ |
---|
| 52 | this.inherited(arguments); |
---|
| 53 | this.messages = i18n.getLocalization("dijit", "common", this.lang); |
---|
| 54 | array.forEach(["buttonSave", "buttonCancel"], function(prop){ |
---|
| 55 | if(!this[prop]){ |
---|
| 56 | this[prop] = this.messages[prop]; |
---|
| 57 | } |
---|
| 58 | }, this); |
---|
| 59 | }, |
---|
| 60 | |
---|
| 61 | buildRendering: function(){ |
---|
| 62 | this.inherited(arguments); |
---|
| 63 | |
---|
| 64 | // Create edit widget in place in the template |
---|
| 65 | // TODO: remove getObject() for 2.0 |
---|
| 66 | var Cls = typeof this.editor == "string" ? (lang.getObject(this.editor) || require(this.editor)) : this.editor; |
---|
| 67 | |
---|
| 68 | // Copy the style from the source |
---|
| 69 | // Don't copy ALL properties though, just the necessary/applicable ones. |
---|
| 70 | // wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize |
---|
| 71 | // is a relative value like 200%, rather than an absolute value like 24px, and |
---|
| 72 | // the 200% can refer *either* to a setting on the node or it's ancestor (see #11175) |
---|
| 73 | var srcStyle = this.sourceStyle, |
---|
| 74 | editStyle = "line-height:" + srcStyle.lineHeight + ";", |
---|
| 75 | destStyle = domStyle.getComputedStyle(this.domNode); |
---|
| 76 | array.forEach(["Weight", "Family", "Size", "Style"], function(prop){ |
---|
| 77 | var textStyle = srcStyle["font" + prop], |
---|
| 78 | wrapperStyle = destStyle["font" + prop]; |
---|
| 79 | if(wrapperStyle != textStyle){ |
---|
| 80 | editStyle += "font-" + prop + ":" + srcStyle["font" + prop] + ";"; |
---|
| 81 | } |
---|
| 82 | }, this); |
---|
| 83 | array.forEach(["marginTop", "marginBottom", "marginLeft", "marginRight", "position", "left", "top", "right", "bottom", "float", "clear", "display"], function(prop){ |
---|
| 84 | this.domNode.style[prop] = srcStyle[prop]; |
---|
| 85 | }, this); |
---|
| 86 | var width = this.inlineEditBox.width; |
---|
| 87 | if(width == "100%"){ |
---|
| 88 | // block mode |
---|
| 89 | editStyle += "width:100%;"; |
---|
| 90 | this.domNode.style.display = "block"; |
---|
| 91 | }else{ |
---|
| 92 | // inline-block mode |
---|
| 93 | editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";"; |
---|
| 94 | } |
---|
| 95 | var editorParams = lang.delegate(this.inlineEditBox.editorParams, { |
---|
| 96 | style: editStyle, |
---|
| 97 | dir: this.dir, |
---|
| 98 | lang: this.lang, |
---|
| 99 | textDir: this.textDir |
---|
| 100 | }); |
---|
| 101 | // set the value in onLoadDeferred instead so the widget has time to finish initializing |
---|
| 102 | //editorParams[("displayedValue" in Cls.prototype || "_setDisplayedValueAttr" in Cls.prototype) ? "displayedValue" : "value"] = this.value; |
---|
| 103 | this.editWidget = new Cls(editorParams, this.editorPlaceholder); |
---|
| 104 | |
---|
| 105 | if(this.inlineEditBox.autoSave){ |
---|
| 106 | // Remove the save/cancel buttons since saving is done by simply tabbing away or |
---|
| 107 | // selecting a value from the drop down list |
---|
| 108 | domConstruct.destroy(this.buttonContainer); |
---|
| 109 | } |
---|
| 110 | }, |
---|
| 111 | |
---|
| 112 | postCreate: function(){ |
---|
| 113 | this.inherited(arguments); |
---|
| 114 | |
---|
| 115 | var ew = this.editWidget; |
---|
| 116 | |
---|
| 117 | if(this.inlineEditBox.autoSave){ |
---|
| 118 | this.own( |
---|
| 119 | // Selecting a value from a drop down list causes an onChange event and then we save |
---|
| 120 | aspect.after(ew, "onChange", lang.hitch(this, "_onChange"), true), |
---|
| 121 | |
---|
| 122 | // ESC and TAB should cancel and save. |
---|
| 123 | on(ew, "keydown", lang.hitch(this, "_onKeyDown")) |
---|
| 124 | ); |
---|
| 125 | }else{ |
---|
| 126 | // If possible, enable/disable save button based on whether the user has changed the value |
---|
| 127 | if("intermediateChanges" in ew){ |
---|
| 128 | ew.set("intermediateChanges", true); |
---|
| 129 | this.own(aspect.after(ew, "onChange", lang.hitch(this, "_onIntermediateChange"), true)); |
---|
| 130 | this.saveButton.set("disabled", true); |
---|
| 131 | } |
---|
| 132 | } |
---|
| 133 | }, |
---|
| 134 | |
---|
| 135 | startup: function(){ |
---|
| 136 | this.editWidget.startup(); |
---|
| 137 | this.inherited(arguments); |
---|
| 138 | }, |
---|
| 139 | |
---|
| 140 | _onIntermediateChange: function(/*===== val =====*/){ |
---|
| 141 | // summary: |
---|
| 142 | // Called for editor widgets that support the intermediateChanges=true flag as a way |
---|
| 143 | // to detect when to enable/disabled the save button |
---|
| 144 | this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave()); |
---|
| 145 | }, |
---|
| 146 | |
---|
| 147 | destroy: function(){ |
---|
| 148 | this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM |
---|
| 149 | this.inherited(arguments); |
---|
| 150 | }, |
---|
| 151 | |
---|
| 152 | getValue: function(){ |
---|
| 153 | // summary: |
---|
| 154 | // Return the [display] value of the edit widget |
---|
| 155 | var ew = this.editWidget; |
---|
| 156 | return String(ew.get(("displayedValue" in ew || "_getDisplayedValueAttr" in ew) ? "displayedValue" : "value")); |
---|
| 157 | }, |
---|
| 158 | |
---|
| 159 | _onKeyDown: function(e){ |
---|
| 160 | // summary: |
---|
| 161 | // Handler for keydown in the edit box in autoSave mode. |
---|
| 162 | // description: |
---|
| 163 | // For autoSave widgets, if Esc/Enter, call cancel/save. |
---|
| 164 | // tags: |
---|
| 165 | // private |
---|
| 166 | |
---|
| 167 | if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){ |
---|
| 168 | if(e.altKey || e.ctrlKey){ |
---|
| 169 | return; |
---|
| 170 | } |
---|
| 171 | // If Enter/Esc pressed, treat as save/cancel. |
---|
| 172 | if(e.keyCode == keys.ESCAPE){ |
---|
| 173 | e.stopPropagation(); |
---|
| 174 | e.preventDefault(); |
---|
| 175 | this.cancel(true); // sets editing=false which short-circuits _onBlur processing |
---|
| 176 | }else if(e.keyCode == keys.ENTER && e.target.tagName == "INPUT"){ |
---|
| 177 | e.stopPropagation(); |
---|
| 178 | e.preventDefault(); |
---|
| 179 | this._onChange(); // fire _onBlur and then save |
---|
| 180 | } |
---|
| 181 | |
---|
| 182 | // _onBlur will handle TAB automatically by allowing |
---|
| 183 | // the TAB to change focus before we mess with the DOM: #6227 |
---|
| 184 | // Expounding by request: |
---|
| 185 | // The current focus is on the edit widget input field. |
---|
| 186 | // save() will hide and destroy this widget. |
---|
| 187 | // We want the focus to jump from the currently hidden |
---|
| 188 | // displayNode, but since it's hidden, it's impossible to |
---|
| 189 | // unhide it, focus it, and then have the browser focus |
---|
| 190 | // away from it to the next focusable element since each |
---|
| 191 | // of these events is asynchronous and the focus-to-next-element |
---|
| 192 | // is already queued. |
---|
| 193 | // So we allow the browser time to unqueue the move-focus event |
---|
| 194 | // before we do all the hide/show stuff. |
---|
| 195 | } |
---|
| 196 | }, |
---|
| 197 | |
---|
| 198 | _onBlur: function(){ |
---|
| 199 | // summary: |
---|
| 200 | // Called when focus moves outside the editor |
---|
| 201 | // tags: |
---|
| 202 | // private |
---|
| 203 | |
---|
| 204 | this.inherited(arguments); |
---|
| 205 | if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){ |
---|
| 206 | if(this.getValue() == this._resetValue){ |
---|
| 207 | this.cancel(false); |
---|
| 208 | }else if(this.enableSave()){ |
---|
| 209 | this.save(false); |
---|
| 210 | } |
---|
| 211 | } |
---|
| 212 | }, |
---|
| 213 | |
---|
| 214 | _onChange: function(){ |
---|
| 215 | // summary: |
---|
| 216 | // Called when the underlying widget fires an onChange event, |
---|
| 217 | // such as when the user selects a value from the drop down list of a ComboBox, |
---|
| 218 | // which means that the user has finished entering the value and we should save. |
---|
| 219 | // tags: |
---|
| 220 | // private |
---|
| 221 | |
---|
| 222 | if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){ |
---|
| 223 | fm.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value |
---|
| 224 | } |
---|
| 225 | }, |
---|
| 226 | |
---|
| 227 | enableSave: function(){ |
---|
| 228 | // summary: |
---|
| 229 | // User overridable function returning a Boolean to indicate |
---|
| 230 | // if the Save button should be enabled or not - usually due to invalid conditions |
---|
| 231 | // tags: |
---|
| 232 | // extension |
---|
| 233 | return this.editWidget.isValid ? this.editWidget.isValid() : true; |
---|
| 234 | }, |
---|
| 235 | |
---|
| 236 | focus: function(){ |
---|
| 237 | // summary: |
---|
| 238 | // Focus the edit widget. |
---|
| 239 | // tags: |
---|
| 240 | // protected |
---|
| 241 | |
---|
| 242 | this.editWidget.focus(); |
---|
| 243 | |
---|
| 244 | if(this.editWidget.focusNode){ |
---|
| 245 | // IE can take 30ms to report the focus event, but focus manager needs to know before a 0ms timeout. |
---|
| 246 | fm._onFocusNode(this.editWidget.focusNode); |
---|
| 247 | |
---|
| 248 | if(this.editWidget.focusNode.tagName == "INPUT"){ |
---|
| 249 | this.defer(function(){ |
---|
| 250 | _TextBoxMixin.selectInputText(this.editWidget.focusNode); |
---|
| 251 | }); |
---|
| 252 | } |
---|
| 253 | } |
---|
| 254 | } |
---|
| 255 | }); |
---|
| 256 | |
---|
| 257 | |
---|
| 258 | var InlineEditBox = declare("dijit.InlineEditBox" + (has("dojo-bidi") ? "_NoBidi" : ""), _Widget, { |
---|
| 259 | // summary: |
---|
| 260 | // An element with in-line edit capabilities |
---|
| 261 | // |
---|
| 262 | // description: |
---|
| 263 | // Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that |
---|
| 264 | // when you click it, an editor shows up in place of the original |
---|
| 265 | // text. Optionally, Save and Cancel button are displayed below the edit widget. |
---|
| 266 | // When Save is clicked, the text is pulled from the edit |
---|
| 267 | // widget and redisplayed and the edit widget is again hidden. |
---|
| 268 | // By default a plain Textarea widget is used as the editor (or for |
---|
| 269 | // inline values a TextBox), but you can specify an editor such as |
---|
| 270 | // dijit.Editor (for editing HTML) or a Slider (for adjusting a number). |
---|
| 271 | // An edit widget must support the following API to be used: |
---|
| 272 | // |
---|
| 273 | // - displayedValue or value as initialization parameter, |
---|
| 274 | // and available through set('displayedValue') / set('value') |
---|
| 275 | // - void focus() |
---|
| 276 | // - DOM-node focusNode = node containing editable text |
---|
| 277 | |
---|
| 278 | // editing: [readonly] Boolean |
---|
| 279 | // Is the node currently in edit mode? |
---|
| 280 | editing: false, |
---|
| 281 | |
---|
| 282 | // autoSave: Boolean |
---|
| 283 | // Changing the value automatically saves it; don't have to push save button |
---|
| 284 | // (and save button isn't even displayed) |
---|
| 285 | autoSave: true, |
---|
| 286 | |
---|
| 287 | // buttonSave: String |
---|
| 288 | // Save button label |
---|
| 289 | buttonSave: "", |
---|
| 290 | |
---|
| 291 | // buttonCancel: String |
---|
| 292 | // Cancel button label |
---|
| 293 | buttonCancel: "", |
---|
| 294 | |
---|
| 295 | // renderAsHtml: Boolean |
---|
| 296 | // Set this to true if the specified Editor's value should be interpreted as HTML |
---|
| 297 | // rather than plain text (ex: `dijit.Editor`) |
---|
| 298 | renderAsHtml: false, |
---|
| 299 | |
---|
| 300 | // editor: String|Function |
---|
| 301 | // MID (ex: "dijit/form/TextBox") or constructor for editor widget |
---|
| 302 | editor: TextBox, |
---|
| 303 | |
---|
| 304 | // editorWrapper: String|Function |
---|
| 305 | // Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel |
---|
| 306 | // buttons. |
---|
| 307 | editorWrapper: InlineEditor, |
---|
| 308 | |
---|
| 309 | // editorParams: Object |
---|
| 310 | // Set of parameters for editor, like {required: true} |
---|
| 311 | editorParams: {}, |
---|
| 312 | |
---|
| 313 | // disabled: Boolean |
---|
| 314 | // If true, clicking the InlineEditBox to edit it will have no effect. |
---|
| 315 | disabled: false, |
---|
| 316 | |
---|
| 317 | onChange: function(/*===== value =====*/){ |
---|
| 318 | // summary: |
---|
| 319 | // Set this handler to be notified of changes to value. |
---|
| 320 | // tags: |
---|
| 321 | // callback |
---|
| 322 | }, |
---|
| 323 | |
---|
| 324 | onCancel: function(){ |
---|
| 325 | // summary: |
---|
| 326 | // Set this handler to be notified when editing is cancelled. |
---|
| 327 | // tags: |
---|
| 328 | // callback |
---|
| 329 | }, |
---|
| 330 | |
---|
| 331 | // width: String |
---|
| 332 | // Width of editor. By default it's width=100% (ie, block mode). |
---|
| 333 | width: "100%", |
---|
| 334 | |
---|
| 335 | // value: String |
---|
| 336 | // The display value of the widget in read-only mode |
---|
| 337 | value: "", |
---|
| 338 | |
---|
| 339 | // noValueIndicator: [const] String |
---|
| 340 | // The text that gets displayed when there is no value (so that the user has a place to click to edit) |
---|
| 341 | noValueIndicator: has("ie") <= 6 ? // font-family needed on IE6 but it messes up IE8 |
---|
| 342 | "<span style='font-family: wingdings; text-decoration: underline;'>    ✍    </span>" : |
---|
| 343 | "<span style='text-decoration: underline;'>    ✍    </span>", //   == |
---|
| 344 | |
---|
| 345 | constructor: function(/*===== params, srcNodeRef =====*/){ |
---|
| 346 | // summary: |
---|
| 347 | // Create the widget. |
---|
| 348 | // params: Object|null |
---|
| 349 | // Hash of initialization parameters for widget, including scalar values (like title, duration etc.) |
---|
| 350 | // and functions, typically callbacks like onClick. |
---|
| 351 | // The hash can contain any of the widget's properties, excluding read-only properties. |
---|
| 352 | // srcNodeRef: DOMNode|String? |
---|
| 353 | // If a srcNodeRef (DOM node) is specified: |
---|
| 354 | // |
---|
| 355 | // - use srcNodeRef.innerHTML as my value |
---|
| 356 | // - replace srcNodeRef with my generated DOM tree |
---|
| 357 | |
---|
| 358 | this.editorParams = {}; |
---|
| 359 | }, |
---|
| 360 | |
---|
| 361 | postMixInProperties: function(){ |
---|
| 362 | this.inherited(arguments); |
---|
| 363 | |
---|
| 364 | // save pointer to original source node, since Widget nulls-out srcNodeRef |
---|
| 365 | this.displayNode = this.srcNodeRef; |
---|
| 366 | |
---|
| 367 | // connect handlers to the display node |
---|
| 368 | this.own( |
---|
| 369 | on(this.displayNode, a11yclick, lang.hitch(this, "_onClick")), |
---|
| 370 | on(this.displayNode, "mouseover, focus", lang.hitch(this, "_onMouseOver")), |
---|
| 371 | on(this.displayNode, "mouseout, blur", lang.hitch(this, "_onMouseOut")) |
---|
| 372 | ); |
---|
| 373 | |
---|
| 374 | this.displayNode.setAttribute("role", "button"); |
---|
| 375 | if(!this.displayNode.getAttribute("tabIndex")){ |
---|
| 376 | this.displayNode.setAttribute("tabIndex", 0); |
---|
| 377 | } |
---|
| 378 | |
---|
| 379 | if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){ |
---|
| 380 | this.value = lang.trim(this.renderAsHtml ? this.displayNode.innerHTML : |
---|
| 381 | (this.displayNode.innerText || this.displayNode.textContent || "")); |
---|
| 382 | } |
---|
| 383 | if(!this.value){ |
---|
| 384 | this.displayNode.innerHTML = this.noValueIndicator; |
---|
| 385 | } |
---|
| 386 | |
---|
| 387 | domClass.add(this.displayNode, 'dijitInlineEditBoxDisplayMode'); |
---|
| 388 | }, |
---|
| 389 | |
---|
| 390 | setDisabled: function(/*Boolean*/ disabled){ |
---|
| 391 | // summary: |
---|
| 392 | // Deprecated. Use set('disabled', ...) instead. |
---|
| 393 | // tags: |
---|
| 394 | // deprecated |
---|
| 395 | kernel.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0"); |
---|
| 396 | this.set('disabled', disabled); |
---|
| 397 | }, |
---|
| 398 | |
---|
| 399 | _setDisabledAttr: function(/*Boolean*/ disabled){ |
---|
| 400 | // summary: |
---|
| 401 | // Hook to make set("disabled", ...) work. |
---|
| 402 | // Set disabled state of widget. |
---|
| 403 | this.domNode.setAttribute("aria-disabled", disabled ? "true" : "false"); |
---|
| 404 | if(disabled){ |
---|
| 405 | this.displayNode.removeAttribute("tabIndex"); |
---|
| 406 | }else{ |
---|
| 407 | this.displayNode.setAttribute("tabIndex", 0); |
---|
| 408 | } |
---|
| 409 | domClass.toggle(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled); |
---|
| 410 | this._set("disabled", disabled); |
---|
| 411 | }, |
---|
| 412 | |
---|
| 413 | _onMouseOver: function(){ |
---|
| 414 | // summary: |
---|
| 415 | // Handler for onmouseover and onfocus event. |
---|
| 416 | // tags: |
---|
| 417 | // private |
---|
| 418 | if(!this.disabled){ |
---|
| 419 | domClass.add(this.displayNode, "dijitInlineEditBoxDisplayModeHover"); |
---|
| 420 | } |
---|
| 421 | }, |
---|
| 422 | |
---|
| 423 | _onMouseOut: function(){ |
---|
| 424 | // summary: |
---|
| 425 | // Handler for onmouseout and onblur event. |
---|
| 426 | // tags: |
---|
| 427 | // private |
---|
| 428 | domClass.remove(this.displayNode, "dijitInlineEditBoxDisplayModeHover"); |
---|
| 429 | }, |
---|
| 430 | |
---|
| 431 | _onClick: function(/*Event*/ e){ |
---|
| 432 | // summary: |
---|
| 433 | // Handler for onclick event. |
---|
| 434 | // tags: |
---|
| 435 | // private |
---|
| 436 | if(this.disabled){ |
---|
| 437 | return; |
---|
| 438 | } |
---|
| 439 | if(e){ |
---|
| 440 | e.stopPropagation(); |
---|
| 441 | e.preventDefault(); |
---|
| 442 | } |
---|
| 443 | this._onMouseOut(); |
---|
| 444 | |
---|
| 445 | // Since FF gets upset if you move a node while in an event handler for that node... |
---|
| 446 | this.defer("edit"); |
---|
| 447 | }, |
---|
| 448 | |
---|
| 449 | edit: function(){ |
---|
| 450 | // summary: |
---|
| 451 | // Display the editor widget in place of the original (read only) markup. |
---|
| 452 | // tags: |
---|
| 453 | // private |
---|
| 454 | |
---|
| 455 | if(this.disabled || this.editing){ |
---|
| 456 | return; |
---|
| 457 | } |
---|
| 458 | this._set('editing', true); |
---|
| 459 | |
---|
| 460 | // save some display node values that can be restored later |
---|
| 461 | this._savedTabIndex = domAttr.get(this.displayNode, "tabIndex") || "0"; |
---|
| 462 | |
---|
| 463 | if(!this.wrapperWidget){ |
---|
| 464 | // Placeholder for edit widget |
---|
| 465 | // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly |
---|
| 466 | // when Calendar dropdown appears, which happens automatically on focus. |
---|
| 467 | var placeholder = domConstruct.create("span", null, this.domNode, "before"); |
---|
| 468 | |
---|
| 469 | // Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons) |
---|
| 470 | var Ewc = typeof this.editorWrapper == "string" ? lang.getObject(this.editorWrapper) : this.editorWrapper; |
---|
| 471 | this.wrapperWidget = new Ewc({ |
---|
| 472 | value: this.value, |
---|
| 473 | buttonSave: this.buttonSave, |
---|
| 474 | buttonCancel: this.buttonCancel, |
---|
| 475 | dir: this.dir, |
---|
| 476 | lang: this.lang, |
---|
| 477 | tabIndex: this._savedTabIndex, |
---|
| 478 | editor: this.editor, |
---|
| 479 | inlineEditBox: this, |
---|
| 480 | sourceStyle: domStyle.getComputedStyle(this.displayNode), |
---|
| 481 | save: lang.hitch(this, "save"), |
---|
| 482 | cancel: lang.hitch(this, "cancel"), |
---|
| 483 | textDir: this.textDir |
---|
| 484 | }, placeholder); |
---|
| 485 | if(!this.wrapperWidget._started){ |
---|
| 486 | this.wrapperWidget.startup(); |
---|
| 487 | } |
---|
| 488 | if(!this._started){ |
---|
| 489 | this.startup(); |
---|
| 490 | } |
---|
| 491 | } |
---|
| 492 | var ww = this.wrapperWidget; |
---|
| 493 | |
---|
| 494 | // to avoid screen jitter, we first create the editor with position: absolute, visibility: hidden, |
---|
| 495 | // and then when it's finished rendering, we switch from display mode to editor |
---|
| 496 | // position: absolute releases screen space allocated to the display node |
---|
| 497 | // opacity:0 is the same as visibility: hidden but is still focusable |
---|
| 498 | // visibility: hidden removes focus outline |
---|
| 499 | |
---|
| 500 | domClass.add(this.displayNode, "dijitOffScreen"); |
---|
| 501 | domClass.remove(ww.domNode, "dijitOffScreen"); |
---|
| 502 | domStyle.set(ww.domNode, { visibility: "visible" }); |
---|
| 503 | domAttr.set(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode |
---|
| 504 | |
---|
| 505 | // After edit widget has finished initializing (in particular need to wait for dijit.Editor), |
---|
| 506 | // or immediately if there is no onLoadDeferred Deferred, |
---|
| 507 | // replace the display widget with edit widget, leaving them both displayed for a brief time so that |
---|
| 508 | // focus can be shifted without incident. |
---|
| 509 | var ew = ww.editWidget; |
---|
| 510 | var self = this; |
---|
| 511 | when(ew.onLoadDeferred, lang.hitch(ww, function(){ |
---|
| 512 | // set value again in case the edit widget's value is just now valid |
---|
| 513 | ew.set(("displayedValue" in ew || "_setDisplayedValueAttr" in ew) ? "displayedValue" : "value", self.value); |
---|
| 514 | this.defer(function(){ // defer needed so that the change of focus doesn't happen on mousedown which also sets focus |
---|
| 515 | // the saveButton should start out disabled in most cases but the above set could have fired onChange |
---|
| 516 | ww.saveButton.set("disabled", "intermediateChanges" in ew); |
---|
| 517 | this.focus(); // both nodes are showing, so we can switch focus safely |
---|
| 518 | this._resetValue = this.getValue(); |
---|
| 519 | }); |
---|
| 520 | })); |
---|
| 521 | }, |
---|
| 522 | |
---|
| 523 | _onBlur: function(){ |
---|
| 524 | // summary: |
---|
| 525 | // Called when focus moves outside the InlineEditBox. |
---|
| 526 | // Performs garbage collection. |
---|
| 527 | // tags: |
---|
| 528 | // private |
---|
| 529 | |
---|
| 530 | this.inherited(arguments); |
---|
| 531 | if(!this.editing){ |
---|
| 532 | /* causes IE focus problems, see TooltipDialog_a11y.html... |
---|
| 533 | this.defer(function(){ |
---|
| 534 | if(this.wrapperWidget){ |
---|
| 535 | this.wrapperWidget.destroy(); |
---|
| 536 | delete this.wrapperWidget; |
---|
| 537 | } |
---|
| 538 | }); |
---|
| 539 | */ |
---|
| 540 | } |
---|
| 541 | }, |
---|
| 542 | |
---|
| 543 | destroy: function(){ |
---|
| 544 | if(this.wrapperWidget && !this.wrapperWidget._destroyed){ |
---|
| 545 | this.wrapperWidget.destroy(); |
---|
| 546 | delete this.wrapperWidget; |
---|
| 547 | } |
---|
| 548 | this.inherited(arguments); |
---|
| 549 | }, |
---|
| 550 | |
---|
| 551 | _showText: function(/*Boolean*/ focus){ |
---|
| 552 | // summary: |
---|
| 553 | // Revert to display mode, and optionally focus on display node |
---|
| 554 | // tags: |
---|
| 555 | // private |
---|
| 556 | |
---|
| 557 | var ww = this.wrapperWidget; |
---|
| 558 | domStyle.set(ww.domNode, { visibility: "hidden" }); // hide the editor from mouse/keyboard events |
---|
| 559 | domClass.add(ww.domNode, "dijitOffScreen"); |
---|
| 560 | domClass.remove(this.displayNode, "dijitOffScreen"); |
---|
| 561 | domAttr.set(this.displayNode, "tabIndex", this._savedTabIndex); |
---|
| 562 | if(focus){ |
---|
| 563 | fm.focus(this.displayNode); |
---|
| 564 | } |
---|
| 565 | }, |
---|
| 566 | |
---|
| 567 | save: function(/*Boolean*/ focus){ |
---|
| 568 | // summary: |
---|
| 569 | // Save the contents of the editor and revert to display mode. |
---|
| 570 | // focus: Boolean |
---|
| 571 | // Focus on the display mode text |
---|
| 572 | // tags: |
---|
| 573 | // private |
---|
| 574 | |
---|
| 575 | if(this.disabled || !this.editing){ |
---|
| 576 | return; |
---|
| 577 | } |
---|
| 578 | this._set('editing', false); |
---|
| 579 | |
---|
| 580 | var ww = this.wrapperWidget; |
---|
| 581 | var value = ww.getValue(); |
---|
| 582 | this.set('value', value); // display changed, formatted value |
---|
| 583 | |
---|
| 584 | this._showText(focus); // set focus as needed |
---|
| 585 | }, |
---|
| 586 | |
---|
| 587 | setValue: function(/*String*/ val){ |
---|
| 588 | // summary: |
---|
| 589 | // Deprecated. Use set('value', ...) instead. |
---|
| 590 | // tags: |
---|
| 591 | // deprecated |
---|
| 592 | kernel.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0"); |
---|
| 593 | return this.set("value", val); |
---|
| 594 | }, |
---|
| 595 | |
---|
| 596 | _setValueAttr: function(/*String*/ val){ |
---|
| 597 | // summary: |
---|
| 598 | // Hook to make set("value", ...) work. |
---|
| 599 | // Inserts specified HTML value into this node, or an "input needed" character if node is blank. |
---|
| 600 | |
---|
| 601 | val = lang.trim(val); |
---|
| 602 | var renderVal = this.renderAsHtml ? val : val.replace(/&/gm, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, """).replace(/\n/g, "<br>"); |
---|
| 603 | this.displayNode.innerHTML = renderVal || this.noValueIndicator; |
---|
| 604 | this._set("value", val); |
---|
| 605 | |
---|
| 606 | if(this._started){ |
---|
| 607 | // tell the world that we have changed |
---|
| 608 | this.defer(function(){ |
---|
| 609 | this.onChange(val); |
---|
| 610 | }); // defer prevents browser freeze for long-running event handlers |
---|
| 611 | } |
---|
| 612 | }, |
---|
| 613 | |
---|
| 614 | getValue: function(){ |
---|
| 615 | // summary: |
---|
| 616 | // Deprecated. Use get('value') instead. |
---|
| 617 | // tags: |
---|
| 618 | // deprecated |
---|
| 619 | kernel.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.", "", "2.0"); |
---|
| 620 | return this.get("value"); |
---|
| 621 | }, |
---|
| 622 | |
---|
| 623 | cancel: function(/*Boolean*/ focus){ |
---|
| 624 | // summary: |
---|
| 625 | // Revert to display mode, discarding any changes made in the editor |
---|
| 626 | // tags: |
---|
| 627 | // private |
---|
| 628 | |
---|
| 629 | if(this.disabled || !this.editing){ |
---|
| 630 | return; |
---|
| 631 | } |
---|
| 632 | this._set('editing', false); |
---|
| 633 | |
---|
| 634 | // tell the world that we have no changes |
---|
| 635 | this.defer("onCancel"); // defer prevents browser freeze for long-running event handlers |
---|
| 636 | |
---|
| 637 | this._showText(focus); |
---|
| 638 | } |
---|
| 639 | }); |
---|
| 640 | |
---|
| 641 | if(has("dojo-bidi")){ |
---|
| 642 | InlineEditBox = declare("dijit.InlineEditBox", InlineEditBox, { |
---|
| 643 | _setValueAttr: function(){ |
---|
| 644 | this.inherited(arguments); |
---|
| 645 | this.applyTextDir(this.displayNode); |
---|
| 646 | } |
---|
| 647 | }); |
---|
| 648 | } |
---|
| 649 | |
---|
| 650 | InlineEditBox._InlineEditor = InlineEditor; // for monkey patching |
---|
| 651 | |
---|
| 652 | return InlineEditBox; |
---|
| 653 | }); |
---|