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

  ViewVC Help
Powered by ViewVC 1.1.26