/* Copyright 2005-2006 Josh Clark and Global Moxie. All rights reserved.
   $Id$
*/

BM = {};
BM.onDOM = {
    Events : [],
    finished : false,
    kickstart : function() {
        /* for Mozilla/Opera9 */
        if (document.addEventListener) {
          document.addEventListener("DOMContentLoaded", BM.onDOM.doEvents, false);
        }
    
        /* for Internet Explorer */
        /*@cc_on @*/
        /*@if (@_win32)
            document.write("<script id=__ie_onload defer src=\"//:\"><\/script>");
            var script = document.getElementById("__ie_onload");
            script.onreadystatechange = function() {
                if (this.readyState == "complete") {
                    BM.onDOM.doEvents(); // call the onload handler
                }
            };
            // Clear reference to prevent leaks in IE
            script = null;
        /*@end @*/

        /* for Safari and Konqueror */
        if (/KHTML|WebKit/i.test(navigator.userAgent)) { // sniff
            BM.onDOM.safariRepeat = setInterval(function() {
                if (/loaded|complete/.test(document.readyState)) {
                    BM.onDOM.doEvents(); // call the onload handler
                }
            }, 10);
        }

        /* a backstop for other browsers */
        Event.observe(window, 'load', BM.onDOM.doEvents); //all events

    },
    addEvent : function (listener) {
        BM.onDOM.Events.push(listener);
    },
    doEvents : function() {
        if (BM.onDOM.finished) return;
        BM.onDOM.finished = true;
        if (BM.onDOM.safariRepeat) clearInterval(BM.onDOM.safariRepeat);
        BM.onDOM.Events.each(function(f) { f() } );
        BM.onDOM.Events = [];
    }
};
BM.onDOM.kickstart();

BM.onDOM.addEvent( function() {
    //preload progress image, have to wait til after we have real imagedir
    BM.progressImage = new Image;
    BM.progressImage.src = '/assets/img/bmcp_progress.gif';
    BM.progressHTML = '<img src="' + BM.progressImage.src
          + '" alt="" width="10" height="10" />';

    BM.Behavior.init();
} );



/*  BM.BEHAVIOR
****************************************************************/
BM.Behavior = {
    rules : { },
    init : function() {
        Object.extend(this.rules, {
         _each: function(iterator) {
           for (key in this) {
             if(key == '_each') continue;
             var value = this[key];
             var pair = [key, value];
             pair.key = key;
             pair.value = value;
             iterator(pair);
           }
         }
        });
        this.apply(this.rules);
    },
    register : function(rule) {
        Object.extend(this.rules, rule);
    },
    apply : function() {
        var observer = null;
        this.unloadCache();
        this.rules._each(function(rule) {
          var selectors = $A(rule.key.split(','));
          selectors.each(function(selector) {
            var pair = selector.split(':');
            var event = pair[1];
            $$(pair[0]).each(function(element) {
              if(pair[1] == '' || pair.length == 1) rule.value(element);
              else this.addObserver(element,event,rule.value);
            }.bind(this));
          }.bind(this));
        }.bind(this));
    },
    unloadCache: function() {
        if (!this._cache) return;
        for (var i = 0; i < this._cache.length; i++) {
          Event.stopObserving.apply(this, this._cache[i]);
          this._cache[i][0] = null;
        }
        this._cache = [];
    },
    _cache: [],
    addObserver : function(element,eventName,func) {
        var observer = function(event) {
          var e = Event.element(event);
          if(e.nodeType == 3) e = e.parentNode; //Safari bug, fixed in Webkit
          func($(e), event);
        };
        this._cache.push([element, eventName, observer]);
        if (eventName == 'keypress') { // Avoid munging of keypress
            Event._observeAndCache(element, 'keypress', observer, false);
        }
        else {
            Event.observe(element, eventName, observer);
        }
    }
};


