dojo.require("dojo.parser");
dojo.require("dojo.io.script");
dojo.require("dojox.data.JsonpReadStore");
dojo.require("dijit.form.Button");
dojo.require("dijit.form.Form");
dojo.require("dijit.form.ComboBox");
dojo.require("dijit.layout.LayoutContainer");

//djConfig.searchIds.push("fnTagging");

// =============
// = RDF Facet =
// =============
RdfFacet = function() {
    // Do nothing
}

RdfFacet.decode = function(data) {
    console.debug("RdfFacet.decode");
    decode = '';
    
    if (typeof data != "undefined") {
        // Remove Sx ... X
        var data = data.substr(2, data.length -3);
        var decoded = unescape(data.replace(/(E([0-9abcdefABCDEF]{2}))/g, "%$2"));
    
        // To enable word wrapping on Firefox add zero length spaces.
        // Should try to use zero width space &#8203; but having some problems
        // with a space appearing.
        decoded = decoded.replace(/-/g, "-<wbr>")
        console.debug("RdfFacet.decode: decoded: " + decoded);
    }
    return decoded;
}


// ===========
// = TAGGING =
// ===========

Tagging = function() {
    this._tags = new Array();
    this._tagsMap = new Array();
}

Tagging.prototype._tags;
Tagging.prototype._tagsMap;

Tagging.prototype._instance = null;

Tagging.getInstance = function() {
    if (Tagging._instance == null) {
        console.debug("Creating new Tagging object");
        Tagging._instance = new Tagging();
    }
    
    return Tagging._instance;
}

/**
 *  Build the tag summary box
 */
Tagging.prototype.buildTags = function() {
    console.debug("buildTags");
    var tagging = dojo.byId('fnTagging');

    if (tagging) {
        // Get/recreate fntagging
        var container = new dijit.layout.LayoutContainer({id: 'fnTagging'}, tagging);
        
        // Add fnTaggingSummary
        var summary = new dijit.layout.LayoutContainer({id: 'fnTaggingSummary'});
        container.addChild(summary);

        // Add fnTaggingReadStore
        var readStore = new dojox.data.JsonpReadStore({
                                        id:'fnTaggingReadStore',
                                        dojoType: 'dojox.data.JsonpReadStore',
                                        jsId: 'fnTaggingReadStore',
                                        url: 'http://community.forum.nokia.com/tagging/index/get-matching-tags?url=' + encodeURI(location.href)
        });
        container.addChild(readStore);

        // Add fnTaggingAddTagForm
        var addTagForm = new dijit.form.Form({
                                        id: 'fnTaggingAddTagForm'
        });
        container.addChild(addTagForm);
        var addTagFormDOM = dojo.byId('fnTaggingAddTagForm');

        // Create dummy placeholders for the form data.
        var tagDOM = document.createElement('div');
        tagDOM.setAttribute('id', 'fnTaggingTag');
        var addTagButtonDOM = document.createElement('span');
        addTagButtonDOM.setAttribute('id', 'fnTaggingAddTag');
        addTagButtonDOM.className = 'fnButton';
        addTagFormDOM.appendChild(tagDOM);
        addTagFormDOM.appendChild(addTagButtonDOM);

        var imageButton = document.createElement('input');
        imageButton.setAttribute('id', 'fnTaggingAddTagImageButton');
        imageButton.setAttribute('type', 'button');
        imageButton.setAttribute('value', 'Add Tags');
        addTagButtonDOM.appendChild(imageButton);

        // Add fnTaggingTag
        var tagToAdd = new dijit.form.ComboBox({
                                        store: readStore,
                                        searchAttr: 'name',
                                        id: 'fnTaggingTagToAdd',
                                        name: 'fnTaggingTagToAdd',
                                        autoComplete: false,
                                        'class': 'fnTaggingAddTag',
                                        hasDownArrow: false,
                                        maxLength: 36
        }, tagDOM);

        // Add event to add the tag
        dojo.connect(dojo.byId('fnTaggingAddTagForm'), 'onkeypress', this, 'addTagCheckKeyPress')

        // Add events to add the tag
        dojo.connect(dojo.byId('fnTaggingAddTagImageButton'), 'onclick', this, 'addTag')
    
        // Add fnTaggingMessage
        var message = new dijit.layout.LayoutContainer({id: 'fnTaggingMessage'});
        container.addChild(message);
    
        var metaTags = document.getElementsByName('qfnZuserE5FtagQ');
        var tags = new Array();

        for (i = 0; i < metaTags.length; i++) {
            var decodedTag = RdfFacet.decode(metaTags[i].content);
            this._tags[this._tags.length] = decodedTag;
            this._tagsMap[decodedTag] = metaTags[i].content;
        }
        this.displayTags();
        this.displayAddTagForm();
    }
}

