VIE Widget - Image Search

How To

First, we need to include the dependencies in the header:

...
<script type="text/javascript" src=".../jquery.js"></script>
<script type="text/javascript" src=".../underscore.js"></script>
<script type="text/javascript" src=".../backbone.js"></script>
<script type="text/javascript" src=".../jquery.rdfquery.js"></script>
<script type="text/javascript" src=".../vie-2.0.0.js"></script>
<script type="text/javascript" src=".../jquery-ui.js"></script>

<!--  the current VIE widget -->
<script type="text/javascript" src="./vie.widget.image_search.js"></script>
...

Now, we can read-in the embedded RDFa annotations from the webpage:

    <script type="text/javascript">
        $(function() {
            // initialize a global VIE object
            var myVIE = window.myVIE = new VIE();
            myVIE.loadSchema("http://schema.rdfs.org/all.json", {
                baseNS : "http://schema.org/",
                success : function () {
                    // read RDFa from the trext in the HTML
                    myVIE.use(new myVIE.RdfaService);
                    myVIE.load({element: $('[about]')}).using('rdfa').execute();
                    
                    finishSetUp();
                },
                error : function (e) {
                    //....
                }
            });
            
            function finishSetUp () {
                // set-up of the Image-widget
                $('#image_container')
                .vieImageSearch({
                    // your local VIE instance
                    vie         : myVIE,
                    // the maximal number of elements to query each service for
                    max_objects : 8,
                    // setup of each service
                    services    : {
                        flickr : {
                            api_key : "<your_FLICKR_API_KEY_HERE>",
                            // whether to use it or not
                            use: true,
                            // some weight for the relevance sorting algorithm
                            weight: 1.0
                        },
                        gimage : {
                            use: true,
                            weight: 1.0
                        },
                        ookaboo : {
                            use: true,
                            weight: 1.0
                        }
                    }
                });
            }
         });

And finally, registering the annotated RDFa elements with a click-event handler to trigger the semantic image search:

            $('[about]')
             .click(function () {
                 $('#image_container').vieImageSearch({
                     entity: $(this).attr('about')
                 });
             });

User: "Great! But this searches only for Persons and Places... How can I extend it?"
Me: "Glad that you ask! Let me show you how to customize the widget to search for a Product:"

            $('#image_container')
            .vieImageSearch({
               //URLs are generated dependence of the given <Entity-Type, Service> tuple
                ts_url : {
                    "Product" : {
                        'gimage' : function (entity, serviceId) {
                            var widget = this;
			        		var service = widget.options.services[serviceId];
			        		var url = service.base_url.replace("\/$", "");
	                        
	                        if (entity.has("brand")) {
	                        	var brand = entity.get("brand");
	                        	var brandName = 
	                        		VIE.Util.extractLanguageString(
	                        		    brand, 
	                        		    ["name", "rdf:label"], 
	                        		    widget.options.lang);
	                            url += "&q=" + brandName.replace(/ /g, '+'); // *no* type-specific keywords
	    		        		url += (service.tail_url)? service.tail_url(widget, service) : "";
	                            return url;
	                        } else {
	                        	return undefined;
	                        }
                        }
                    }
                },
                ts_data : { //data is generated in dependence of the given <Entity-Type, Service> tuple
                    "Product" : {
                        'ookaboo' : function (entity, serviceId) {
                            var widget = this;
	                		var service = widget.options.services[serviceId];
	                		// this might return some data, usually not needed
	                		return ...;
	            		}
                    }
                }
            });

User: "Nice! And how can I extend it to query my own CMS repository?"
Me: "Phew... Tough one... Not! (assuming you have implemented it with proper CORS support!)"
"What you need is to add another service to the options like shown below and implement the following skeleton:"

