timedtextarea.xml (10945B)
1 <?xml version="1.0"?> 2 <!-- 3 A combination of Mozilla's textarea, timed-textbox, input-box, and 4 input-box-spell bindings, with the timed-textbox's Return key 5 event handler removed 6 7 Note: It would be much nicer if a) Mozilla offered this natively or 8 b) we just extended the timed-textbox binding directly, but since it's based 9 on html:input rather than html:textarea, doing so breaks things in various 10 ways (though it may be possible with some tweaking) 11 12 Also note: spellcheck code here is a slightly adjusted version 13 of a patch by Neil Deakin on Bugzilla that wasn't approved in time for 14 Firefox 2.0 (https://bugzilla.mozilla.org/show_bug.cgi?id=346787). 15 When there's native support for spellcheck="true" in XUL, we'll hopefully be 16 able to use that, though it'll still need to work as a timed textarea... 17 --> 18 19 <!DOCTYPE bindings [ 20 <!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd" > 21 %textcontextDTD; 22 23 <!-- These aren't yet included in textcontext.dtd in Minefield, so we reproduce them here 24 (rather than including the massive browser.dtd) --> 25 <!ENTITY spellAddToDictionary.label "Add to dictionary"> 26 <!ENTITY spellAddToDictionary.accesskey "o"> 27 <!ENTITY spellEnable.label "Spell check this field"> 28 <!ENTITY spellEnable.accesskey "S"> 29 <!ENTITY spellNoSuggestions.label "(No spelling suggestions)"> 30 <!ENTITY spellDictionaries.label "Languages"> 31 <!ENTITY spellDictionaries.accesskey "l"> 32 <!ENTITY spellAddDictionaries.label "Add dictionaries..."> 33 <!ENTITY spellAddDictionaries.accesskey "A"> 34 ]> 35 36 <bindings xmlns="http://www.mozilla.org/xbl" 37 xmlns:html="http://www.w3.org/1999/xhtml" 38 xmlns:xbl="http://www.mozilla.org/xbl" 39 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 40 <binding id="timed-textarea" extends="chrome://global/content/bindings/textbox.xml#textbox"> 41 <implementation> 42 <field name="mInputField">null</field> 43 <property name="inputField" readonly="true"> 44 <getter><![CDATA[ 45 if (!this.mInputField) 46 this.mInputField = document.getAnonymousElementByAttribute(this, "anonid", "input"); 47 return this.mInputField; 48 ]]></getter> 49 </property> 50 51 <field name="_timer">null</field> 52 <property name="timeout" 53 onset="this.setAttribute('timeout', val); return val;" 54 onget="return parseInt(this.getAttribute('timeout')) || 0;"/> 55 <property name="value"> 56 <getter> 57 return this.inputField.value; 58 </getter> 59 <setter> 60 <![CDATA[ 61 this.inputField.value = val; 62 if (this._timer) 63 clearTimeout(this._timer); 64 return val; 65 ]]> 66 </setter> 67 </property> 68 <method name="_fireCommand"> 69 <parameter name="me"/> 70 <body> 71 <![CDATA[ 72 me._timer = null; 73 me.doCommand(); 74 ]]> 75 </body> 76 </method> 77 78 79 <!-- Spellcheck code --> 80 <field name="_spellCheckInitialized">false</field> 81 <property name="spellCheckerUI" readonly="true"> 82 <getter><![CDATA[ 83 if (!this._spellCheckInitialized) { 84 this._spellCheckInitialized = true; 85 86 const CI = Components.interfaces; 87 if (!document instanceof CI.nsIDOMXULDocument) 88 return null; 89 90 var textbox = this; 91 92 if (!textbox || !textbox instanceof CI.nsIDOMXULTextBoxElement) 93 return null; 94 95 try { 96 var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]. 97 getService(CI.mozIJSSubScriptLoader); 98 loader.loadSubScript("chrome://global/content/inlineSpellCheckUI.js", this); 99 100 if ("InlineSpellCheckerUI" in this) 101 this.InlineSpellCheckerUI.init( 102 textbox.inputField.QueryInterface(CI.nsIDOMNSEditableElement).editor 103 ); 104 } catch(ex) { 105 // this throws an error on window open... 106 } 107 } 108 109 return this.InlineSpellCheckerUI; 110 ]]></getter> 111 </property> 112 113 <constructor> 114 <![CDATA[ 115 // can't initialize the spell checker in the constructor as not 116 // everything is initialized and the editor will fail to create the 117 // inline spell checker object 118 setTimeout(this._delayedInitSpellCheck, 0, this) 119 120 // oninput doesn't seem to fire on text drag 121 this.inputField.addEventListener('dragdrop', function(event) { 122 document.getBindingParent(event.target).doInput(); 123 }, false); 124 ]]> 125 </constructor> 126 127 <method name="_delayedInitSpellCheck"> 128 <parameter name="me"/> 129 <body><![CDATA[ 130 var spellui = me.spellCheckerUI; 131 if (spellui) 132 spellui.enabled = true; 133 ]]></body> 134 </method> 135 136 <method name="_doPopupItemEnabling"> 137 <parameter name="popupNode"/> 138 <body> 139 <![CDATA[ 140 var children = popupNode.childNodes; 141 for (var i = 0; i < children.length; i++) { 142 var command = children[i].getAttribute("cmd"); 143 if (command) { 144 var controller = document.commandDispatcher.getControllerForCommand(command); 145 var enabled = controller.isCommandEnabled(command); 146 if (enabled) { 147 children[i].removeAttribute("disabled"); 148 } 149 else { 150 children[i].setAttribute("disabled", "true"); 151 } 152 } 153 } 154 ]]> 155 </body> 156 </method> 157 158 <method name="_doPopupItemEnablingSpell"> 159 <parameter name="popupNode"/> 160 <body> 161 <![CDATA[ 162 var spellui = this.spellCheckerUI; 163 if (!spellui || !spellui.canSpellCheck) { 164 this._setMenuItemVisibility("spell-no-suggestions", false); 165 this._setMenuItemVisibility("spell-check-enabled", false); 166 this._setMenuItemVisibility("spell-check-separator", false); 167 this._setMenuItemVisibility("spell-add-to-dictionary", false); 168 this._setMenuItemVisibility("spell-suggestions-separator", false); 169 this._setMenuItemVisibility("spell-dictionaries", false); 170 return; 171 } 172 173 spellui.initFromEvent(document.popupRangeParent, 174 document.popupRangeOffset); 175 176 var enabled = spellui.enabled; 177 document.getAnonymousElementByAttribute(this, "anonid", 178 "spell-check-enabled").setAttribute("checked", enabled); 179 180 var overMisspelling = spellui.overMisspelling; 181 this._setMenuItemVisibility("spell-add-to-dictionary", overMisspelling); 182 this._setMenuItemVisibility("spell-suggestions-separator", overMisspelling); 183 184 // suggestion list 185 var suggestionsSeparator = document.getAnonymousElementByAttribute(this, 186 "anonid", "spell-add-to-dictionary"); 187 var numsug = spellui.addSuggestionsToMenu(popupNode, suggestionsSeparator, 5); 188 this._setMenuItemVisibility("spell-no-suggestions", overMisspelling && numsug == 0); 189 190 // dictionary list 191 var dictmenu = document.getAnonymousElementByAttribute(this, "anonid", 192 "spell-dictionaries-menu"); 193 var addsep = document.getAnonymousElementByAttribute(this, "anonid", 194 "spell-language-separator"); 195 196 var numdicts = spellui.addDictionaryListToMenu(dictmenu, addsep); 197 this._setMenuItemVisibility("spell-dictionaries", enabled); 198 199 this._doPopupItemEnabling(popupNode); 200 ]]> 201 </body> 202 </method> 203 204 <method name="_doPopupItemDisabling"> 205 <body><![CDATA[ 206 if (this.spellCheckerUI) { 207 this.spellCheckerUI.clearSuggestionsFromMenu(); 208 this.spellCheckerUI.clearDictionaryListFromMenu(); 209 } 210 ]]></body> 211 </method> 212 213 <method name="_setMenuItemVisibility"> 214 <parameter name="anonid"/> 215 <parameter name="visible"/> 216 <body><![CDATA[ 217 document.getAnonymousElementByAttribute(this, "anonid", anonid). 218 hidden = ! visible; 219 ]]></body> 220 </method> 221 222 223 <method name="doTextCommand"> 224 <parameter name="command"/> 225 <body> 226 <![CDATA[ 227 var controller = document.commandDispatcher.getControllerForCommand(command); 228 controller.doCommand(command); 229 ]]> 230 </body> 231 </method> 232 233 <method name="doInput"> 234 <body> 235 <![CDATA[ 236 if (this._timer) { 237 clearTimeout(this._timer); 238 } 239 this._timer = this.timeout && setTimeout(this._fireCommand, this.timeout, this); 240 ]]> 241 </body> 242 </method> 243 </implementation> 244 245 246 <handlers> 247 <handler event="input"> 248 <![CDATA[ 249 this.doInput(); 250 ]]> 251 </handler> 252 </handlers> 253 254 255 <content context="_child"> 256 <xul:hbox class="textbox-input-box" flex="1"> 257 <html:textarea class="textbox-textarea" flex="1" anonid="input" 258 xbl:inherits="onfocus,onblur,xbl:text=value,disabled,tabindex,rows,cols,readonly,wrap"><children/></html:textarea> 259 <xul:menupopup anonid="input-box-contextmenu" 260 onpopupshowing="if (document.commandDispatcher.focusedElement != this.parentNode.firstChild) { this.parentNode.firstChild.focus(); } this.parentNode.parentNode._doPopupItemEnablingSpell(this);" 261 onpopuphiding="this.parentNode.parentNode._doPopupItemDisabling(this);" 262 oncommand="var cmd = event.originalTarget.getAttribute('cmd'); if(cmd) { this.parentNode.parentNode.doTextCommand(cmd); event.stopPropagation(); }"> 263 <xul:menuitem label="&spellNoSuggestions.label;" anonid="spell-no-suggestions" disabled="true"/> 264 <xul:menuitem label="&spellAddToDictionary.label;" accesskey="&spellAddToDictionary.accesskey;" anonid="spell-add-to-dictionary" 265 oncommand="this.parentNode.parentNode.parentNode.spellCheckerUI.addToDictionary();"/> 266 <xul:menuseparator anonid="spell-suggestions-separator"/> 267 <xul:menuitem label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" cmd="cmd_undo"/> 268 <xul:menuseparator/> 269 <xul:menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"/> 270 <xul:menuitem label="©Cmd.label;" accesskey="©Cmd.accesskey;" cmd="cmd_copy"/> 271 <xul:menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"/> 272 <xul:menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"/> 273 <xul:menuseparator/> 274 <xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/> 275 <xul:menuseparator anonid="spell-check-separator"/> 276 <xul:menuitem label="&spellEnable.label;" type="checkbox" accesskey="&spellEnable.accesskey;" anonid="spell-check-enabled" 277 oncommand="this.parentNode.parentNode.parentNode.spellCheckerUI.toggleEnabled();"/> 278 <xul:menu label="&spellDictionaries.label;" accesskey="&spellDictionaries.accesskey;" anonid="spell-dictionaries"> 279 <xul:menupopup anonid="spell-dictionaries-menu" 280 onpopupshowing="event.stopPropagation();" 281 onpopuphiding="event.stopPropagation();"> 282 <xul:menuseparator anonid="spell-language-separator"/> 283 <xul:menuitem anonid="spell-add-dictionaries-main" label="&spellAddDictionaries.label;" 284 accesskey="&spellAddDictionaries.accesskey;" 285 oncommand="nsContextMenu.prototype.addDictionaries();"/> 286 </xul:menupopup> 287 </xul:menu> 288 </xul:menupopup> 289 </xul:hbox> 290 291 </content> 292 </binding> 293 </bindings>