Tagging.prototype.displayTags = function() {
    console.debug("displayTags");
    // Sort and remove duplicates
    this._tags = this._tags.sort();
    this._tags = this.removeDuplicates(this._tags);
    var tagLink = 'http://www.forum.nokia.com/search/?ut=1&k=qfnZuserE5FtagQ';
    var taggingDiv = dojo.byId('fnTaggingSummary');
    if (taggingDiv) {
        var tagHeader = '<h3 class="fnTaggingSummaryHeader">User Tags</h3>'

        // Output tags
        var tagList = tagHeader;
        
        // If no tags exist add a notice message
        if (this._tags.length == 0) {
            tagList += '<div class="fnTaggingNoTagMessage">There are no tags associated with this page.</div>';
        }
        else {
            tagList += '<div class="fnTaggingTagMessage">This page has been tagged with</div><div  class="fnTaggingTags">';
            for (i = 0; i < this._tags.length; i++) {
                var tag = this._tags[i];
                tagList += '<a href="' + tagLink + this._tagsMap[tag] + '">' + tag + '</a>';
                // Add a seperator if we are not the last element
                if ((i + 1) < this._tags.length) {
                    tagList += ', ';
                }
            }            
            tagList += '</div>';
        }
        if (User.isUserAuthenticated()) {
            tagList += "<p> Tags can contain alphanumeric characters, spaces, dashes and periods. Separate tags with a comma “,”.</p>";
        }

        taggingDiv.innerHTML = tagList;
    }
}

Tagging.prototype.displayAddTagForm = function() {
    console.debug("displayAddTagForm");
    var addTagForm = dojo.byId('fnTaggingAddTagForm');
    
    if (addTagForm) {
        if (User.isUserAuthenticated()) {
            console.debug("Displaying add tag form");
            addTagForm.style.display = 'block';
        }
        else {
            console.debug("Hiding add tag form");
            addTagForm.style.display = 'none';
        }
    }
}

/**
 * Remove duplicates from an array
 */
Tagging.prototype.removeDuplicates = function(arr) {
    console.debug("removeDuplicates");
    //get sorted array as input and returns the same array without duplicates.
    var result = new Array();
    var lastValue = "";

    for (var i = 0; i < arr.length; i++) {
        var curValue = arr[i];
        if (curValue != lastValue) {
            result[result.length] = curValue;
        }
        lastValue = curValue;
    }
    return result;
}

/**
 * Add a tag
 */
Tagging.prototype.addTag = function() {
    console.debug("addTag");
    var tag = dojo.byId('fnTaggingTagToAdd');
    
    if (tag.value == '') {
        console.debug('No tag has been set. Doing nothing');
        messages = dojo.byId('fnTaggingMessage');
        messages.style.display = "block";
        messages.innerHTML = '<p class="piazzaError">No tag has been entered.</p>';
    }
    else {
        dojo.io.script.get({
            url: "http://community.forum.nokia.com/tagging/index/add-tag?tag=" + tag.value + "&url=" + encodeURI(location.href)
        });
    }
    return false;
}