BM.Behavior.register( {
    'li.term-card:click' : function(e,evt) {
      var cn = e.className || '';
      if ( cn.indexOf('term-card') == -1 ) {
        if (cn == 'delete') return;
        e = e.up('li.term-card');
      }
      if (e.hasClassName('top-card')) {
        e.removeClassName('top-card');
      }
      else {
        TagMe.nudgeCard(e);
//      setTimeout(function(){ TagMe.pushToTop(e); e = null; }, 800);
      }
    },
    'a.delete:click' : function(link,evt) {
        var li = link.up('li');
        var cn = li.className;
        if ( li.hasClassName('term-card') ) { TagMe.removeItem(li); }
        else if ( li.hasClassName('wiki-item') ) {
            var termItem = li.up('li.term-card');
            if (termItem) {
                var term = TagMe.terms[termItem.title];
                if (term) { term.removeWiki(link); }
            }
        }
        Event.stop(evt);
    },
    '#sourceForm:submit' : function(form,evt) {
        Event.stop(evt);
        var text = $F('sourceText');
        var boundaries = text.match(/(\b)/g);
        if (!boundaries) return;
        var words = boundaries.length / 2; // number of words
        if (words < 5) { TagMe.addTerm(text) } //make short terms a phrase, too
        TagMe.showStatus(BM.progressHTML + ' Analyzing your text...');
        var starter = $('starterTitle');
        if (starter.visible()) {
            new Effect.SlideUp(starter,{duration:0.4});
            new Effect.Appear('tagStatus', {duration:0.4});
        }
        TagMe.askYahoo({
          request : 'terms',
          context : text,
          onsuccess : TagMe.buildNewTerms
        });
    }
 } );


/*  TAGME class
****************************************************************/
TagMe = {
    restURL : '/cgi-bin/tagme.cgi',
    terms : $H(),
    buildNewTerms : function(ajax) {
        var json = TagMe.handleAjaxResponse(ajax);
        if (json.result) {
            $(json.result).each(TagMe.addTerm);
            TagMe.showStatus(BM.progressHTML + ' Fetching Wikipedia info...');
            TagMe.checkDoneWithWikis(); // in case no new terms
            BM.Behavior.apply();
        }
        else if (json.error) { return TagMe.showError(json.error); }
        else { return TagMe.showError(json) }
    },
    buildNewWikis : function(ajax) {
        var json = TagMe.handleAjaxResponse(ajax);
        if (!json.result) { 
            if (json.error) { return TagMe.showError(json.error); }
            else { return TagMe.showError(json) }
        }
        var id = json.result.term;
        var term = TagMe.terms[id];
        term.loading = null;
        if (!term) return;
        $(json.result.wikis).each(function(rwiki) {
            var wiki = new TagMe.Wiki(rwiki);
            term.addWiki(wiki);
        });
        TagMe.checkDoneWithWikis();
        BM.Behavior.apply();
    },
    addTerm : function(phrase) {
        if ( TagMe.terms[phrase] ) return; // don't add more than one of the same
        TagMe.terms[phrase] = new TagMe.Term(phrase);
    },
    removeItem : function(li) {
        if ( li.hasClassName('term-card') && li.title ) {
            TagMe.terms[li.title].removeTerm()
        }
        else { // shouldn't get here, but just in case...
            li.remove();
        }
    },
    nudgeCard : function(card) {
        card.addClassName('top-card');
        card.siblings().each(TagMe.backToStack);
    },
    pushToTop : function(card) {
        var list = card.up('ul.card-catalog');
        card.remove();
        list.appendChild(card);
    },
    backToStack : function(card) {
        card.removeClassName('top-card');
    },
    mtagString : function() {
       var terms = TagMe.terms.values();
       var wikis = terms.collect(function(term) { return term.wiki.values() } ).flatten();
       var mtags = wikis.collect(function(wiki){ return wiki.mtag });
       return mtags.join('<br />');
    },
    updateTagList : function() {
        $('tagList').innerHTML = TagMe.mtagString();
    },
    checkDoneWithWikis : function() {
        //clear status bar if no other wikis being built
        var loading = TagMe.terms.values().find(function(t){ return t.loading });
        if (!loading) {
            TagMe.showStatus("All done. Review your card catalog for tag quality.");
            new Effect.Highlight('tagStatus',{duration:2,startcolor: '#99ff99', endcolor:'#ffffcc'});
        }
    },
    
    askYahoo : function(option) {
        var url = TagMe.restURL + '/' + option.request; // terms | wikipedia
        var param = {
          context : option.context,
          query : option.query,
          termName : option.termName
       };
       var ajax = new Ajax.Request(url, {
           parameters : param,
           onSuccess : option.onsuccess,
           onFailure : TagMe.ajaxError
       });
       return ajax;
    },
    handleAjaxResponse : function(ajax) {
        var json = ajax.responseText;
         var obj;
        try { eval('obj='+json); }
        catch(e) { return TagMe.showError(json); }
        if (!obj) { return TagMe.showError(json); }
        return obj;
    },
    ajaxError : function(ajax) {
        var txt = ajax.status ;
        if (ajax.statusText && ajax.statusText != 'OK') { //safari 'OK'
            txt += ' (' + ajax.statusText + ')';
        }
        TagMe.showError(txt);
    },
    showError : function(text) {
        TagMe.showStatus('<p><strong>Oops. We had a boo boo.</strong><p>' + text);
    },
    showStatus : function(text) {
        $('tagStatus').innerHTML = text;
    }
};

