[ Index ] |
PHP Cross Reference of phpwcms V1.4.3 _r380 (23.11.09) |
[Summary view] [Print] [Text view]
1 /* Script: Autocompleter.js 2 3rd party script for managing autocomplete functionality. 3 4 Details: 5 Author - Harald Kirschner <mail [at] digitarald.de> 6 Refactored by - Aaron Newton <aaron [dot] newton [at] cnet [dot] com> 7 License - MIT-style license 8 Version - 1.0rc3 9 Source - http://digitarald.de/project/autocompleter/ 10 11 Dependencies: 12 Mootools 1.1 - <Class.Extras>, <Element.Event>, <Element.Selectors>, <Element.Form>, <Element.Dimensions>, <Fx.Style>, <Ajax>, <Json> 13 Autocompleter - <Observer.js> 14 15 Namespace: Autocompleter 16 All functions are part of the <Autocompleter> namespace. 17 */ 18 var Autocompleter = {}; 19 /* Class: Autocompleter.Base 20 Base class for the Autocompleter classes. 21 22 Arguments: 23 el - (DOM element or id) element to observe. 24 options - (object) key/value set of options. 25 26 Options: 27 minLength - (integer, default 1) Minimum length to start auto compliter 28 useSelection - (boolean, default true) Select completed text part (works only for appended strings) 29 markQuery - (boolean, default true) Mark queried string with <span class="autocompleter-queried">*</span> 30 inheritWidth - (boolean, default true) Inherit width for the autocompleter overlay from the input field 31 maxChoices - (integer, default 10). Maximum of suggested fields. 32 injectChoice - (function, optional). Callback for injecting the list element with the arguments (itemValue, itemIndex), take a look at updateChoices for default behaviour. 33 onSelect - Event Function. Fires when when an item gets selected; passed the input and the value selected. 34 onShow - Event Function. Fires when autocompleter list shows. 35 onHide - Event Function. Fires when autocompleter list hides. 36 customTarget - (element, optional). Allows to override the autocompleter list element with your own list element. 37 className - (string, default 'autocompleter-choices'). Class name for the list element. 38 zIndex - (integer, default 42). z-Index for the list element. 39 observerOptions - optional Options Object. For the Observer class. 40 fxOptions - optional Options Object. For the Fx.Style on the list element. 41 allowMulti - (boolean, defaults to false) allow more than one value, seperated by a delimeter 42 delimeter - (string) default delimter between multi values (defaults to ", ") 43 autotrim - (boolean) trim the delimeter on blur 44 allowDupes - (boolean, defaults to false) if multi, prevent duplicate entries 45 baseHref - (string) the base url where the css and image assets are located (defaults to cnet's servers you should change) 46 47 Note: 48 If you're not cnet, you should download these assets to your own local: 49 http://www.cnet.com/html/rb/assets/global/autocompleter/Autocompleter.css 50 http://www.cnet.com/html/rb/assets/global/autocompleter/spinner.gif 51 52 Then either change this script or pass in the local when you instantiate the class. 53 54 Example: 55 This base class is not used directly (but rather its inheritants are such as <Autocompleter.Ajax>) 56 so there is no example here. 57 */ 58 Autocompleter.Base = new Class({ 59 60 options: { 61 minLength: 1, 62 useSelection: true, 63 markQuery: true, 64 inheritWidth: true, 65 dropDownWidth: 100, 66 maxChoices: 10, 67 injectChoice: null, 68 onSelect: Class.empty, 69 onShow: Class.empty, 70 onHide: Class.empty, 71 customTarget: null, 72 className: 'autocompleter-choices', 73 zIndex: 42, 74 observerOptions: {}, 75 fxOptions: {}, 76 multi: false, 77 delimeter: ', ', 78 autotrim: true, 79 allowDupes: false, 80 /* if you're not cnet, you should download these assets to your own local: 81 http://www.cnet.com/html/rb/assets/global/autocompleter/Autocompleter.css 82 http://www.cnet.com/html/rb/assets/global/autocompleter/spinner.gif 83 */ 84 baseHref: 'http://www.cnet.com/html/rb/assets/global/autocompleter/' 85 }, 86 87 initialize: function(el, options) { 88 this.setOptions(options); 89 if(!$('AutocompleterCss')) window.addEvent('domready', function(){ 90 new Asset.css(this.options.baseHref+'Autocompleter.css', {id: 'AutocompleterCss'}); 91 }.bind(this)); 92 this.element = $(el); 93 this.build(); 94 this.observer = new Observer(this.element, this.prefetch.bind(this), $merge({ 95 delay: 400 96 }, this.options.observerOptions)); 97 this.value = this.observer.value; 98 this.queryValue = null; 99 this.element.addEvent('blur', function(e){ 100 this.autoTrim.delay(50, this, e); 101 }.bind(this)); 102 this.addEvent('onSelect', function(){ 103 this.element.focus(); 104 this.userChose = true; 105 (function(){ 106 this.userChose = false; 107 }).delay(100, this); 108 }.bind(this)); 109 }, 110 111 /* Property: build 112 Builds the html structure for choices and appends the events to the element. 113 Override this function to modify the html generation. */ 114 115 build: function() { 116 if ($(this.options.customTarget)) this.choices = this.options.customTarget; 117 else { 118 this.choices = new Element('ul', { 119 'class': this.options.className, 120 'styles': {zIndex: this.options.zIndex} 121 }).injectInside(document.body); 122 this.fix = new OverlayFix(this.choices); 123 } 124 this.fx = this.choices.effect('opacity', $merge({wait: false, duration: 200}, this.options.fxOptions)) 125 .addEvent('onStart', function() { 126 if (this.fx.now) return; 127 this.choices.setStyle('display', ''); 128 this.fix.show(); 129 }.bind(this)) 130 .addEvent('onComplete', function() { 131 if (this.fx.now) return; 132 this.choices.setStyle('display', 'none'); 133 this.fix.hide(); 134 }.bind(this)).set(0); 135 this.element.setProperty('autocomplete', 'off') 136 .addEvent((window.ie || window.webkit ) ? 'keydown' : 'keypress', 137 this.onCommand.bindWithEvent(this)) 138 .addEvent('mousedown', this.onCommand.bindWithEvent(this, [true])) 139 .addEvent('focus', this.toggleFocus.bind(this, [true])) 140 .addEvent('blur', this.toggleFocus.bind(this, [false])) 141 .addEvent('trash', this.destroy.bind(this)); 142 }, 143 144 autoTrim: function(e){ 145 if(this.userChose) return this.userChose = false; 146 var del = this.options.delimeter; 147 var val = this.element.getValue(); 148 if(this.options.autotrim && val.test(del+"$")){ 149 e = new Event(e); 150 this.observer.value = this.element.value = val.substring(0, val.length-del.length); 151 } 152 return this.observer.value 153 }, 154 /* Property: getQueryValue 155 Returns the user's input to use to match against the full list. When options.multi == true, this value is the last entered string after the last index of the delimeter. 156 157 Arguments: 158 value - (string) optional, the value to clean; defaults to this.observer.value 159 160 Examples: 161 (start code) 162 user input: blue 163 getQueryValue() returns "blue" 164 165 user input: blue, green, yellow 166 options.multi = true 167 options.delimter = ", " 168 getQueryValue() returns "yellow" 169 170 user input: blue, green, yellow, 171 options.multi = true 172 options.delimter = ", " 173 getQueryValue() returns "" 174 (end) 175 */ 176 getQueryValue: function(value){ 177 value = $pick(value, this.observer.value); 178 return (this.options.multi)?value.lastElement(this.options.delimeter).toString():value||''; 179 }, 180 181 /* Property: destroy 182 Remove the autocomplete functionality from the input. 183 */ 184 destroy: function() { 185 this.choices.remove(); 186 }, 187 188 toggleFocus: function(state) { 189 this.focussed = state; 190 if (!state) this.hideChoices(); 191 }, 192 193 onCommand: function(e, mouse) { 194 var val = this.getQueryValue(); 195 if (mouse && this.focussed) this.prefetch(); 196 if (e.key) switch (e.key) { 197 case 'enter': 198 if (this.selected && this.visible) { 199 this.choiceSelect(this.selected); 200 e.stop(); 201 } return; 202 case 'up': case 'down': 203 if (this.getQueryValue() != (val || this.queryValue)) this.prefetch(); 204 else if (this.queryValue === null) break; 205 else if (!this.visible) this.showChoices(); 206 else { 207 this.choiceOver((e.key == 'up') 208 ? this.selected.getPrevious() || this.choices.getLast() 209 : this.selected.getNext() || this.choices.getFirst() ); 210 this.setSelection(); 211 } 212 e.stop(); return; 213 case 'esc': case 'tab': 214 this.hideChoices(); 215 if (this.options.multi) this.element.value = this.element.getValue().trimLastElement(); 216 return; 217 } 218 this.value = false; 219 }, 220 221 setSelection: function() { 222 if (!this.options.useSelection) return; 223 var del = this.options.delimeter; 224 var qVal = this.getQueryValue(this.queryValue); 225 var elVal = this.getQueryValue(this.element.getValue()); 226 var startLength; 227 if(this.options.multi) { 228 var index = this.queryValue.lastIndexOf(del); 229 var delLength = (index<0)?0:del.length; 230 startLength = qVal.length+(index<0?0:index)+delLength; 231 } else startLength = qVal.length; 232 233 if (elVal.indexOf(qVal) != 0) return; 234 var insert = this.selected.inputValue.substr(startLength); 235 if (window.ie) { 236 var sel = document.selection.createRange(); 237 sel.text = insert; 238 sel.move("character", - insert.length); 239 sel.findText(insert); 240 sel.select(); 241 } else { 242 var offset = (this.options.multi && this.element.value.test(del))? 243 this.element.getValue().length-elVal.length+qVal.length 244 :this.queryValue.length; 245 this.element.value = this.element.value.substring(0, offset) + insert; 246 this.element.selectionStart = offset; 247 this.element.selectionEnd = this.element.value.length; 248 } 249 this.value = this.observer.value = this.element.value; 250 }, 251 /* Property: hideChoices 252 Hides the choices from the user. 253 */ 254 hideChoices: function() { 255 if (!this.visible) return; 256 this.visible = this.value = false; 257 this.observer.clear(); 258 this.fx.start(0); 259 this.fireEvent('onHide', [this.element, this.choices]); 260 }, 261 262 /* Property: showChoices 263 Shows the choices to the user. 264 */ 265 showChoices: function() { 266 if (this.visible || !this.choices.getFirst()) return; 267 this.visible = true; 268 var pos = this.element.getCoordinates(this.options.overflown); 269 this.choices.setStyles({'left': pos.left, 'top': pos.bottom}); 270 this.choices.setStyle('width', (this.options.inheritWidth)?pos.width:this.options.dropDownWidth); 271 this.fx.start(1); 272 this.choiceOver(this.choices.getFirst()); 273 this.fireEvent('onShow', [this.element, this.choices]); 274 }, 275 276 prefetch: function() { 277 var val = this.getQueryValue(this.element.getValue()); 278 if (val.length < this.options.minLength) this.hideChoices(); 279 else if (val == this.queryValue) this.showChoices(); 280 else this.query(); 281 }, 282 283 updateChoices: function(choices) { 284 this.choices.empty(); 285 this.selected = null; 286 if (!choices || !choices.length) return; 287 if (this.options.maxChoices < choices.length) choices.length = this.options.maxChoices; 288 choices.each(this.options.injectChoice || function(choice, i){ 289 var el = new Element('li').setHTML(this.markQueryValue(choice)); 290 el.inputValue = choice; 291 this.addChoiceEvents(el).injectInside(this.choices); 292 }, this); 293 this.showChoices(); 294 }, 295 296 choiceOver: function(el) { 297 if (this.selected) this.selected.removeClass('autocompleter-selected'); 298 this.selected = el.addClass('autocompleter-selected'); 299 }, 300 301 choiceSelect: function(el) { 302 if(this.options.multi) { 303 var del = this.options.delimeter; 304 var value = (this.element.value.trimLastElement(del) + el.inputValue).split(del); 305 var fin = []; 306 if (!this.options.allowDupes) { 307 value.each(function(item){ 308 if(fin.contains(item))fin.remove(item); //move it to the end 309 fin.include(item); 310 }) 311 } else fin = value; 312 this.observer.value = this.element.value = fin.join(del)+del; 313 } else this.observer.value = this.element.value = el.inputValue; 314 315 316 this.hideChoices(); 317 this.fireEvent('onSelect', [this.element, el.inputValue], 20); 318 }, 319 320 /* Property: markQueryValue 321 Marks the queried word in the given string with <span class="autocompleter-queried">*</span> 322 Call this i.e. from your custom parseChoices, same for addChoiceEvents 323 324 Arguments: 325 txt - (string) the string to mark 326 */ 327 markQueryValue: function(txt) { 328 var val = (this.options.multi)?this.lastQueryElementValue:this.queryValue; 329 return (this.options.markQuery && val) ? txt.replace(new RegExp('^(' + val.escapeRegExp() + ')', 'i'), '<span class="autocompleter-queried">$1</span>') : txt; 330 }, 331 332 /* Property: addChoiceEvents 333 Appends the needed event handlers for a choice-entry to the given element. 334 335 Arguments: 336 el - (DOM element or id) the element to add 337 */ 338 addChoiceEvents: function(el) { 339 return el.addEvents({ 340 'mouseover': this.choiceOver.bind(this, [el]), 341 'mousedown': this.choiceSelect.bind(this, [el]) 342 }); 343 }, 344 query: Class.empty 345 }); 346 347 Autocompleter.Base.implement(new Events); 348 Autocompleter.Base.implement(new Options); 349 350 /* Class: OverlayFix 351 Private class used by <Autocompleter> - basically an <IframeShim>. 352 */ 353 var OverlayFix = new Class({ 354 355 initialize: function(el) { 356 this.element = $(el); 357 if (window.ie){ 358 this.element.addEvent('trash', this.destroy.bind(this)); 359 this.fix = new Element('iframe', { 360 'properties': {'frameborder': '0', 'scrolling': 'no', 'src': 'javascript:false;'}, 361 'styles': {'position': 'absolute', 'border': 'none', 'display': 'none', 'filter': 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)'}}) 362 .injectAfter(this.element); 363 } 364 }, 365 366 show: function() { 367 if (this.fix) this.fix.setStyles($extend( 368 this.element.getCoordinates(), {'display': '', 'zIndex': (this.element.getStyle('zIndex') || 1) - 1})); 369 return this; 370 }, 371 372 hide: function() { 373 if (this.fix) this.fix.setStyle('display', 'none'); 374 return this; 375 }, 376 377 destroy: function() { 378 this.fix.remove(); 379 } 380 381 }); 382 383 String.extend({ 384 lastElement: function(separator){ 385 separator = separator || ' '; 386 var txt = this; //(separator.test(' $'))?this:this.trim(); 387 var index = txt.lastIndexOf(separator); 388 var result = (index == -1)? txt: txt.substr(index + separator.length, txt.length); 389 return result; 390 }, 391 392 393 trimLastElement: function(separator){ 394 separator = separator || ' '; 395 var txt = this; //(separator.test(' $'))?this:this.trim(); 396 var index = this.lastIndexOf(separator); 397 return (index == -1)? "": txt.substr(0, index + separator.length); 398 } 399 }); 400 401 /* do not edit below this line */ 402 /* Section: Change Log 403 404 $Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/common/3rdParty/Autocomplete/Autocompleter.js,v $ 405 $Log: Autocompleter.js,v $ 406 Revision 1.7 2008/02/21 20:07:58 newtona 407 fixing a typo in Autocompleter 408 409 Revision 1.6 2007/10/29 18:28:57 newtona 410 fixed a bug in autocompleter, see: http://forum.mootools.net/viewtopic.php?pid=31481#p31481 411 412 Revision 1.5 2007/09/05 18:36:58 newtona 413 fixing all js warnings in the code base; they weren't breaking anything, but they can create performance issues and it's good practice... 414 415 Revision 1.4 2007/08/15 01:03:30 newtona 416 Added more event info for Autocompleter.js 417 Slimbox no longer adds css to the page if there aren't any images found for the instance 418 Iframeshim now exits quietly if you try and position it before the dom is ready 419 jsonp now handles having more than one request open at a time 420 removed a console.log statement from window.cnet.js (shame on me for leaving it there) 421 422 Revision 1.3 2007/06/12 20:26:52 newtona 423 *** empty log message *** 424 425 Revision 1.2 2007/06/07 18:43:35 newtona 426 added CSS to autocompleter.js 427 removed string.cnet.js dependencies from template parser and stickyWin.default.layout.js 428 429 Revision 1.1 2007/06/02 01:35:17 newtona 430 *** empty log message *** 431 432 433 */
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Dec 30 05:55:15 2009 | Cross-referenced by PHPXref 0.7 |