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

Contents of /tag_complete/jquery-tac.js

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26