Tagging.prototype.addTagCheckKeyPress = function(event) {
    if (event.keyCode == dojo.keys.ENTER) {
        console.debug("Enter pressed");
        this.addTag();
    }
    else {
        console.debug("Some other key pressed");
    }
}

/**
 * Callback function used after a tag is added
 */
Tagging.prototype.addedTag = function(addedTags, duplicateTags, blacklistedTags, illegalTags, errorTags, addedTagsEncoded) {
    console.debug("addedTag");
    console.debug("Add Tag returned:", addedTags);
    console.debug("Add Tag returned: (encoded)", addedTagsEncoded);
    
    for (var i = 0; i < addedTags.length; i++) {
        this._tags[this._tags.length] = addedTags[i];
        this._tagsMap[addedTags[i]] = addedTagsEncoded[i];
    }
    this.displayTags();
    
    var message = ""
    
    if (addedTags.length == 1) {
        message += "<p>Added tag: " + addedTags + "</p>";
    }
    else if  (addedTags.length > 1) {
        message += "<p>Added tags: " + addedTags + "</p>";
    }
    
    if (duplicateTags.length == 1) {
        message += "<p class='piazzaError'>Tag already defined: " + duplicateTags + "</p>";
    }
    else if  (duplicateTags.length > 1) {
        message += "<p class='piazzaError'>Tags already defined: " + duplicateTags + "</p>";
    }
    
    if (blacklistedTags.length == 1) {
        message += "<p class='piazzaError'>Offensive tag NOT added: " + blacklistedTags + "</p>";
    }
    else if  (blacklistedTags.length > 1) {
        message += "<p class='piazzaError'>Offensive tags NOT added: " + blacklistedTags + "</p>";
    }
    
    if (illegalTags.length == 1) {
        message += "<p class='piazzaError'>Tag not allowed: " + illegalTags + "</p>";
    }
    else if  (illegalTags.length > 1) {
        message += "<p class='piazzaError'>Tags not allowed: " + illegalTags + "</p>";
    }
    
    if (errorTags.length == 1) {
        message += "<p class='piazzaError'>Error adding tag: " + errorTags + "</p>";
    }
    else if  (errorTags.length > 1) {
        message += "<p class='piazzaError'>Error adding tags: " + errorTags + "</p>";
    }
        
    messages = dojo.byId('fnTaggingMessage');
    messages.style.display = "block";
    messages.innerHTML = message;
    
    // Clear the fnTaggingTagToAdd
    tagToAdd = dijit.byId('fnTaggingTagToAdd');
    tagToAdd.setDisplayedValue('', true);
}

// ===============
// = User object =
// ===============
User = function() {
}

/**
 * Check the cookies to see if the user is authenticated
 */
User.isUserAuthenticated = function() {
    console.debug("isUserAuthenticated");
    if (this.getCookie('fnlogged')) {
        console.debug("User is authenticated");
        return true;
    }
    else {
        console.debug("User is not authenticated");
        return false;
    }
}

/**
 * Utility function to get a cookie
 *
 * @param name of the cookie
 * @return the cookie value or null if it is not set or
 * can not be accessed
 */
User.getCookie = function(name) {
    console.debug("getCookie");
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i = 0; i < ca.length; i++) {
        var c = ca[i];
        console.debug("Cookie:", c);
        
        while (c.charAt(0) == ' ') {
            c = c.substring(1, c.length);
        }
        
        if (c.indexOf(nameEQ) == 0) {
            console.debug("Returing:", c.substring(nameEQ.length, c.length));
            return c.substring(nameEQ.length, c.length);
        }
    }
    return false;
}


/**
 * Callback function used after a tag is added
 */
