1
dojo.provide("dijit.form.Button");
3
dojo.require("dijit.form._FormWidget");
4
dojo.require("dijit._Container");
6
dojo.declare("dijit.form.Button",
7
dijit.form._FormWidget,
10
// Basically the same thing as a normal HTML button, but with special styling.
12
// Buttons can display a label, an icon, or both.
13
// A label should always be specified (through innerHTML) or the label
14
// attribute. It can be hidden via showLabel=false.
16
// | <button dojoType="dijit.form.Button" onClick="...">Hello world</button>
19
// | var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
20
// | dojo.body().appendChild(button1.domNode);
23
// Text to display in button.
24
// If the label is hidden (showLabel=false) then and no title has
25
// been specified, then label is also set as title attribute of icon.
29
// Set this to true to hide the label text and display only the icon.
30
// (If showLabel=false then iconClass must be specified.)
31
// Especially useful for toolbars.
32
// If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
34
// The exception case is for computers in high-contrast mode, where the label
35
// will still be displayed, since the icon doesn't appear.
39
// Class to apply to div in button to make it display an icon
43
// Defines the type of button. "button", "submit", or "reset".
46
baseClass: "dijitButton",
48
templatePath: dojo.moduleUrl("dijit.form", "templates/Button.html"),
50
attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
51
label: { node: "containerNode", type: "innerHTML" },
52
iconClass: { node: "iconNode", type: "class" }
56
_onClick: function(/*Event*/ e){
58
// Internal function to handle click actions
59
if(this.disabled || this.readOnly){
62
this._clicked(); // widget click actions
63
return this.onClick(e); // user click actions
66
_onButtonClick: function(/*Event*/ e){
68
// Handler when the user activates the button portion.
69
// If is activated via a keystroke, stop the event unless is submit or reset.
70
if(e.type!='click' && !(this.type=="submit" || this.type=="reset")){
73
if(this._onClick(e) === false){ // returning nothing is same as true
74
e.preventDefault(); // needed for checkbox
75
}else if(this.type=="submit" && !this.focusNode.form){ // see if a nonform widget needs to be signalled
76
for(var node=this.domNode; node.parentNode/*#5935*/; node=node.parentNode){
77
var widget=dijit.byNode(node);
78
if(widget && typeof widget._onSubmit == "function"){
86
_setValueAttr: function(/*String*/ value){
87
// Verify that value cannot be set for BUTTON elements.
88
var attr = this.attributeMap.value || '';
89
if(this[attr.node||attr||'domNode'].tagName == 'BUTTON'){
90
// On IE, setting value actually overrides innerHTML, so disallow for everyone for consistency
91
if(value != this.value){
92
console.debug('Cannot change the value attribute on a Button widget.');
97
_fillContent: function(/*DomNode*/ source){
98
// Overrides _Templated._fillcContent().
99
// If button label is specified as srcNodeRef.innerHTML rather than
100
// this.params.label, handle it here.
101
if(source && !("label" in this.params)){
102
this.attr('label', source.innerHTML);
106
postCreate: function(){
107
if (this.showLabel == false){
108
dojo.addClass(this.containerNode,"dijitDisplayNone");
110
dojo.setSelectable(this.focusNode, false);
111
this.inherited(arguments);
114
onClick: function(/*Event*/ e){
116
// Callback for when button is clicked.
117
// If type="submit", return true to perform submit, or false to cancel it.
120
return true; // Boolean
123
_clicked: function(/*Event*/ e){
125
// Internal overridable function for when the button is clicked
128
setLabel: function(/*String*/ content){
130
// Deprecated. Use attr('label', ...) instead.
131
dojo.deprecated("dijit.form.Button.setLabel() is deprecated. Use attr('label', ...) instead.", "", "2.0");
132
this.attr("label", content);
134
_setLabelAttr: function(/*String*/ content){
136
// Hook for attr('label', ...) to work.
138
// Set the label (text) of the button; takes an HTML string.
139
this.containerNode.innerHTML = this.label = content;
141
if (this.showLabel == false && !this.params.title){
142
this.titleNode.title = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
148
dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container], {
150
// A button with a drop down
153
// | <button dojoType="dijit.form.DropDownButton" label="Hello world">
154
// | <div dojotype="dijit.Menu">...</div>
158
// | var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
159
// | dojo.body().appendChild(button1);
162
baseClass : "dijitDropDownButton",
164
templatePath: dojo.moduleUrl("dijit.form" , "templates/DropDownButton.html"),
166
_fillContent: function(){
167
// Overrides Button._fillContent().
169
// My inner HTML contains both the button contents and a drop down widget, like
170
// <DropDownButton> <span>push me</span> <Menu> ... </Menu> </DropDownButton>
171
// The first node is assumed to be the button content. The widget is the popup.
173
if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
174
//FIXME: figure out how to filter out the widget and use all remaining nodes as button
175
// content, not just nodes[0]
176
var nodes = dojo.query("*", this.srcNodeRef);
177
dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]);
179
// save pointer to srcNode so we can grab the drop down widget after it's instantiated
180
this.dropDownContainer = this.srcNodeRef;
185
if(this._started){ return; }
187
// the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM,
188
// make it invisible, and store a reference to pass to the popup code.
190
var dropDownNode = dojo.query("[widgetId]", this.dropDownContainer)[0];
191
this.dropDown = dijit.byNode(dropDownNode);
192
delete this.dropDownContainer;
194
dijit.popup.prepare(this.dropDown.domNode);
196
this.inherited(arguments);
199
destroyDescendants: function(){
201
this.dropDown.destroyRecursive();
202
delete this.dropDown;
204
this.inherited(arguments);
207
_onArrowClick: function(/*Event*/ e){
209
// Handler for when the user mouse clicks on menu popup node
210
if(this.disabled || this.readOnly){ return; }
211
this._toggleDropDown();
214
_onDropDownClick: function(/*Event*/ e){
215
// on Firefox 2 on the Mac it is possible to fire onclick
216
// by pressing enter down on a second element and transferring
217
// focus to the DropDownButton;
218
// we want to prevent opening our menu in this situation
219
// and only do so if we have seen a keydown on this button;
220
// e.detail != 0 means that we were fired by mouse
221
var isMacFFlessThan3 = dojo.isFF && dojo.isFF < 3
222
&& navigator.appVersion.indexOf("Macintosh") != -1;
223
if(!isMacFFlessThan3 || e.detail != 0 || this._seenKeydown){
224
this._onArrowClick(e);
226
this._seenKeydown = false;
229
_onDropDownKeydown: function(/*Event*/ e){
230
this._seenKeydown = true;
233
_onDropDownBlur: function(/*Event*/ e){
234
this._seenKeydown = false;
237
_onKey: function(/*Event*/ e){
239
// Handler when the user presses a key on drop down widget
240
if(this.disabled || this.readOnly){ return; }
241
if(e.charOrCode == dojo.keys.DOWN_ARROW){
242
if(!this.dropDown || this.dropDown.domNode.style.visibility=="hidden"){
244
this._toggleDropDown();
251
// Called magically when focus has shifted away from this widget and it's dropdown
252
this._closeDropDown();
253
// don't focus on button. the user has explicitly focused on something else.
254
this.inherited(arguments);
257
_toggleDropDown: function(){
259
// Toggle the drop-down widget; if it is up, close it; if not, open it.
260
if(this.disabled || this.readOnly){ return; }
261
dijit.focus(this.popupStateNode);
262
var dropDown = this.dropDown;
263
if(!dropDown){ return; }
265
// If there's an href, then load that first, so we don't get a flicker
266
if(dropDown.href && !dropDown.isLoaded){
268
var handler = dojo.connect(dropDown, "onLoad", function(){
269
dojo.disconnect(handler);
270
self._openDropDown();
275
this._openDropDown();
278
this._closeDropDown();
282
_openDropDown: function(){
283
var dropDown = this.dropDown;
284
var oldWidth=dropDown.domNode.style.width;
290
around: this.domNode,
292
// TODO: add user-defined positioning option, like in Tooltip.js
293
this.isLeftToRight() ? {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'}
294
: {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'},
295
onExecute: function(){
296
self._closeDropDown(true);
298
onCancel: function(){
299
self._closeDropDown(true);
302
dropDown.domNode.style.width = oldWidth;
303
self.popupStateNode.removeAttribute("popupActive");
304
self._opened = false;
307
if(this.domNode.offsetWidth > dropDown.domNode.offsetWidth){
308
var adjustNode = null;
309
if(!this.isLeftToRight()){
310
adjustNode = dropDown.domNode.parentNode;
311
var oldRight = adjustNode.offsetLeft + adjustNode.offsetWidth;
313
// make menu at least as wide as the button
314
dojo.marginBox(dropDown.domNode, {w: this.domNode.offsetWidth});
316
adjustNode.style.left = oldRight - this.domNode.offsetWidth + "px";
319
this.popupStateNode.setAttribute("popupActive", "true");
324
// TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
327
_closeDropDown: function(/*Boolean*/ focus){
329
dijit.popup.close(this.dropDown);
330
if(focus){ this.focus(); }
331
this._opened = false;
336
dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, {
338
// A combination button and drop-down button.
339
// Users can click one side to "press" the button, or click an arrow
340
// icon to display the drop down.
343
// | <button dojoType="dijit.form.ComboButton" onClick="...">
344
// | <span>Hello world</span>
345
// | <div dojoType="dijit.Menu">...</div>
349
// | var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
350
// | dojo.body().appendChild(button1.domNode);
353
templatePath: dojo.moduleUrl("dijit.form", "templates/ComboButton.html"),
355
attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
357
tabIndex: ["focusNode", "titleNode"]
360
// optionsTitle: String
361
// Text that describes the options menu (accessibility)
364
baseClass: "dijitComboButton",
368
postCreate: function(){
369
this.inherited(arguments);
370
this._focalNodes = [this.titleNode, this.popupStateNode];
371
dojo.forEach(this._focalNodes, dojo.hitch(this, function(node){
373
this.connect(node, "onactivate", this._onNodeFocus);
374
this.connect(node, "ondeactivate", this._onNodeBlur);
376
this.connect(node, "onfocus", this._onNodeFocus);
377
this.connect(node, "onblur", this._onNodeBlur);
382
focusFocalNode: function(node){
384
// Focus the focal node node.
386
// Called by _KeyNavContainer for (when example) this button is in a toolbar.
389
this._focusedNode = node;
393
hasNextFocalNode: function(){
395
// Returns true if this widget has no node currently
396
// focused or if there is a node following the focused one.
397
// False is returned if the last node has focus.
399
// Called by _KeyNavContainer for (when example) this button is in a toolbar.
402
return this._focusedNode !== this.getFocalNodes()[1];
405
focusNext: function(){
407
// Focus the focal node following the current node with focus,
408
// or the first one if no node currently has focus.
410
// Called by _KeyNavContainer for (when example) this button is in a toolbar.
413
this._focusedNode = this.getFocalNodes()[this._focusedNode ? 1 : 0];
414
dijit.focus(this._focusedNode);
417
hasPrevFocalNode: function(){
419
// Returns true if this widget has no node currently
420
// focused or if there is a node before the focused one.
421
// False is returned if the first node has focus.
423
// Called by _KeyNavContainer for (when example) this button is in a toolbar.
426
return this._focusedNode !== this.getFocalNodes()[0];
429
focusPrev: function(){
431
// Focus the focal node before the current node with focus
432
// or the last one if no node currently has focus.
434
// Called by _KeyNavContainer for (when example) this button is in a toolbar.
437
this._focusedNode = this.getFocalNodes()[this._focusedNode ? 0 : 1];
438
dijit.focus(this._focusedNode);
441
getFocalNodes: function(){
443
// Returns an array of focal nodes for this widget.
445
// Called by _KeyNavContainer for (when example) this button is in a toolbar.
448
return this._focalNodes;
451
_onNodeFocus: function(evt){
452
this._focusedNode = evt.currentTarget;
453
var fnc = this._focusedNode == this.focusNode ? "dijitDownArrowButtonFocused" : "dijitButtonContentsFocused";
454
dojo.addClass(this._focusedNode, fnc);
457
_onNodeBlur: function(evt){
458
var fnc = evt.currentTarget == this.focusNode ? "dijitDownArrowButtonFocused" : "dijitButtonContentsFocused";
459
dojo.removeClass(evt.currentTarget, fnc);
463
this.inherited(arguments);
464
this._focusedNode = null;
468
dojo.declare("dijit.form.ToggleButton", dijit.form.Button, {
470
// A button that can be in two states (checked or not).
471
// Can be base class for things like tabs or checkbox or radio buttons
473
baseClass: "dijitToggleButton",
476
// Corresponds to the native HTML <input> element's attribute.
477
// In markup, specified as "checked='checked'" or just "checked".
478
// True if the button is depressed, or the checkbox is checked,
479
// or the radio button is selected, etc.
482
attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap),
483
{checked:"focusNode"}),
485
_clicked: function(/*Event*/ evt){
486
this.attr('checked', !this.checked);
489
_setCheckedAttr: function(/*Boolean*/ value){
490
this.checked = value;
491
dojo.attr(this.focusNode || this.domNode, "checked", value);
492
dijit.setWaiState(this.focusNode || this.domNode, "pressed", value);
493
this._setStateClass();
494
this._handleOnChange(value, true);
497
setChecked: function(/*Boolean*/ checked){
499
// Deprecated. Use attr('checked', true/false) instead.
500
dojo.deprecated("setChecked("+checked+") is deprecated. Use attr('checked',"+checked+") instead.", "", "2.0");
501
this.attr('checked', checked);
506
// Reset the widget's value to what it was at initialization time
508
this._hasBeenBlurred = false;
510
// set checked state to original setting
511
this.attr('checked', this.params.checked || false);