--- tag_complete/tag_complete.js 2006/08/18 17:23:51 41 +++ tag_complete/tag_complete.js 2006/08/19 23:11:12 53 @@ -7,59 +7,260 @@ var _tag = { name: new Array(), obj: new Array(), - selected_name: new Array(), - selected_obj: new Array() + tag2i: new Array(), + selected: new Array(), + entered: { // array of entered tags + i: new Array(), // offset to tags + tag: new Array() // mapping from tag i to entered + }, + + current: null, // current selected suggestion + last_tags: '', + + select: function( tag ) { + + $.log.info('filter '+tag); + + var j = 0; + var tag_len = tag.length; + var suggest = ''; + + for(var i = 0; i < _tag.name.length; i++) { + var t = _tag.name[i]; + if ( t.substr(0, tag_len) == tag ) { + $.log.debug('selected ['+i+']: ' + t ); + + jQuery.className.add( _tag.obj[i], 'selected' ); + + suggest += '' + t + ' '; + _tag.selected[j++] = { + i: i, + obj: _tag.obj[i] + } + } + } + + $('#suggest').html( suggest ); + if (j > 0) { + $('#suggest a:nth(0)').addClass('selected'); + _tag.current = 0; + } + + }, + + clean_selected: function() { + for(var i = 0; i < _tag.selected.length; i++) { + jQuery.className.remove( _tag.selected[i].obj, 'selected' ); + } + + var c = _tag.current; + if (c == null) return c; + + $('#suggest').html(''); + _tag.current = null; + + return c; + }, + + + take_suggested: function() { + var c = _tag.current; + if (c == null) { + $.log.debug('no current, return true'); + return true; + } + + var s = $('#suggest a:nth('+c+')').html(); + if (s == null) { + $.log.debug('no suggest, return true'); + return true; + } + + var i = _tag.selected[c].i; + $.log.debug('take_suggested '+i+':'+s); + _tag.add_tag( s, i ); + return false; + }, + + add_tag: function( t, i ) { + $.log.info('add '+i+': '+t); + _tag.focus(); + jQuery.className.add( _tag.obj[i], 'entered' ); + var entered_i = _tag.entered.i.length; + _tag.entered.i[ entered_i ] = i; + _tag.entered.tag[i] = entered_i; + _tag.clean_selected(); + $('#tags').val( + $('#tags').val().replace( + /[^ ]*$/, t + ' ' + ) + ); + return false; + }, + + remove_tag: function( t, i ) { + $.log.info('remove '+i+': '+t); + _tag.focus(); + jQuery.className.remove( _tag.obj[i], 'entered' ); + // remove selected tag and rebuild tags + + var ent_i = _tag.entered.tag[i]; + $.log.debug('entered i:'+ent_i); + _tag.entered.i.splice(ent_i,1); + _tag.entered.tag[i] = null; + + $.log.debug("tags "+_tag.entered.i.join(",")); + + var tags = ''; + for (var j = 0; j < _tag.entered.i.length; j++) { + var tag_i = _tag.entered.i[j]; + tags += _tag.name[ tag_i ] + ' '; + _tag.entered.tag[ tag_i ] = j; + } + + _tag.clean_selected(); + $.log.debug('tags left: '+tags); + $('#tags').val( tags ); + return false; + }, + + tag: function( t, i ) { + if (_tag.entered.tag[i] != null) { + _tag.remove_tag( t, i ); + } else { + _tag.add_tag( t, i ); + } + }, + + move_suggested: function( where ) { + var c = _tag.current; + if (c == null) { + $.log.error('_tag.current is null'); + return; + } + var to = c + where; + $.log.info('move_suggested('+where+') '+c+' -> '+to); + if (to < 0 || to >= _tag.selected.length) { + $.log.error('move to invalid element '+to); + return; + } + var s = '#suggest a:nth('+c+')'; + $( s ).removeClass('selected'); + $.log.debug('remove selected from '+s); + s = '#suggest a:nth('+to+')'; + $( s ).addClass('selected'); + $.log.debug('add selected to '+s); + _tag.current = to; + }, + + parse: function() { + $.log.info('re-parse tags'); + + var t = $('#tags').val().replace(/^ */,'').replace(/ *$/,'').split(/ /); + + _tag.entered = { + i: new Array(), + tag: new Array() + }; + + $('.entered').removeClass('entered'); + + var ids = ''; + for (var i = 0; i < t.length; i++) { + var tag = t[i]; + if (_tag.tag2i[ tag ] != null) { + var tag_i = _tag.tag2i[ tag ]; + _tag.entered.tag[ tag ] = + _tag.entered.i.push( tag_i ) - 1; + jQuery.className.add( _tag.obj[ tag_i ], 'entered' ); + ids += i + ':' + tag_i + ' '; + } else { + ids += i + ':{' + tag + '} '; + } + } + $.log.debug('tags: '+t.join(','), 'ids:'+ids); + + }, + + current_tag: function() { + var tags = $('#tags').val(); + if (tags != _tag.last_tags) { + _tag.last_tags = tags; + _tag.parse(); + } + return tags.replace(/^([^ ][^ ]* )*/, ''); + }, + + focus: function() { + // $('#tags').focus() doesn't work! + document.getElementById('tags').focus(); + } + }; $(document).ready( function() { $('.tag').each( function(i) { - _tag.name[i] = this.firstChild.nodeValue; + var n = this.firstChild.nodeValue; + _tag.name[i] = n; _tag.obj[i] = this; + _tag.tag2i[n] = i; + this.onclick = function() { + return _tag.tag( n, i ); + } + this.href = '#'+i; // FIXME debug }); $.log.info( 'found ' + _tag.name.length + ' tags' ); $.log.info( 'hook onchange to #tags_form' ); - $('#tags_form').change( function() { - $.log.debug('submit #tags_form'); - //this.submit(); - return false; - }).keyup( function() { - var t = $('#tags').val().replace(/^([^ ][^ ]* )*/, ''); - - if (! t ) return; + $('#tags_form').keyup( function(e) { - $.log.debug('tag: ' + t); + $.log.debug('keyup: '+e.keyCode); - for(var i = 0; i < _tag.selected_obj.length; i++) { - jQuery.className.remove( _tag.selected_obj[i], 'selected' ); + switch (e.keyCode) { + case 38: // up + e.preventDefault(); + _tag.move_suggested( -1 ); + return false; + case 40: // down + e.preventDefault(); + _tag.move_suggested( +1 ); + return false; + case 9: // tab + case 13: // return + if (_tag.current != null) _tag.focus(); + e.preventDefault(); + return false; + case 8: // backspace + case 46: // del + _tag.parse(); + return false; } - _tag.selected_name = Array(); - _tag.selected_obj = Array(); - var j = 0; - if (t == '') return; + var t = _tag.current_tag(); - for(var i = 0; i < _tag.name.length; i++) { - if ( _tag.name[i].substr(0, t.length) == t ) { - var t = _tag.name[i]; - $.log.debug('selected ' + t ); + $.log.debug('tag: ' + t + ' ['+t.length+']'); - jQuery.className.add( _tag.obj[i], 'selected' ); + _tag.clean_selected(); - _tag.selected_obj[j] = _tag.obj[i]; - _tag.selected_name[j] = _tag.name[i]; - j++; - } - } + if (t == '') return false; - $.log.debug('selected ' + _tag.selected_obj.length + ' tags'); + _tag.select(t); + + $.log.info('selected ' + _tag.selected.length + ' tags'); + + return true; + }).submit( function() { + $.log.debug('submit'); + return _tag.take_suggested(); + }).blur( function() { + $.log.debug('blur'); + return _tag.take_suggested(); + }); - $('#suggest').html( _tag.selected_name.join(" ") ); - }).focus(); - $.log.toggle(); + _tag.focus(); });