You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@stanbol.apache.org by sz...@apache.org on 2011/10/07 11:02:18 UTC

svn commit: r1179969 [2/3] - in /incubator/stanbol/trunk/commons/web/webvie/src/main/resources/org/apache/stanbol/enhancer/webvie/static/lib/vie: ./ proxy/ proxy/class_http.php proxy/node_proxy.js proxy/proxy.php vie-latest.debug.js vie-latest.js

Added: incubator/stanbol/trunk/commons/web/webvie/src/main/resources/org/apache/stanbol/enhancer/webvie/static/lib/vie/vie-latest.debug.js
URL: http://svn.apache.org/viewvc/incubator/stanbol/trunk/commons/web/webvie/src/main/resources/org/apache/stanbol/enhancer/webvie/static/lib/vie/vie-latest.debug.js?rev=1179969&view=auto
==============================================================================
--- incubator/stanbol/trunk/commons/web/webvie/src/main/resources/org/apache/stanbol/enhancer/webvie/static/lib/vie/vie-latest.debug.js (added)
+++ incubator/stanbol/trunk/commons/web/webvie/src/main/resources/org/apache/stanbol/enhancer/webvie/static/lib/vie/vie-latest.debug.js Fri Oct  7 09:02:17 2011
@@ -0,0 +1,2491 @@
+
+Able = function(){
+};
+    // takes a list of services or just one
+Able.prototype = {
+    using: function(services) {
+        var service = this;
+        if ( services instanceof Array ) {
+            _(services).each(function(s){
+                service._using(s);
+            });
+        } else {
+            var s = services;
+            service._using(s);
+        }
+        return this;
+    },
+    _using: function(service) {
+        var serviceObj = typeof service === "string" ? this.vie.service(service) : service;
+        this.services.push(serviceObj);
+        return this;
+    },
+    init: function(options, methodName) {
+        this.methodName = methodName;
+        this.options = options;
+        this.services = options.from || options.using || options.to || [];
+        this.vie = options.vie;
+        this.deferred = jQuery.Deferred();
+
+        // Public deferred-methods
+        this.resolve = this.deferred.resolve;
+        this.resolveWith = this.deferred.resolveWith;
+        this.reject = this.deferred.reject;
+        this.rejectWith = this.deferred.rejectWith;
+
+        // Synonyms
+        this.success = this.done = this.deferred.done;
+        this.fail = this.deferred.fail;
+        this.then = this.deferred.then; // Takes 2 arguments, successCallbacks, failCallbacks
+        this.always = this.deferred.always;
+        this.from = this.using;
+        this.to = this.using;
+    },
+    // Running the actual method
+    execute: function() {
+        // call service.load
+        var able = this;
+        _(this.services).each(function(service){
+            service[able.methodName](able);
+        });
+        return this;
+    }
+};
+// ### Handle dependencies
+//
+// VIE tries to load its dependencies automatically. 
+// Please note that this autoloading functionality only works on the server.
+// On browser Backbone needs to be included manually.
+
+// Require [underscore.js](http://documentcloud.github.com/underscore/) 
+// using CommonJS require if it isn't yet loaded.
+//
+// On node.js underscore.js can be installed via:
+//
+//     npm install underscore
+var _ = this._;
+if (!_ && (typeof require !== 'undefined')) { _ = require('underscore')._; }
+if (!_) {
+    throw 'VIE requires underscore.js to be available';
+}
+
+// Require [Backbone.js](http://documentcloud.github.com/backbone/) 
+// using CommonJS require if it isn't yet loaded.
+//
+// On node.js Backbone.js can be installed via:
+//
+//     npm install backbone
+var Backbone = this.Backbone;
+if (!Backbone && (typeof require !== 'undefined')) { Backbone = require('backbone'); }
+if (!Backbone) {
+    throw 'VIE requires Backbone.js to be available';
+}
+
+// Require [jQuery](http://jquery.com/) using CommonJS require if it 
+// isn't yet loaded.
+//
+// On node.js jQuery can be installed via:
+//
+//     npm install jquery
+var jQuery = this.jQuery;
+if (!jQuery && (typeof require !== 'undefined')) { jQuery = require('jquery'); }
+if (!jQuery) {
+    throw 'VIE requires jQuery to be available';
+}
+
+var VIE;
+VIE = function(config){
+    this.config = (config) ? config : {};
+    this.services = {};
+    this.entities = new this.Collection();
+
+    this.Entity.prototype.entities = this.entities;
+    this.entities.vie = this;
+    this.Entity.prototype.entityCollection = this.Collection;
+    this.Entity.prototype.vie = this;
+
+    this.defaultProxyUrl = (this.config.defaultProxyUrl) ? this.config.defaultProxyUrl : "../utils/proxy/proxy.php";
+    
+    this.Namespaces.prototype.vie = this;
+    this.namespaces = new this.Namespaces(
+        (this.config.defaultNamespace) ? 
+            this.config.defaultNamespace : 
+            "http://ontology.vie.js/"
+    );
+    
+    this.Type.prototype.vie = this;
+    this.Types.prototype.vie = this;
+    this.Attribute.prototype.vie = this;
+    this.Attributes.prototype.vie = this;
+    this.types = new this.Types();
+    
+    this.types.add("Thing");
+
+    if (this.config.classic !== false) {
+        // Load Classic API as well
+        this.RDFa = new this.ClassicRDFa(this);
+        this.RDFaEntities = new this.ClassicRDFaEntities(this);
+        this.EntityManager = new this.ClassicEntityManager(this);
+
+        this.cleanup = function() {
+            this.entities.reset();
+        };
+    }
+};
+
+VIE.prototype.use = function(service, name) {
+  if (!name) {
+    name = service.name;
+  }
+  service.vie = this;
+  service.name = name;
+  if (service.init) {
+      service.init();
+  }
+  this.services[name] = service;
+};
+
+VIE.prototype.service = function(name) {
+  if (!this.services[name]) {
+    throw "Undefined service " + name;
+  }
+  return this.services[name];
+};
+
+VIE.prototype.getServicesArray = function() {
+  var res = [];
+  _(this.services).each(function(service, i){res.push(service);});
+  return res;
+};
+
+VIE.prototype.Able = Able;
+
+// Declaring the ..able classes
+// Loadable
+VIE.prototype.load = function(options) {
+  if (!options) { options = {}; }
+  options.vie = this;
+  return new this.Loadable(options);
+};
+
+VIE.prototype.Loadable = function (options) {
+    this.init(options,"load");
+};
+
+VIE.prototype.Loadable.prototype = new VIE.prototype.Able();
+
+// Savable
+VIE.prototype.save = function(options) {
+  if (!options) { options = {}; }
+  options.vie = this;
+  return new this.Savable(options);
+};
+
+VIE.prototype.Savable = function(options){
+    this.init(options, "save");
+};
+
+VIE.prototype.Savable.prototype = new VIE.prototype.Able();
+
+// Removable
+VIE.prototype.remove = function(options) {
+  if (!options) { options = {}; }
+  options.vie = this;
+  return new this.Removable(options);
+};
+
+VIE.prototype.Removable = function(options){
+    this.init(options, "remove");
+};
+
+VIE.prototype.Removable.prototype = new VIE.prototype.Able();
+
+// Analyzable
+VIE.prototype.analyze = function(options) {
+  if (!options) { options = {}; }
+  options.vie = this;
+  return new this.Analyzable(options);
+};
+
+VIE.prototype.Analyzable = function (options) {
+    this.init(options, "analyze");
+};
+
+VIE.prototype.Analyzable.prototype = new VIE.prototype.Able();
+
+// Findable
+VIE.prototype.find = function(options) {
+  if (!options) { options = {}; }
+  options.vie = this;
+  return new this.Findable(options);
+};
+
+VIE.prototype.Findable = function (options) {
+    this.init(options, "find");
+};
+
+VIE.prototype.Findable.prototype = new VIE.prototype.Able();
+
+if(typeof(exports) !== 'undefined' && exports !== null) {
+    exports.VIE = VIE;
+}
+// File:   Util.js <br />
+// Author: <a href="http://github.com/neogermi/">Sebastian Germesin</a>
+//
+
+// Utilities for the day-to-day VIE.js usage
+
+// extension to jQuery to compare two arrays on equality
+// found: <a href="http://stackoverflow.com/questions/1773069/using-jquery-to-compare-two-arrays">http://stackoverflow.com/questions/1773069/using-jquery-to-compare-two-arrays</a>
+jQuery.fn.compare = function(t) {
+    if (this.length != t.length) { return false; }
+    var a = this.sort(),
+        b = t.sort();
+    for (var i = 0; t[i]; i++) {
+        if (a[i] !== b[i]) { 
+                return false;
+        }
+    }
+    return true;
+};
+
+// Extension to the JS native Array implementation to remove values from an array.
+// from: <a href="http://sebastian.germes.in/blog/2011/09/javascripts-missing-array-remove-function/">http://sebastian.germes.in/blog/2011/09/javascripts-missing-array-remove-function/</a>
+if (!Array.prototype.remove) {
+  Array.prototype.remove = function () {
+    var args = this.remove.arguments;
+
+    if (args[0] && args[0] instanceof Array) {
+      var a = args[0];
+      for (var i = 0; i < a.length; i++) {
+        this.remove(a[i]);
+      }
+    } else {
+      for (var i = 0; i < args.length; i++) {
+        while(true) {
+          var index = this.indexOf(args[i]);
+          if (index !== -1)
+            this.splice(index, 1);
+          else
+            break;
+        }
+      }
+    }
+  return this;
+  };
+}
+
+//Extension to the JS native Array implementation to remove duplicates from an array.
+//This actually leaves the original Array untouched and returns a copy with no duplicates.
+if (!Array.prototype.unduplicate) {
+	Array.prototype.unduplicate = function () {
+	    var sorted_arr = this.sort();
+	    var results = [];
+	    for (var i = 0; i < sorted_arr.length; i++) {
+	        if (i === sorted_arr.length-1 || sorted_arr[i] != sorted_arr[i+1]) {
+	            results.push(sorted_arr[i]);
+	        }
+	    }
+	    return results;
+	};
+} 
+
+
+VIE.Util = {
+		// converts a given URI into a CURIE (or save CURIE), based
+		// on the given VIE.Namespaces object.
+	toCurie : function (uri, safe, namespaces) {
+        var delim = ":";
+        for (var k in namespaces.toObj()) {
+            if (uri.indexOf(namespaces.get(k)) === 1) {
+                var pattern = new RegExp("^" + "<" + namespaces.get(k));
+                if (k === '') delim = '';
+                return ((safe)? "[" : "") + 
+                        uri.replace(pattern, k + delim).replace(/>$/, '') +
+                        ((safe)? "]" : "");
+            }
+        }
+        throw new Error("No prefix found for URI '" + uri + "'!");
+    },
+
+	// checks, whether the given string is a CURIE.
+    isCurie : function (something, namespaces) {
+    	try {
+    		 VIE.Util.toUri (something, namespaces);
+    		 return true;
+    	} catch (e) {
+    		return false;
+    	}
+    },
+
+	// converts a given CURIE (or save CURIE) into a URI, based
+	// on the given VIE.Namespaces object.
+    toUri : function (curie, namespaces) {
+        var delim = ":";
+        for (var k in namespaces.toObj()) {
+            if (k !== "" && (curie.indexOf(k) === 0 || curie.indexOf(k) === 1)) {
+                var pattern = new RegExp("^" + "\\[{0,1}" + k + delim);
+                return "<" + curie.replace(pattern, namespaces.get(k)).replace(/\]{0,1}$/, '') + ">";
+            }
+        }
+        //default:
+        if (curie.indexOf(delim) === -1 && namespaces.base()) {
+            return "<" + namespaces.base() + curie + ">";
+        }
+        throw new Error("No prefix found for CURIE '" + curie + "'!");
+    },
+    
+    // checks, whether the given string is a URI.
+    isUri : function (something) {
+        return (typeof something === "string" && something.search(/^<.+:.+>$/) === 0);
+    }
+    
+};
+VIE.prototype.Entity = function(attrs, opts) {
+
+    var vie = this;
+    
+    var mapAttributeNS = function (attr, ns) {
+        var a = attr;
+        if (ns.isUri (attr) || attr.indexOf('@') === 0) {
+            //ignore
+        } else if (ns.isCurie(attr)) {
+            a = ns.uri(attr);
+        } else if (!ns.isUri(attr)) {
+        	if (attr.indexOf(":") === -1) {
+                a = '<' + ns.base() + attr + '>';
+        	} else {
+                a = '<' + attr + '>';
+        	}
+        }
+        return a;
+    };
+        
+    if ('@type' in attrs) {
+        if (_.isArray(attrs['@type'])) {
+            attrs['@type'] = _.map(attrs['@type'], function(val){
+                if (this.types.get(val)) {
+                    return this.types.get(val).id;
+                }
+                else {
+                    return val;
+                }
+            }, vie.vie);
+        }
+        else if (typeof attrs['@type'] === 'string') {
+            if (this.vie.types.get(attrs['@type'])) {
+                attrs['@type'] = this.vie.types.get(attrs['@type']).id;
+            }
+        }
+    } else {
+        // provide "Thing" as the default type if none was given
+        attrs['@type'] = this.vie.types.get("Thing").id;
+    }
+    
+    //the following provides full seamless namespace support
+    //for attributes. It should not matter, if you
+    //query for `model.get('name')` or `model.get('foaf:name')`
+    //or even `model.get('http://xmlns.com/foaf/0.1/name');`
+    //However, if we just overwrite `set()` and `get()`, this
+    //raises a lot of side effects, so we need to expand
+    //the attributes before we create the model.
+    attrs = (attrs) ? attrs : {};
+    _.each (attrs, function (value, key) {
+        var newKey = mapAttributeNS(key, this.namespaces);
+        if (key !== newKey) {
+            delete attrs[key];
+            attrs[newKey] = value;
+        }
+    }, vie.vie);
+    
+    var Model = Backbone.Model.extend({
+        idAttribute: '@subject',
+
+        initialize: function(attributes, options) {
+            if (attributes['@subject']) {
+                this.id = this['@subject'] = this.toReference(attributes['@subject']);
+            }
+        },
+                
+        get: function (attr) {
+            attr = mapAttributeNS(attr, this.vie.vie.namespaces);
+            var value = Backbone.Model.prototype.get.call(this, attr);
+            
+            if (_.isArray(value)) {
+                value = _.map(value, function(v) {
+                    if (this.vie.vie.entities.get(v)) {
+                        return this.vie.vie.entities.get(v);
+                    }
+                    else if (attr === '@type' && this.vie.vie.types.get(v)) {
+                        return this.vie.vie.types.get(v);
+                    } else {
+                        return v;
+                    }
+                }, this);
+            } else {
+                if (this.vie.vie.entities.get(value)) {
+                    value = this.vie.vie.entities.get(value);
+                } else if (attr === '@type' && this.vie.vie.types.get(value)) {
+                    value = this.vie.vie.types.get(value);
+                }
+            }
+            return value;
+        },
+        
+        set : function(attrs, options) {
+            if (!attrs) return this;
+            if (attrs.attributes) 
+                attrs = attrs.attributes;
+          
+            _.each (attrs, function (value, key) {
+                var newKey = mapAttributeNS(key, this.vie.vie.namespaces);
+                if (key !== newKey) {
+                    delete attrs[key];
+                    attrs[newKey] = value;
+                }
+            }, this); 
+            return Backbone.Model.prototype.set.call(this, attrs, options);
+        },
+        
+        unset: function (attr, opts) {
+            attr = mapAttributeNS(attr, this.vie.vie.namespaces);
+            return Backbone.Model.prototype.unset.call(this, attr, opts);
+        },
+        
+        getSubject: function(){
+            if (typeof this.id === "undefined") {
+                this.id = this.get(this.idAttribute);
+            }
+            if (typeof this.id === 'string') {
+                if (this.id.substr(0, 7) === 'http://' || this.id.substr(0, 4) === 'urn:') {
+                    return this.toReference(this.id);
+                }
+                return this.id;
+            }
+            return this.cid.replace('c', '_:bnode');
+        },
+        
+        getSubjectUri: function(){
+            return this.fromReference(this.getSubject());
+        },
+        
+        isReference: function(uri){
+            var matcher = new RegExp("^\\<([^\\>]*)\\>$");
+            if (matcher.exec(uri)) {
+                return true;
+            }
+            return false;
+        },
+        
+        toReference: function(uri){
+            if (this.isReference(uri)) {
+                return uri;
+            }
+            return '<' + uri + '>';
+        },
+        
+        fromReference: function(uri){
+            if (!this.isReference(uri)) {
+                return uri;
+            }
+            return uri.substring(1, uri.length - 1);
+        },
+        
+        as: function(encoding){
+            if (encoding === "JSON") {
+                return this.toJSON();
+            }
+            if (encoding === "JSONLD") {
+                return this.toJSONLD();
+            }
+            throw "Unknown encoding " + encoding;
+        },
+        
+        toJSONLD: function(){
+            var instanceLD = {};
+            var instance = this;
+            _.each(instance.attributes, function(value, name){
+                var entityValue = instance.get(name);
+
+                if (name === '@type' && entityValue) {
+                    entityValue = entityValue.id;
+                }
+
+                if (value instanceof instance.vie.vie.Collection) {
+                    entityValue = value.map(function(instance) {
+                        return instance.getSubject();
+                    });
+                }
+
+                // TODO: Handle collections separately
+                instanceLD[name] = entityValue;
+            });
+            
+            instanceLD['@subject'] = instance.getSubject();
+            
+            return instanceLD;
+        },
+        
+        setOrAdd: function (arg1, arg2) {
+            var entity = this;
+            if (typeof arg1 === "string" && arg2) {
+                // calling entity.setOrAdd("rdfs:type", "example:Musician")
+                entity._setOrAddOne(arg1, arg2);
+            }
+            else 
+                if (typeof arg1 === "object") {
+                    // calling entity.setOrAdd({"rdfs:type": "example:Musician", ...})
+                    _(arg1).each(function(val, key){
+                        entity._setOrAddOne(key, val);
+                    });
+                }
+            return this;
+        },
+        
+        _setOrAddOne: function (prop, value) {
+            var val = this.get(prop);
+            // No value yet, use the set method
+            if (!val) {
+                var obj = {};
+                obj[prop] = value;
+                this.set(obj);
+            }
+            else {
+                // Make sure not to set the same value twice
+                if (val !== value && (!(val instanceof Array) && val.indexOf(value) === -1)) {
+                    // Value already set, make sure it's an Array and extend it
+                    if (!(val instanceof Array)) {
+                        val = [val];
+                    }
+                    val.push(value);
+                    var obj = {};
+                    obj[prop] = val;
+                    this.set(obj);
+                }
+            }
+        },
+        
+        hasType: function(type){
+            type = this.vie.vie.types.get(type);
+            return this.hasPropertyValue("@type", type);
+        },
+        
+        hasPropertyValue: function(property, value) {
+            var t = this.get(property);
+            if (!(value instanceof Object)) {
+                value = this.vie.entities.get(value);
+            }
+            if (t instanceof Array) {
+                return t.indexOf(value) !== -1;
+            }
+            else {
+                return t === value;
+            }
+        },
+        
+        isof: function (type) {
+            var types = this.get('@type');
+            
+            types = (_.isArray(types))? types : [ types ];
+            
+            for (var t = 0; t < types.length; t++) {
+                if (this.vie.vie.types.get(types[t]) && 
+                    this.vie.vie.types.get(types[t]).isof(type)) {
+                    return true;
+                }
+            }
+            return false;
+        },
+        
+        isEntity: true,
+        
+        vie: vie
+    });
+    
+    return new Model(attrs, opts);    
+};
+VIE.prototype.Collection = Backbone.Collection.extend({
+    model: VIE.prototype.Entity,
+
+    get: function(id) {
+        if (id == null) return null;
+        id = this.toReference(id);
+        return this._byId[id.id != null ? id.id : id];
+    },
+
+    addOrUpdate: function(model) {
+        var collection = this;
+        if (_.isArray(model)) {
+            var entities = [];
+            _.each(model, function(item) {
+                entities.push(collection.addOrUpdate(item));
+            });
+            return entities;
+        }
+
+        if (!model.isEntity) {
+            model = new this.model(model);
+        }
+
+        if (!model.id) {
+            this.add(model);
+            return model;
+        }
+
+        if (this.get(model.id)) {
+            var existing = this.get(model.id);
+            if (model.attributes) {
+                return existing.set(model.attributes);
+            }
+            return existing.set(model);
+        }
+        this.add(model);
+        return model;
+    },
+
+    isReference: function(uri){
+        var matcher = new RegExp("^\\<([^\\>]*)\\>$");
+        if (matcher.exec(uri)) {
+            return true;
+        }
+        return false;
+    },
+        
+    toReference: function(uri){
+        if (this.isReference(uri)) {
+            return uri;
+        }
+        return '<' + uri + '>';
+    },
+        
+    fromReference: function(uri){
+        if (!this.isReference(uri)) {
+            return uri;
+        }
+        return uri.substring(1, uri.length - 1);
+    }
+});
+// File:   Type.js <br />
+// Author: <a href="http://github.com/neogermi/">Sebastian Germesin</a>
+//
+
+// Adding capability of handling type/class structure and inheritance to VIE. 
+if (VIE.prototype.Type) {
+	throw "ERROR: VIE.Type is already defined. Please check your installation!";
+}
+if (VIE.prototype.Types) {
+	throw "ERROR: VIE.Types is already defined. Please check your installation!";
+}
+
+// The constructor of a VIE.Type. 
+//Usage: ``var personType = new vie.Type("Person", []).inherit("Thing");``
+// This creates a type person in the base namespace that has no attributes
+// but inherits from the type "Thing". 
+VIE.prototype.Type = function (id, attrs) {
+    if (id === undefined || typeof id !== 'string') {
+        throw "The type constructor needs an 'id' of type string! E.g., 'Person'";
+    }
+
+    this.id = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id);
+
+    // checks whether such a type is already defined. 
+    if (this.vie.types.get(this.id)) {
+        throw "The type " + this.id + " is already defined!";
+    }    
+    
+    // the supertypes (parentclasses) of the current type.
+    this.supertypes = new this.vie.Types();
+    // the subtypes (childclasses) of the current type.
+    this.subtypes = new this.vie.Types();
+    
+    // the given attributes as a `vie.Attributes` element.
+    this.attributes = new this.vie.Attributes(this, (attrs)? attrs : []);
+    
+    // checks whether the current type inherits of the
+    // given type, e.g.,: ``personType.isof("Thing");``
+    // would evaluate to `true`.
+    // We can either pass a type object or a string that
+    // represents the id of the type.
+    this.isof = function (type) {
+        type = this.vie.types.get(type);
+        if (type) {
+            return type.subsumes(this.id);
+        } else {
+            throw "No valid type given";
+        }
+    };
+    
+    // checks whether the current type subsumes the
+    // given type, e.g.,: ``thingType.subsumes("Person");``
+    // would evaluate to `true`.
+    // We can either pass a type object or a string that
+    // represents the id of the type.
+    this.subsumes = function (type) {
+        type = this.vie.types.get(type);
+        if (type) {
+            if (this.id === type.id) {
+                return true;
+            }
+            var subtypes = this.subtypes.list();
+            for (var c = 0; c < subtypes.length; c++) {
+                var childObj = subtypes[c];
+                if (childObj) {
+                     if (childObj.id === type.id || childObj.subsumes(type)) {
+                         return true;
+                     }
+                }
+            }
+            return false;
+        } else {
+            throw "No valid type given";
+        }
+    };
+    
+    //inherit all attributes from the supertype (recursively).
+    //we can either pass a string (id) of the supertype, the
+    //supertype itself or an array of both.
+    this.inherit = function (supertype) {
+        if (typeof supertype === "string") {
+            this.inherit(this.vie.types.get(supertype));
+        }
+        else if (supertype instanceof this.vie.Type) {
+            supertype.subtypes.add(this);
+            this.supertypes.add(supertype);
+            try {
+                // only for validation of attribute-inheritance!
+            	// if this throws an error (inheriting two attributes
+            	// that cannot be combined) we reverse all changes. 
+                this.attributes.list();
+            } catch (e) {
+                supertype.subtypes.remove(this);
+                this.supertypes.remove(supertype);
+                throw e;
+            }
+        } else if (jQuery.isArray(supertype)) {
+            for (var i = 0; i < supertype.length; i++) {
+                this.inherit(supertype[i]);
+            }
+        } else {
+            throw "Wrong argument in VIE.Type.inherit()";
+        }
+        return this;
+    };
+        
+    // serializes the hierarchy of child types into an
+    // object.
+    this.hierarchy = function () {
+        var obj = {id : this.id, subtypes: []};
+        var list = this.subtypes.list();
+        for (var c = 0; c < list.length; c++) {
+            var childObj = this.vie.types.get(list[c]);
+            obj.subtypes.push(childObj.hierarchy());
+        }
+        return obj;
+    };
+        
+    // returns the id of the type.
+    this.toString = function () {
+        return this.id;
+    };
+    
+};
+
+//basically a convenience class that represents a list of `VIE.Type`s.
+//var types = new vie.Types();
+VIE.prototype.Types = function () {
+        
+    this._types = {};
+    
+    //Adds a `VIE.Type` to the types.
+    //This throws an exception if a type with the given id
+    //already exists.
+    this.add = function (id, attrs) {
+        if (this.get(id)) {
+            throw "Type '" + id + "' already registered.";
+        } 
+        else {
+            if (typeof id === "string") {
+                var t = new this.vie.Type(id, attrs);
+                this._types[t.id] = t;
+                return t;
+            } else if (id instanceof this.vie.Type) {
+            	this._types[id.id] = id;
+                return id;
+            } else {
+                throw "Wrong argument to VIE.Types.add()!";
+            }
+        }
+    };
+    
+    //This is the same as ``this.remove(id); this.add(id, attrs);``
+    this.addOrOverwrite = function(id, attrs){
+        if (this.get(id)) {
+            this.remove(id);
+        }
+        return this.add(id, attrs);
+    };
+    
+    //Retrieve a type by either it's id or by the type itself
+    //(for convenience issues).
+    //Returnes **undefined** if no type has been found.
+    this.get = function (id) {
+        if (!id) return undefined;
+        if (typeof id === 'string') {
+            var lid = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id);
+            return this._types[lid];
+        } else if (id instanceof this.vie.Type) {
+            return this.get(id.id);
+        }
+        return undefined;
+    };
+    
+    //Removes a type of given id from the type. This also
+    // removes all children if their only parent were this
+    //type. Furthermore, this removes the link from the
+    //super- and subtypes.
+    this.remove = function (id) {
+        var t = this.get(id);
+        if (!t) {
+            return this;
+        }
+        delete this._types[t.id];
+        
+        var subtypes = t.subtypes.list();
+        for (var c = 0; c < subtypes.length; c++) {
+            var childObj = subtypes[c];
+            if (childObj.supertypes.list().length === 1) {
+                //recursively remove all children 
+                //that inherit only from this type
+                this.remove(childObj);
+            } else {
+                childObj.supertypes.remove(t.id);
+            }
+        }
+        return t;
+    };
+    
+    //returns an array of all types.
+    this.toArray = this.list = function () {
+        var ret = [];
+        for (var i in this._types) {
+            ret.push(this._types[i]);
+        }
+        return ret;
+    };
+};
+// File:   Attribute.js <br />
+// Author: <a href="http://github.com/neogermi/">Sebastian Germesin</a>
+//
+
+// Adding capability of handling attribute structure and inheritance to VIE. 
+if (VIE.prototype.Attribute) {
+	throw "ERROR: VIE.Attribute is already defined. Please check your installation!";
+}
+if (VIE.prototype.Attributes) {
+	throw "ERROR: VIE.Attributes is already defined. Please check your installation!";
+}
+
+//The constructor of a VIE.Attribute. 
+//Usage: ``var knowsAttr = new vie.Attribute("knows", ["Person"]);``
+//This creates a attribute that describes a **knows** relationship between persons.
+VIE.prototype.Attribute = function (id, range, domain) {
+    if (id === undefined || typeof id !== 'string') {
+        throw "The attribute constructor needs an 'id' of type string! E.g., 'Person'";
+    }
+    if (range === undefined) {
+        throw "The attribute constructor needs 'range'.";
+    }
+    if (domain === undefined) {
+        throw "The attribute constructor needs a 'domain'.";
+    }
+    
+    this._domain = domain;
+    this.range = (jQuery.isArray(range))? range : [ range ];
+    //TODO! this.count = {};
+   
+    this.id = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id);
+    
+    // checks, whether the current attribute applies in the given range.
+    // If range is a string, this does simply string comparison, if it
+    // is a VIE.Type or an ID of a VIE.Type, then inheritance is checked as well.
+    this.applies = function (range) {
+        if (this.vie.types.get(range)) {
+            range = this.vie.types.get(range);
+        }
+        for (var r = 0; r < this.range.length; r++) {
+            var x = this.vie.types.get(this.range[r]);
+            if (x === undefined && typeof range === "string") {
+                if (range === this.range[r]) {
+                    return true;
+                }
+            }
+            else {
+                if (range.isof(this.range[r])) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    };
+            
+};
+
+// basically a convenience class that represents a list of `VIE.Attribute`s.
+// As attributes are part of a certain `VIE.Type`, it needs to be passed on
+// for inheritance checks:
+// var attrs = new vie.Attributes(vie.types.get("Thing"), []);
+VIE.prototype.Attributes = function (domain, attrs) {
+    
+    this.domain = domain;
+    
+    this._local = {};
+    this._attributes = {};
+    
+    //add a `VIE.Attribute` to the attributes.
+    //Either pass a full `VIE.Attribute` object or
+    //an id/range pair which then gets transformed into
+    //a VIE.Attribute element.
+    this.add = function (id, range) {
+        if (this.get(id)) {
+            throw "Attribute '" + id + "' already registered for domain " + this.domain.id + "!";
+        } 
+        else {
+            if (typeof id === "string") {
+                var a = new this.vie.Attribute(id, range, this.domain);
+                this._local[a.id] = a;
+                return a;
+            } else if (id instanceof this.vie.Type) {
+                id.domain = this.domain;
+                id.vie = this.vie;
+            	this._local[id.id] = id;
+                return id;
+            } else {
+                throw "Wrong argument to VIE.Types.add()!";
+            }
+        }
+    };
+    
+    //removes a `VIE.Attribute` from the attributes.
+    this.remove = function (id) {
+        var a = this.get(id);
+        if (a.id in this._local) {
+            delete this._local[a.id];
+            return a;
+        }
+        throw "The attribute " + id + " is inherited and cannot be removed from the domain " + this.domain.id + "!";
+    };
+    
+    //retrieve a `VIE.Attribute` from the attributes by it's id.
+    this.get = function (id) {
+        if (typeof id === 'string') {
+            var lid = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id);
+            return this._inherit()._attributes[lid];
+        } else if (id instanceof this.vie.Attribute) {
+            return this.get(id.id);
+        } else {
+            throw "Wrong argument in VIE.Attributes.get()";
+        }
+    };
+    
+    // creates a full list of all attributes (local and inherited).
+    // the ranges of inherited attributes with the same id will be merged. 
+    this._inherit = function () {
+        var attributes = jQuery.extend(true, {}, this._local);
+        
+        var inherited = this.domain.supertypes.list().map(
+            function (x) {
+               return x.attributes; 
+            });
+        
+        var add = {};
+        var merge = {};
+        
+        for (var a = 0; a < inherited.length; a++) {
+            var attrs = inherited[a].list();
+            for (var x = 0; x < attrs.length; x++) {
+                var id = attrs[x].id;
+                if (!(id in attributes)) {
+                    if (!(id in add) && !(id in merge)) {
+                        add[id] = attrs[x];
+                    }
+                    else {
+                        if (!merge[id]) {
+                            merge[id] = [];
+                        }
+                        if (id in add) {
+                            merge[id] = jQuery.merge(merge[id], add[id].range);
+                            delete add[id];
+                        }
+                        merge[id] = jQuery.merge(merge[id], attrs[x].range);
+                        merge[id] = merge[id].unduplicate();
+                    }
+                }
+            }
+        }
+        
+        //add
+        jQuery.extend(attributes, add);
+        
+        // merge
+        for (var id in merge) {
+            var merged = merge[id];
+            var ranges = [];
+            for (var r = 0; r < merged.length; r++) {
+                var p = this.vie.types.get(merged[r]);
+                var isAncestorOf = false;
+                if (p) {
+                    for (var x = 0; x < merged.length; x++) {
+                        if (x === r) {
+                            continue;
+                        }
+                        var c = this.vie.types.get(merged[x]);
+                        if (c && c.isof(p)) {
+                            isAncestorOf = true;
+                            break;
+                        }
+                    }
+                }
+                if (!isAncestorOf) {
+                    ranges.push(merged[r]);
+                }
+            }
+            attributes[id] = new this.vie.Attribute(id, ranges, this);
+        }
+
+        this._attributes = attributes;
+        return this;
+    };
+
+    // returns an Array of all attributes, combined
+    // with the inherited ones.
+    this.toArray = this.list = function (range) {
+        var ret = [];
+        var attributes = this._inherit()._attributes;
+        for (var a in attributes) {
+            if (!range || attributes[a].applies(range)) {
+                ret.push(attributes[a]);
+            }
+        }
+        return ret;
+    };
+        
+    if (!jQuery.isArray(attrs)) {
+        attrs = [ attrs ];
+    }
+    
+    for (var a = 0; a < attrs.length; a++) {
+        this.add(attrs[a].id, attrs[a].range);
+    }
+};
+// File:   Namespace.js <br />
+// Author: <a href="http://github.com/neogermi/">Sebastian Germesin</a>
+//
+
+// Adding capability of handling different namespaces to VIE. 
+
+if (VIE.prototype.Namespaces) {
+	throw "ERROR: VIE.Namespaces is already defined. Please check your installation!";
+}
+
+ 
+// Usage: ``var namespaces = new VIE.Namespaces("http://base.namespace.com/");``
+// We can also bootstrap namespaces by passing an object:
+//``var namespaces = new vie.Namespaces("http://base.namespace.com/", {"foaf": "http://xmlns.com/foaf/0.1/"});
+VIE.prototype.Namespaces = function (base, namespaces) {
+    
+	// Within VIE, we can define a base namespace, to support easier syntax for
+	// querying types of entities.
+	if (!base) {
+        throw "Please provide a base namespace!";
+    }
+	this._base = base;
+    
+    this.base = function (ns) {
+    	// getter
+        if (!ns) { 
+            return this._base;
+        }
+        // setter
+        else if (typeof ns === "string") {
+            this._base = ns;
+        } else {
+            throw "Please provide a valid namespace!";
+        }
+        return this;
+    };
+    
+    this._namespaces = (namespaces)? namespaces : {};
+    
+    //Add new namespacs. This also checks if there are
+    //prefixes or namespaces already defined to avoid
+    //ambiguities in the namespaces. Use `addOrReplace()`
+    //to simply overwrite them. 
+    this.add = function (k, v) {
+    	//we can also pass multiple namespaces as an object.
+        if (typeof k === "object") {
+            for (var k1 in k) {
+                this.add(k1, k[k1]);
+            }
+            return this;
+        }
+        //use `add("", "http://new.base.namespace/");` to set
+        //a new base namespace. This is the same as 
+        //`base("http://new.base.namespace/");`
+        if (k === "") {
+            this.base(v);
+        }
+        //check if we overwrite existing mappings
+        else if (this.containsPrefix(k) && v !== this._namespaces[k]) {
+            throw "ERROR: Trying to register namespace prefix mapping (" + k + "," + v + ")!" +
+                  "There is already a mapping existing: '(" + k + "," + this.get(k) + ")'!";
+        } else {
+            jQuery.each(this._namespaces, function (k1,v1) {
+                if (v1 === v && k1 !== k) {
+                    throw "ERROR: Trying to register namespace prefix mapping (" + k + "," + v + ")!" +
+                          "There is already a mapping existing: '(" + k1 + "," + v + ")'!";
+                }
+            });
+        }
+        this._namespaces[k] = v;
+        
+        return this;
+    };
+    
+    // this has the same capabilities as `add(k, v);` but
+    // overwrites already exising mappings.
+    this.addOrReplace = function (k, v) {
+        if (typeof k === "object") {
+            for (var k1 in k) {
+                this.addOrReplace(k1, k[k1]);
+            }
+            return this;
+        }
+        var self = this;
+        //check if we overwrite existing mappings
+        if (this.containsPrefix(k) && v !== this._namespaces[k]) {
+            this.remove(k);
+        } else {
+            jQuery.each(this._namespaces, function (k1,v1) {
+                if (v1 === v && k1 !== k) {
+                    self.remove(k1);
+                }
+            });
+        }
+        return this.add(k, v);
+    };
+    
+    // get a namespace (or *undefined*) for a given prefix.
+    this.get = function (k) {
+        return this._namespaces[k];
+    };
+
+    // get a prefix (or *undefined*) for a given namespace.
+    this.getPrefix = function (v) {
+        jQuery.each(this._namespaces, function (k1,v1) {
+            if (v1 === v) {
+                return k1;
+            }
+        });
+        return undefined;
+    };
+    
+    // check if a prefix exists. 
+    this.containsPrefix = function (k) {
+        return (k in this._namespaces);
+    };
+    
+    // check if a namespace exists. 
+    this.containsNamespace = function (v) {
+        return this.getPrefix(v) !== undefined;
+    };
+
+    //update the prefix *p* with the namespace *n*.
+	this.update = function (p, n) {
+        this._namespaces[p] = n;
+        return this;
+    };
+    
+    // remove the namespace with the prefix *p*
+    this.remove = function (p) {
+        delete this._namespaces[p];
+        return this;
+    };
+    
+    // return a copy of the internal structure of the namespaces
+    // as key/value pairs.
+    this.toObj = function () {
+        return jQuery.extend({'' : this._base}, this._namespaces);
+    };
+    
+    // transform a URI into a CURIE with the given
+    // namespaces. If *safe* is true, this returns
+    // a SCURIE. 
+    this.curie = function(uri, safe){
+        return VIE.Util.toCurie(uri, safe, this);
+    };
+    
+    // checks whether the given string is a CURIE.
+    this.isCurie = function (something) {
+    	return VIE.Util.isCurie(something, this);
+    };
+    
+    // transforms a CURIE into a URI.
+    this.uri = function (curie) {
+        return VIE.Util.toUri (curie, this);
+    };
+    
+    // checks wether the given string is a URI.
+    this.isUri = VIE.Util.isUri;
+};
+// Classic VIE API bindings to new VIE
+VIE.prototype.ClassicRDFa = function(vie) {
+    this.vie = vie;
+};
+
+VIE.prototype.ClassicRDFa.prototype = {
+    readEntities: function(selector) {
+        var jsonEntities = [];
+        var entities = this.vie.RDFaEntities.getInstances(selector);
+        _.each(entities, function(entity) {
+            jsonEntities.push(entity.toJSONLD());
+        });
+        return jsonEntities;
+    }
+};
+
+VIE.prototype.ClassicRDFaEntities = function(vie) {
+    this.vie = vie;
+};
+
+VIE.prototype.ClassicRDFaEntities.prototype = {
+    getInstances: function(selector) {
+        if (!this.vie.services.rdfa) {
+            this.vie.use(new this.vie.RdfaService());
+        }
+        var foundEntities = null;
+        var loaded = false;
+        this.vie.load({element: selector}).from('rdfa').execute().done(function(entities) {
+            foundEntities = entities;
+            loaded = true;
+        });
+
+        while (!loaded) {
+        }
+
+        return foundEntities;
+    },
+
+    getInstance: function(selector) {
+        var instances = this.getInstances(selector);
+        if (instances && instances.length) {
+            return instances[0];
+        }
+        return null;
+    }
+};
+
+VIE.prototype.ClassicEntityManager = function(vie) {
+    this.vie = vie;
+    this.entities = this.vie.entities;
+};
+
+VIE.prototype.ClassicEntityManager.prototype = {
+    getBySubject: function(subject) {
+        return this.vie.entities.get(subject);
+    },
+
+    getByJSONLD: function(json) {
+        return this.vie.entities.addOrUpdate(json);
+    }
+};
+// File:   DBPediaService.js <br />
+// Author: <a href="http://github.com/neogermi/">Sebastian Germesin</a>
+//
+
+(function(){
+    
+VIE.prototype.DBPediaService = function(options) {
+    var defaults = {
+        name : 'dbpedia',
+        defaultProxyUrl : "../utils/proxy/proxy.php",
+        namespaces : {
+            owl    : "http://www.w3.org/2002/07/owl#",
+            yago   : "http://dbpedia.org/class/yago/",
+            dbonto : 'http://dbpedia.org/ontology/'
+        }
+    };
+    this.options = jQuery.extend(defaults, options ? options : {});
+
+    this.vie = null; // will be set via VIE.use();
+    this.name = this.options.name;
+    this.connector = new DBPediaConnector(this.options);
+
+    jQuery.ajaxSetup({
+        converters: {"text application/rdf+json": function(s){return JSON.parse(s);}}
+    });
+
+};
+
+VIE.prototype.DBPediaService.prototype = {
+    init: function() {
+        
+       for (var key in this.options.namespaces) {
+            try {
+                var val = this.options.namespaces[key];
+                this.vie.namespaces.add(key, val);
+            } catch (e) {
+                //this means that the namespace is already in the VIE.namespace
+                //ignore for now!
+            }
+        }
+        this.namespaces = new this.vie.Namespaces(this.options.namespaces);
+        
+        this.rules = [
+             //rule to transform a DBPedia person into a VIE person
+             {
+                'left' : [
+                    '?subject a <http://dbpedia.org/ontology/Person>',
+                 ],
+                 'right': function(ns){
+                     return function(){
+                         return jQuery.rdf.triple(this.subject.toString() +
+                         ' a <http://schema.org/Person>', {
+                             namespaces: ns
+                         });
+                     };
+                 }(this.namespaces.toObj())
+             }
+        ];
+    },
+    
+    // VIE API load implementation
+    load: function(loadable){
+        var correct = loadable instanceof this.vie.Loadable;
+        if (!correct) {throw "Invalid Loadable passed";}
+        
+        var service = this;
+        var entity = loadable.options.entity;
+        if(!entity){
+            console.warn("DBPediaConnector: No entity to look for!");
+            loadable.resolve([]);
+        };
+        var success = function (results) {
+        	var id = entity.replace(/^</, '').replace(/>$/, '');
+        	
+        	if (results[id]) {
+    			var e = service.vie.entities.get(entity);
+        		if (!e) {
+        			var attrs = {
+        		        '@subject': entity,
+        		        '@type': results[id]["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"]
+        			};
+        			delete results[id]["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"];
+        			
+        			jQuery.extend(attrs, results[id]);
+        			service.vie.entities.add(attrs);
+        			e = service.vie.entities.get(entity);
+        		}
+        		loadable.resolve([e]);
+        	} else {
+        		loadable.reject(undefined);
+        	}
+        };
+        var error = function (e) {
+            loadable.reject(e);
+        };
+        this.connector.load(entity, success, error);
+    }
+};
+var DBPediaConnector = function(options){
+    this.options = options;
+};
+
+DBPediaConnector.prototype = {
+    
+    load: function (uri, success, error, options) {
+        if (!options) { options = {}; }
+        var url = uri
+        .replace(/^</, '').replace(/>$/, '')
+        .replace('resource', 'data') + ".jrdf";
+        
+        var proxyUrl = this._proxyUrl();
+        var format = options.format || "application/rdf+json";
+
+        if (typeof exports !== "undefined" && typeof process !== "undefined") {
+            // We're on Node.js, don't use jQuery.ajax
+            return this.loadNode(url, success, error, options, format);
+        }
+        
+        jQuery.ajax({
+            success: function(response){
+                success(response);
+            },
+            error: error,
+            type: (proxyUrl) ? "POST" : "GET",
+            url: proxyUrl || url,
+            data: (proxyUrl) ? {
+                    proxy_url: url, 
+                    content: "",
+                    verb: "GET",
+                    format: format
+                } : text,
+            dataType: format,
+            contentType: proxyUrl ? undefined : "text/plain",
+            accepts: {"application/rdf+json": "application/rdf+json"}
+        });
+    },
+
+    loadNode: function (uri, success, error, options, format) {
+        var request = require('request');
+        var r = request({
+            method: "GET",
+            uri: uri,
+            headers: {
+                Accept: format
+            }
+        }, function(error, response, body) {
+            success({results: JSON.parse(body)});
+        });
+        r.end();
+    },
+    
+    _proxyUrl: function () {
+        return this.options.proxyUrl || this.options.defaultProxyUrl;
+    }
+};
+})();
+
+/* TODO: give same functionality as RdfaService or remove
+
+VIE.prototype.RdfaRdfQueryService = function(options) {
+    if (!options) {
+        options = {};
+    }
+    this.vie = null;
+    this.name = 'rdfa';
+
+    if (typeof jQuery.rdf !== 'function') {
+        throw "RdfQuery is not loaded";
+    }
+};
+
+VIE.prototype.RdfaRdfQueryService.prototype = {
+
+    load: function(loadable){
+        var service = this;
+        var correct = loadable instanceof this.vie.Loadable;
+        if (!correct) {
+            throw "Invalid Loadable passed";
+        }
+        
+        var element = loadable.options.element ? loadable.options.element : jQuery(document);
+        
+        var rdf = jQuery(element).rdfa();
+        
+        jQuery.each(jQuery(element).xmlns(), function(prefix, ns){
+            service.vie.namespaces.addOrReplace(prefix, ns.toString());
+        });
+        
+        var entities = {}
+        rdf.where('?subject ?property ?object').each(function(){
+            var subject = this.subject.toString();
+            if (!entities[subject]) {
+                entities[subject] = {
+                    '@subject': subject
+                };
+            }
+            var propertyUri = this.property.toString();
+            
+            var val;
+            if (typeof this.object.value === "string") {
+                val = this.object.value;
+            }
+            else {
+                val = this.object.toString();
+            }
+            if (!entities[subject][propertyUri]) {
+                entities[subject][propertyUri] = val;
+            }
+            else 
+                if (!_.isArray(entities[subject][propertyUri])) {
+                    entities[subject][propertyUri] = [entities[subject][propertyUri]];
+                    entities[subject][propertyUri].push(val);
+                }
+                else {
+                    entities[subject][propertyUri].push(val);
+                }
+        });
+        
+        var vieEntities = [];
+        jQuery.each(entities, function(){
+            vieEntities.push(service.vie.entities.addOrUpdate(this));
+        });
+        loadable.resolve(vieEntities);
+    }
+};
+
+
+*/VIE.prototype.RdfaService = function(options) {
+    if (!options) {
+        options = {};
+    }
+    this.vie = null;
+    this.name = 'rdfa';
+    this.subjectSelector = options.subjectSelector ? options.subjectSelector : "[about],[typeof],[src],[href],html";
+    this.predicateSelector = options.predicateSelector ? options.predicateSelector : "[property],[rel]";
+    this.views = [];
+};
+
+VIE.prototype.RdfaService.prototype = {
+    
+    load : function(loadable) {
+        var service = this;
+        var correct = loadable instanceof this.vie.Loadable;
+        if (!correct) {
+            throw "Invalid Loadable passed";
+        }
+
+        var element;
+        if (!loadable.options.element) {
+            if (typeof document === 'undefined') { 
+                return loadable.resolve([]);
+            }
+            element = jQuery(document);
+        } else {
+            element = loadable.options.element;
+        }
+    
+        var ns = this.xmlns(element);
+        for (var prefix in ns) {
+            this.vie.namespaces.addOrReplace(prefix, ns[prefix]);
+        }
+        
+        var entities = [];
+        jQuery(this.subjectSelector, element).add(jQuery(element).filter(this.subjectSelector)).each(function() {
+            var entity = service._readEntity(jQuery(this));
+            if (entity) {
+                entities.push(entity);
+            }
+        });
+        loadable.resolve(entities);
+    },
+
+    save : function(savable) {
+        var correct = savable instanceof this.vie.Savable;
+        if (!correct) {
+            throw "Invalid Savable passed";
+        }
+    
+        if (!savable.options.element) {
+            // FIXME: we could find element based on subject
+            throw "Unable to write entity to RDFa, no element given";
+        }
+    
+        if (!savable.options.entity) {
+            throw "Unable to write to RDFa, no entity given";
+        }
+    
+        this._writeEntity(savable.options.entity, savable.options.element);
+        savable.resolve();
+    },
+    
+    _readEntity : function(element) {
+        var subject = this.getElementSubject(element);
+        var type = this._getElementType(element);
+        
+        var entity = this._readEntityPredicates(subject, element, false);
+        //if (jQuery.isEmptyObject(entity)) {
+        //    return null;
+        //}
+    
+        entity['@subject'] = subject;
+        if (type) {
+        	entity['@type'] = type;
+        }
+        
+        var entityInstance = new this.vie.Entity(entity);
+        entityInstance = this.vie.entities.addOrUpdate(entityInstance);
+        this._registerEntityView(entityInstance, element);
+        return entityInstance;
+    },
+    
+    _writeEntity : function(entity, element) {
+        var service = this;
+        this._findPredicateElements(this.getElementSubject(element), element, true).each(function() {
+            var predicateElement = jQuery(this);
+            var predicate = service.getElementPredicate(predicateElement);
+            if (!entity.has(predicate)) {
+                return true;
+            }
+    
+            var value = entity.get(predicate);
+            if (value === service.readElementValue(predicate, predicateElement)) {
+                return true;
+            }
+    
+            service.writeElementValue(predicate, predicateElement, value);
+        });
+        return true;
+    },
+    
+    _getViewForElement : function(element) {
+        var viewInstance;
+        jQuery.each(this.views, function() {
+            if (this.el.get(0) === element.get(0)) {
+                viewInstance = this;
+                return false;
+            }
+        });
+        return viewInstance;
+    },
+    
+    _registerEntityView : function(entity, element) {
+        var service = this;
+        var viewInstance = this._getViewForElement(element);
+        if (viewInstance) {
+            return viewInstance;
+        }
+    
+        viewInstance = new this.vie.view.Entity({
+            model: entity,
+            el: element,
+            tagName: element.get(0).nodeName,
+            vie: this.vie,
+            service: this.name
+        });
+        this.views.push(viewInstance);
+    
+        // Find collection elements and create collection views for them
+        _.each(entity.attributes, function(value, predicate) {
+            var attributeValue = entity.get(predicate);
+            if (attributeValue instanceof service.vie.Collection) {
+                jQuery.each(service.getElementByPredicate(predicate, element), function() {
+                    service._registerCollectionView(attributeValue, jQuery(this));
+                });
+            }
+        });
+        return viewInstance;
+    },
+    
+    _registerCollectionView : function(collection, element) {
+        var viewInstance = this._getViewForElement(element);
+        if (viewInstance) {
+            return viewInstance;
+        }
+    
+        var entityTemplate = element.children(':first-child');
+    
+        viewInstance = new this.vie.view.Collection({
+            collection: collection,
+            model: collection.model,
+            el: element,
+            template: entityTemplate,
+            service: this,
+            tagName: element.get(0).nodeName
+        });
+        this.views.push(viewInstance);
+        return viewInstance;
+    },
+    
+    _getElementType : function (element) {
+    	var type;
+     	if (jQuery(element).attr('typeof')) {
+	     	type = jQuery(element).attr('typeof');
+		     	if (type.indexOf("://") !== -1) {
+		     	return "<" + type + ">";
+	     	}
+	     	else {
+		     	return type;
+	     	}
+     	}
+     	return null;
+     },
+    
+    getElementSubject : function(element) {
+        if (typeof document !== 'undefined') {
+            if (element === document) {
+                return document.baseURI;
+            }
+        }
+        var subject = undefined;
+        jQuery(element).closest(this.subjectSelector).each(function() {
+            if (jQuery(this).attr('about')) {
+                subject = jQuery(this).attr('about');
+                return true;
+            }
+            if (jQuery(this).attr('src')) {
+                subject = jQuery(this).attr('src');
+                return true;
+            }
+            if (jQuery(this).attr('typeof')) {
+                subject = this;
+                return true;
+            }
+    
+            // We also handle baseURL outside browser context by manually
+            // looking for the `<base>` element inside HTML head.
+            if (jQuery(this).get(0).nodeName === 'HTML') {
+                jQuery(this).find('base').each(function() {
+                    subject = jQuery(this).attr('href');
+                });
+            }
+        });
+                
+        if (!subject) {
+            return undefined;
+        }
+                
+        if (typeof subject === 'object') {
+            return subject;
+        }
+    
+        return "<" + subject + ">";
+    },
+    
+    setElementSubject : function(subject, element) {
+        if (jQuery(element).attr('src')) {
+            return jQuery(element).attr('src', subject);
+        }
+        return jQuery(element).attr('about', subject);
+    },
+    
+    getElementPredicate : function(element) {
+        var predicate;
+        predicate = element.attr('property');
+        if (!predicate) {
+            predicate = element.attr('rel');
+        }
+        return predicate;
+    },
+    
+    getElementBySubject : function(subject, element) {
+        var service = this;
+        return jQuery(element).find(this.subjectSelector).add(jQuery(element).filter(this.subjectSelector)).filter(function() {
+            if (service.getElementSubject(jQuery(this)) !== subject) {
+                return false;
+            }
+     
+            return true;
+        });
+    },
+    
+    getElementByPredicate : function(predicate, element) {
+        var service = this;
+        var subject = this.getElementSubject(element);
+        return jQuery(element).find(this.predicateSelector).add(jQuery(element).filter(this.predicateSelector)).filter(function() {
+            if (service.getElementPredicate(jQuery(this)) !== predicate) {
+                return false;
+            }
+    
+            if (service.getElementSubject(jQuery(this)) !== subject) {
+                return false;
+            }
+     
+            return true;
+        });
+    },
+    
+    _readEntityPredicates : function(subject, element, emptyValues) {
+        var service = this;
+        var entityPredicates = {};
+    
+        this._findPredicateElements(subject, element, true).each(function() {
+            var predicateElement = jQuery(this);
+            var predicate = service.getElementPredicate(predicateElement);
+            var value = service.readElementValue(predicate, predicateElement);
+    
+            if (value === null && !emptyValues) {
+                return;
+            }
+    
+            entityPredicates[predicate] = value;
+        });
+    
+        if (jQuery(element).get(0).tagName !== 'HTML') {
+            jQuery(element).parent('[rev]').each(function() {
+                entityPredicates[jQuery(this).attr('rev')] = service.getElementSubject(this); 
+            });
+        }
+    
+        return entityPredicates;
+    },
+    
+    _findPredicateElements : function(subject, element, allowNestedPredicates) {
+        var service = this;
+        return jQuery(element).find(this.predicateSelector).add(jQuery(element).filter(this.predicateSelector)).filter(function() {
+            if (service.getElementSubject(this) !== subject) {
+                return false;
+            }
+            if (!allowNestedPredicates) {
+                if (!jQuery(this).parents('[property]').length) {
+                    return true;
+                }
+                return false;
+            }
+    
+            return true;
+        });
+    },
+    
+    readElementValue : function(predicate, element) {
+        // The `content` attribute can be used for providing machine-readable
+        // values for elements where the HTML presentation differs from the
+        // actual value.
+        var content = element.attr('content');
+        if (content) {
+            return content;
+        }
+                
+        // The `resource` attribute can be used to link a predicate to another
+        // RDF resource.
+        var resource = element.attr('resource');
+        if (resource) {
+            return "<" + resource + ">";
+        }
+                
+        // `href` attribute also links to another RDF resource.
+        var href = element.attr('href');
+        if (href && element.attr('rel') === predicate) {
+            return "<" + href + ">";
+        }
+    
+        // If the predicate is a relation, we look for identified child objects
+        // and provide their identifiers as the values. To protect from scope
+        // creep, we only support direct descentants of the element where the
+        // `rel` attribute was set.
+        if (element.attr('rel')) {
+            var value = [];
+            var service = this;
+            jQuery(element).children(this.subjectSelector).each(function() {
+                value.push(service.getElementSubject(this));
+            });
+            return value;
+        }
+    
+        // If none of the checks above matched we return the HTML contents of
+        // the element as the literal value.
+        return element.html();
+    },
+    
+    writeElementValue : function(predicate, element, value) {
+    	
+    	//TODO: this is a hack, please fix!
+     	if (value instanceof Array && value.length > 0) value = value[0];
+        
+     	// The `content` attribute can be used for providing machine-readable
+        // values for elements where the HTML presentation differs from the
+        // actual value.
+        var content = element.attr('content');
+        if (content) {
+            element.attr('content', value);
+            return;
+        }
+                
+        // The `resource` attribute can be used to link a predicate to another
+        // RDF resource.
+        var resource = element.attr('resource');
+        if (resource) {
+            element.attr('resource', value);
+        }
+    
+        // Property has inline value. Change the HTML contents of the property
+        // element to match the new value.
+        element.html(value);
+    },
+    
+    // mostyl copied from http://code.google.com/p/rdfquery/source/browse/trunk/jquery.xmlns.js
+    xmlns : function (elem) {
+        var $elem;
+        if (!elem) {
+            if (typeof document === 'undefined') { 
+                return {};
+            }
+            $elem = jQuery(document);
+        } else {
+            $elem = jQuery(elem);
+        }
+        
+        var obj = {};
+        
+        $elem.each(function (i, e) {
+            if (e.attributes && e.attributes.getNamedItemNS) {
+                for (i = 0; i < e.attributes.length; i += 1) {
+                    var attr = e.attributes[i];
+                    if (/^xmlns(:(.+))?$/.test(attr.nodeName)) {
+                        prefix = /^xmlns(:(.+))?$/.exec(attr.nodeName)[2] || '';
+                        value = attr.nodeValue;
+                        if (prefix === '' || value !== '') {
+                            obj[prefix] = attr.nodeValue;
+                        }
+                    }
+                }
+            }
+        });
+        
+        return obj;
+    }
+
+};
+// File:   StanbolService.js
+// Author: <a href="mailto:sebastian.germesin@dfki.de">Sebastian Germesin</a>
+// Author: <a href="mailto:szaby.gruenwald@salzburgresearch.at">Szaby Gruenwald</a>
+//
+(function(){
+VIE.prototype.StanbolService = function(options) {
+    var defaults = {
+        name : 'stanbol',
+        url: 'http://dev.iks-project.eu:8080/',
+        defaultProxyUrl : "../utils/proxy/proxy.php",
+        namespaces : {
+            semdeski : "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#",
+            semdeskf : "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#",
+            skos: "http://www.w3.org/2004/02/skos/core#",
+            foaf: "http://xmlns.com/foaf/0.1/",
+            opengis: "http://www.opengis.net/gml/",
+            dbpedia: "http://dbpedia.org/ontology/",
+            owl : "http://www.w3.org/2002/07/owl#",
+            geonames : "http://www.geonames.org/ontology#",
+            enhancer : "http://fise.iks-project.eu/ontology/",
+            entityhub: "http://www.iks-project.eu/ontology/rick/model/",
+            rdfs: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+            dc  : 'http://purl.org/dc/terms/',
+            foaf: 'http://xmlns.com/foaf/0.1/',
+            schema: 'http://schema.org/'
+        }
+    };
+    this.options = jQuery.extend(defaults, options ? options : {});
+
+    this.vie = null; // will be set via VIE.use();
+    this.name = this.options.name;
+    this.connector = new StanbolConnector(this.options);
+    
+    jQuery.ajaxSetup({
+        converters: {"text application/rdf+json": function(s){return JSON.parse(s);}}
+    });
+
+};
+
+VIE.prototype.StanbolService.prototype = {
+    init: function(){
+        
+        for (var key in this.options.namespaces) {
+            try {
+                var val = this.options.namespaces[key];
+                this.vie.namespaces.add(key, val);
+            } catch (e) {
+                //this means that the namespace is already in the VIE.namespace
+                //ignore for now!
+            }
+        }
+        this.namespaces = new this.vie.Namespaces(this.vie.namespaces.base(), this.options.namespaces);
+        
+        this.rules = [
+            //rule to add backwards-relations to the triples
+            //this makes querying for entities a lot easier!
+            {'left' : [
+                '?subject a <http://fise.iks-project.eu/ontology/EntityAnnotation>',
+                '?subject enhancer:entity-type ?type',
+                '?subject enhancer:confidence ?confidence',
+                '?subject enhancer:entity-reference ?entity',
+                '?subject dc:relation ?relation',
+                '?relation a <http://fise.iks-project.eu/ontology/TextAnnotation>',
+                '?relation enhancer:selected-text ?selected-text',
+                '?relation enhancer:selection-context ?selection-context',
+                '?relation enhancer:start ?start',
+                '?relation enhancer:end ?end'
+            ],
+             'right' : [
+                 '?entity a ?type',
+                 '?entity enhancer:hasTextAnnotation ?relation',
+                 '?entity enhancer:hasEntityAnnotation ?subject'
+             ]
+             },
+             //rule(s) to transform a Stanbol person into a VIE person
+             {
+                'left' : [
+                    '?subject a dbpedia:Person>',
+                 ],
+                 'right': function(ns){
+                     return function(){
+                         return jQuery.rdf.triple(this.subject.toString() +
+                         ' a <' + ns.base() + 'Person>', {
+                             namespaces: ns.toObj()
+                         });
+                     };
+                 }(this.namespaces)
+             },
+             {
+                'left' : [
+                    '?subject a foaf:Person>',
+                 ],
+                 'right': function(ns){
+                     return function(){
+                         return jQuery.rdf.triple(this.subject.toString() +
+                         ' a <' + ns.base() + 'Person>', {
+                             namespaces: ns.toObj()
+                         });
+                     };
+                 }(this.namespaces)
+             }
+        ];
+        
+        this.vie.types.add('enhancer:EntityAnnotation', [
+            //TODO: add attributes
+        ]).inherit("Thing");
+        this.vie.types.add('enhancer:TextAnnotation', [
+            //TODO: add attributes
+        ]).inherit("Thing");
+        this.vie.types.add('enhancer:Enhancement', [
+            //TODO: add attributes
+        ]).inherit("Thing");
+    },
+    // VIE API analyze implementation
+    analyze: function(analyzable) {
+        var service = this;
+
+        var correct = analyzable instanceof this.vie.Analyzable;
+        if (!correct) {throw "Invalid Analyzable passed";}
+
+        var element = analyzable.options.element ? analyzable.options.element : jQuery('body');
+
+        var text = service._extractText(element);
+
+        if (text.length > 0) {
+            var service = this;
+            //query enhancer with extracted text
+            var success = function (results) {
+                var entities = service._enhancer2Entities(service, results);
+                analyzable.resolve(entities);
+            };
+            var error = function (e) {
+                analyzable.reject(e);
+            };
+
+            this.connector.analyze(text, success, error);
+
+        } else {
+            console.warn("No text found in element.");
+            analyzable.resolve([]);
+        }
+
+    },
+    
+    // VIE API load implementation
+    // Runs a Stanbol entityhub find
+    find: function(findable){
+        var correct = findable instanceof this.vie.Findable;
+        if (!correct) {throw "Invalid Findable passed";}
+        var service = this;
+        // The term to find, * as wildcard allowed
+        var term = escape(findable.options.term);
+        if(!term){
+            console.warn("StanbolConnector: No term to look for!");
+            findable.resolve([]);
+        };
+        var limit = (typeof findable.options.limit === "undefined") ? 20 : findable.options.limit;
+        var offset = (typeof findable.options.offset === "undefined") ? 0 : findable.options.offset;
+        var success = function (results) {
+            // TODO: Return an array of vie entities
+            var resultArray = _(results).map(
+                function(v,k){
+                    v.id=k;
+                    return v;
+                }
+            );
+            findable.resolve(resultArray);
+        };
+        var error = function (e) {
+            findable.reject(e);
+        };
+        this.connector.find(term, limit, offset, success, error);
+    },
+    
+    // VIE API load implementation
+    // Runs a Stanbol entityhub find
+    load: function(loadable){
+        var correct = loadable instanceof this.vie.Loadable;
+        if (!correct) {throw "Invalid Loadable passed";}
+        var service = this;
+        
+        var entity = loadable.options.entity;
+        if(!entity){
+            console.warn("StanbolConnector: No entity to look for!");
+            loadable.resolve([]);
+        };
+        var success = function (results) {
+            var entities = service._enhancer2Entities(service, results);
+            loadable.resolve(entities);
+        };
+        var error = function (e) {
+            loadable.reject(e);
+        };
+        this.connector.load(entity, success, error);
+    },
+    
+    _extractText: function (element) {
+        if (element.get(0) && 
+            element.get(0).tagName && 
+            (element.get(0).tagName == 'TEXTAREA' ||
+            element.get(0).tagName == 'INPUT' && element.attr('type', 'text'))) {
+            return element.get(0).val();
+        }
+        else {
+            return element
+                .text()    //get the text of element
+                .replace(/\s+/g, ' ') //collapse multiple whitespaces
+                .replace(/\0\b\n\r\f\t/g, '').trim(); // remove non-letter symbols
+        }
+    },
+
+    _enhancer2Entities: function (service, results) {
+        //transform data from Stanbol into VIE.Entities
+
+        if (typeof jQuery.rdf !== 'function') {
+            return this._enhancer2EntitiesNoRdfQuery(service, results);
+        }
+        var rdf = jQuery.rdf().load(results, {});
+
+        //execute rules here!
+        if (service.rules) {
+            var rules = jQuery.rdf.ruleset();
+            for (var prefix in service.namespaces.toObj()) {
+                if (prefix !== "") {
+                	rules.prefix(prefix, service.namespaces.get(prefix));
+                }
+            }
+            for (var i = 0; i < service.rules.length; i++) {
+                rules.add(service.rules[i]['left'], service.rules[i]['right']);
+            }
+            rdf = rdf.reason(rules, 10); // execute the rules only 10 times to avoid looping
+        }
+        var entities = {};
+        rdf.where('?subject ?property ?object').each(function() {
+            var subject = this.subject.toString();
+            if (!entities[subject]) {
+                entities[subject] = {
+                    '@subject': subject,
+                    '@context': service.namespaces.toObj(),
+                    '@type': []
+                };
+            }
+            var propertyUri = this.property.toString();
+            var propertyCurie;
+
+            propertyUri = propertyUri.substring(1, propertyUri.length - 1);
+            try {
+                property = jQuery.createCurie(propertyUri, {namespaces: service.namespaces.toObj()});
+            } catch (e) {
+                property = propertyUri;
+                console.warn(propertyUri + " doesn't have a namespace definition in '", service.namespaces.toObj());
+            }
+            entities[subject][property] = entities[subject][property] || [];
+
+            function getValue(rdfQueryLiteral){
+                if(typeof rdfQueryLiteral.value === "string"){
+                    return rdfQueryLiteral.value;
+                } else if (rdfQueryLiteral.value._string){
+                    return rdfQueryLiteral.toString();
+                } else {
+                    return rdfQueryLiteral.value;
+                }
+            }
+            entities[subject][property].push(getValue(this.object));
+        });
+
+        _(entities).each(function(ent){
+            ent["@type"] = ent["@type"].concat(ent["rdfs:type"]);
+            delete ent["rdfs:type"];
+            _(ent).each(function(value, property){
+                if(value.length === 1){
+                    ent[property] = value[0];
+                }
+            });
+        });
+
+        var vieEntities = [];
+        jQuery.each(entities, function() {
+            var entityInstance = new service.vie.Entity(this);
+            entityInstance = service.vie.entities.addOrUpdate(entityInstance);
+            vieEntities.push(entityInstance);
+        });
+        return vieEntities; 
+    },
+
+    _enhancer2EntitiesNoRdfQuery: function (service, results) {
+        jsonLD = [];
+        _.forEach(results.results, function(value, key) {
+            var entity = {};
+            entity['@subject'] = '<' + key + '>';
+            _.forEach(value, function(triples, predicate) {
+                predicate = '<' + predicate + '>';
+                _.forEach(triples, function(triple) {
+                    if (triple.type === 'uri') {
+                        triple.value = '<' + triple.value + '>';
+                    }
+
+                    if (entity[predicate] && !_.isArray(entity[predicate])) {
+                        entity[predicate] = [entity[predicate]];
+                    }
+
+                    if (_.isArray(entity[predicate])) {
+                        entity[predicate].push(triple.value);
+                        return;
+                    }
+                    entity[predicate] = triple.value;
+                });
+            });
+            jsonLD.push(entity);
+        });
+        return jsonLD;
+    }
+};
+
+var StanbolConnector = function(options){
+    this.options = options;
+    this.baseUrl = options.url.replace(/\/$/, '');
+    this.enhancerUrlPrefix = "/engines";
+    this.entityhubUrlPrefix = "/entityhub";
+    //TODO: this.ontonetUrlPrefix = "/ontonet";
+    //TODO: this.rulesUrlPrefix = "/rules";
+    //TODO: this.factstoreUrlPrefix = "/factstore";
+};
+StanbolConnector.prototype = {
+    
+    analyze: function(text, success, error, options) {
+        if (!options) { options = {}; }
+        var enhancerUrl = this.baseUrl + this.enhancerUrlPrefix;
+        var proxyUrl = this._proxyUrl();
+        var format = options.format || "application/rdf+json";
+
+        if (typeof exports !== "undefined" && typeof process !== "undefined") {
+            // We're on Node.js, don't use jQuery.ajax
+            return this.analyzeNode(enhancerUrl, text, success, error, options, format);
+        }
+        
+        jQuery.ajax({
+            success: function(response){
+                success(response);
+            },
+            error: error,
+            type: "POST",
+            url: proxyUrl || enhancerUrl,
+            data: (proxyUrl) ? {
+                    proxy_url: enhancerUrl, 
+                    content: text,
+                    verb: "POST",
+                    format: format,
+                } : text,
+            dataType: format,
+            contentType: proxyUrl ? undefined : "text/plain",
+            accepts: {"application/rdf+json": "application/rdf+json"}
+
+        });
+    },
+
+    analyzeNode: function(url, text, success, error, options, format) {
+        var request = require('request');
+        var r = request({
+            method: "POST",
+            uri: url,
+            body: text,
+            headers: {
+                Accept: format
+            }
+        }, function(error, response, body) {
+            success({results: JSON.parse(body)});
+        });
+        r.end();
+    },
+    
+    load: function (uri, success, error, options) {
+        if (!options) { options = {}; }
+        uri = uri.replace(/^</, '').replace(/>$/, '');
+        var url = this.baseUrl + this.entityhubUrlPrefix + "/sites/entity?id=" + uri;
+        var proxyUrl = this._proxyUrl();
+        var format = options.format || "application/rdf+json";
+        
+        jQuery.ajax({
+            success: function(response){
+                success(response);
+            },
+            error: error,
+            type: (proxyUrl) ? "POST" : "GET",
+            url: proxyUrl || url,
+            data: (proxyUrl) ? {
+                    proxy_url: url, 
+                    content: "",
+                    verb: "GET",
+                    format: format
+                } : null,
+            dataType: format,
+            contentType: proxyUrl ? undefined : "text/plain",
+            accepts: {"application/rdf+json": "application/rdf+json"}
+        });
+    },
+    
+    find: function (term, limit, offset, success, error, options) {
+        // curl -X POST -d "name=Bishofsh&limit=10&offset=0" http://localhost:8080/entityhub/sites/find
+        if (!options) { options = {}; }
+        if (offset == null) {
+            offset = 0;
+        }
+        if (limit == null) {
+            limit = 10;
+        }
+        
+        var url = this.baseUrl + this.entityhubUrlPrefix + "/sites/find";
+        var proxyUrl = this._proxyUrl();
+        var format = options.format || "application/rdf+json";
+        
+        jQuery.ajax({
+            success: function(response){
+                success(response);
+            },
+            error: error,
+            type: "POST",
+            url: proxyUrl || url,
+            data: (proxyUrl) ? {
+                    proxy_url: url, 
+                    content: {
+                        name : term,
+                        limit : limit,
+                        offset: offset
+                    },
+                    verb: "POST",
+                    format: format,
+                    type: "text/plain"
+                } : "name=" + term + "&limit=" + limit + "&offset=" + offset,
+            dataType: format,
+            contentType: proxyUrl ? undefined : "text/plain",
+            accepts: {"application/rdf+json": "application/rdf+json"}
+        });
+    },
+    
+    _proxyUrl: function(){
+        this.proxyUrl = "";
+        if(this.baseUrl.indexOf(":") !== -1){
+            return this.options.proxyUrl || this.options.defaultProxyUrl;
+        } else {
+            return '';
+        }
+    }
+};
+})();
+
+if (!VIE.prototype.view) {
+    VIE.prototype.view = {};
+}
+
+VIE.prototype.view.Collection = Backbone.View.extend({
+    // Ensure the collection view gets updated when items get added or removed
+    initialize: function() {
+        this.template = this.options.template;
+        this.service = this.options.service;
+        if (!this.service) {
+            throw "No RDFa service provided to the Collection View";
+        }
+
+        this.entityViews = {};
+        _.bindAll(this, 'addItem', 'removeItem', 'refreshItems');
+        this.collection.bind('add', this.addItem);
+        this.collection.bind('remove', this.removeItem);
+
+        // Make the view aware of existing entities in collection
+        var view = this;
+        this.collection.forEach(function(entity) {
+            view.registerItem(entity, view.collection);
+        });
+    },
+
+    addItem: function(entity, collection) {
+        if (collection != this.collection) {
+            return;
+        }
+
+        if (!this.template || this.template.length === 0) {
+            return;
+        }
+
+        var entityView = this.service._registerEntityView(entity, this.cloneElement(this.template));
+        var entityElement = entityView.render().el;
+        if (entity.id) {
+            this.service.setElementSubject(entity.getSubjectUri(), entityElement);
+        }
+
+        // TODO: Ordering
+        this.el.append(entityElement);
+        
+        this.trigger('add', entityView);
+        this.entityViews[entity.cid] = entityView;
+        entityElement.show();
+    },
+
+    registerItem: function(entity, collection) {
+        var element = this.service.getElementBySubject(entity.id, this.el);
+        if (!element) {
+            return;
+        }
+
+        var entityView = this.service._registerEntityView(entity, element);
+        this.entityViews[entity.cid] = entityView;
+    },
+
+    removeItem: function(entity) {
+        if (!this.entityViews[entity.cid]) {
+            return;
+        }
+
+        this.entityViews[entity.cid].el.remove();
+        delete(this.entityViews[entity.cid]);
+    },
+
+    refreshItems: function(collection) {
+        var view = this;
+        jQuery(this.el).empty();
+        collection.forEach(function(entity) {
+            view.addItem(entity, collection);
+        });
+    },
+
+    cloneElement: function(element) {
+        var newElement = jQuery(element).clone(false);
+        var service = this.service;
+        if (typeof newElement.attr('about') !== 'undefined') {
+            // Direct match with container
+            newElement.attr('about', '');
+        }
+        newElement.find('[about]').attr('about', '');
+        var subject = this.service.getElementSubject(newElement);
+        service._findPredicateElements(subject, newElement, false).each(function() {
+            service.writeElementValue(jQuery(this), '');
+        });
+
+        return element;
+    }
+});
+if (!VIE.prototype.view) {
+    VIE.prototype.view = {};
+}
+
+VIE.prototype.view.Entity = Backbone.View.extend({
+    initialize: function(options) {
+        this.service = options.service ? options.service : 'rdfa';
+        this.vie = options.vie;
+
+        // Ensure view gets updated when properties of the Entity change.
+        _.bindAll(this, 'render');
+        this.model.bind('change', this.render);
+    },
+
+    // Rendering a view means writing the properties of the Entity back to
+    // the element containing our RDFa annotations.
+    render: function() {
+        this.vie.save({
+                element: this.el, 
+                entity: this.model
+            }).
+            to(this.service).
+            execute();
+        return this;
+    }
+});