Source: searchers/s4IndexSearcher.js

/** 
 * 
 * @class Searches a Spatial Suite index. An S4 index must have been created and populated on the host. See https://github.com/Septima/spatialsuite-s4#5-configure-your-search-index
 * @constructs Septima.Search.S4IndexSearcher
 * @augments Septima.Search.Searcher
 * @param {Object} options S4IndexSearcher expects these properties:<br/>
 * @param {string} options.host "Service endpoint (SpatialSuite Url) eg.: [http:]//sps-demo.septima.dk[:80]. Protocol and port are optional"
 * @param {string} options.datasources Comma separated list of indexed data sources to search. Default: "*"
 *  
 * @example 
 * var s4IndexSearcherOptions = {
 *     host : "sps-demo.septima.dk",
 *     onSelect : function(result) {
 *          jQuery('#log').prepend('<b>'+ Spatial Suite objekt + ':</b> '+ result.title + '<br/>');
 *     },
 *     datasources : "*",
 *     matchesPhrase: "Begynder med ",
 *     searchDelay: 200
 * };
 * searchers.push({title : '', searcher : new Septima.Search.S4IndexSearcher(s4IndexSearcherOptions)});
 *
 */
    Septima.Search.S4IndexSearcher = Septima.Class (
		Septima.Search.Searcher, /**  @lends Septima.Search.S4IndexSearcher# */{
		indexedDatasources: null,
		datasourcesToSearch: "*", 

	/**
	 * This function is called when a result is selected.
	 * @callback Septima.Search.S4IndexSearcher~selectCallback
	 * @param {Object} result
	 * @param result.title {string}
	 * @param result.description {string}
	 * @param result.data {object} S4IndexResult
	 * @param result.data.heading {string} title
	 */

    initialize: function (options) {
    	options.usesGeoFunctions = true;
		this.Searcher(options);
		if (options === undefined){
			throw "New Septima.Search.S4IndexSearcher(options): Options missing.";
		}
	    this.indexedDatasources = null;
	    
    	this.ajaxURL = '';
    	if (options.host) {
    		this.ajaxURL = options.host;
    	} else if (options.indexProtocol && options.indexHost && options.indexPort){
			this.ajaxURL = options.indexProtocol + '://' + options.indexHost + ':' + options.indexPort;
		}
    	
    	this.allowDetails = false;
	    if (options.allowDetails){
	    	this.allowDetails = options.allowDetails;
	    }
    	
    	this.getDatasources();

	    if (options.datasources === undefined){
		    this.datasourcesToSearch = "*";
	    }else{
		    this.datasourcesToSearch = options.datasources;
		    if (this.datasourcesToSearch === "" || this.datasourcesToSearch === " "){
			    this.datasourcesToSearch = "*";
		    }
	    }
    },

    getDatasources: function(){
		var deferred = jQuery.Deferred();
		jQuery.ajax({
    		url: this.ajaxURL + '/jsp/modules/s4/getDatasources.jsp',
    		
	        dataType: 'jsonp',
            cache : false,
            timeout : 10000,
            crossDomain : true,
            async:true,
            success:  Septima.bind(function(deferred, data, textStatus,  jqXHR){
            	this.indexedDatasources = [];
            	if (typeof data.results !== 'undefined'){
                	for (var i=0;i<data.results.length;i++){
                		var datasource = data.results[i].datasource;
                		if (datasource.featurecount > 0 && this.inDatasourcesToSearch(datasource.id)){
                    		this.indexedDatasources.push(datasource);
        					this.registerTarget(datasource.featuretypesingle);
                		}
                	}
            	}
        		deferred.resolve(this.createQueryResult());
          }, this, deferred),
          error : Septima.bind(function (deferred, jqXHR, textStatus, errorThrown) {
      		deferred.resolve(this.createQueryResult());
            },this, deferred)
          });
		return deferred.promise(); 
    },
    
    inDatasourcesToSearch: function(datasource){
    	if (this.datasourcesToSearch == "*"){
    		return true;
    	}else{
        	var arrDatasourcesToSearch = this.datasourcesToSearch.split(" ");
        	for (var i=0;i<arrDatasourcesToSearch.length;i++){
        		if (arrDatasourcesToSearch[i].toLowerCase()==datasource.toLowerCase()){
        			return true;
        		}
        	}
    	}
    	return false;
    },
    
    getDatasourceIdFromTarget: function(target){
    	for (var i=0;i<this.indexedDatasources.length;i++){
    		if (target.toLowerCase() == this.indexedDatasources[i].featuretypesingle.toLowerCase()){
    			return this.indexedDatasources[i].id;
    		}
    	}
    	return "";
    },
    
    fetchData: function (query, caller) {
    	if (this.indexedDatasources === null){
    	    this.getDatasources().done(Septima.bind(function(query, caller){
    	    	this.myFetchData(query, caller);
    	    	}, this, query, caller));
    	    return;
    	}else{
    		this.myFetchData(query, caller);
    	}
    },
    
    sq: function(query){
		var deferred = jQuery.Deferred();
    	if (this.indexedDatasources === null){
    	    this.getDatasources().done(Septima.bind(function(query, deferred){
    	    	this.mySq(query, deferred);
    	    	}, this, query, deferred));
    	    return;
    	}else{
    		this.mySq(query, deferred);
    	}
		return deferred.promise(); 
    },
    
    mySq: function(query, deferred){
    	var datasources = this.datasourcesToSearch;

    	if (query.hasTarget){
    		if (this.hasTarget(query.target)){
        		datasources = this.getDatasourceIdFromTarget(query.target);
    		}
    	}
    	
		//var params = {querywkt: query.wkt, datasources: datasources, limit: query.limit };
		var params = {datasources: datasources, limit: query.limit };
		
		if (query.wkt){
			params.querywkt = query.wkt; 
		}
		
		if (query.distWkt){
			params.distwkt = query.distWkt; 
			//Backwards compatibility
			params.wkt = query.distWkt; 
		}
		
	    var c = jQuery.ajax({
	        url: this.ajaxURL + '/jsp/modules/s4/queryIndex.jsp',
	        data: params,
	        dataType: (this.ajaxURL === "") ? 'json' : 'jsonp',
	        method: (this.ajaxURL === "") ? 'post': 'get',
	        contentType: (this.ajaxURL === "") ? 'application/x-www-form-urlencoded' : 'application/javascript; charset=utf-8'});
	    
	    
	    c.done(Septima.bind(function(  query, deferred, data, textStatus, jqXHR){
	    	var queryResult = this.createQueryResult();
	    	for (var i=0; i < data.results.length; i++){
	    		var datasource = data.results[i].datasource;
	    		var features = data.results[i].features;
	    		if (features.length > 0){
	    	    	for (var j=0;j<features.length;j++){
	    	    		var feature = features[j];
	                    var resultGeometry = this.translateWktToGeoJsonObject(feature.wkt);
	    	    		var result = queryResult.addResult(feature.title, feature.description, resultGeometry, feature);
	    	    		result.target =  datasource.featuretypesingle;
	            		if (datasource.iconuri !== undefined){
	            			result.image = datasource.iconuri; 
	            		}
	            		result.distance = feature.properties.distance;
	    	    	}
	    		}
	    		
	    	}
	    	deferred.resolve(queryResult);
	    }, this, query, deferred));
    },
    
    getIndexedDatasourcesAsNewQueries: function(){
    	var queryResult = this.createQueryResult();
    	for (i=0;i<this.indexedDatasources.length;i++){
    		var datasource = this.indexedDatasources[i];
    		var result = queryResult.addNewQuery(datasource.featuretypeplural +  " (" + datasource.featurecount + ")", null, datasource.featuretypesingle + ":", null, null);
    		if (datasource.iconuri !== undefined){
    			result.image = datasource.iconuri; 
    		}
    	}
    	return queryResult;
    },
    
    myFetchData: function (query, caller) {
    	if (this.indexedDatasources === null || this.indexedDatasources.length === 0){
    		caller.fetchSuccess(this.createQueryResult());
    		return;
    	}
    	
    	var datasources = this.datasourcesToSearch;

    	if (query.hasTarget){
    		if (this.hasTarget(query.target)){
        		datasources = this.getDatasourceIdFromTarget(query.target);
    		}
    	}else if(query.queryString === ''){
    		if (this.blankBehavior == "search"){
        		caller.fetchSuccess(this.getIndexedDatasourcesAsNewQueries());
    		}else{
        		caller.fetchSuccess(this.createQueryResult());
    		}
    		return;
    	}
    	
    	var limitType = "collapse";
    	if (query.type == 'list.force'){
    		limitType = "cut";
    	}
    	
    	var xhr = jQuery.ajax({
            url: this.ajaxURL + '/jsp/modules/s4/queryIndex.jsp',
            data: {query:query.queryString, limit: query.limit, datasources: datasources, limitType: limitType},
	        dataType: 'jsonp',
            cache : false,
            timeout : 10000,
            crossDomain : true,
            async:true,
	        success : Septima.bind(function (caller, query, data, textStatus,  jqXHR) {
                data.query = query;
	        	this.success (caller, data, textStatus,  jqXHR);
	        },this, caller, query),
	        error : Septima.bind(function (caller, jqXHR, textStatus, errorThrown) {
	        	if (textStatus.toLowerCase() != 'abort' ){
		        	this.error (caller, jqXHR, textStatus, errorThrown);
	        	}
	        },this, caller)     
          });
		
	    caller.registerOnCancelHandler ( Septima.bind(function (xhr) {
	    	if (xhr && xhr.readystate != 4){
	    		xhr.abort();
	    	}
	    },this,xhr));
		    	
    },
    

	success : function(caller, data, textStatus, jqXHR) {
		if (caller.isActive()) {
			if (jqXHR.status == 200) {
				caller.fetchSuccess(this.parseResult(data));
			} else {
				caller.fetchError(this, jqXHR.statusText);
			}
		}

	},
	
    error: function(caller, jqXHR, textStatus, errorThrown){
        caller.fetchError(this, errorThrown);
    },
    
    parseResult: function (data) {
        var queryResult = this.createQueryResult();
        var query = data.query;
        
        for (var i=0;i<data.results.length;i++){
        	var description;
        	var result = data.results[i];
        	var datasource = result.datasource;
        	if (result.type == 'DatasourceWithMatchingFeaturesIncluded'){
        		for (var j=0;j<result.features.length;j++){
        			var feature = result.features[j];
        			feature.datasource = datasource;
                    var resultGeometry = this.translateWktToGeoJsonObject(feature.wkt);
                    description = this.getFeatureDescription(feature);
                    var result1;
        	        if (query.hasTarget) {
        	        	result1 = queryResult.addResult(feature.title, description, resultGeometry, feature);
        	        }else{
        	        	result1 = queryResult.addResult(feature.title + " (" + datasource.featuretypesingle + ")", description, resultGeometry, feature);
        	        }
		        	result1.target =  datasource.featuretypesingle;
            		if (datasource.iconuri !== undefined){
            			result1.image = datasource.iconuri; 
            		}
        		}
        	}else if (result.type == 'DatasourceWithMatchingFeatures'){
				description = null;
        		var newQuery = queryResult.addNewQuery(datasource.featuretypeplural +  " (" + datasource.featurecount + ")", description, datasource.featuretypesingle + ": " + query.queryString, null, null);
        		if (datasource.iconuri !== undefined){
        			newQuery.image = datasource.iconuri; 
        		}
        	}
        }
        return queryResult;
    },

    getFeatureDescription: function(feature){
    	var description = feature.description;
//    	if (!this.allowDetails){
//    		description = description + " " + this.getFeatureLinksAsHtml(feature); 
//    	}
    	return description; 
    },
    
    hasDetails: function(feature){
    	if (this.allowDetails){
        	if (typeof feature.hasDetails === "undefined"){
        		feature.hasDetails = false;
            	for (var i=0;i<feature.pcol.row.length;i++){
            		var row = feature.pcol.row[i];
            		if (typeof row._name !== "undefined" && (row._name !== 'column' || (row._name === 'column' && row.format === ''))){
                		feature.hasDetails = true;
                		return true;
            		}
            	}
        	}else{
            	return feature.hasDetails;
        	}
    	}else{
    		return false;
    	}
    },

    getDetailHandlersForResult: function(result){
    	var feature = result.data;
    	var detailHandlerDefs = [];
    	
    	if (this.hasDetails(feature)){
    		var detailsHandler = Septima.bind(function(result, deferred, detailsContent){

    			var  items = [];
    	    	for (var i=0;i<feature.pcol.row.length;i++){
    	    		var row = feature.pcol.row[i];
    	    		if (typeof row._name !== "undefined" && (row._name !== 'column' || (row._name === 'column' && row.format === ''))){
            			var label = row.label;
            			var value = row.value;
               			items.push({type: "labelvalue", label: label, value: value});
    	    		}
    	    	}
        		deferred.resolve(detailsContent.formatItems(items));
        	}, this);
        	detailHandlerDefs = ([{"buttonText":"Attributter", "buttonImage": Septima.Search.icons.details.moreHeader, "handler": detailsHandler, "more": true}]);
    	}

        return detailHandlerDefs;
    },
    
    getCustomButtonsForResult: function(result){
    	var feature = result.data;
    	var customButtonDef;
    	var customButtonDefs = [];
    	for (var i=0;i<feature.pcol.row.length;i++){
    		var row = feature.pcol.row[i];
    		if (row.format === 'hyperlink'){
    			customButtonDef = {
    					"buttonText": row.label,
    					"buttonImage": Septima.Search.icons.linksymbol,
    					"callBack": Septima.bind(function(url){
    						window.open(url, "_blank");
    					}, this, row.value)
    			};
    			customButtonDefs.push(customButtonDef);
			}else if (row.format === 'javascriptlink'){
    			customButtonDef = {
    					"buttonText": row.label,
    					"buttonImage": Septima.Search.icons.linksymbol,
                        "callBack": Septima.bind(function(code){
                            jQuery.globalEval(code);
                        }, this, row.value)
    			};
    			customButtonDefs.push(customButtonDef);
			}    		
    	}

    	return customButtonDefs;
    },

	getFeatureLinksAsHtml: function(feature){
    	var html = "";
    	for (var i=0;i<feature.pcol.row.length;i++){
    		var row = feature.pcol.row[i];
    		if (row.format == 'hyperlink'){
    			if (html.length>0){
    				html += " - ";
    			}
    			html += "<a href='" + row.value + "' target= '_blank'>" + row.label + "</a>";
    		}
    	}
        return html;
	},

    CLASS_NAME: 'Septima.Search.S4IndexSearcher'
    	
});