/[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 57 - (show annotations)
Sun Aug 20 17:25:13 2006 UTC (17 years, 8 months ago) by dpavlin
File MIME type: application/javascript
File size: 6818 byte(s)
correct suggestion clicks (it should always add tag), remove_tag will now leave
text box empty if you deselect all
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 // 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 obj.focus();
71 obj.take_suggested();
72 return false;
73 case 13: // return
74 if (obj.current_selected != null) obj.focus();
75 return true;
76 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 suggest += '<a href="#"';
123 if (this.suggested.length == 0) suggest += ' class="suggested" ';
124 suggest += '">' + t + '</a> ';
125 this.suggested.push(t);
126 }
127 }
128
129 $(this.suggest_selector).html( suggest );
130 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.add_tag( t );
136 });
137 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 $.log.debug('take_suggested: '+s);
173 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 if (tags.length) tags += ' ';
249 $.log.debug('removed '+o+' left: '+tags);
250 $(this.tags_input).val( tags );
251 return false;
252 }
253
254 tac.prototype.tag = function( t ) {
255 if (this.entered.tag[t] != null) {
256 this.remove_tag( t );
257 } else {
258 this.add_tag( t );
259 }
260 }
261
262 /*
263 parse current string into tags
264 */
265
266 tac.prototype.parse = function() {
267 $.log.info('re-parse tags');
268
269 var t = $(this.tags_input).val().replace(/^ */,'').replace(/ *$/,'').split(/ /);
270
271 this.entered = {
272 ordered: new Array(),
273 tag: new Array()
274 };
275
276 if (this.entered.ordered.length) $('.entered').removeClass('entered');
277
278 var debug = '';
279 for (var i = 0; i < t.length; i++) {
280 var tag = t[i];
281 if (this.t.exists[ tag ] != null) {
282 this.entered.tag[tag] = this.entered.ordered.length;
283 this.entered.ordered.push( tag );
284 jQuery.className.add( this.t.obj[ tag ], 'entered' );
285 debug += tag + ' '
286 } else {
287 debug += '{'+tag+'} ';
288 }
289 }
290 $.log.debug('parsed '+debug+' to '+this.entered.ordered.join(','));
291
292 }
293
294 tac.prototype.current_tag = function() {
295 var tags = $(this.tags_input).val();
296 if (tags != this.last_tags) {
297 this.last_tags = tags;
298 this.parse();
299 }
300 return tags.replace(/^([^ ][^ ]* )*/, '');
301 }
302
303 tac.prototype.focus = function() {
304 // $(this.tags_input).focus() doesn't work!
305 document.getElementById('tags').focus();
306 }
307
308 // example usage
309 var tags;
310 $(document).ready( function() {
311 tags = new tac();
312 });
313

  ViewVC Help
Powered by ViewVC 1.1.26