function Tagger( objvar ) {

    // {{{ vars

    this.cookieName = objvar;
    this.cookieExpires = null;
    this.cookiePath = null;
    this.cookieDomain = null;
    this.cookieSecure = false;
    this.cookieChecked = false;
    this.cookieSupported = false;
    this.delimiter = ';';
    this.inputIDPrefix = objvar;
    this.taggedClass = objvar +'-tagged';
    this.untaggedClass = objvar +'-untagged';
    this.inputText = null;
    this.tags = new Array();
    this.objvar = objvar;

    // }}}

    // {{{ cookieCheck

    this.cookieCheck = function() {
        if ( this.cookieChecked )  return this.cookieSupported;

        this.cookieSet('cookie_support_check', 1);
        this.cookieSupported = (this.cookieGet('cookie_support_check') == true);
        this.cookieDel('cookie_support_check');

        this.cookieChecked = true;
        return this.cookieSupported
    }

    // }}}
    // {{{ cookieSet

    this.cookieSet = function( name, value, expires, path, domain, secure ) {
        if ( expires == null )  expires = this.cookieExpires;
        if ( path == null    )  path = this.cookiePath;
        if ( domain == null  )  domain = this.cookieDomain;
        if ( secure == null  )  secure = this.cookieSecure;

        cookie = name +'='+ escape(value)
            +( expires ? '; expires='+ expires.toGMTString() : '')
            +( path ? '; path='+ path : '')
            +( domain ? '; domain='+ domain : '')
            +( secure ? '; secure' : '')
        ;

        document.cookie = cookie;
    }

    // }}}
    // {{{ cookieGet

    this.cookieGet = function( name ) {
        var key = name +'=';
        var start = document.cookie.indexOf('; '+ key);
        var end = null;

        if ( start == -1 ) {
            start = document.cookie.indexOf(key);
            if ( start != 0 )  return null;
        }
        else {
            start += 2;
        }

        end = document.cookie.indexOf(';', start);
        if ( end == -1 )  end = document.cookie.length;

        return unescape(document.cookie.substring(start + key.length, end));
    }

    // }}}
    // {{{ cookieDel

    this.cookieDel = function( name, path, domain ) {
        if ( this.cookieGet(name) ) {
            document.cookie = name +'='
                +( path ? '; path='+ path : '' )
                +( domain ? '; domain='+ domain : '' )
                +'; expires=Thu, 01-Jan-70 00:00:01 GMT'
            ;
        }
    }

    // }}}

    // {{{ check

    this.check = function() {
        return document.getElementById && this.cookieCheck();
    }

    // }}}
    // {{{ init

    this.init = function() {
        var str = this.cookieGet(this.cookieName);

        if ( str != null ) {
            var list = str.split(this.delimiter);

            for ( var i = 0; i < list.length; i++ ) {
                this.tags[list[i]] = true;
            }
        }
    }

    // }}}
    // {{{ syncToCookie

    this.syncToCookie = function() {
        var str = '';
        var id;

        for ( id in this.tags ) {
            if ( this.tags[id] ) {
                if ( str != '' )  str += this.delimiter;
                str += id;
            }
        }

        this.cookieSet(this.cookieName, str);
    }

    // }}}
    // {{{ syncUI

    this.syncUI = function() {
        if ( ! this.check() )  return;

        var id;

        for ( id in this.tags ) {
            var input = document.getElementById(this.inputIDPrefix + id +'-input');
            if ( input == null )  continue;

            input.checked = this.tags[id];

            var span = document.getElementById(this.inputIDPrefix + id);
            if ( span == null )  continue;

            if ( this.tags[id] ) {
                span.className = this.taggedClass;
            }
            else {
                span.className = this.untaggedClass;
            }
        }
    }

    // }}}
    // {{{ toggle

    this.toggle = function( id, state ) {
        if ( ! this.check() )  return;
        this.tags[id] = state;
        this.syncToCookie();

        var span = document.getElementById(this.inputIDPrefix + id);
        if ( span == null )  return;

        span.className = this.tags[id] ? this.taggedClass : this.untaggedClass;
    }

    // }}}
    // {{{ tag

    this.tag = function( id ) {
        return this.toggle(id, true);
    }

    // }}}
    // {{{ untag

    this.untag = function( id ) {
        return this.toggle(id, false);
    }

    // }}}
    // {{{ isTagged

    this.isTagged = function( id ) {
        return this.tags[id];
    }

    // }}}
    // {{{ clear

    this.clear = function() {
        var id;

        for ( id in this.tags ) {
            this.tags[id] = false;
        }

        this.syncToCookie();
        this.syncUI();
    }

    // }}}
    // {{{ writeCheckbox

    this.writeCheckbox = function( id, labelFirst ) {
        if ( ! this.check() )  return;

        var checkbox = '<input type="checkbox" id="'+ this.inputIDPrefix + id
            +'-input" onclick="'+ this.objvar +'.toggle(\''+ id
            +'\', this.checked)"'
            + (this.isTagged(id) ? ' checked="checked"' : '')
            +'>'
        ;

        var label = '';

        if ( this.inputText ) {
            label = '<label for="'+ this.inputIDPrefix + id +'-input" id="'
                + this.inputIDPrefix + id +'-label">'
                + (labelFirst ? '' : ' ')
                + this.inputText
                + (labelFirst ? ' ': '')
                +'</label>'
            ;
        }

        document.write(
            '<span id="'+ this.inputIDPrefix + id +'" class="'
            + (this.isTagged(id) ? this.taggedClass : this.untaggedClass)
            +'">'
        );

        if ( labelFirst )  document.write(label);
        document.write(checkbox);
        if ( ! labelFirst )  document.write(label);

        document.write('</span>');
    }

    // }}}
    // {{{ taggedIDs

    this.taggedIDs = function() {
        var result = new Array();
        var id;

        for ( id in this.tags ) {
            if ( this.tags[id] ) {
                result[result.length] = id;
            }
        }

        return result;
    }

    // }}}

}

