javascripts/right-autocompleter-src.js in right-rails-0.4.4 vs javascripts/right-autocompleter-src.js in right-rails-0.5.0

- old
+ new

@@ -1,18 +1,18 @@ /** * The autocompletion feature implemented with RightJS * * Home page: http://rightjs.org/ui/autocompleter * - * @copyright (C) 2009 Nikolay V. Nemshilov aka St. + * @copyright (C) 2009-2010 Nikolay V. Nemshilov */ if (!RightJS) { throw "Gimme RightJS. Please." }; /** * The RightJS UI Autocompleter unit base class * - * Copyright (C) Nikolay V. Nemshilov aka St. + * Copyright (C) 2009-2010 Nikolay V. Nemshilov */ var Autocompleter = new Class(Observer, { extend: { EVENTS: $w('show hide update load select done'), @@ -25,63 +25,53 @@ threshold: 200, // the typing pause threshold cache: true, // the use the results cache local: null, // an optional local search results list - fxName: 'slide', - fxDuration: 'short', + fxName: 'slide', // list appearance fx name + fxDuration: 'short', // list appearance fx duration - spinner: 'native', + spinner: 'native', // spinner element reference - relName: 'autocompleter' + cssRule: '[rel^=autocompleter]' }, - // scans the document for autocompletion fields - rescan: function(scope) { - var key = Autocompleter.Options.relName; - var reg = new RegExp('^'+key+'+\\[(.*?)\\]$'); - - ($(scope)||document).select('input[rel^="'+key+'"]').each(function(input) { - if (!input.autocompleter) { - var data = input.get('data-'+key+'-options'); - var options = Object.merge(eval('('+data+')')); - var match = input.get('rel').match(reg); - - if (match) { - var url = match[1]; - - // if looks like a list of local options - if (url.match(/^['"].*?['"]$/)) { - options.local = eval('['+url+']'); - } else if (!url.blank()) { - options.url = url; - } - } - - new Autocompleter(input, options); - } - }); - } + current: null, // reference to the currently active options list + instances: {}, // the input <-> instance map + + // finds/instances an autocompleter for the event + find: function(event) { + var input = event.target; + if (input.match(Autocompleter.Options.cssRule)) { + var uid = $uid(input); + if (!Autocompleter.instances[uid]) + new Autocompleter(input); + } + }, + + // DEPRECATED scans the document for autocompletion fields + rescan: function(scope) { } }, /** * basic constructor * * @param mixed the input element reference, a string id or the element instance * @param Object options */ initialize: function(input, options) { + this.input = $(input); // don't low it down! this.$super(options); // storing the callbacks so we could detach them later this._watch = this.watch.bind(this); this._hide = this.hide.bind(this); - this.input = $(input).onKeyup(this._watch).onBlur(this._hide); + this.input.onKeyup(this._watch).onBlur(this._hide); this.container = $E('div', {'class': 'autocompleter'}).insertTo(this.input, 'after'); - this.input.autocompleter = this; + this.input.autocompleter = Autocompleter.instances[$uid(input)] = this; }, // kills the autocompleter destroy: function() { this.input.stopObserving('keyup', this._watch).stopObserving('blur', this._hide); @@ -89,11 +79,11 @@ return this; }, // catching up with some additonal options setOptions: function(options) { - this.$super(options); + this.$super(this.grabOptions(options)); // building the correct url template with a placeholder if (!this.options.url.includes('%{search}')) { this.options.url += (this.options.url.includes('?') ? '&' : '?') + this.options.param + '=%{search}'; } @@ -149,10 +139,32 @@ return this.fire('done').hide(); }, // protected + // trying to extract the input element options + grabOptions: function(options) { + var input = this.input; + var options = options || eval('('+input.get('data-autocompleter-options')+')') || {}; + var keys = Autocompleter.Options.cssRule.split('[').last().split(']')[0].split('^='), + key = keys[1], value = input.get(keys[0]), match; + + // trying to extract options + if (value && (match = value.match(new RegExp('^'+ key +'+\\[(.*?)\\]$')))) { + match = match[1]; + + // deciding whether it's a list of local options or an url + if (match.match(/^['"].*?['"]$/)) { + options.local = eval('['+ match +']'); + } else if (!match.blank()) { + options.url = match; + } + } + + return options; + }, + // works with the 'prev' and 'next' methods select: function(what, fallback) { var current = this.container.first('li.current'); if (current) { current = current[what]('li') || current; @@ -161,11 +173,11 @@ return this.fire('select', (current || fallback).radioClass('current')); }, // receives the keyboard events out of the input element watch: function(event) { - // skip the overlaping key codes that are already watched in the hooker.js + // skip the overlaping key codes that are already watched in the document.js if ([27,37,38,39,40,13].include(event.keyCode)) return; if (this.input.value.length >= this.options.minLength) { if (this.timeout) { this.timeout.cancel(); @@ -271,19 +283,16 @@ }); /** * The document events hooking * - * Copyright (C) Nikolay V. Nemshilov aka St. + * Copyright (C) 2009-2010 Nikolay V. Nemshilov */ document.on({ - ready: function() { - Autocompleter.rescan(); - }, - // the autocompletion list navigation keydown: function(event) { + // if there is an active options list, hijacking the navigation buttons if (Autocompleter.current) { var name; switch (event.keyCode) { case 27: name = 'hide'; break; @@ -296,9 +305,14 @@ if (name) { Autocompleter.current[name](); event.stop(); } + } + + // otherwise trying to find/instanciate an autocompliter + else { + Autocompleter.find(event); } } }); document.write("<style type=\"text/css\">div.autocompleter{display:none;position:absolute;z-index:100000000;border:none;margin:0;padding:0;background:white;-moz-box-shadow:#BBB .2em .2em .4em;-webkit-box-shadow:#BBB .2em .2em .4em;overflow:hidden}div.autocompleter ul{list-style:none;margin:0;padding:0;border:none;background:none;*border-bottom:1px solid #CCC}div.autocompleter ul li{list-style:none;font-weight:normal;margin:0;padding:.2em .4em;border:1px solid #CCC;border-top:none;border-bottom:none;background:none;cursor:pointer}div.autocompleter ul li:first-child{border-top:1px solid #CCC}div.autocompleter ul li:last-child{border-bottom:1px solid #CCC;*border-bottom:none}div.autocompleter ul li.current{background:#DDD}div.autocompleter-spinner{position:absolute;z-index:100000000;text-align:center;font-size:140%;font-family:Georgia;background:#DDD;color:#666;display:none;width:1em;margin:0;padding:0}div.autocompleter-spinner div.dot-1{margin-left:-4px}div.autocompleter-spinner div.dot-2{}div.autocompleter-spinner div.dot-3{margin-left:4px}</style>"); \ No newline at end of file