fnTaggingAddedTag = function(addedTags, duplicateTags, blacklistedTags, illegalTags, errorTags, addedTagsEncoded) {
    console.debug("fnTaggingAddedTag");
    var tagging = Tagging.getInstance();
    tagging.addedTag(addedTags, duplicateTags, blacklistedTags, illegalTags, errorTags, addedTagsEncoded);
}

// ==========
// = RATING =
// ==========
Rating = function() {
    this._rating = 0;
    this._starWidth = 17;
    this._maxRating = 5;
}

Rating.prototype._rating;

Rating.prototype._instance = null;

Rating.getInstance = function() {
    if (Rating._instance == null) {
        console.debug("Creating new Rating object");
        Rating._instance = new Rating();
    }
    
    return Rating._instance;
}

/**
 *  Build the ratings box
 */
Rating.prototype.buildRating = function() {
    console.debug("buildRating");
    var rating = dojo.byId('fnRating');

    if (rating) {
        console.debug("Found rating div");
        if (User.isUserAuthenticated() == false) {
            rating.innerHTML = "<p><a href='http://www.forum.nokia.com/dynamic/login.html'>Login</a> or <a href='http://www.forum.nokia.com/main/registration/registration.html'>Register</a> to rate this page</p>";
        }
        
        // Render current page rating
        var userRatingDom = document.createElement('div');
        userRatingDom.setAttribute('id', 'fnUserRating');
        userRatingDom.className = 'rating';
        rating.appendChild(userRatingDom);
        
        var pageRatingDom = document.createElement('div');
        pageRatingDom.setAttribute('id', 'fnPageRating');
        pageRatingDom.className = 'rating';
        rating.appendChild(pageRatingDom);
        
        var pageRatingMeta = document.getElementsByName('qfnZuserE5FratingQ');
        var pageRating = 0;
        if (pageRatingMeta.length == 1) {
           pageRating = RdfFacet.decode(pageRatingMeta[0].content);
        }
        this.displayPageRating(pageRating);
        
        if (User.isUserAuthenticated()) {
            console.debug("Display user rating");
            console.debug("get-user-rating-for-url");
            if (dojo.isIE) {
                // Call the get twice to try and work around
                // strange authentication problem
                dojo.io.script.get({
                    url: "http://community.forum.nokia.com/tagging/index/no-op"
                });
            }
            
            // Display the loading image in the place where the user rarting will go
            this.displayLoading();
            dojo.io.script.get({
                url: "http://community.forum.nokia.com/tagging/index/get-user-rating-for-url?url=" + encodeURI(location.href)
            });
            return false;
            
        }
        else {
            console.debug("User not logged in");
            this.displayUserRating(0, false);
        }
    }
    console.debug("buildRating done");
}


Rating.prototype.displayLoading = function() {
    console.debug('displayLoading');
    
    locationId = 'fnUserRating';
    var ratingDiv = dojo.byId(locationId);

    if (ratingDiv) {
        var loadingAnimator = dojo.byId('loadingAnimator' + locationId);
        if (loadingAnimator) {
            loadingAnimator.style.display = 'inline';
        }
        else {
            var loaderContainerDiv = document.createElement('div');
    
            ratingDiv.appendChild(loaderContainerDiv);
            var animationId = 'loadingAnimator' + locationId;
            var loadingAnimation = dojo.byId(animationId);
            if (loadingAnimation == null) {
                console.debug('Rendering loading animator');
                loadingAnimation = new dojox.widget.Loader({id: animationId, attachToPointer: false}, loaderContainerDiv);
            }
        }
    }
}

Rating.prototype.hideLoading = function() {
    console.debug('hideLoading');
    
    locationId = 'fnUserRating';
    var ratingDiv = dojo.byId(locationId);

    if (ratingDiv) {
        var loadingAnimator = dojo.byId('loadingAnimator' + locationId);
        if (loadingAnimator) {
            loadingAnimator.style.display = 'none';
        }
    }
}


/**
 * Callback function used to display user rating
 */
