Changeset 8248

Show
Ignore:
Timestamp:
04/23/07 12:13:03 (19 months ago)
Author:
becky
Message:

from david bolter
fixes #2420

Location:
dijit/trunk
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • dijit/trunk/form/Checkbox.js

    r7824 r8248  
    1414        [dijit.base.FormElement, dijit.base.TemplatedWidget], 
    1515        { 
    16                 // summary 
    17                 //      Same as a native HTML checkbox, but with fancy styling 
     16                // summary: 
     17                //              Same as an HTML checkbox, but with fancy styling. 
     18 
     19                // Implementation details 
     20                // 
     21                // pattern: MVC 
     22                //   Control: User interacts with real html inputs 
     23                //     Event listeners are added for input node events 
     24                //     These handlers make sure to update the view based on input state 
     25                //   View: The view is basically the the dijit (tundra) sprint image. 
     26                //   Model: The dijit checked state is synched with the input node. 
     27                // 
     28                // There are two modes: 
     29                //   1. Image not used or failed to load 
     30                //   2. Image loaded and used. 
     31                // In case 1, the regular html inputs are shown and used by the user. 
     32                // In case 2, the regular html inputs are invisible but still used by 
     33                // the user. They are turned invisible and overlay the dijit image. 
     34                // 
     35                // Layout 
     36                //   Styling is controlled in 3 places: tundra, template, and  
     37                // programmatically in Checkbox.js. The latter is required  
     38                // because of two modes of dijit checkbox: image loaded, vs  
     39                // image not loaded. Also for accessibility it is important  
     40                // that dijit work with images off (a browser preference). 
    1841 
    1942                templatePath: dojo.uri.moduleUri("dijit.form", "templates/Checkbox.html"), 
    2043 
    21                 //      Value of "type" attribute for <input>, and waiRole attribute also. 
    22                 //      User probably shouldn't adjust this. 
     44                //      Value of "type" attribute for <input> 
    2345                _type: "checkbox", 
    2446 
     
    3355                value: "on", 
    3456 
    35                 // This shared object keeps track of all widgets, grouped by name 
    36                 _groups: { }, 
    37  
    38                 postMixInProperties: function(){ 
    39                         dijit.form.Checkbox.superclass.postMixInProperties.apply(this, arguments); 
    40                          
    41                         // set tabIndex="0" because if tabIndex=="" user won't be able to tab to the field 
    42                         if(!this.disabled && this.tabIndex==""){ this.tabIndex="0"; }            
    43                 }, 
    44  
    4557                postCreate: function(){ 
    4658 
    4759                        // find the image to use, as notated in the CSS file, but use it as a foreground 
    48                         // image 
    4960                        var bi = dojo.html.getComputedStyle(this.imageContainer, "background-image"); 
    5061                        var href = bi.charAt(4)=='"' ? bi.slice(5,-2) : bi.slice(4,-1); // url(foo) --> foo, url("foo") --> foo 
     
    5263                        var img = this.imageNode = document.createElement("img"); 
    5364                        var self=this; 
     65                         
     66                        // inputNode.checked must be assigned before img.onload handler 
     67                        this.inputNode.checked=this.checked; 
     68                        // note: onImageLoad may get called as a side-effect of this assignment 
    5469                        img.onload = function(){ self.onImageLoad(); } 
    5570                        img.src = href; 
    56  
    57                         this._setValue(this.checked); 
    58  
    59                         // find any associated label and create a labelled-by relationship 
    60                         // assumes <label for="inputId">label text </label> rather than 
    61                         // <label><input type="xyzzy">label text</label> 
    62                         var notcon = true; 
    63                         if(this.id != ""){ 
    64                                 var labels = document.getElementsByTagName("label"); 
    65                                 if (labels != null && labels.length > 0){ 
    66                                         for(var i=0; i<labels.length; i++){ 
    67                                                 if (labels[i].htmlFor == this.id){ 
    68                                                         labels[i].id = (labels[i].htmlFor + "label"); 
    69                                                         this._connectEvents(labels[i]); 
    70                                                         dijit.util.wai.setAttr(this.domNode, "waiState", "labelledby", labels[i].id); 
    71                                                         break; 
    72                                                 } 
    73                                         } 
    74                                 } 
    75                         } 
    76                         this._connectEvents(this.domNode); 
    77                         // this is needed here for IE 
    78                         this.inputNode.checked=this.checked; 
    79                         this._register(); 
     71                        this._setDisabled(this.disabled); 
    8072                }, 
    8173 
    8274                onImageLoad: function(){ 
    8375                        this.imageLoaded = true; 
     76 
    8477                        // set image container size to just show one sprite 
    85                         this.width = 16;        //      this.imageNode.width/6; 
    86                         this.height = 16;       // this.imageNode.height/2; 
    87                         this.imageContainer.style.width = this.width + "px"; 
    88                         this.imageContainer.style.height = this.height + "px"; 
    89  
    90                         // Hide the HTML native checkbox and display the image instead 
    91                         this.inputNode.style.display="none"; 
     78                        if (!this.width) { 
     79                                this.width = 16; 
     80                        } // dojo.html.getPixelValue is not succeeding for all browsers 
     81                        if (!this.height) { 
     82                                this.height = 16; 
     83                        } 
     84 
     85                        // Turn the input element invisible and make sure it overlays 
     86                        // the dojo image container. 
     87                        dojo.html.addClass(this.inputNode,"dojoCheckboxInputInvisible"); 
     88                        dojo.html.addClass(this.imageContainer,"dojoCheckboxImageContainer"); 
     89                         
     90                        dojo.html.applyBrowserClass(this.domNode); 
     91 
     92                        var imageContainerStyle = this.imageContainer.style 
     93                        var inputStyle = this.inputNode.style; 
     94                        var domNodeStyle = this.domNode.style; 
     95                          
     96                        // Force size based on width and height. 
     97                        inputStyle.width = imageContainerStyle.width = this.width + "px"; 
     98                        inputStyle.height = imageContainerStyle.height = this.height + "px"; 
     99                        domNodeStyle.position = "relative"; 
     100                        domNodeStyle.fontFamily = "monospace"; // webkit spacing hack 
     101                         
     102                        // User will always interact with input element 
     103                        this._connectEvents(this.inputNode); 
     104                         
    92105                        this.imageContainer.appendChild(this.imageNode); 
    93  
    94                         // position image to display right sprite 
    95                         this._setValue(this.checked); 
    96                 }, 
    97                  
    98                 uninitialize: function(){ 
    99                         this._deregister(); 
    100                 }, 
    101  
     106                         
     107                        this._updateView(); 
     108                }, 
     109                 
    102110                _connectEvents: function(/*DomNode*/ node){ 
     111                        dojo.event.browser.addListener(node, "onfocus", dojo.lang.hitch(this, "mouseOver")); 
     112                        dojo.event.browser.addListener(node, "onblur", dojo.lang.hitch(this, "mouseOut")); 
    103113                        dojo.event.browser.addListener(node, "onmouseover", dojo.lang.hitch(this, "mouseOver")); 
    104114                        dojo.event.browser.addListener(node, "onmouseout", dojo.lang.hitch(this, "mouseOut")); 
    105                         dojo.event.browser.addListener(node, "onkey", dojo.lang.hitch(this, "onKey")); 
    106                         dojo.event.browser.addListener(node, "onclick", dojo.lang.hitch(this, "_onClick")); 
     115                        dojo.event.browser.addListener(node, "onclick", dojo.lang.hitch(this, "onClick")); 
    107116                        dojo.html.disableSelection(node); 
    108117                }, 
    109118 
    110                 _onClick: function(/*Event*/ e){ 
    111                         if(this.disabled == false){ 
    112                                 this.setValue(!this.checked); 
    113                         } 
    114                         dojo.event.browser.stopEvent(e); 
    115                         this.onClick(); 
    116                 }, 
    117  
    118                 _register: function(){ 
    119                         // summary: add this widget to _groups 
    120                         if(this._groups[this.name] == null){ 
    121                                 this._groups[this.name]=[]; 
    122                         } 
    123                         this._groups[this.name].push(this); 
    124                 }, 
    125  
    126                 _deregister: function(){ 
    127                         // summary: remove this widget from _groups 
    128                         var idx = dojo.lang.find(this._groups[this.name], this, true); 
    129                         this._groups[this.name].splice(idx, 1); 
    130                 }, 
    131  
    132                 setValue: function(/*boolean*/ bool){ 
    133                         // summary: set the checkbox state 
    134                         this._setValue(bool); 
    135                 }, 
    136  
    137                 onClick: function(){ 
    138                         // summary: user overridable callback function for checkbox being clicked 
    139                 }, 
    140  
    141                 onKey: function(/*Event*/ e){ 
    142                         // summary: callback when user hits a key 
    143                         var k = dojo.event.browser.keys; 
    144                         if(e.key == " "){ 
    145                                 this._onClick(e); 
    146                         } 
     119                _setDisabled: function(/*Boolean*/ disabled){ 
     120                        this.domNode.disabled = this.inputNode.disabled = this.disabled = disabled; 
     121                }, 
     122                 
     123                onValueChanged: function(newValue){ 
     124                        // summary: implement this to capture value change events. 
     125                }, 
     126                 
     127                _lastValueReported: null, 
     128                setValue: function(newValue){ 
     129                        // summary: set the value of the widget. 
     130                        if (newValue != this._lastValueReported){ 
     131                                this.onValueChanged(newValue); 
     132                                this._lastValueReported = newValue; 
     133                        } 
     134                }, 
     135 
     136                getValue: function(){ 
     137                        // summary: get the value of the widget. 
     138                        return this._lastValueReported; 
     139                }, 
     140                 
     141                onClick: function(/*Event*/ e){ 
     142                        // dojo.event.browser.stopEvent(e); // bad for Opera 
     143                        this._updateView(); 
    147144                }, 
    148145 
     
    150147                        // summary: callback when user moves mouse over checkbox 
    151148                        this.hover=true; 
    152                         this._setValue(this.checked); 
     149                        this._updateView(); 
    153150                }, 
    154151 
     
    156153                        // summary: callback when user moves mouse off of checkbox 
    157154                        this.hover=false; 
    158                         this._setValue(this.checked); 
     155                        this._updateView(); 
    159156                }, 
    160157 
    161158                // offset from left of image 
    162159                _leftOffset: 0, 
    163  
    164                 _setValue: function(/*Boolean*/ bool){ 
    165                         // summary: 
    166                         //      sets checkbox to given value 
    167                         //      set state of hidden checkbox node to correspond to given value. 
    168                         this.checked = bool; 
    169                         this.inputNode.checked = this.checked; 
    170                         if(this.disabled){ 
    171                                 this.inputNode.setAttribute("disabled",true); 
    172                         }else{ 
    173                                 this.inputNode.removeAttribute("disabled"); 
    174                         } 
    175                         dijit.util.wai.setAttr(this.domNode, "waiState", "checked", this.checked); 
     160                 
     161                _updateView: function(/*Optional Widget*/ awidget) { 
     162                        var w = awidget || this; 
     163                        w.checked=w.inputNode.checked; 
     164                        this.setValue(w.checked? this.inputNode.value:""); 
     165 
     166                        this._setDisabled(w.disabled); 
    176167 
    177168                        // show the right sprite, depending on state of checkbox 
    178                         if(this.imageLoaded){ 
    179                                 var left = this._leftOffset + (this.checked ? 0 : this.width ) + (this.disabled ? this.width*2 : (this.hover ? this.width*4 : 0 )); 
    180                                 this.imageNode.style.marginLeft = -1*left + "px"; 
    181                         } 
     169                        if(w.imageLoaded){ 
     170                                var left = w._leftOffset + (w.checked ? 0 : w.width ) + (w.disabled ? this.width*2 : (w.hover ? w.width*4 : 0 )); 
     171                                w.imageNode.style.marginLeft = -1*left + "px"; 
     172                        } 
     173                        if (!awidget) { 
     174                                this.updateContext(); 
     175                        } 
     176                }, 
     177                 
     178                updateContext: function(){ 
     179                        // stub; 
    182180                } 
    183181        } 
     
    187185        dijit.form.Checkbox, 
    188186        { 
    189                 // summary 
    190                 //      Same as an HTML radio button, but with fancy styling 
    191  
     187                // summary: 
     188                //              Same as an HTML radio, but with fancy styling. 
     189 
     190                // Implementation details 
     191                // 
     192                // Specialization: 
     193                // We keep track of dijit radio groups so that we can update the state 
     194                // of all the siblings (the "context") in a group based on input  
     195                // events. We don't rely on browser radio grouping. 
     196                // 
     197                // At the time of implementation not all browsers fire the same events 
     198                // when a different radio button in a group is checked (and the previous 
     199                // unchecked). When the events do fire, e.g. a focus event on the newly 
     200                // checked radio, the checked state of that "newly checked" radio is  
     201                // set to true in some browsers and false in others. 
     202                // It is vital that the view of the resulting input states be correct 
     203                // so that at the time of form submission the intended data is sent. 
     204                 
    192205                _type: "radio", 
    193  
    194                 _onClick: function(/*Event*/ e){ 
    195                         if(!this.disabled && !this.checked){ 
    196                                 this.setValue(true); 
    197                         } 
    198                         e.stopPropagation(); 
    199                         this.onClick(); 
    200                 }, 
    201  
    202                 setValue: function(/*boolean*/ bool){ 
    203                         this._setValue(bool); 
    204  
    205                         // if turning this widget on, then turn others in same group off 
    206                         if(bool){ 
    207                                 dojo.lang.forEach(this._groups[this.name], function(widget){ 
    208                                         if(widget != this){ 
    209                                                 widget._setValue(false); 
    210                                         } 
    211                                 }, this); 
    212                         } 
     206                 
     207                // This shared object keeps track of all widgets, grouped by name 
     208                _groups: { }, 
     209 
     210                _register: function(){ 
     211                        // summary: add this widget to _groups 
     212                        if(this._groups[this.name] == null){ 
     213                                this._groups[this.name]=[]; 
     214                        } 
     215                        this._groups[this.name].push(this); 
     216                }, 
     217 
     218                _deregister: function(){ 
     219                        // summary: remove this widget from _groups 
     220                        var idx = dojo.lang.find(this._groups[this.name], this, true); 
     221                        this._groups[this.name].splice(idx, 1); 
     222                }, 
     223                 
     224                uninitialize: function(){ 
     225                        this._deregister(); 
     226                }, 
     227 
     228                updateContext: function() { 
     229                        dojo.lang.forEach(this._groups[this.name], function(widget){ 
     230                                if(widget != this){ 
     231                                        widget._updateView(widget); 
     232                                } 
     233                        }, this); 
    213234                }, 
    214235 
    215236                onImageLoad: function(){ 
    216                         // position to second row of sprites (the radio buttons) 
    217237                        this._leftOffset = 96;  // this.imageNode.width/2; 
     238                        this._register(); 
    218239                        dijit.form.Checkbox.prototype.onImageLoad.call(this); 
    219240                } 
  • dijit/trunk/form/templates/Checkbox.html

    r7525 r8248  
    1 <span class="dojoInlineBox" tabIndex="${this.tabIndex}" waiRole="${this._type}" id="${this.id}" 
    2         ><div dojoAttachPoint="imageContainer" class="dojoCheckbox" style="overflow:hidden;"></div 
    3         ><input type="${this._type}" name="${this.name}" value="${this.value}" dojoAttachPoint="inputNode" 
     1<span 
     2 >&nbsp;&nbsp;<span dojoAttachPoint="imageContainer" class="dojoCheckbox" style="overflow:hidden;"></span 
     3 ><input dojoAttachPoint="inputNode" class="dojoCheckboxInput" id="${this.id}" tabIndex="${this.tabIndex}" type="${this._type}" name="${this.name}" value="${this.value}" 
    44></span> 
  • dijit/trunk/tests/form/test_Checkbox.html

    r7245 r8248  
    3535}); 
    3636</script> 
     37<style> label { margin-right: 0.80em; } </style> 
    3738</head> 
    3839<body class="tundra" style="padding: 50px;"> 
    3940 
    4041<p> 
    41 Here are some checkboxes.  Try clicking, and hovering: 
     42Here are some checkboxes.  Try clicking, and hovering, tabbing, and using the space bar to select: 
    4243</p> 
    4344 
     
    6263        <br> 
    6364        <div id="checkboxContainer"></div> 
    64                 <label for="checkboxContainer">cb6: instantiated from script</label> 
     65                <label for="cb6">cb6: instantiated from script</label> 
    6566        <br> 
     67<p> 
     68Here are some radio buttons.  Try clicking, and hovering, tabbing, and arrowing 
     69</p> 
    6670        <p> 
    6771                <span>Radio group #1:</span> 
     
    7478        </p> 
    7579        <p> 
    76                 <span>Radio group #2:</span> 
     80                <span>Radio group #2: (no default value, and has breaks)</span><br> 
    7781                <input type="radio" name="g2" id="g2rb1" value="top40" dojoType="dijit.form.RadioButton"/> 
    78                 <label for="g2rb1">top 40</label> 
     82                <label for="g2rb1">top 40</label><br> 
    7983                <input type="radio" name="g2" id="g2rb2" value="oldies" dojoType="dijit.form.RadioButton"/> 
    80                 <label for="g2rb2">oldies</label> 
    81                 <input type="radio" name="g2" id="g2rb3" value="country" dojoType="dijit.form.RadioButton" disabled="disabled" checked="checked"/> 
    82                 <label for="g2rb3">country</label> 
     84                <label for="g2rb2">oldies</label><br> 
     85                <input type="radio" name="g2" id="g2rb3" value="country" dojoType="dijit.form.RadioButton"/> 
     86                <label for="g2rb3">country</label><br> 
     87                (Note if using keyboard: tab to navigate, and use arrow or space to select) 
    8388        </p> 
    8489        <p> 
     
    8691                <input type="radio" name="g3" id="g3rb1" value="rock"/> 
    8792                <label for="g3rb1">rock</label> 
    88                 <input type="radio" name="g3" id="g3rb2" value="jazz"/> 
     93                <input type="radio" name="g3" id="g3rb2" value="jazz" disabled="disabled"/> 
    8994                <label for="g3rb2">jazz</label> 
    90                 <input type="radio" name="g3" id="g3rb3" value="classical" disabled="disabled"  checked="checked"/> 
     95                <input type="radio" name="g3" id="g3rb3" value="classical" checked="checked"/> 
    9196                <label for="g3rb3">classical</label> 
    9297        </p> 
  • dijit/trunk/themes/tundra/tundra.css

    r8036 r8248  
    151151} 
    152152 
     153 
    153154.dojoCheckbox { 
    154         /* location of checkbox sprint image */ 
    155         background-image: url(checkmark.gif); 
     155        background-image: url(checkmark.gif); /* checkbox sprint image */ 
     156        width: 16px; 
     157        height: 16px; 
     158} 
     159 
     160/* to go in dijit.css */ 
     161.dojoCheckboxImageContainer, 
     162.dojoCheckboxInputInvisible { 
     163        border: 0; 
     164        margin: 0; 
     165        padding: 0; 
     166} 
     167 
     168/* to go in dijit.css */ 
     169.dojoCheckbox { 
     170        /* to go in dijit.css */ 
     171        position: relative; 
     172} 
     173 
     174/* to go in dijit.css */ 
     175.dojoCheckboxImageContainer, 
     176.dojoCheckboxInputInvisible { 
     177        position: absolute; 
     178        left: 0; 
     179        top: 0; 
     180} 
     181 
     182/* to go in dijit.css */ 
     183.dojoCheckboxInputInvisible { 
     184        z-index: 100; 
     185        opacity: 0.01; 
     186        filter: alpha(opacity=0); 
     187} 
     188 
     189/* to go in dijit.css */ 
     190.dj_ie .dojoCheckboxImageContainer,  
     191.dj_ie .dojoCheckboxInputInvisible { 
     192        top: 3px; 
    156193} 
    157194