$('#image_container').vieImageSearch({
	services : {
	    'myowncmsimageservicebackend' : {
                use       : {true|false},
	    		weight    : 1.0,
	            api_key   : undefined,
	            base_url  : "...",
	            // this method performs the query and forwards the result to the callback!
	            query : function (serviceId, entity, queryId) {
	            	var widget = this;
	            	var service = widget.options.services[serviceId];
	            	var ret = widget._entityToUrlData.call(widget, entity, serviceId);
	            	jQuery.ajax({
                   		url : ret["url"],
                   		data : JSON.stringify(ret["data"]),
                   		dataType : "application/json",
                   		contentType : "application/json",
                   		type : "POST",
                   		complete : service.callback(widget, entity.getSubjectUri(), serviceId, queryId)
               		});
	            },
	            tail_url  : function (widget, service) {
	                var url = "&sort=relevance";
	                url += "&max_elements=" + widget.options.max_objects;
	                url += "&api_key=" + service.api_key;
	                url += "format=json";
	                
	                return url;
	            },
	            // this method transforms the raw data into the VIE world!
	            callback  : function (widget, entityId, serviceId, queryId) {
	                return function (response) {
	                    var objects = [];
	                    //...
	                	var data = {entityId : entityId, serviceId: serviceId, queryId : queryId, time: new Date(), objects: objects};
                        widget._trigger('end_query', undefined, data);
                        widget.render(data);
	                };
                }
	    }
	}
});
Me: "Eventually that would look something like that:"

   $('#image_container').vieImageSearch({
       services : {
           'myowncmsimageservicebackend' : {
                use       : true,
	    		weight    : 1.0,
	            api_key   : undefined,
	            base_url  : "http://mycms.com/images/?method=related",
	            query : function (serviceId, entity, queryId) {
	            	var widget = this;
	            	var service = widget.options.services[serviceId];
	            	var ret = widget._entityToUrlData.call(widget, entity, serviceId);
	            	
	            	if (ret && ret["url"]) {
	            		jQuery.getJSON(ret["url"], 
	            			service.callback(widget, entity.getSubjectUri(), serviceId, queryId));
	            	} else {
	            		widget._trigger("error", undefined, {
	                         msg: "No type-specific URL can be acquired for entity. Please add/overwrite widget.options[\"Thing\"][" + serviceId + "]!", 
	                         id : entity.getSubjectUri(), 
	                         service : serviceId, 
	                         queryId : queryId});
	            	}
	            },
	            tail_url  : function (widget, service) {
	                var url = "&sort=relevance";
	                url += "&max_elements=" + widget.options.max_objects;
	                url += "&api_key=" + service.api_key;
	                url += "format=json";
	                
	                return url;
	            },
	            callback  : function (widget, entityId, serviceId, queryId) {
	                return function (data) {
	                	  var service = widget.options.services[serviceId];
	                      var objects = [];
	                      if (data.stat === 'ok' && data.photos.total > 0) {
	                          //put them into bins
	                          for (var i = 0; i < data.photos.photo.length; i++) {
	                              var photo = data.photos.photo[i];
	                              
	                              var obj = {
	                           		  "weight" : (service.weight)? service.weight : 1.0,
	                                      "thumbnail" : photo["thumbnail"],
	                                      "original" : photo["origional"],
	                                      "height" : photo["height"],
	                                      "width" : photo["width"]
	                              };
	                              objects.push(obj);
	                          }
	                      }
	                      var data = {entityId : entityId, serviceId: serviceId, queryId : queryId, time: new Date(), objects: objects};
	                      widget._trigger('end_query', undefined, data);
	                      widget.render(data);
	                  };
	            }
           }
       }
   });
        

User: "Awesome! Are there any other options/events/methods, I should know about?"
Me: "Not that many... Here they are:"

Events

Pre-init

    // the event is thrown when a service-query is started
    $('#image_container').bind('vieimagesearchstart_query', function (ev) {...});
    // the event is thrown when a service-query ends
    $('#image_container').bind('vieimagesearchend_query', function (ev) {...});
    // the event is thrown when a warning arised
    $('#image_container').bind('vieimagesearchwarn', function (ev) {...});
    // the event is thrown when an error happened
    $('#image_container').bind('vieimagesearcherror', function (ev) {...});

Options

Post-init

    // the instance of VIE
    $('#image_container').vieImageSearch("option", "vie", myVIE);
    // the maximal number of elements to be queried from each service
    $('#image_container').vieImageSearch("option", "max_objects", 10);
    // an array of languages that is used to perform language-specific queries
    $('#image_container').vieImageSearch("option", "lang", ["en", "de", "es", ...]);
    // a timeout if the queries take too long. default: 10 seconds
    $('#image_container').vieImageSearch("option", "timeout", 1000);
    // you might probably want to change the rendering of the images.
    // using the options, you can overwrite the "render" key with a method
    // that takes an array of VIE Entities (typeof 'VIEImageResult') 
    // as input to render the images
    $('#image_container').vieImageSearch("option", "render", function (objEntities) {...});

Options

Pre-init

    $('#image_container').vieImageSearch({
        "vie"         : myVIE,
        "render"      : function (objEntities) {...},
        "max_objects" : 10,
        "lang"        : ["en", "de", "es", ...],
        "timeout"     : 10000,
        "services"    : {
            "flickr" :  {
                "use" : {true|false},
                "weight" : 1.0,
                "api_key" : "...",
                "base_url" : "..."
                ...
            },
            ...
        }
    });

Methods

    // get the widget's instance
    var widget = $('#image_container').data("vieImageSearch");
    
    // list all available services
    widget.options.services;

    // change to use a service, e.g., gimages, post-init)
    $('#image_container').vieImageSearch("useService", 'gimage', {true|false});
If that documentation is of no help for you, do not hesitate to contact me. I am a friendly person who loves to support you with integrating the widget in your site.
Click here to go back to the demo.

Download