fnTaggingDisplayUserRating = function(userRating) {
    console.debug("fnTaggingDisplayUserRating");
    
    // remove the loading image.
    Rating.getInstance().hideLoading();
    
    if (typeof userRating == "undefined") {
        console.debug('User has not rated this page');
        Rating.getInstance().displayUserRating(0, true);
    }
    else {
        console.debug('User rating for page: ' + userRating.rating);
        Rating.getInstance().displayUserRating(userRating.rating, true);
    }
}

/**
 * Add/update the users rating for this page
 */
fnTaggingAddUserRating = function(userRating) {
    console.debug("fnTaggingAddUserRating");
    if (User.isUserAuthenticated()) {
        // display the loading animation
        Rating.getInstance().displayLoading();
        
        console.debug("Add user rating");
        var t = new Date()
        dojo.io.script.get({
            url: "http://community.forum.nokia.com/tagging/index/add-rating?rating=" + userRating + "&ver=" + t.getTime() + "&url=" + encodeURI(location.href)
        });
    }
    else {
        console.debug("User not logged in, not fetching rating");
    }
}

/**
 * Callback function used after a tag is added
 */
fnTaggingAddedRating = function(pageRating, userRating) {
    console.debug("fnTaggingAddedRating");
    console.debug("fnTaggingAddedRating pageRating: " + pageRating['meanRating']);
    console.debug("fnTaggingAddedRating userRating: " + userRating);
    // Update the users rating
    var userRatingLi = dojo.byId('fnUserRatingValue');
    userRatingLi.style.width = (userRating * Rating.getInstance()._starWidth)  + 'px';
    
    // Update the page rating
    var pageRatingLi = dojo.byId('fnPageRatingValue');
    pageRatingLi.style.width = (pageRating['meanRating'] * Rating.getInstance()._starWidth)  + 'px';
    Rating.getInstance().hideLoading();
}



/**
 * Display the users rating stars
 */
Rating.prototype.displayUserRating = function(rating, displaySelector) {
    console.debug("displayUserRating");
    
    var ratingDiv = dojo.byId('fnUserRating');

    if (ratingDiv) {
        // Build up the rating
        var userRatingTitleDom = document.createElement('span');
        userRatingTitleDom.className = 'ratingtitle';
        userRatingTitleDom.innerHTML = 'How helpful was this to you?:';
        
        var userRatingUlDom = document.createElement('ul');
        userRatingUlDom.className = 'unit-rating';
        userRatingUlDom.style.width = (this._starWidth * this._maxRating) + 'px';
        
        var userRatingLiDom = document.createElement('li');
        userRatingLiDom.className = 'current-rating';
        userRatingLiDom.style.width = (rating * this._starWidth)  + 'px';
        userRatingLiDom.setAttribute('id', 'fnUserRatingValue');
        
        ratingDiv.appendChild(userRatingTitleDom);
        ratingDiv.appendChild(userRatingUlDom);
        userRatingUlDom.appendChild(userRatingLiDom);
        
        for (i = 1; i < this._maxRating + 1; i++) {
            var userRatingLiDom = document.createElement('li');
            
            if (displaySelector) {
                var userRatingADom = document.createElement('a');
                userRatingADom.className = 'r' + i + '-unit rater';
                userRatingADom.title = i + ' out of ' + this._maxRating;
                userRatingADom.href = 'javascript:fnTaggingAddUserRating(' + i + ');';
                userRatingADom.innerHTML = i;
                userRatingLiDom.appendChild(userRatingADom);
            }
            else {
                userRatingUlDom.setAttribute('title', 'You must be logged in to rate');
                userRatingUlDom.setAttribute('alt', 'You must be logged in to rate');
            }          
            userRatingUlDom.appendChild(userRatingLiDom);
        }
        
        //     <span class="ratingCounts">(0.0 / 5 - 0 votes cast)</span>        
    }
}

/**
 * Display or update/replace the page rating
 */
