Changeset 169 for ruby/branches/0.5/public/javascripts/controls.js
- Timestamp:
- Aug 6, 2009, 11:21:29 PM (15 years ago)
- Location:
- ruby/branches/0.5
- Files:
-
- 1 edited
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
ruby/branches/0.5/public/javascripts/controls.js
r164 r169 1 1 // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 2 // (c) 2005-200 7Ivan Krstic (http://blogs.law.harvard.edu/ivan)3 // (c) 2005-200 7Jon Tirsen (http://www.tirsen.com)2 // (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan) 3 // (c) 2005-2008 Jon Tirsen (http://www.tirsen.com) 4 4 // Contributors: 5 5 // Richard Livsey 6 6 // Rahul Bhargava 7 7 // Rob Wills 8 // 8 // 9 9 // script.aculo.us is freely distributable under the terms of an MIT-style license. 10 10 // For details, see the script.aculo.us web site: http://script.aculo.us/ 11 11 12 // Autocompleter.Base handles all the autocompletion functionality 12 // Autocompleter.Base handles all the autocompletion functionality 13 13 // that's independent of the data source for autocompletion. This 14 14 // includes drawing the autocompletion menu, observing keyboard 15 15 // and mouse events, and similar. 16 16 // 17 // Specific autocompleters need to provide, at the very least, 17 // Specific autocompleters need to provide, at the very least, 18 18 // a getUpdatedChoices function that will be invoked every time 19 // the text inside the monitored textbox changes. This method 19 // the text inside the monitored textbox changes. This method 20 20 // should get the text for which to provide autocompletion by 21 21 // invoking this.getToken(), NOT by directly accessing … … 31 31 // Additionally, ',' in the above example can be replaced with 32 32 // a token array, e.g. { tokens: [',', '\n'] } which 33 // enables autocompletion on multiple tokens. This is most 34 // useful when one of the tokens is \n (a newline), as it 33 // enables autocompletion on multiple tokens. This is most 34 // useful when one of the tokens is \n (a newline), as it 35 35 // allows smart autocompletion after linebreaks. 36 36 … … 38 38 throw("controls.js requires including script.aculo.us' effects.js library"); 39 39 40 var Autocompleter = { } 40 var Autocompleter = { }; 41 41 Autocompleter.Base = Class.create({ 42 42 baseInitialize: function(element, update, options) { 43 element = $(element) 44 this.element = element; 45 this.update = $(update); 46 this.hasFocus = false; 47 this.changed = false; 48 this.active = false; 49 this.index = 0; 43 element = $(element); 44 this.element = element; 45 this.update = $(update); 46 this.hasFocus = false; 47 this.changed = false; 48 this.active = false; 49 this.index = 0; 50 50 this.entryCount = 0; 51 51 this.oldElementValue = this.element.value; … … 60 60 this.options.frequency = this.options.frequency || 0.4; 61 61 this.options.minChars = this.options.minChars || 1; 62 this.options.onShow = this.options.onShow || 63 function(element, update){ 62 this.options.onShow = this.options.onShow || 63 function(element, update){ 64 64 if(!update.style.position || update.style.position=='absolute') { 65 65 update.style.position = 'absolute'; 66 66 Position.clone(element, update, { 67 setHeight: false, 67 setHeight: false, 68 68 offsetTop: element.offsetHeight 69 69 }); … … 71 71 Effect.Appear(update,{duration:0.15}); 72 72 }; 73 this.options.onHide = this.options.onHide || 73 this.options.onHide = this.options.onHide || 74 74 function(element, update){ new Effect.Fade(update,{duration:0.15}) }; 75 75 76 if(typeof(this.options.tokens) == 'string') 76 if(typeof(this.options.tokens) == 'string') 77 77 this.options.tokens = new Array(this.options.tokens); 78 78 // Force carriage returns as token delimiters anyway … … 81 81 82 82 this.observer = null; 83 83 84 84 this.element.setAttribute('autocomplete','off'); 85 85 … … 92 92 show: function() { 93 93 if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); 94 if(!this.iefix && 94 if(!this.iefix && 95 95 (Prototype.Browser.IE) && 96 96 (Element.getStyle(this.update, 'position')=='absolute')) { 97 new Insertion.After(this.update, 97 new Insertion.After(this.update, 98 98 '<iframe id="' + this.update.id + '_iefix" '+ 99 99 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' + … … 103 103 if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); 104 104 }, 105 105 106 106 fixIEOverlapping: function() { 107 107 Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); … … 151 151 return; 152 152 } 153 else 154 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 153 else 154 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 155 155 (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return; 156 156 … … 159 159 160 160 if(this.observer) clearTimeout(this.observer); 161 this.observer = 161 this.observer = 162 162 setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); 163 163 }, … … 171 171 onHover: function(event) { 172 172 var element = Event.findElement(event, 'LI'); 173 if(this.index != element.autocompleteIndex) 173 if(this.index != element.autocompleteIndex) 174 174 { 175 175 this.index = element.autocompleteIndex; … … 178 178 Event.stop(event); 179 179 }, 180 180 181 181 onClick: function(event) { 182 182 var element = Event.findElement(event, 'LI'); … … 185 185 this.hide(); 186 186 }, 187 187 188 188 onBlur: function(event) { 189 189 // needed to make click events working 190 190 setTimeout(this.hide.bind(this), 250); 191 191 this.hasFocus = false; 192 this.active = false; 193 }, 194 192 this.active = false; 193 }, 194 195 195 render: function() { 196 196 if(this.entryCount > 0) { 197 197 for (var i = 0; i < this.entryCount; i++) 198 this.index==i ? 199 Element.addClassName(this.getEntry(i),"selected") : 198 this.index==i ? 199 Element.addClassName(this.getEntry(i),"selected") : 200 200 Element.removeClassName(this.getEntry(i),"selected"); 201 if(this.hasFocus) { 201 if(this.hasFocus) { 202 202 this.show(); 203 203 this.active = true; … … 208 208 } 209 209 }, 210 210 211 211 markPrevious: function() { 212 if(this.index > 0) this.index-- 212 if(this.index > 0) this.index--; 213 213 else this.index = this.entryCount-1; 214 214 this.getEntry(this.index).scrollIntoView(true); 215 215 }, 216 216 217 217 markNext: function() { 218 if(this.index < this.entryCount-1) this.index++ 218 if(this.index < this.entryCount-1) this.index++; 219 219 else this.index = 0; 220 220 this.getEntry(this.index).scrollIntoView(false); 221 221 }, 222 222 223 223 getEntry: function(index) { 224 224 return this.update.firstChild.childNodes[index]; 225 225 }, 226 226 227 227 getCurrentEntry: function() { 228 228 return this.getEntry(this.index); 229 229 }, 230 230 231 231 selectEntry: function() { 232 232 this.active = false; … … 245 245 } else 246 246 value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); 247 247 248 248 var bounds = this.getTokenBounds(); 249 249 if (bounds[0] != -1) { … … 258 258 this.oldElementValue = this.element.value; 259 259 this.element.focus(); 260 260 261 261 if (this.options.afterUpdateElement) 262 262 this.options.afterUpdateElement(this.element, selectedElement); … … 270 270 271 271 if(this.update.firstChild && this.update.down().childNodes) { 272 this.entryCount = 272 this.entryCount = 273 273 this.update.down().childNodes.length; 274 274 for (var i = 0; i < this.entryCount; i++) { … … 277 277 this.addObservers(entry); 278 278 } 279 } else { 279 } else { 280 280 this.entryCount = 0; 281 281 } … … 283 283 this.stopIndicator(); 284 284 this.index = 0; 285 285 286 286 if(this.entryCount==1 && this.options.autoSelect) { 287 287 this.selectEntry(); … … 299 299 300 300 onObserverEvent: function() { 301 this.changed = false; 301 this.changed = false; 302 302 this.tokenBounds = null; 303 303 if(this.getToken().length>=this.options.minChars) { … … 352 352 getUpdatedChoices: function() { 353 353 this.startIndicator(); 354 355 var entry = encodeURIComponent(this.options.paramName) + '=' + 354 355 var entry = encodeURIComponent(this.options.paramName) + '=' + 356 356 encodeURIComponent(this.getToken()); 357 357 … … 359 359 this.options.callback(this.element, entry) : entry; 360 360 361 if(this.options.defaultParams) 361 if(this.options.defaultParams) 362 362 this.options.parameters += '&' + this.options.defaultParams; 363 363 364 364 new Ajax.Request(this.url, this.options); 365 365 }, … … 383 383 // 384 384 // - partialSearch - If false, the autocompleter will match entered 385 // text only at the beginning of strings in the 385 // text only at the beginning of strings in the 386 386 // autocomplete array. Defaults to true, which will 387 387 // match text at the beginning of any *word* in the … … 400 400 // Defaults to true. 401 401 // 402 // It's possible to pass in a custom function as the 'selector' 402 // It's possible to pass in a custom function as the 'selector' 403 403 // option, if you prefer to write your own autocompletion logic. 404 404 // In that case, the other options above will not apply unless … … 428 428 var count = 0; 429 429 430 for (var i = 0; i < instance.options.array.length && 431 ret.length < instance.options.choices ; i++) { 430 for (var i = 0; i < instance.options.array.length && 431 ret.length < instance.options.choices ; i++) { 432 432 433 433 var elem = instance.options.array[i]; 434 var foundPos = instance.options.ignoreCase ? 435 elem.toLowerCase().indexOf(entry.toLowerCase()) : 434 var foundPos = instance.options.ignoreCase ? 435 elem.toLowerCase().indexOf(entry.toLowerCase()) : 436 436 elem.indexOf(entry); 437 437 438 438 while (foundPos != -1) { 439 if (foundPos == 0 && elem.length != entry.length) { 440 ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 439 if (foundPos == 0 && elem.length != entry.length) { 440 ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 441 441 elem.substr(entry.length) + "</li>"); 442 442 break; 443 } else if (entry.length >= instance.options.partialChars && 443 } else if (entry.length >= instance.options.partialChars && 444 444 instance.options.partialSearch && foundPos != -1) { 445 445 if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { … … 451 451 } 452 452 453 foundPos = instance.options.ignoreCase ? 454 elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 453 foundPos = instance.options.ignoreCase ? 454 elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 455 455 elem.indexOf(entry, foundPos + 1); 456 456 … … 458 458 } 459 459 if (partial.length) 460 ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) 460 ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)); 461 461 return "<ul>" + ret.join('') + "</ul>"; 462 462 } … … 475 475 Field.activate(field); 476 476 }, 1); 477 } 477 }; 478 478 479 479 Ajax.InPlaceEditor = Class.create({ … … 605 605 }, 606 606 getText: function() { 607 return this.element.innerHTML ;607 return this.element.innerHTML.unescapeHTML(); 608 608 }, 609 609 handleAJAXFailure: function(transport) { … … 781 781 var js = transport.responseText.strip(); 782 782 if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check 783 throw 'Server returned an invalid collection representation.';783 throw('Server returned an invalid collection representation.'); 784 784 this._collection = eval(js); 785 785 this.checkForExternalText(); … … 938 938 }; 939 939 940 // Delayed observer, like Form.Element.Observer, 940 // Delayed observer, like Form.Element.Observer, 941 941 // but waits for delay after last key input 942 942 // Ideal for live-search fields … … 948 948 this.callback = callback; 949 949 this.timer = null; 950 this.lastValue = $F(this.element); 950 this.lastValue = $F(this.element); 951 951 Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); 952 952 },
Note:
See TracChangeset
for help on using the changeset viewer.