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