Rating.prototype.displayPageRating = function(pageRating) {
    console.debug("displayPageRating");
    
    var ratingDiv = dojo.byId('fnPageRating');
    ratingDiv.innerHTML = '';
    
    var pageRatingTitleDom = document.createElement('span');
    pageRatingTitleDom.className = 'ratingtitle ratingOverallTitle';
    pageRatingTitleDom.innerHTML = 'Overall Rating:';
    
    var pageRatingUlDom = document.createElement('ul');
    pageRatingUlDom.className = 'unit-rating';
    pageRatingUlDom.style.width = (this._starWidth * this._maxRating) + 'px';
    
    var pageRatingLiDom = document.createElement('li');
    pageRatingLiDom.setAttribute('id', 'fnPageRatingValue');
    pageRatingLiDom.className = 'current-rating';
    pageRatingLiDom.style.width = (pageRating * this._starWidth)  + 'px';
    
    ratingDiv.appendChild(pageRatingTitleDom);
    ratingDiv.appendChild(pageRatingUlDom);
    pageRatingUlDom.appendChild(pageRatingLiDom);
    
    console.debug("Page rating rendering ... done");
}


// ========================================
// = Code to run once the above is loaded =
// ========================================

// run buildTags once the page has been loaded.
var tagging = Tagging.getInstance();
var rating = Rating.getInstance();

dojo.addOnLoad(function(){ tagging.buildTags(); });
dojo.addOnLoad(function(){ rating.buildRating(); });


/*
   Behaviour v1.1 by Ben Nolan, June 2005. Based largely on the work
   of Simon Willison (see comments by Simon below).

   Description:

   	Uses css selectors to apply javascript behaviours to enable
   	unobtrusive javascript in html documents.

   Usage:   

	var myrules = {
		'b.someclass' : function(element){
			element.onclick = function(){
				alert(this.innerHTML);
			}
		},
		'#someid u' : function(element){
			element.onmouseover = function(){
				this.innerHTML = "BLAH!";
			}
		}
	};

	Behaviour.register(myrules);

	// Call Behaviour.apply() to re-apply the rules (if you
	// update the dom, etc).

   License:

   	This file is entirely BSD licensed.

   More information:

   	http://ripcord.co.nz/behaviour/

*/   

var Behaviour = {
	list : new Array,

	register : function(sheet){
		Behaviour.list.push(sheet);
	},

	start : function(){
		Behaviour.addLoadEvent(function(){
			Behaviour.apply();
		});
	},

	apply : function(){
		for (h=0;sheet=Behaviour.list[h];h++){
			for (selector in sheet){
				list = document.getElementsBySelector(selector);

				if (!list){
					continue;
				}

				for (i=0;element=list[i];i++){
					sheet[selector](element);
				}
			}
		}
	},

	addLoadEvent : function(func){
		var oldonload = window.onload;

		if (typeof window.onload != 'function') {
			window.onload = func;
		} else {
			window.onload = function() {
				oldonload();
				func();
			}
		}
	}
}

Behaviour.start();

/*
   The following code is Copyright (C) Simon Willison 2004.

   document.getElementsBySelector(selector)
   - returns an array of element objects from the current document
     matching the CSS selector. Selectors can contain element names, 
     class names and ids and can be nested. For example:

       elements = document.getElementsBySelect('div#main p a.external')

     Will return an array of all 'a' elements with 'external' in their 
     class attribute that are contained inside 'p' elements that are 
     contained inside the 'div' element which has id="main"

   New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
   See http://www.w3.org/TR/css3-selectors/#attribute-selectors

   Version 0.4 - Simon Willison, March 25th 2003
   -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
   -- Opera 7 fails 
*/

function getAllChildren(e) {
  // Returns all children of element. Workaround required for IE5/Windows. Ugh.
  return e.all ? e.all : e.getElementsByTagName('*');
}

