$(function() {
    if (typeof(amplafi.categoryCloud) =="undefined") {
        amplafi.categoryCloud = {
            messagePointSelectedCssClass: "amplafiMepSelected",      
            messagePointUnselectedCssClass: "amplafiMepUnselected",       
            messagePointAutoSelectedCssClass: "amplafiMepAutoselected",      
            messagePointAutoUnselectedCssClass: "amplafiMepAutounselected", 
            messagePointInProgressCssClass: "amplafiMepInProgress",
            messagePointConfigurationError: "amplafiMepConfigError",
            messagePointProblemSelectedCssClass: "amplafiMepSelectedbad",
            messagePointNotEnoughDataSelectedCssClass: "amplafiMepSelectedNotEnoughData",            
        };
        amplafi.categoryCloud._cssChoiceList = 
          // com.amplafi.core.messagehandling.MessagePointAction#PENDING - Message has not yet been sent to MEP - therefore the output (unblocked action) will be in-progress. whereas a block action will be instantly in effect.
          {'pnd' : {'styles':[ amplafi.categoryCloud.messagePointAutoSelectedCssClass, amplafi.categoryCloud.messagePointUnselectedCssClass, amplafi.categoryCloud.messagePointSelectedCssClass ]},
        // com.amplafi.core.messagehandling.MessagePointAction#ACCEPTED - Message has ALREADY been sent to MEP - therefore an blocking action will be in progress and an unblocked will be instant.
           'acp' : {'styles':[  amplafi.categoryCloud.messagePointAutoSelectedCssClass, amplafi.categoryCloud.messagePointUnselectedCssClass, amplafi.categoryCloud.messagePointSelectedCssClass ]},
        // com.amplafi.core.messagehandling.MessagePointAction#USER_BLOCK - Message has been blocked by the user
           'ubk': {'styles':[  amplafi.categoryCloud.messagePointAutoSelectedCssClass, amplafi.categoryCloud.messagePointUnselectedCssClass, amplafi.categoryCloud.messagePointSelectedCssClass ]},
        // com.amplafi.core.messagehandling.MessagePointAction#SYSTEM_FORCE - Message has been forced by the system
           'sfc': {'styles':[  amplafi.categoryCloud.messagePointAutoSelectedCssClass ]},
           'ufd': {'styles':[  amplafi.categoryCloud.messagePointAutoSelectedCssClass, amplafi.categoryCloud.messagePointUnselectedCssClass, amplafi.categoryCloud.messagePointSelectedCssClass ]},
          // com.amplafi.core.messagehandling.MessagePointAction#FILTER_BY_PROPERTIES - Some information misses for the message to go out to the service.
           'fxt': {'styles':[  amplafi.categoryCloud.messagePointNotEnoughDataSelectedCssClass ], 'msg':'Message was not pushed to the point because of some info missing, probably you forgot to specify a date or location? Try to edit the message. '},
          // com.amplafi.core.messagehandling.MessagePointAction#BAD - There was a problem while processing the message
           'bad': {'styles':[  amplafi.categoryCloud.messagePointProblemSelectedCssClass, amplafi.categoryCloud.messagePointUnselectedCssClass, amplafi.categoryCloud.messagePointSelectedCssClass], 'msg':'An error occured while publishing the message. We are investigating the problem right now.'},
           //for the situations when ExternalEntityStatus is not null on the message point envelope record.
           'err': {'styles':[  amplafi.categoryCloud.messagePointProblemSelectedCssClass, amplafi.categoryCloud.messagePointUnselectedCssClass, amplafi.categoryCloud.messagePointSelectedCssClass], 'msg':'An error occured while publishing the message. We are investigating the problem right now.'}
          };
        amplafi.categoryCloud._getCssClass = function(messagePointData) {
            var currentChoice;
            var currentMessagePointAction = messagePointData['messagePointAction'];
            var nextMessagePointAction = messagePointData['nextMessagePointAction'];
            if ( nextMessagePointAction ) {
                currentChoice = amplafi.categoryCloud._cssChoiceList[nextMessagePointAction];
            } else if(messagePointData['externalEntityStatus']){
                currentChoice = amplafi.categoryCloud._cssChoiceList['err'];
            } else {
                currentChoice = amplafi.categoryCloud._cssChoiceList[currentMessagePointAction];
            }             
            return currentChoice;
        };
        amplafi.categoryCloud._getAllowDeselect = function(currentChoice) {
            return currentChoice && currentChoice['styles'].length > 1;
        };
        amplafi.categoryCloud._getLinkUri = function(messagePointData) {
            var uri = messagePointData['publicUri'];
            var messageUri = messagePointData['messageUri'];
            return messageUri?messageUri:uri;
        };
        amplafi.categoryCloud._getUserMessageEndPointOverride = function(messagePointCloud) {
            var userMessageEndPointOverrideInput = messagePointCloud.attr("userMessageEndPointOverride");
            var userMessageEndPointOverrideInputElement = $(userMessageEndPointOverrideInput);
            var userMessageEndPointOverride = $.parseJSON(userMessageEndPointOverrideInputElement.val());
            if ( userMessageEndPointOverride == null ) {
                userMessageEndPointOverrideInputElement.val("[]");
                userMessageEndPointOverride = [];
            }
            return userMessageEndPointOverride;
        };
    
        amplafi.categoryCloud._mepCloud_createDeselectLinks = function(messagePointCloud, messagePointElement, userMessageEndPointOverride, messagePointData) {
            var currentChoice = amplafi.categoryCloud._getCssClass(messagePointData);
            var allowDeselect = amplafi.categoryCloud._getAllowDeselect(currentChoice);
            var mepId = messagePointElement.attr("cloudId");
            var mepName = messagePointData['displayName'];
            var deselectLink;
            var mepClass = '';
            if (allowDeselect) {
                deselectLink = '<a href="#" title="Exclude" class="removeMep">X</a>';
                mepClass += ' mepOne';
            } else {
                deselectLink = '';
                mepClass += ' mepOneViewOnly';
            }
            mepClass += ' ext-link';
    
            if ( currentChoice && userMessageEndPointOverride ) {
                mepClass += ' ' + currentChoice['styles'][userMessageEndPointOverride];
            }

            var linkUri = amplafi.categoryCloud._getLinkUri(messagePointData);
            var title;
            if(currentChoice && currentChoice['msg'] ){
                title = currentChoice['msg'];
            } else {
                title = 'Visit ' + linkUri;
            }
            var outLink = '<a target="_blank" class="'+mepClass+'" href="' + linkUri +
                '" title="'+title+'" mepId="'+mepId+'">'+mepName+'</a>';
            $('<span class="amplafiCategoryCloud amplafiCategoryCloudHalf">' + outLink +  deselectLink + '</span>').appendTo(messagePointElement);
            
        }
        amplafi.categoryCloud._mepCloudCreate = function(index, cloudElement) {
            var cloud = $(cloudElement);
            var userMessageEndPointOverride = amplafi.categoryCloud._getUserMessageEndPointOverride(cloud);
            cloud.prop("messagePointData", {'ampmep_1': {'publicUri': 'http://my.yahoo.com/', 'messagePointAction': 'pnd', 'nextMessagePointAction': 'acp'}});
            var messagePointData = $(cloud).prop("messagePointData");
            $(".ui-selectee", cloud).each(function(index, cloudItemElement) {
                var cloudItem = $(cloudItemElement);
                var cloudId = cloudItem.attr("cloudId");
                var override = userMessageEndPointOverride[cloudId]? userMessageEndPointOverride[cloudId]:0;
                amplafi.categoryCloud._mepCloud_createDeselectLinks(cloud, cloudItem, override, messagePointData[cloudId]);
            });
        }
        
        amplafi.flow.defs.defineCall({
            eligibleDestinationMessageEndPointListFlow : {
                success : function(responseObject) {
                    var af = amplafi.flow;
                    amplafi.categoryCloud.eligibleDestinationMessageEndPointList = af._getFlowValue(responseObject, "eligibleDestinationMessageEndPointList");
                    
                }
            }
        });
        var mepClouds = $(".mepCloud");
        if ( mepClouds.length > 0) {
            amplafi.flow.eligibleDestinationMessageEndPointListFlow();
            // hack that only works for single envelope.
            mepClouds.each(amplafi.categoryCloud._mepCloudCreate);
        }
    }
});
if (false) {
    amplafi.categoryCloud = function(){
        this.postCreate();
    };
    amplafi.categoryCloud.prototype = {
        // summary:
        //      A widget that manages a list of tags.

        isContainer : true,
        templatePath: "",
        
        // topicCloudCssClass: String: the html class every topic node is expected to have
        topicCloudCssClass: "amplafiCategoryCloud",
        topicCssClass: "amplafiCategoryCloudTopic",
        tagCssClass: "amplafiCategoryCloudTag",
        
        topicSelectedCssClass: "amplafiCategoryCloudSelected",
        topicUnselectedCssClass: "amplafiCategoryCloudUnselected",

        // the default category css class when the default category is not selected.
        topicUnselectedDefaultCssClass: "amplafiCategoryCloudUnselectedDefault",
        
        tagSelectedCssClass: "amplafiCategoryCloudSelected",
        tagUnselectedCssClass: "amplafiCategoryCloudUnselected",
        
        messagePointSelectedCssClass: "amplafiMepSelected",      
        messagePointUnselectedCssClass: "amplafiMepUnselected",       
        messagePointAutoSelectedCssClass: "amplafiMepAutoselected",      
        messagePointAutoUnselectedCssClass: "amplafiMepAutounselected", 
        messagePointInProgressCssClass: "amplafiMepInProgress",
        messagePointConfigurationError: "amplafiMepConfigError",
        messagePointProblemSelectedCssClass: "amplafiMepSelectedbad",
        messagePointNotEnoughDataSelectedCssClass: "amplafiMepSelectedNotEnoughData",
        /**
         * Key is the dbCode of the current com.amplafi.core.messagehandling.MessagePointAction
         * Notes: index=0 : system default action.
         *        index=1 : user selected blocking
         *        index=2 : user selected unblocking
         */
        _cssChoiceList : { },
        userMessageEndPointOverrideNodeId : null,
        // map ( messagePoint.id : index in _cssChoiceList ) - stored in the userMessageEndPointOverrideNode.value
        userMessageEndPointOverrideNode: null,
        userMessageEndPointOverride : null,

        // selectedTopicIdsNodeId: String: html node that will contain list of selected topic ids
        selectedTopicIdsNodeId : null,
        selectedTopicIdsNode : null,
        selectedCategories : {},
        defaultTopicId : null,
        selectAllNodeId : null,
        selectAllNode : null,

        // selectedTagsNodeId: String: html node that will contain list of selected tags
        selectedTagsNodeId : null,
        selectedTagsNode : null,
        selectedTags : [],
        
        // mepData: json: Data keyed on topics. If supplied, they'll appear when the related topic is selected
        mepData : null,
        
        mepUrlDisplayId : null,
        mepUrlDisplayNode : null,
        // div including related text
        mepUriBlockId : null,
        mepUriBlockNode : null,
        _tagListNode : null,
        _topicListNode : null,
        _topicNodes : null,
        

        // postCreate needs to attach the onClick handlers to the topic node.
        postCreate: function() {
            var self = this;
        	this._cssChoiceList = 
        		// com.amplafi.core.messagehandling.MessagePointAction#PENDING - Message has not yet been sent to MEP - therefore the output (unblocked action) will be in-progress. whereas a block action will be instantly in effect.
        		{'pnd' : {'styles':[ this.messagePointAutoSelectedCssClass, this.messagePointUnselectedCssClass, this.messagePointSelectedCssClass ]},
    		// com.amplafi.core.messagehandling.MessagePointAction#ACCEPTED - Message has ALREADY been sent to MEP - therefore an blocking action will be in progress and an unblocked will be instant.
     		     'acp' : {'styles':[  this.messagePointAutoSelectedCssClass, this.messagePointUnselectedCssClass, this.messagePointSelectedCssClass ]},
    		// com.amplafi.core.messagehandling.MessagePointAction#USER_BLOCK - Message has been blocked by the user
     			 'ubk': {'styles':[  this.messagePointAutoSelectedCssClass, this.messagePointUnselectedCssClass, this.messagePointSelectedCssClass ]},
    		// com.amplafi.core.messagehandling.MessagePointAction#SYSTEM_FORCE - Message has been forced by the system
     			 'sfc': {'styles':[  this.messagePointAutoSelectedCssClass ]},
     			 'ufd': {'styles':[  this.messagePointAutoSelectedCssClass, this.messagePointUnselectedCssClass, this.messagePointSelectedCssClass ]},
     			// com.amplafi.core.messagehandling.MessagePointAction#FILTER_BY_PROPERTIES - Some information misses for the message to go out to the service.
     			 'fxt': {'styles':[  this.messagePointNotEnoughDataSelectedCssClass ], 'msg':'Message was not pushed to the point because of some info missing, probably you forgot to specify a date or location? Try to edit the message. '},
         		// com.amplafi.core.messagehandling.MessagePointAction#BAD - There was a problem while processing the message
     			 'bad': {'styles':[  this.messagePointProblemSelectedCssClass, this.messagePointUnselectedCssClass, this.messagePointSelectedCssClass], 'msg':'An error occured while publishing the message. We are investigating the problem right now.'},
     			 //for the situations when ExternalEntityStatus is not null on the message point envelope record.
     			 'err': {'styles':[  this.messagePointProblemSelectedCssClass, this.messagePointUnselectedCssClass, this.messagePointSelectedCssClass], 'msg':'An error occured while publishing the message. We are investigating the problem right now.'}
        		};
    	
        	if ( this.userMessageEndPointOverrideNodeId ) {
        		this.userMessageEndPointOverrideNode = amplafi.html.byId(this.userMessageEndPointOverrideNodeId);
	            if ( this.userMessageEndPointOverrideNode ) {
	            	eval("this.userMessageEndPointOverride="+this.userMessageEndPointOverrideNode.value);
	            }
        	}
        	if ( this.selectedTopicIdsNodeId ){
	            this.selectedTopicIdsNode = amplafi.html.byId(this.selectedTopicIdsNodeId);
	            if ( !this.selectedTopicIdsNode ) {
	            	amplafi.html.debug("selectedTopicIdsNode is not set! Looking for node with id="+this.selectedTopicIdsNodeId);
	            } else {
	            	eval("this.selectedCategories="+this.selectedTopicIdsNode.value);
	            }
        	}
        	if ( this.selectAllNodeId ) {
        		this.selectAllNode = amplafi.html.byId(this.selectAllNodeId);
        	}
        	if ( this.mepUriBlockId) {
        		this.mepUriBlockNode = amplafi.html.byId(this.mepUriBlockId);
        	}
        	if ( this.mepUrlDisplayId) {
        		this.mepUrlDisplayNode = amplafi.html.byId(this.mepUrlDisplayId);
        	}
            if (this.mepData) {
                eval("this.mepData=" + this.mepData);
                this.displayMeps();
            }
               
            // attach behavior to the topics
            this._topicNodes = this._findAllTopics();
            if (this._topicNodes) {
                this._topicNodes.each(function(index, element) {
                    self.addTopicBehavior(element);
                });
                this._findTopicListNode().click(function(eventObject) {
                    self.focusFirstTopic(eventObject);
                });
            }
            // attach behavior to tags
            if ( this.selectedTagsNodeId ) {
	            this.selectedTagsNode = amplafi.html.byId(this.selectedTagsNodeId);
	            if ( this.selectedTagsNode ) {
	            	eval("this.selectedTags="+this.selectedTagsNode.value);
	            } else { 
	            	amplafi.html.debug("selectedTagsNode is not set! Looking for node with id="+this.selectedTagsNodeId);
	            }
            }
            var tagNodes = amplafi.html.getElementsByClass(this.tagCssClass, this.domNode);
            tagNodes.each(function(index, element) {
                self.addTagBehavior(element);
            });

            // attach behavior to tag input
            if (this.domNode) {
                var inputNodes = this.domNode.getElementsByTagName('input');
                if (inputNodes.length==2 && amplafi.string.endsWith(inputNodes[0].id, "addTag")
                        && amplafi.string.endsWith(inputNodes[1].id, "addTagButton")) {
                    $(inputNodes[0]).keypress(function(eventObject) {
                        self.triggerNewTag(eventObject);
                    });
                    $(inputNodes[1]).click(function(eventObject) {
                        self.triggerNewTag(eventObject);
                    });
                    $(this._findTagListNode()).click(function(eventObject) {
                        self.focusTagInput(eventObject);
                    });
                }
            }
        },
        addChild: function() {
            // override addChild and do nothing
        	// this is for adding a new widget.
            return null;
        },
        triggerNewTag: function(evt){
            var inp = evt.target.id;
            if (amplafi.string.endsWith(inp, "Button")) {
                inp = inp.substring(0, inp.length - 6);
                evt = null;
            }
            // Triggers the creation of a new tag from the value of the given input field.
            if (!evt || (evt.keyCode || evt.which)==13){
                inp = amplafi.html.byId(inp);
                this.addNewTag(inp.value, true);
                inp.value='';
                if (evt)
                    evt.preventDefault();
            }
        },
        addTopicBehavior: function(htmlNode, overrideContainerNode, pos, ref, insertIndex){
            if (this.disabled) return;
            var self = this;
            if ( amplafi.html.hasClass(htmlNode, this.topicCssClass)) {
            	this._setDeselectedClass(htmlNode);
                $(htmlNode).one("click", function(eventObject) {
                    self._handleOnTopicClick(eventObject);
                });
                $(htmlNode).one("keypress", function(eventObject) {
                    self._handleTopicKey(eventObject);
                });
            } else {
                throw new Error(htmlNode+" does not have class "+this.topicCssClass);
            }

            // look for the topic already being selected and set the class if needed.
            if ( this._isTopicSelected(htmlNode) ) {
                this._setSelectedClass(htmlNode);
            }
        },
        _handleOnTopicClick : function(evt) {
            var target = evt.target;
            var add;
            var self = this;
            if ( this.selectAllNode && target==this.selectAllNode ) {
            	add = this.selectedCategories['allCategoriesSelected'] = !this.selectedCategories['allCategoriesSelected'];
                var topicNodes = this._findAllTopics();
                // topicNodes includes selectAllNode
                if ( add ) {
                	$(topicNodes).each(function(index, element) {
                	    self._selectTopic(element);
                	});
                } else {
                    $(topicNodes).each(function(index, element) {
                        self._deselectTopic(element);
                    });
                }
            } else if ( this._isTopicSelected(target)) {
            	add = false;
                this._deselectTopic(target);
                // also deselect the all button
                if (this.selectAllNode && this.selectedCategories['allCategoriesSelected']) {
                	this._setDeselectedClass(this.selectAllNode);
                	this.selectedCategories['allCategoriesSelected'] = false;
                }
            } else {
            	add = true;
                this._selectTopic(target);
            }
            
            var self = this;
            // In minimized message view, each topic is displayed in 2 places - once in the popup and once in the minimized list that only displays the selected topics.
            // so this finds the other node for the topic that was just clicked on and sets it class correctly.
            $(this._topicNodes).each(function(node) {
                if (node!=target && amplafi.html.getAttribute(node, 'topicId')==amplafi.html.getAttribute(target, 'topicId')) {
                	if ( add ) {
                		self._setSelectedClass(node);
                	} else {
                		self._setDeselectedClass(node);
                	}
                }
            });
            
            this.selectedTopicIdsNode.value = $.toJSON(this.selectedCategories);
            amplafi.html.debug("selected topic ids now="+this.selectedTopicIdsNode.value);
            this.displayMeps();
            // so that the href isn't followed
            if (evt.preventDefault)
                evt.preventDefault();
        },
        _handleTopicKey : function(evt) {
            if (evt.key==' ')
                this._handleOnTopicClick(evt);
        },
        _getTopicId : function(htmlNode) {
            var topicId = amplafi.html.getAttribute(htmlNode, "topicId");
            if ( topicId == null ) {
                throw new Error("'topicId' attribute missing from " + htmlNode);
            }
            return topicId;
        },
        _getPaddedTopicId : function(htmlNode) {
        	var topicId = this._getTopicId(htmlNode);
        	return topicId;
        },

        // TODO use MessagePointCloud widget
        displayMeps : function() {
            if (!this.mepData || !this.mepUriBlockNode || !this.mepUrlDisplayNode)
                return;
            var topicIds = this.selectedCategories.categoriesSelected;
            var displData = [];

            for (var mep in this.mepData) {
            	var messagePointData = this.mepData[mep];
                var mepId = messagePointData['entityId'];
                var inTopics = messagePointData['broadcastTopic'];
                var mepName = messagePointData['displayName'];
                var uri = messagePointData['publicUri'];
                var currentMessagePointAction = messagePointData['messagePointAction'];
                var nextMessagePointAction = messagePointData['nextMessagePointAction'];
                var messageUri = messagePointData['messageUri'];
                var fullMessage = messagePointData['fullMessage'];
                var currentChoice;
                if ( nextMessagePointAction ) {
                    currentChoice = this._cssChoiceList[nextMessagePointAction];
                } else if(messagePointData['externalEntityStatus']){
                	currentChoice = this._cssChoiceList['err'];
                } else {
                    currentChoice = this._cssChoiceList[currentMessagePointAction];
                }                	
                var allowDeselect = currentChoice && currentChoice['styles'].length > 1;
                if ( this.userMessageEndPointOverride ) {
                    if ( !this.userMessageEndPointOverride[mepId] ) {
                    	//  only set to system default behavior if no messagePointAction
                    	this.userMessageEndPointOverride[mepId] = 0;
                    }
	                for (var i in topicIds) {
	                    if (amplafi.html.inArray(inTopics, topicIds[i])) {
	                        var deselectLink;
	                        var mepClass = '';
	                        if (allowDeselect) {
	                            deselectLink = '<a href="#" title="Exclude" class="removeMep">X</a>';
	                            mepClass += ' mepOne';
	                        } else {
	                            deselectLink = '';
	                            mepClass += ' mepOneViewOnly';
	                        }
	                        mepClass += ' ext-link';
	                        var override = this.userMessageEndPointOverride[mepId];
	                        if ( currentChoice ) {
	                        	mepClass += ' ' + currentChoice['styles'][override];
	                        }
	                        if ( nextMessagePointAction ) {
	                        	mepClass += this.messagePointInProgressCssClass;
	                        }
	                        var title = 'Visit' + uri;
	                        if(currentChoice && currentChoice['msg'] ){
	                        	title = currentChoice['msg'];
	                        }
	                        
	                        var outLink = '<a target="_blank" class="'+mepClass+'" href="' + (messageUri?messageUri:uri) +
	                        '" title="'+title+'" mepId="'+mepId+'">'+mepName+'</a>';
	                        displData.push('<span class="amplafiCategoryCloud amplafiCategoryCloudHalf">' + outLink +  deselectLink + '</span>');
	                        break;
	                    }
	                }
	            }
            }
            if (displData.length > 0) {
                var showHtml = displData.join(" ");
                this.mepUrlDisplayNode.innerHTML = showHtml;

                if (!amplafi.html.isShowing(this.mepUriBlockId)) {
                    amplafi.html.byId(this.mepUriBlockId).fadeIn(100);
                }
            } else {
                amplafi.html.byId(this.mepUriBlockId).fadeOut(100);
            }
            amplafi.util.onContentChanged();
            var meps = amplafi.html.getElementsByClass("removeMep", this.domNode);
            meps.each(function(index, element) {
                $(element).click(function(eventObject) {
                    self.handleOnMepClick(eventObject);
                })
            });
        },
        _getClickedMessagePointId : function(htmlNode) {
            var mepId = amplafi.html.getAttribute(htmlNode, "mepId");
            if ( mepId == null ) {
                throw new Error("'mepId' attribute missing from " + htmlNode);
            }
            return mepId;
        },
        messagesErrorCallback : function(result, error, id) {
            alert(error);
        },
        addTagBehavior : function(htmlNode, overrideContainerNode, pos, ref, insertIndex){
            if (this.disabled) return;
            var self = this;
            if ( amplafi.html.hasClass(htmlNode, this.tagCssClass)) {
                amplafi.html.addClass(htmlNode, this.tagUnselectedCssClass);
                amplafi.html.removeClass(htmlNode, this.tagSelectedCssClass);
                $(htmlNode).one("click", function(eventObject) {
                    self._handleOnTagClick(eventObject);
                    }).
                    one("keypress",function(eventObject) {
                        self._handleTagKey(eventObject);
                    });
            } else {
                throw new Error(htmlNode+" does not have class "+this.tagCssClass);
            }
            // look for the tag already being selected and set the class if needed.
            if (this.selectedTagsNode) {
            	var paddedTag = this._getPaddedTag(htmlNode);
                var x = amplafi.lang.find(this.selectedTags, paddedTag);
                if ( x >= 0 ) {
                	// not using replaceClass because the unselected class may not be present
                	amplafi.html.removeClass(htmlNode, this.tagUnselectedCssClass);
                	amplafi.html.addClass(htmlNode, this.tagSelectedCssClass);
                }
            }
        },
        _handleOnTagClick : function(evt) {
            if (!this.selectedTagsNode) return;
            
            var target = evt.target;
            var paddedTag = this._getPaddedTag(target);
            var x = amplafi.lang.find(this.selectedTags, paddedTag);
            if ( x >= 0 ) {
                // tag previously selected
            	this.selectedTags.splice(x, 1);
                amplafi.html.replaceClass(target, this.tagUnselectedCssClass, this.tagSelectedCssClass);
            } else {
                // tag just selected
            	this.selectedTags.splice(0,0, paddedTag);
                amplafi.html.replaceClass(target, this.tagSelectedCssClass, this.tagUnselectedCssClass);
            }
            this.selectedTagsNode.value = $.toJSON(this.selectedTags);
            amplafi.html.debug("selected tags now="+this.selectedTagsNode.value);
            // so that the href isn't followed
            if (evt.preventDefault)
                evt.preventDefault();            
        },
        _handleTagKey : function(evt) {
            if (evt.key==' ')
                this._handleOnTagClick(evt);
        },
        addNewTag : function(content, selected){
            content = amplafi.string.trim(content);
            if (amplafi.string.isBlank(content)) {
                this.focusTagInput();
                return;
            }
            var existingTag = this.getTagNode(content);
            if (existingTag){
                var x = amplafi.lang.find(this.selectedTags, content);
                if ( x < 0 == selected ) {
                	// turn on previously unselected if selected = true 
                	// turn off previous selected tag if selected = false
            		// only turn off existing tag if selected is false otherwise entering a tag twice turns tag off the second time.
            		this.switchTagSelection(existingTag);
            	}
                this.focusTagInput();
                return;
            }
            var a = $('<a href="#" class="'+this.topicCloudCssClass + ' ' + this.tagCssClass+'" tagId="'+content+'">'+content+'</a>');
            var parent = this._findTagListNode();
            var form = parent.getElementsByTagName('div')[0];
            parent.insertBefore(a, form);
            parent.insertBefore(document.createTextNode(' '), form);

            this.addTagBehavior(a);
            if (selected) {
                this.switchTagSelection(a);
            }
            this.focusTagInput();
        },
        focusTagInput : function(e) {
            var node = this._findTagListNode();
            if (e && e.target!=node) {
                return;
            }
            var inp = amplafi.html.getElementsByClass('tagInput', node)[0];
            inp.focus();
        },
        focusFirstTopic : function(e) {
            var node = this._findTopicListNode();
            if (e && e.target!=node) {
                return;
            }
            var inp = node.getElementsByTagName('a');
            if (inp && inp.length>0)
                inp[0].focus();
        },
        // returns the dom node containing the given tag text
        getTagNode: function(text){
            // perhaps we could have all those stored, but this is fast anyway.
            var parent = this._findTagListNode();
            var kids = parent.getElementsByTagName("a");
            var found = $('a[tagId="'+kid+'"]')
            return found;
        },
        // switch selection state for this tag node
        switchTagSelection : function(htmlNode){
            var evt = {};
            evt.target = htmlNode;
            this._handleOnTagClick(evt);
        },
        _getPaddedTag : function(htmlNode) {
            var tag = amplafi.html.getAttribute(htmlNode, "tagId");
            if ( tag == null ) {
                return "";
            }
            return tag;
        },
        _findTagListNode : function() {
            if (!this._tagListNode) {
                this._tagListNode = this._findAncestorNode('tagsList');
            }
            return this._tagListNode;
        },
        _findTopicListNode : function() {
            if (!this._topicListNode) {
                this._topicListNode = this._findAncestorNode('topicsList');
            }
            return this._topicListNode;
        },
        _findAncestorNode: function(clazzName) {
            var nodes = amplafi.html.getElementsByClassName(clazzName, this.domNode);
            return nodes.length>0 ? nodes[0] : null;
        },
        _findAllTopics : function() {
            return amplafi.html.getElementsByClass(this.topicCssClass, this.domNode);
        },
        _deselectTopic : function(target) {
            var paddedTopicId = this._getPaddedTopicId(target);
            if (!this.selectedCategories.categoriesSelected)
            	this.selectedCategories.categoriesSelected = [];
            
            var selectedCategoriesArr = this.selectedCategories.categoriesSelected;            	
            var x = amplafi.lang.find(selectedCategoriesArr, paddedTopicId);
            if ( x >= 0 ) {
            	selectedCategoriesArr.splice(x, 1);
            	this._setDeselectedClass(target);
            }
        },
        _selectTopic : function(target) {
            var paddedTopicId = this._getPaddedTopicId(target);
            if (!this.selectedCategories.categoriesSelected)
            	this.selectedCategories.categoriesSelected = [];
            
            var selectedCategoriesArr = this.selectedCategories.categoriesSelected;
            this._setSelectedClass(target);
            if ( amplafi.html.inArray(selectedCategoriesArr, paddedTopicId)) {
        		return;
            }
            selectedCategoriesArr.splice(0,0, paddedTopicId);
        },
        // TODO use MessagePointCloud widget
        // first click USER_BLOCK
        // second click USER_FORCE (skip if no 
        // third click neutral
        handleOnMepClick : function(evt) {
            var targetNode = evt.target.parentNode.getElementsByTagName('a')[0];
            var messagePointId = this._getClickedMessagePointId(targetNode);
            var messagePointData = this.mepData[messagePointId];
            var currentMessagePointAction = messagePointData['messagePointAction'];
            var cssChoiceArray = this._cssChoiceList[currentMessagePointAction];
            if ( cssChoiceArray && cssChoiceArray.length > 1 ) {
            	// it is possible to change the style
            	var oldCssIndex = this.userMessageEndPointOverride[messagePointId];
            	amplafi.html.removeClass(targetNode,cssChoiceArray[oldCssIndex]);
            	this.userMessageEndPointOverride[messagePointId] = (this.userMessageEndPointOverride[messagePointId] +1) % cssChoiceArray.length;
            	amplafi.html.addClass(targetNode,cssChoiceArray[this.userMessageEndPointOverride[messagePointId]]);
            	// write back to the output node.
            	this.userMessageEndPointOverrideNode.value = $.toJSON(this.userMessageEndPointOverride);
            }
            // so that the href isn't followed
            if (evt.preventDefault) {
                evt.preventDefault();
            }
        },
        _setDeselectedClass : function(target) {
            // not using a replaceClass because the unselected class may not be present
        	var unselectedCssClass = amplafi.html.getAttribute(target, "unselectedCssClass");
        	if ( unselectedCssClass != null) {
        		amplafi.html.addClass(target, unselectedCssClass);        		
        	} else if ( this._isDefaultTopic(target)) {
        		amplafi.html.addClass(target, this.topicUnselectedDefaultCssClass);
        	} else {
        		amplafi.html.addClass(target, this.topicUnselectedCssClass);
        	}
            amplafi.html.removeClass(target, this.topicSelectedCssClass);
        },
        _setSelectedClass : function(target) {
            // not using a replaceClass because the unselected class may not be present
        	// also defaulttopic may be changed.
        	var unselectedCssClass = amplafi.html.getAttribute(target, "unselectedCssClass");
        	if ( unselectedCssClass != null) {
        		amplafi.html.removeClass(target, unselectedCssClass);        		
        	}
            amplafi.html.removeClass(target, this.topicUnselectedCssClass);
            amplafi.html.removeClass(target, this.topicUnselectedDefaultCssClass);
            amplafi.html.addClass(target, this.topicSelectedCssClass);
        },
        _isTopicSelected : function(target) {
        	if (this.selectedCategories.allCategoriesSelected)
        		return true;
            var selectedCategoriesArr = this.selectedCategories.categoriesSelected;
            if (!selectedCategoriesArr)
            	return false;
            var paddedTopicId = this._getPaddedTopicId(target);
            return amplafi.html.inArray(selectedCategoriesArr, paddedTopicId);
        },
        _isDefaultTopic : function(target) {
        	var targetTopicId = this._getTopicId(target);
        	var defaultTopic = this.defaultTopicId == targetTopicId;
        	return defaultTopic;
        }
    };
}

