Source: defaultview/defaultView.js

 /**
 *  
 * @class Searches an array of records
 * @extends Septima.Search.View
 * @constructs Septima.Search.DefaultView
 * @param {Object} options DefaultView expects these properties:
 * @param options.input {string} Name of DOM container in which to draw the input field
 * @param options.placeholder {string} Place holder text displayed in the empty search input field
 * 
 * @example 
 * var view = new Septima.Search.DefaultView(
 *   {
 *     input : 'septimasearch',
 *     placeholder : 'Search'
 *   });
 *
 */
Septima.Search.DefaultView = Septima.Class (Septima.Search.View, /**  @lends Septima.Search.DefaultView# */{
	
	initialize: function (options) {
		if (options.input instanceof jQuery){
			this.container = options.input;
		}else{
		    this.container = jQuery('#'+options.input);
		}
	    
	    this.placeHolderText = "Søg";
		if (options.placeholder !== undefined){
			this.placeHolderText = options.placeholder;
		}
		
		this.detailsButtonCaption = "Se detaljer";
		if (options.detailsButtonCaption !== undefined){
			this.detailsButtonCaption = options.detailsButtonCaption;
		}
		
        this.showNewQueryIcon = true;

		//Vars
	    this.visibleInterests = [];
	    this.isVisible = false;
	    this.advancedIsVisible = false;
	    this.$gui = null;
	    this.$suggestionContainer = null;
	    this.$suggestionList = null;
	    this.header = null;
	    this.fixedDisplay = null;
	    this.fixedDisplayHideFunction = null;
	    this.fixedDisplayText = null;
	    this.hasFixedDisplay = false;
	    this.myInputField = null;
	    this.targetBit = null;
	    this.targetLabel = null;
	    this.resultList = null;
	    this.searchBackGround = null;
	    this.detailsContentProviderType = Septima.Search.AccordionDetailsContent;

		this.View(options);
		//this.myController.addOnAddSearcherHandler(Septima.bind(this.onAddSearcher, this));
	},
	
	//Public Functions 
	setMaxHeight: function(intHeight){
		if (intHeight !== null && intHeight > 100 && this.resultList !== null){
			this.resultList.css({ "max-height": intHeight + 'px' });
		}
	},
	
	top: function(){
		if (this.myInputField !== null){
			return this.myInputField.offset().top + 60;
		}else{
			return null;
		}
	},

	closeFixedText: function () {
		this.fixedDisplay.hide();
		this.fixedDisplayHideFunction();
		this.header.show();
		this.hasFixedDisplay = false;
		this.evaluateResultsVisibilityInterest();
	},


	/**
	 * Display a fixed text which can be closed by the user. When the text is closed the hidFunction will be called
     * @param {string} text. Text to be displayed in the search box
     * @param {function} hideFunction. Function to call when user closes the text.
	 */
	showFixedText: function (text, hideFunction) {
		this.header.hide();
		this.fixedDisplayText.html(text);
		this.fixedDisplay.show();
		this.hasFixedDisplay = true;
		this.evaluateResultsVisibilityInterest();
		this.fixedDisplayHideFunction = hideFunction;
	},

	//Interface Implementation
	draw: function () {
		this.$gui = this.createGui(this.container);
		this.myInputField.betterAutocomplete('init',
			'http://bla.bla.', {
				cacheLimit: 0, 
				selectKeys: [13], 
				crossOrigin: true, 
				charLimit:0 
			}, {
				fetchRemoteData:      Septima.bind(this.fetchRemoteData,this),
				themeResult:          Septima.bind(this.themeResult,this),
				insertSuggestionList: Septima.bind(this.insertSuggestionList,this),
				constructURL:         Septima.bind(this.constructURL,this),
				select:               Septima.bind(this.select,this),
				show:                 Septima.bind(this.bacEventShow,this),
				hide:                 Septima.bind(this.bacEventHide,this),
				getGroup:             Septima.bind(this.getGroup,this),
				beginFetching:        Septima.bind(this.beginFetching,this),
				finishFetching:       Septima.bind(this.finishFetching,this),
				afterClear:           Septima.bind(this.afterClear,this),
				backSpace:            Septima.bind(this.backSpace,this)
			}
		);
		this.bac = this.myInputField.data('better-autocomplete');
		var groups = [];
		for (var i=0;i<this.searchers.length;i++){
			groups.push(this.searchers[i].getId());
		}
		this.bac.createGroups(groups);
	},

	getGroup: function(result){
		if (result.searcher){
			return result.searcher.getId();
		}else{
			return "";
		}
	},
	
	beginFetching: function(){
		this.searchBackGround.addClass('ssSearchBackgroundLoading');
	},
	
	finishFetching: function(){
		this.searchBackGround.removeClass('ssSearchBackgroundLoading');
	},
	
	afterClear: function(){
		this.clearSuggestions();
	},
	
	backSpace: function(){
		if (this.targetLabel.html().length > 0){
			this.removeTarget();
			this.bac.repeatSearch();
		}
	},
	
	clearAll: function(){
		this.doTextSearch("");
	},
	
	focus: function (){
        this.myInputField[0].focus();
	},
	
	blur: function (force) {
        this.myInputField[0].blur();
		if (force) {
			this.hideResults();
		}
	},
	
//	onAddSearcher: function(){
//		this.refresh();
//	},

	refresh: function(){
		if (this.hasStarted){
			this.bac.repeatSearch();
		}
	},
	
	doTextSearch: function (searchText) {
		this.clearSuggestions();
		this.myController.cancelFetches();
		var colonPos = searchText.indexOf(":");
		if (colonPos > 0){
			var potentialTarget = searchText.substring(0, colonPos);
			if (this.myController.isValidTarget(potentialTarget)){
				this.showTarget(potentialTarget);
				this.bac.doSearch(jQuery.trim(searchText.replace(potentialTarget+':','')));
			}else{
				this.removeTarget();
			    this.bac.doSearch(searchText);
			}
		}else{
			this.removeTarget();
		    this.bac.doSearch(searchText);
		}
	},

    setSuggestions: function (searchId, suggestions) {
        if (suggestions.length > 0){
            this.$suggestionList.addClass('ssSuggestionsContent');
            for (var i=0;i<suggestions.length;i++){
                var suggestion = suggestions[i];
                var $suggestionLink = jQuery('<span class="ssButton" >' + suggestion.title + '</span> ').appendTo(this.$suggestionList);
                $suggestionLink.click(Septima.bind(function(newquery){
                    this.doTextSearch(newquery);
                }, this, suggestion.suggestion));
            }
        } else {
            this.$suggestionList.removeClass('ssSuggestionsContent');
        }
    },
    
    clearSuggestions: function () {
        this.$suggestionList.removeClass('ssSuggestionsContent');
        this.$suggestionList.empty();
    },
    
	setResults: function (results) {
		this.bac.renderResults(results);
	},

	selectHighLighted: function(){
		this.bac.select();
	},
	//Private functions

	constructURL: function (path, query) {
	    return query;
	},

	insertSuggestionList: function ($results, $input) {
		this.resultList = $results;
	    this.resultscontainer.append($results);
	},

	fetchRemoteData: function (query, completeCallback, timeout, crossOrigin) {
		this.clearSuggestions();
		this.details.clear();
		if (query.length === 0){
			this.searchBackGround.removeClass('ssSearchBackgroundHasContent');
		}else{
			this.searchBackGround.addClass('ssSearchBackgroundHasContent');
		}
		query = this.completeQueryWithTarget(query);
		this.myController.fetchData(query, Septima.bind(function (callback,result, isLast) {
			callback (result, isLast);
		},this,completeCallback), this.limit, timeout);
	},

	themeResult: function (result) {
		var searcher;
		var extraButtonDefs;
		if (result.hasOwnProperty("newquery") && result.newquery !== null){
	        if (!result.hasOwnProperty("image") || result.image === null || result.image === "" || result.image === undefined){
	        	searcher = result.searcher;
	        	if (searcher.hasOwnProperty("iconURI") && searcher.iconURI !== null && searcher.iconURI !== "" && searcher.iconURI !== undefined){
		        	result.image = searcher.iconURI;
	        	}else{
		        	result.image = Septima.Search.icons.result.defaultNewQueryIcon;
	        	}
	        }
			extraButtonDefs = [{"buttonText": "", "buttonImage": Septima.Search.icons.list.newQueryIcon, fixed: true}];
			return Septima.Search.ThemeResult(result, [], extraButtonDefs);
		}else{
	        if (!result.hasOwnProperty("image") || result.image === null || result.image === ""){
	        	searcher = result.searcher;
	        	if (searcher.hasOwnProperty("iconURI") && searcher.iconURI !== null && searcher.iconURI !== ""){
		        	result.image = searcher.iconURI;
	        	}else{
		        	result.image = Septima.Search.icons.result.defaultIcon;
	        	}
	        }
			if (result.searcher.hasdetailHandlerDefs(result)){
				extraButtonDefs = [{"buttonText": this.detailsButtonCaption, "buttonImage": Septima.Search.icons.list.detailsIcon, "callBack": Septima.bind(function(result){this.doDetail(result);}, this, result), fixed: true}];
				return Septima.Search.ThemeResult(result, [], extraButtonDefs);
			}else{
				var customButtonDefs = [];
				if (result.searcher){
					customButtonDefs = result.searcher.getCustomButtonDefs(result);
				}
				return Septima.Search.ThemeResult(result, customButtonDefs);
			}
		}

	},

	select: function (result, $input) {
	    //this.details.container.removeClass("ssActive");
	    this.details.hide();
		this.myController.select(result, this);
		return false;
	},

	removeTarget: function(){
		this.targetLabel.html("");
		this.targetBit.hide();
		this.myInputField.width(this.header.innerWidth() - 60);
	},

	showTarget: function(target){
		this.targetLabel.html(target);
		this.targetBit.show();
		this.myInputField.width(this.header.innerWidth() - this.targetBit.outerWidth() - 60);
	},

	completeQueryWithTarget: function(query){
		var returnQuery = query;
		if (this.targetLabel.html() !== ""){
			returnQuery = this.targetLabel.html() + ":" + query;
		}
		return returnQuery;
	},

	/**
	 * private internal method of Septima.Search.DefaultView
     * @private
	 */
	createGui: function ($container) {
		$container.addClass('septimasearch');
		this.header = jQuery('<div class="ssSearchHeader"/>');
		this.searchBackGround = jQuery('<div class="ssSearchBackground"></div>');
		this.header.append(this.searchBackGround);
		
		this.targetBit = jQuery("<span class='ssTargetbit'></span>");
		this.targetLabel = jQuery("<span class='ssTargetlabel'></span>");
		var targetCloseButton = jQuery("<span class='ssTargetclosebutton'></span>");
		targetCloseButton.click(Septima.bind(function(){
				this.removeTarget();
				this.bac.repeatSearch();
			}, this));
		this.targetBit.append(this.targetLabel);
		this.targetBit.append(targetCloseButton);
		this.searchBackGround.append(this.targetBit);
		
		this.myInputField = jQuery('<input class="ssInput" placeholder="' + this.placeHolderText + '" autocomplete="off" role="textbox" aria-autocomplete="list" aria-haspopup="true">');
		this.searchBackGround.append(this.myInputField);
		var searchmenu = jQuery('<div class="ssSearchMenu"></div>');
		this.$suggestionList = jQuery('<div class="ssSuggestions"></div>');
		searchmenu.append(this.$suggestionList);
		this.header.append(searchmenu);

		var searchIcon = jQuery("<div class='ssSearchIcon'></div>");
		this.searchBackGround.append(searchIcon);

		var spinner = jQuery("<div class='ssSpinner'><div class='bounce1'></div><div class='bounce2'></div><div class='bounce3'></div></div>");
		this.searchBackGround.append(spinner);

		var closeButton = jQuery("<div class='ssCloseButton'></div>");
		this.searchBackGround.append(closeButton);

		this.resultscontainer = jQuery('<div class="ssSearchResult"/>');
		
		$container.append(this.header);
		//$container.append(this.fixedDisplay);
		$container.append(this.resultscontainer);

		//Details
		this.details = this.createDetailsView($container, this.detailsContentProviderType);

		$container.bind({mouseleave:Septima.bind(this.guiMouseLeave,this)});
	    this.myInputField.bind({mouseenter: Septima.bind(this.guiMouseEnter,this)});

		this.myInputField.bind({focus: Septima.bind(this.inputFocus,this), blur: Septima.bind(this.inputBlur,this)});
		
		closeButton.bind({click: Septima.bind(this.clearAll,this)});

		this.removeTarget();
	},
	
	//Control showing and hiding of GUI
	setResultsVisibilityInterest: function (party, interested) {
	    var partyFound = false;
	    for (var i=0;i<this.visibleInterests.length;i++){
	        if (this.visibleInterests[i].party == party){
	            partyFound = true;
	            this.visibleInterests[i].interested = interested;
	        }
	    }
	    if (!partyFound){
	        this.visibleInterests.push({party: party, interested: interested});
	    }
	    if (interested && !this.hasFixedDisplay){
	        this.showResults();
	    }else{
	        var anyInterested = false;
	        for (i=0;i<this.visibleInterests.length;i++){
	            if (this.visibleInterests[i].interested){
	                anyInterested = true;
	            }
	        }
	        if (anyInterested && !this.hasFixedDisplay){
	            this.showResults();
	        }else{
		        this.hideResults();
	        }
	    }
	},

	createDetailsView: function(container, contentProviderType){
		var details = {};
		details.contentProvider = new contentProviderType(details);
		details.results = [];
		
		details.container = jQuery('<div class="ssDetails"/>');
		var detailul = jQuery('<ul class="better-autocomplete"/>');
		details.container.append(detailul);
		
		details.header = jQuery('<li class="ssDetailHeader"/>');
		detailul.append(details.header);

		details.content = jQuery('<li class="ssDetailBody"/>');
		details.content.addClass("ssActive");
		
		detailul.append(details.content);

		container.append(details.container);
		details.container.removeClass("ssActive");
		
		details.show = function(){
			if (typeof details.result !== 'undefined' && details.result !== null){
				details.container.addClass("ssActive");
				//TBD find septimasearch->detailsactive
			}
		};
		
		details.hide = function(){
			details.container.removeClass("ssActive");
			//TBD find septimasearch->detailsactive
		};
		
		details.clear = function(){
			if(details.result !== undefined && details.result !== null){
				details.results = [];
				details.hide();
				details.clearGui();
				details.contentProvider.forgetState();
			}
		};
		
		details.clearGui = function(){
			if(details.result !== undefined && details.result !== null){
				details.result = null;
				details.contentProvider.destroyUI();
				details.header.empty();
		    	details.content.empty();
			}
		};
		
		//details.extraButtonDefs = [{"buttonText": "Luk", "buttonImage": Septima.Search.icons.details.back, "callBack": Septima.bind(function(){this.details.clear();}, this)}];
		
		details.back = function(){
			if (details.results.length > 0){
				var result = details.results.pop();
				result.searcher.onSelect(result);
				details.render(result);
				details.show();
			}
		};
		
		details.setResult = function(result){
			if(details.result !== undefined && details.result !== null){
				details.results.push(details.result);
			}
			details.render(result);
		};
		
		details.render = function(result){
			details.clearGui();
			details.result = result;
			var backButton;
			var title;
			if (details.results.length > 0){
				title = details.results[details.results.length - 1].title;
				backButton = jQuery('<div class="ssBackButton"><img title="' + title + '" src="' + Septima.Search.icons.details.back + '"><div class="ssTxt">' + title + '</div></div>');
				backButton.click(Septima.bind(function(details){
					details.back();
				}, this, details));
			}else{
				backButton = jQuery('<div class="ssBackButton"><img title="Luk" src="' + Septima.Search.icons.details.back + '"><div class="ssTxt">Luk</div></div>');
				backButton.click(Septima.bind(function(details){
					details.clear();
				}, this, details));
			}
			details.header.append(backButton);

			var output;
			output = jQuery("<div class='ssResult'/>");
            if (result.hasOwnProperty("image") && result.image !== null && result.image !== ""){
            	output = output.append('<div class="ssResultImage"><img src="' + result.image + '"></div>');
            }else{
	        	var searcher = result.searcher;
	        	if (searcher.hasOwnProperty("iconURI") && searcher.iconURI !== null && searcher.iconURI !== ""){
		        	result.image = searcher.iconURI;
	        	}else{
		        	result.image = Septima.Search.icons.result.defaultIcon;
	        	}
            	output = output.append('<div class="ssResultImage"><img src="' + result.image + '"></div>');
            }

            output = output.append('<div class="ssResultTitle">' + result.title + '</div>');
            
            if (result.hasOwnProperty("description") && result.description !== null){
                var description = jQuery('<div class="ssResultDescription" >' + result.description + '</div>');
                description.attr( "title", result.description + "' \"" );
            	output.append(description);
            }else{
            	output.append('<div class="ssResultDescription hide"></div>');
            }
            
            details.header.append(output);

	    	details.content.append(details.contentProvider.getUI(result));    		
	    	details.content.addClass("ssActive");
		};
		
		details.mouseLeave = function(){
			//details.content.removeClass("ssActive");
		};
		
		details.mouseEnter = function(){
			//details.content.addClass("ssActive");
		};
		
		details.container.bind({mouseleave:Septima.bind(details.mouseLeave, this), mouseenter: Septima.bind(details.mouseEnter, this)});
		
		return details;
		
	},
	
    doDetail: function(result){
		if (result !== undefined){
			this.details.setResult(result);
		    if (this.isVisible){
				this.blur(true);
		    }else{
		    	this.details.show();		    }
		}
    },

    showResults: function () {
	    if (!this.isVisible){
			//console.log('showResults');
			this.details.hide();
	    	this.container.addClass('ssActive');
//	    	jQuery('.searchmenu').show();
//		    this.resultscontainer.show();//searchresult
//		    this.resultList.show(); //ul
	        this.isVisible = true;
	    }
	},

	hideResults: function () {
	    for (var i=0;i<this.visibleInterests.length;i++){
            this.visibleInterests[i].interested = false;
	    }
	    if (this.isVisible){
	    	this.container.removeClass('ssActive');
//	    	jQuery('.searchmenu').hide();
//		    this.resultscontainer.hide();
//		    this.resultList.hide();
	        this.isVisible = false;
	        this.details.show();	    }
	},
	
	//Callbacks
	guiMouseEnter: function () {
		//console.log('guiMouseEnter');
	    this.setResultsVisibilityInterest('gui', true);
	},

	guiMouseLeave: function () {
	    this.setResultsVisibilityInterest('gui', false);
	},

	inputFocus: function () {
	    this.setResultsVisibilityInterest('input', true);
	},

	inputBlur: function () {
	    this.setResultsVisibilityInterest('input', false);
	},

	bacEventShow: function ($results) {
	    this.setResultsVisibilityInterest('bac', true);
	    //this.resultscontainer.show(); 
	    this.bac.show();
	},

	bacEventHide: function ($results) {
	    this.setResultsVisibilityInterest('bac', false);
	},
	
	CLASS_NAME: 'Septima.Search.DefaultView'

});