[ Index ]

PHP Cross Reference of phpwcms V1.4.3 _r380 (23.11.09)

title

Body

[close]

/include/inc_js/mootools/cnet/ -> Autocompleter.js (source)

   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  */


Generated: Wed Dec 30 05:55:15 2009 Cross-referenced by PHPXref 0.7