document.getElementsBySelector = function(selector) {
  // Attempt to fail gracefully in lesser browsers
  if (!document.getElementsByTagName) {
    return new Array();
  }
  // Split selector in to tokens
  var tokens = selector.split(' ');
  var currentContext = new Array(document);
  for (var i = 0; i < tokens.length; i++) {
    token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
    if (token.indexOf('#') > -1) {
      // Token is an ID selector
      var bits = token.split('#');
      var tagName = bits[0];
      var id = bits[1];
      var element = document.getElementById(id);
      if (tagName && element.nodeName.toLowerCase() != tagName) {
        // tag with that ID not found, return false
        return new Array();
      }
      // Set currentContext to contain just this element
      currentContext = new Array(element);
      continue; // Skip to next token
    }
    if (token.indexOf('.') > -1) {
      // Token contains a class selector
      var bits = token.split('.');
      var tagName = bits[0];
      var className = bits[1];
      if (!tagName) {
        tagName = '*';
      }
      // Get elements matching tag, filter them for class selector
      var found = new Array;
      var foundCount = 0;
      for (var h = 0; h < currentContext.length; h++) {
        var elements;
        if (tagName == '*') {
            elements = getAllChildren(currentContext[h]);
        } else {
            elements = currentContext[h].getElementsByTagName(tagName);
        }
        for (var j = 0; j < elements.length; j++) {
          found[foundCount++] = elements[j];
        }
      }
      currentContext = new Array;
      var currentContextIndex = 0;
      for (var k = 0; k < found.length; k++) {
        if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
          currentContext[currentContextIndex++] = found[k];
        }
      }
      continue; // Skip to next token
    }
    // Code to deal with attribute selectors
    if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
      var tagName = RegExp.$1;
      var attrName = RegExp.$2;
      var attrOperator = RegExp.$3;
      var attrValue = RegExp.$4;
      if (!tagName) {
        tagName = '*';
      }
      // Grab all of the tagName elements within current context
      var found = new Array;
      var foundCount = 0;
      for (var h = 0; h < currentContext.length; h++) {
        var elements;
        if (tagName == '*') {
            elements = getAllChildren(currentContext[h]);
        } else {
            elements = currentContext[h].getElementsByTagName(tagName);
        }
        for (var j = 0; j < elements.length; j++) {
          found[foundCount++] = elements[j];
        }
      }
      currentContext = new Array;
      var currentContextIndex = 0;
      var checkFunction; // This function will be used to filter the elements
      switch (attrOperator) {
        case '=': // Equality
          checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
          break;
        case '~': // Match one of space seperated words 
          checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
          break;
        case '|': // Match start with value followed by optional hyphen
          checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
          break;
        case '^': // Match starts with value
          checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
          break;
        case '$': // Match ends with value - fails with "Warning" in Opera 7
          checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
          break;
        case '*': // Match ends with value
          checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
          break;
        default :
          // Just test for existence of attribute
          checkFunction = function(e) { return e.getAttribute(attrName); };
      }
      currentContext = new Array;
      var currentContextIndex = 0;
      for (var k = 0; k < found.length; k++) {
        if (checkFunction(found[k])) {
          currentContext[currentContextIndex++] = found[k];
        }
      }
      // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
      continue; // Skip to next token
    }

    if (!currentContext[0]){
    	return;
    }

    // If we get here, token is JUST an element (not a class or ID selector)
    tagName = token;
    var found = new Array;
    var foundCount = 0;
    for (var h = 0; h < currentContext.length; h++) {
      var elements = currentContext[h].getElementsByTagName(tagName);
      for (var j = 0; j < elements.length; j++) {
        found[foundCount++] = elements[j];
      }
    }
    currentContext = found;
  }
  return currentContext;
}

/* That revolting regular expression explained 
/^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
  \---/  \---/\-------------/    \-------/
    |      |         |               |
    |      |         |           The value
    |      |    ~,|,^,$,* or =
    |   Attribute 
   Tag
*/