/*  TERM object
****************************************************************/
TagMe.Term = Class.create();
TagMe.Term.prototype = {
    initialize: function(phrase) {
        this.term = phrase;
        TagMe.terms[phrase] = this;
        this.wiki = $H();
        this.card = this.buildCard();
        $('WikiCards').appendChild(this.card);
        this.fetchWikis();
    },
    buildCard: function() {
        var card = Builder.node('li', {className:'term-card', title:this.term});
        this.status = Builder.node('span', {className: 'ajax-status'});
        var label = Builder.node('span', {className: 'term-label'},
         [ Builder.node('a', {href: '#', className:'delete', title: 'Remove this topic card'}, 'Remove'), this.term, this.status ]);
        card.wikilist = Builder.node('ol', {className: 'wikipedia'});
        card.appendChild(label);
        card.appendChild(card.wikilist);
        return card;
    },
    removeTerm : function() {
        delete TagMe.terms[this.term];
        this.card.remove();
        TagMe.updateTagList();
    },
    fetchWikis : function() {
        var status = this.status;
        status.innerHTML = BM.progressHTML;
        this.loading = true;
        TagMe.askYahoo({
          request : 'wikipedia',
          query : this.term,
          onsuccess : function(ajax) { status.innerHTML = ''; status = null; TagMe.buildNewWikis(ajax) }
        });
    },
    addWiki : function (wiki ) {
        if(!wiki.mtag) return;
        this.wiki[wiki.url] = wiki;
        this.card.wikilist.appendChild(wiki.item);
        TagMe.updateTagList();
    },
    removeWiki : function(link) {
        var url = link.siblings()[0].href; // wiki link
        var wiki = this.wiki[url];
        if (wiki) { wiki.removeItem(); }
        delete this.wiki[url];
        wiki = null;
        TagMe.updateTagList();
    }
};


/*  WIKI object
****************************************************************/
TagMe.Wiki = Class.create();
TagMe.Wiki.prototype = {
    initialize : function(param) {
        this.url = param.url;
        this.title = param.title;
        var slug = this.url.replace(/.*en.wikipedia.org\/wiki\//, '');
        
        //valid machine tag is the test for whether we want it.
        //we don't want pages that start with Image: or Category:
        //or include a query string
        
        if( slug == this.url || //not the format
            slug.match(/^(image|category)/i) ||
            slug.match(/\?/) ) { return }
        this.mtag = 'wikipedia:en=' + slug;
        this.buildItem();
        return;
    },
    buildItem : function() {
        this.item = Builder.node('li', {className:'wiki-item'},  [
          Builder.node('a', {href: '#', className:'delete', title: 'Remove'}, 'Remove this item'), ' ',
          Builder.node('a', {href:this.url, target:'_blank', title:'this.title'}, this.title) ] );
    },
    removeItem : function() {
        this.item.remove();
    }
};

