/[jquery]/tag_complete/jquery-tac.js
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /tag_complete/jquery-tac.js

Parent Directory Parent Directory | Revision Log Revision Log


Revision 61 - (hide annotations)
Wed Aug 23 15:58:16 2006 UTC (17 years, 8 months ago) by dpavlin
File MIME type: application/javascript
File size: 7363 byte(s)
defer parsing of tags for 0.5s to have better interactivity
1 dpavlin 54 /*
2     tag auto-completer written with jquery (prototype style)
3    
4     2006-08-20 Dobrica Pavlinusic <dpavlin@rot13.org>
5     */
6    
7     function tac(
8     tag_class, // tags are elements with this class
9     tags_form_id, // id of tags edit form
10     foo
11     ) {
12    
13     // all indexed by tag name
14     this.t = {
15     obj: new Array(), // html objects
16     exists: new Array()
17     };
18    
19 dpavlin 59 // all tags indexed by first letter
20 dpavlin 54 this.all_tags = new Array();
21     this.suggested = new Array();
22    
23     this.entered = { // array of entered tags
24     ordered: new Array(), // tags entered in original order
25     tag: new Array() // mapping from tag name to order number
26     };
27    
28     this.current_selected = null; // current selected suggestion
29    
30     this.tags_selector = '.tag';
31     if (tag_class) this.tags_selector = '.'+tag_class;
32     this.tags_form = '#tags_form';
33     if (tags_form_id) this.tags_form = '#'+tags_form_id;
34     this.suggest_selector = '#suggest';
35     this.tags_input = '#tags';
36    
37 dpavlin 58 // all tags from previous event
38 dpavlin 54 this.last_tags = '';
39 dpavlin 58 // just last tag
40     this.last_tag = '';
41 dpavlin 54
42     // parse all tags from html
43     var obj = this;
44     $( this.tags_selector ).each( function(i) {
45     var n = this.firstChild.nodeValue;
46 dpavlin 59 var c = n.substr(0,1);
47 dpavlin 54
48     obj.t.obj[n] = this;
49     obj.t.exists[n] = true;
50 dpavlin 59 if (obj.all_tags[c] == null) {
51     obj.all_tags[c] = new Array( n );
52     } else {
53     obj.all_tags[c].push( n );
54     }
55 dpavlin 54
56     this.onclick = function() {
57     return obj.tag( n );
58     }
59     this.href = '#'+i; // FIXME debug
60     });
61 dpavlin 59 $.log.info( 'found ' + this.t.obj.length + ' tags' );
62 dpavlin 54
63     $.log.info( 'hook onchange to '+this.tags_form );
64     $( this.tags_form ).keyup( function(e) {
65    
66     $.log.debug('keyup: '+e.keyCode);
67    
68 dpavlin 58 var ret = true;
69    
70 dpavlin 54 switch (e.keyCode) {
71     case 38: // up
72     e.preventDefault();
73     obj.move_suggested( -1 );
74     return false;
75     case 40: // down
76     e.preventDefault();
77     obj.move_suggested( +1 );
78     return false;
79     case 9: // tab
80 dpavlin 56 obj.focus();
81     obj.take_suggested();
82     return false;
83 dpavlin 54 case 13: // return
84     if (obj.current_selected != null) obj.focus();
85 dpavlin 56 return true;
86 dpavlin 54 case 8: // backspace
87     case 46: // del
88 dpavlin 58 ret = false;
89 dpavlin 59 break;
90 dpavlin 58 case 37: // left
91     case 39: // right
92     return true;
93 dpavlin 54 }
94    
95    
96     var t = obj.current_tag();
97    
98 dpavlin 58 if (t == '') return false;
99    
100 dpavlin 54 $.log.debug('tag: ' + t + ' ['+t.length+']');
101    
102     obj.clean_suggested();
103 dpavlin 58 obj.suggest(t);
104 dpavlin 54
105 dpavlin 58 return ret;
106 dpavlin 54
107     }).submit( function() {
108     $.log.debug('submit');
109     return obj.take_suggested();
110     }).blur( function() {
111     $.log.debug('blur');
112     return obj.take_suggested();
113     });
114    
115     $.log.toggle();
116    
117     this.focus();
118     }
119    
120     tac.prototype.suggest = function( partial ) {
121    
122     $.log.info('suggest to '+partial);
123    
124     var len = partial.length;
125     var suggest = '';
126     this.suggested = new Array();
127    
128 dpavlin 59 var c = partial.substr(0,1);
129 dpavlin 54
130 dpavlin 59 for(var i = 0; i < this.all_tags[ c ].length; i++) {
131     var t = this.all_tags[c][i];
132     if ( t.substr(0, len) == partial && this.entered.tag[ t ] == null) {
133    
134     //$.log.debug('suggested: ' + t );
135    
136 dpavlin 54 jQuery.className.add( this.t.obj[t], 'suggested' );
137    
138 dpavlin 55 suggest += '<a href="#"';
139 dpavlin 54 if (this.suggested.length == 0) suggest += ' class="suggested" ';
140 dpavlin 55 suggest += '">' + t + '</a> ';
141 dpavlin 54 this.suggested.push(t);
142     }
143     }
144    
145     $(this.suggest_selector).html( suggest );
146 dpavlin 55 var obj = this;
147     $(this.suggest_selector+' a').click( function() {
148     var t = this.firstChild.nodeValue;
149     $.log.debug('click add: '+ t);
150     obj.clean_suggested();
151 dpavlin 57 obj.add_tag( t );
152 dpavlin 55 });
153 dpavlin 54 this.current_suggested = 0;
154     $.log.info('suggested ' + this.suggested.length + ' tags');
155    
156    
157     }
158    
159     tac.prototype.clean_suggested = function() {
160     for(var i = 0; i < this.suggested.length; i++) {
161     var t = this.suggested[i];
162     jQuery.className.remove( this.t.obj[t], 'suggested' );
163     }
164    
165     var c = this.current_suggested;
166     if (c == null) return c;
167    
168     $(this.suggest_selector).html('');
169     this.current_suggested = null;
170    
171     return c;
172     }
173    
174     tac.prototype.take_suggested = function() {
175     var c = this.current_suggested;
176     if (c == null) {
177     $.log.debug('no current_select, return true');
178     return true;
179     }
180    
181     var s = $(this.suggest_selector+' a:nth('+c+')').html();
182     if (s == null) {
183     $.log.debug('no suggest, return true');
184     return true;
185     }
186    
187     var i = this.suggested[c].i;
188 dpavlin 55 $.log.debug('take_suggested: '+s);
189 dpavlin 54 this.add_tag( s );
190     return false;
191     }
192    
193     tac.prototype.move_suggested = function( where ) {
194     var c = this.current_suggested;
195     if (c == null) {
196     $.log.error('this.current_suggested is null');
197     return;
198     }
199     var to = c + where;
200     $.log.info('move_suggested('+where+') '+c+' -> '+to);
201     if (to < 0 || to >= this.suggested.length) {
202     $.log.error('move to invalid element '+to);
203     return;
204     }
205     var s = this.suggest_selector+' a:nth('+c+')';
206     $( s ).removeClass('suggested');
207     $.log.debug('remove suggested from '+s);
208     s = this.suggest_selector+' a:nth('+to+')';
209     $( s ).addClass('suggested');
210     $.log.debug('add suggested to '+s);
211     this.current_suggested = to;
212     }
213    
214     /*
215     tag operations
216     */
217    
218     tac.prototype.add_tag = function( t ) {
219     $.log.info('add '+t);
220     this.focus();
221    
222     jQuery.className.add( this.t.obj[t], 'entered' );
223    
224     var last = this.entered.ordered.length;
225     this.entered.ordered[ last ] = t;
226     this.entered.tag[t] = last;
227    
228     this.clean_suggested();
229    
230     // strip last tag and add new one
231     $(this.tags_input).val(
232     $(this.tags_input).val().replace(
233     /[^ ]*$/, t + ' '
234     )
235     );
236     return false;
237     }
238    
239     tac.prototype.remove_tag = function( t ) {
240     $.log.info('remove '+t);
241    
242     this.focus();
243    
244     var o = this.entered.tag[t];
245     if (o == null) {
246     $.log.error('remove non-entered tag '+t);
247     return false;
248     }
249    
250     jQuery.className.remove( this.t.obj[t], 'entered' );
251    
252     // remove suggested tag and rebuild tags
253    
254     this.entered.ordered.splice(o,1);
255     this.entered.tag[t] = null;
256    
257     for (var i = o; i < this.entered.ordered.length; i++) {
258     this.entered.tag[ this.entered.ordered[i] ] = i;
259     }
260    
261     this.clean_suggested();
262    
263 dpavlin 57 var tags = this.entered.ordered.join(' ');
264     if (tags.length) tags += ' ';
265 dpavlin 54 $.log.debug('removed '+o+' left: '+tags);
266     $(this.tags_input).val( tags );
267     return false;
268     }
269    
270     tac.prototype.tag = function( t ) {
271     if (this.entered.tag[t] != null) {
272     this.remove_tag( t );
273     } else {
274     this.add_tag( t );
275     }
276     }
277    
278     /*
279     parse current string into tags
280     */
281    
282     tac.prototype.parse = function() {
283     $.log.info('re-parse tags');
284    
285     var t = $(this.tags_input).val().replace(/^ */,'').replace(/ *$/,'').split(/ /);
286    
287 dpavlin 58 if (this.entered.ordered.length) $('.entered').removeClass('entered');
288    
289 dpavlin 54 this.entered = {
290     ordered: new Array(),
291     tag: new Array()
292     };
293    
294     var debug = '';
295     for (var i = 0; i < t.length; i++) {
296     var tag = t[i];
297     if (this.t.exists[ tag ] != null) {
298     this.entered.tag[tag] = this.entered.ordered.length;
299     this.entered.ordered.push( tag );
300     jQuery.className.add( this.t.obj[ tag ], 'entered' );
301     debug += tag + ' '
302     } else {
303     debug += '{'+tag+'} ';
304     }
305     }
306     $.log.debug('parsed '+debug+' to '+this.entered.ordered.join(','));
307     }
308    
309     tac.prototype.current_tag = function() {
310     var tags = $(this.tags_input).val();
311 dpavlin 61 var t = this;
312 dpavlin 54 if (tags != this.last_tags) {
313     this.last_tags = tags;
314 dpavlin 61 defer(function() { t.parse() }, 500, 'parse');
315 dpavlin 54 }
316 dpavlin 58 var last_tag = tags.replace(/^([^ ][^ ]* )*/, '');
317     if (last_tag != this.last_tag) {
318     this.last_tag = last_tag;
319     return last_tag;
320     } else {
321 dpavlin 61 defer(function() { t.parse() }, 500, 'parse');
322 dpavlin 58 return '';
323     }
324 dpavlin 54 }
325    
326     tac.prototype.focus = function() {
327     // $(this.tags_input).focus() doesn't work!
328     document.getElementById('tags').focus();
329     }
330    
331     // example usage
332     var tags;
333     $(document).ready( function() {
334     tags = new tac();
335     });
336    

  ViewVC Help
Powered by ViewVC 1.1.26