/[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 65 - (show annotations)
Sun Sep 3 10:36:48 2006 UTC (17 years, 6 months ago) by dpavlin
File MIME type: application/javascript
File size: 7361 byte(s)
a little tweak to suggest
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 // all tags indexed by first letter
20 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 // all tags from previous event
38 this.last_tags = '';
39 // just last tag
40 this.last_tag = '';
41
42 // parse all tags from html
43 var obj = this;
44 $( this.tags_selector ).each( function(i) {
45 var n = this.firstChild.nodeValue;
46 var c = n.substr(0,1);
47
48 obj.t.obj[n] = this;
49 obj.t.exists[n] = true;
50 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
56 this.onclick = function() {
57 return obj.tag( n );
58 }
59 this.href = '#'+i; // FIXME debug
60 });
61 $.log.info( 'found ' + this.t.obj.length + ' tags' );
62
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 var ret = true;
69
70 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 obj.focus();
81 obj.take_suggested();
82 return false;
83 case 13: // return
84 if (obj.current_selected != null) obj.focus();
85 return true;
86 case 8: // backspace
87 case 46: // del
88 ret = false;
89 break;
90 case 37: // left
91 case 39: // right
92 return true;
93 }
94
95
96 var t = obj.current_tag();
97
98 if (t == '') return false;
99
100 $.log.debug('tag: ' + t + ' ['+t.length+']');
101
102 obj.clean_suggested();
103 obj.suggest(t);
104
105 return ret;
106
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 var c = partial.substr(0,1);
129
130 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 jQuery.className.add( this.t.obj[t], 'suggested' );
137
138 suggest += '<a href="#"';
139 if (this.suggested.length == 0) suggest += ' class="suggested" ';
140 suggest += '">' + t + '</a> ';
141 this.suggested.push(t);
142 }
143 }
144
145 $(this.suggest_selector).html( suggest );
146 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 obj.add_tag( t );
152 });
153 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 $.log.debug('take_suggested: '+s);
189 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 var tags = this.entered.ordered.join(' ');
264 if (tags.length) tags += ' ';
265 $.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 if (this.entered.ordered.length) $('.entered').removeClass('entered');
288
289 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 var t = this;
312 if (tags != this.last_tags) {
313 this.last_tags = tags;
314 defer(function() { t.parse() }, 500, 'parse');
315 }
316 var last_tag = tags.replace(/^([^ ][^ ]* )*/, '');
317 if (last_tag != this.last_tag) {
318 this.last_tag = last_tag;
319 } else {
320 defer(function() { t.parse() }, 500, 'parse');
321 }
322 return this.last_tag;
323 }
324
325 tac.prototype.focus = function() {
326 // $(this.tags_input).focus() doesn't work!
327 document.getElementById('tags').focus();
328 }
329
330 // example usage
331 var tags;
332 $(document).ready( function() {
333 tags = new tac();
334 });
335

  ViewVC Help
Powered by ViewVC 1.1.26