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

  ViewVC Help
Powered by ViewVC 1.1.26