Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

more less working, need a simple way to specify resource literals

  • Loading branch information...
commit b53fab4ac3c01ffaf086f528ce9a66fdf9548bc2 1 parent ac8c3b2
@antoniogarrote authored
View
426 deps/sem_ko.js
@@ -1,3 +1,31 @@
+Utils = {};
+Utils.stackCounterLimit = 1000;
+Utils.stackCounter = 0;
+
+Utils.recur = function(c){
+ if(Utils.stackCounter === Utils.stackCounterLimit) {
+ Utils.stackCounter = 0;
+ setTimeout(c, 0);
+ } else {
+ Utils.stackCounter++;
+ c();
+ }
+};
+
+Utils.repeat = function(c,max,floop,fend,env) {
+ if(arguments.length===4) { env = {}; }
+ if(c<max) {
+ env._i = c;
+ floop(function(floop,env){
+ // avoid stack overflow
+ // deadly hack
+ Utils.recur(function(){ Utils.repeat(c+1, max, floop, fend, env); });
+ },env);
+ } else {
+ fend(env);
+ }
+};
+
SemanticKnockOut = {};
window['sko'] = SemanticKnockOut;
@@ -20,6 +48,239 @@ sko.init = function() {
}
};
+// Collection of observable resources
+sko.aboutResourceMap = {};
+
+sko.aboutCounter = 0;
+
+sko.about = function(aboutValue, viewModel, cb) {
+ var currentValue = sko.about[aboutValue];
+ if(currentValue != null) {
+ // returning an observable that was already registered for this node
+ cb(aboutValue);
+ } else {
+ // the about value hasn't been registered yet
+
+ // identifier
+ var nextId = ''+sko.aboutCounter;
+ sko.aboutCounter++;
+
+ if(typeof(aboutValue) === 'string') {
+ // the value is aconstant URI
+
+ var uri = aboutValue;
+ if(uri[0] === "<" && uri[uri.length-1] == ">") {
+ uri = uri.slice(1,uri.length-1);
+ }
+
+ // register the new observer and resource
+ sko.store.node(uri, function(success, resource) {
+ if(success) {
+ // id -> Resource
+ sko.aboutResourceMap[nextId] = new sko.Resource(resource);
+ // id -> observer
+ sko.about[nextId] = ko.observable(uri);
+
+ // we observe changes in the about resource
+ sko.about[nextId].subscribe(function(nextUri) {
+ sko.store.node(nextUri, function(success, nextResource) {
+ if(success) {
+ var newUri = nextResource.toArray()[0].subject.valueOf();
+ sko.aboutResourceMap[nextId].about(newUri);
+ } else {
+ console.log("Error updating resource for URI:"+nextUri+" in SKO about node observer");
+ }
+ });
+ });
+ } else {
+ // what here?
+ }
+
+ cb(nextId);
+ });
+ } else {
+ // The value is a function (maybe an observer)
+ sko.about[nextId] = ko.dependentObservable({
+ read: function(){
+ var uri = aboutValue();
+ if(uri[0] === "<" && uri[uri.length-1] == ">") {
+ uri = uri.slice(1,uri.length-1);
+ }
+ return uri;
+ },
+ write: function(value) {
+ aboutValue(value);
+ },
+ owner: viewModel
+ });
+
+ // register the new observer and resource
+ sko.store.node(sko.about[nextId](), function(success, resource) {
+ if(success) {
+ // id -> Resource
+ sko.aboutResourceMap[nextId] = new sko.Resource(resource);
+
+ // we observe changes in the about resource
+ sko.about[nextId].subscribe(function(nextUri) {
+ sko.store.node(nextUri, function(success, nextResource) {
+ if(success) {
+ if(nextResource.toArray().length>0) {
+ var newUri = nextResource.toArray()[0].subject.valueOf();
+ sko.aboutResourceMap[nextId].about(newUri);
+ } else {
+ // reset resource?
+ }
+ } else {
+ console.log("Error updating resource for URI:"+nextUri+" in SKO about node observer");
+ }
+ });
+ });
+ } else {
+ // what here?
+ }
+ cb(nextId);
+ });
+ }
+ }
+};
+
+sko.rel = function(relValue, node, viewModel, cb) {
+ var nextId = ''+sko.aboutCounter;
+ var relValueUri = null;
+ sko.aboutCounter++;
+
+ if(typeof(relValue) === 'string') {
+ var uri = relValue;
+ if(uri[0] === "<" && uri[uri.length-1] == ">") {
+ uri = uri.slice(1,uri.length-1);
+ }
+ relValueUri = uri;
+
+ sko.about[nextId] = ko.dependentObservable({
+ read: function(){
+ var resource = sko.currentResource(jQuery(node).parent().toArray()[0]);
+ if(resource != null) {
+ console.log("*** Found parent resource: "+resource.about());
+ if(resource[uri]) {
+ var relResourceUri = resource[uri]();
+
+ console.log("*** found related resource: "+relResourceUri);
+ // register the new observer and resource
+ sko.store.node(relResourceUri, function(success, resource) {
+ sko.aboutResourceMap[nextId] = new sko.Resource(resource);
+ });
+
+ return relResourceUri;
+ } else {
+ console.log("!!! parent resource doest not link to the related resource");
+ }
+ } else {
+ console.log("!!! impossible to find parent resource");
+ }},
+
+ // we setup the related object of the parent resource
+ // this will trigger the observer that will update this model proxy
+ write: function(uri) {
+ if(uri[0] === "<" && uri[uri.length-1] == ">") {
+ uri = uri.slice(1,uri.length-1);
+ }
+ var resource = sko.currentResource(jQuery(node).parent().toArray()[0]);
+
+ if(resource != null) {
+ console.log("*** Found parent resource: "+resource.about());
+ if(resource[relValueUri]) {
+ console.log("*** Setting new related resource in parent resource found: "+uri);
+ resource[relValueUri](uri);
+ } else {
+ console.log("!!! parent resource doest not link to the related resource");
+ }
+ } else {
+ console.log("!!! impossible to find parent resource");
+ }
+ },
+
+ owner: viewModel
+ });
+
+ sko.about[nextId].subscribe(function(nextUri) {
+ console.log("!!! change in related resource URI");
+ sko.store.node(nextUri, function(success, nextResource) {
+ if(success) {
+ var newUri = nextResource.toArray()[0].subject.valueOf();
+ sko.aboutResourceMap[nextId].about(newUri);
+ } else {
+ console.log("Error updating resource for URI:"+nextUri+" in SKO about related node observer");
+ }
+ });
+ });
+
+ } else {
+ sko.about[nextId] = ko.dependentObservable({
+ read: function(){
+ var uri = relValue();
+
+ var resource = sko.currentResource(jQuery(node).parent().toArray()[0]);
+ if(resource != null) {
+ console.log("*** Found parent resource: "+resource.about());
+ if(resource[uri]) {
+ var relResourceUri = resource[uri]();
+
+ console.log("*** found related resource: "+relResourceUri);
+ // register the new observer and resource
+ sko.store.node(relResourceUri, function(success, resource) {
+ sko.aboutResourceMap[nextId] = new sko.Resource(resource);
+ });
+
+ return relResourceUri;
+ } else {
+ console.log("!!! parent resource doest not link to the related resource");
+ }
+ } else {
+ console.log("!!! impossible to find parent resource");
+ }
+ },
+
+ // we setup the related object of the parent resource
+ // this will trigger the observer that will update this model proxy
+ write: function(uri) {
+ if(uri[0] === "<" && uri[uri.length-1] == ">") {
+ uri = uri.slice(1,uri.length-1);
+ }
+ var resource = sko.currentResource(jQuery(node).parent().toArray()[0]);
+
+ if(resource != null) {
+ console.log("*** Found parent resource: "+resource.about());
+ if(resource[relValue()]) {
+ console.log("*** Setting new related resource in parent resource found: "+uri);
+ resource[relValue()](uri);
+ relValue(uri);
+ } else {
+ console.log("!!! parent resource doest not link to the related resource");
+ }
+ } else {
+ console.log("!!! impossible to find parent resource");
+ }
+ },
+
+ owner: viewModel
+ });
+
+ sko.about[nextId].subscribe(function(nextUri) {
+ console.log("!!! change in related resource URI");
+ sko.store.node(nextUri, function(success, nextResource) {
+ if(success) {
+ var newUri = nextResource.toArray()[0].subject.valueOf();
+ sko.aboutResourceMap[nextId].about(newUri);
+ } else {
+ console.log("Error updating resource for URI:"+nextUri+" in SKO about related node observer");
+ }
+ });
+ });
+ }
+
+ cb(nextId);
+};
+
sko.Resource = function(node) {
this.valuesMap = {};
var that = this;
@@ -27,11 +288,20 @@ sko.Resource = function(node) {
that.valuesMap[triple.predicate.valueOf()] = triple.object.valueOf();
that[triple.predicate.valueOf()] = ko.observable(triple.object.valueOf());
});
- this.about = node.toArray()[0]['subject'].valueOf();
+ this.about = ko.observable(node.toArray()[0]['subject'].valueOf());
+ this.storeObserverFn = sko.Resource.storeObserver(this);
+
+ // observe changes in the subject of this resource
+ var that = this;
+ this.about.subscribe(function(newUri){
+ console.log("SKO Resource new resource:"+newUri);
+ sko.store.stopObservingNode(that.storeObserverFn);
+ sko.store.startObservingNode(newUri, that.storeObserverFn);
+ });
// observe notifications from KO and the RDF store
sko.Resource.koObserver(this);
- sko.store.startObservingNode(this.about, sko.Resource.storeObserver(this));
+ sko.store.startObservingNode(this.about(), that.storeObserverFn);
};
/**
@@ -45,9 +315,9 @@ sko.Resource.prototype.notifyPropertyChange = function(property, newValue) {
if(this.valuesMap[property] !== newValue) {
// property is already present and the value has changed -> update
- var query = "DELETE { <"+this.about+"> <"+property+"> \""+this.valuesMap[property]+"\" }";
- query = query + " INSERT { <"+this.about+"> <"+property+"> \""+newValue+"\" }";
- query = query + " WHERE { <"+this.about+"> <"+property+"> \""+this.valuesMap[property]+"\" }";
+ var query = "DELETE { <"+this.about()+"> <"+property+"> \""+this.valuesMap[property]+"\" }";
+ query = query + " INSERT { <"+this.about()+"> <"+property+"> \""+newValue+"\" }";
+ query = query + " WHERE { <"+this.about()+"> <"+property+"> \""+this.valuesMap[property]+"\" }";
this.valuesMap[property] = newValue;
console.log("*** updating STORE: \n "+query);
@@ -71,7 +341,7 @@ sko.Resource.koObserver = function(skoResource) {
sko.Resource.storeObserver = function(skoResource) {
return function(node) {
- console.log("*** received notification change from STORE in resource "+skoResource.about);
+ console.log("*** received notification change from STORE in resource "+skoResource.about());
var newValues = {};
node.forEach(function(triple){
@@ -103,7 +373,7 @@ sko.Resource.storeObserver = function(skoResource) {
}
// updateValues
- skoResource.valuesMap = newValueMap
+ skoResource.valuesMap = newValueMap;
for(var i=0; i<toNullify.length; i++) {
skoResource[toNullify[i]](null);
@@ -116,5 +386,143 @@ sko.Resource.storeObserver = function(skoResource) {
for(var i=0; i<toCreate.length; i++) {
skoResource[toCreate[i]] = ko.observable(skoResource.valuesMap[toCreate[i]]);
}
- }
-}
+ };
+};
+
+// custom bindings
+
+ko.bindingHandlers.about = {
+ init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
+ // This will be called when the binding is first applied to an element
+ // Set up any initial state, event handlers, etc. here
+
+ var value = valueAccessor();
+ $(element).attr("about", value);
+ },
+
+ update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
+ // This will be called once when the binding is first applied to an element,
+ // and again whenever the associated observable changes value.
+ // Update the DOM element based on the supplied values here.
+
+ var value = valueAccessor();
+ $(element).attr("about", value);
+ }
+};
+
+
+// trace resources
+sko.traceResources = function(rootNode, model, cb) {
+ var nodes = [];
+ if(jQuery(rootNode).attr("about") || jQuery(rootNode).attr("data-bind")) {
+ nodes.push(rootNode);
+ }
+ var childNodes = jQuery(rootNode).find("*[about], *[data-bind]").toArray();
+ nodes = nodes.concat(childNodes);
+ var registerFn = function(k,env){
+ node = nodes[env._i];
+ var about = jQuery(node).attr("about");
+ var databind;
+
+ if(about == null) {
+ dataBind = jQuery(node).attr("data-bind");
+ if(dataBind != null) {
+ if(dataBind.indexOf("about:") != -1) {
+ var re = new RegExp("\s*([^ ]+)\s*,?");
+ about = re.exec(dataBind.split("about:")[1])[0];
+ if(about[about.length-1] === ',') {
+ about = about.slice(0,about.length-1);
+ }
+ }
+ }
+ }
+
+ if(about != null) {
+ if(typeof(about) === 'string' && about[0] !== '<' && about[about.length-1] !== '>') {
+ about = model[about];
+ }
+
+ sko.about(about, model, function(aboutId) {
+ jQuery(node).attr('aboutId',aboutId);
+ k(registerFn,env);
+ });
+ }
+
+
+ };
+
+ Utils.repeat(0,nodes.length, registerFn, function(env) {
+ cb();
+ });
+};
+
+/**
+ * This function must be called *after* traceResources has been
+ * invoked.
+ */
+sko.currentResource = function(node) {
+ console.log("in current resource");
+ if(node == null) {
+ console.log("!!! top of DOM tree, About node not found");
+ return null;
+ }
+ var aboutId = jQuery(node).attr('aboutId');
+ console.log("about id:"+aboutId);
+
+ if(aboutId) {
+ var uri = sko.about[aboutId]();
+ console.log("uri:"+uri);
+ if(uri != null) {
+ return sko.aboutResourceMap[aboutId];
+ } else {
+ console.log("!!! current resource is null: "+aboutId);
+ }
+ } else {
+ console.log("recurring");
+ return sko.currentResource(jQuery(node).parent().toArray()[0]);
+ }
+};
+
+sko.traceRelations = function(rootNode, model, cb) {
+ var nodes = [];
+ if(jQuery(rootNode).attr("rel") || jQuery(rootNode).attr("data-bind")) {
+ nodes.push(rootNode);
+ }
+ var childNodes = jQuery(rootNode).find("*[rel], *[data-bind]").toArray();
+ nodes = nodes.concat(childNodes);
+ var registerFn = function(k,env){
+ node = nodes[env._i];
+ var rel = jQuery(node).attr("rel");
+ var databind;
+
+ if(rel == null) {
+ dataBind = jQuery(node).attr("data-bind");
+ if(dataBind != null) {
+ if(dataBind.indexOf("rel:") != -1) {
+ var re = new RegExp("\s*([^ ]+)\s*,?");
+ rel = re.exec(dataBind.split("rel:")[1])[0];
+ if(rel[rel.length-1] === ',') {
+ rel = rel.slice(0,rel.length-1);
+ }
+ }
+ }
+ }
+
+ if(rel != null) {
+ if(typeof(rel) === 'string' && rel[0] !== '<' && rel[rel.length-1] !== '>') {
+ rel = model[rel];
+ }
+
+ sko.rel(rel, node, model, function(aboutId) {
+ jQuery(node).attr('aboutId',aboutId);
+ k(registerFn,env);
+ });
+ }
+
+
+ };
+
+ Utils.repeat(0,nodes.length, registerFn, function(env) {
+ cb();
+ });
+};
View
12 src/binding/jsonExpressionRewriting.js
@@ -33,6 +33,10 @@ ko.jsonExpressionRewriting = (function () {
switch (c) {
case '"':
case "'":
+ case "<":
+ tokenStart = position;
+ tokenEndChar = ">";
+ break;
case "/":
tokenStart = position;
tokenEndChar = c;
@@ -107,8 +111,10 @@ ko.jsonExpressionRewriting = (function () {
if (isWriteableValue(value)) {
if (propertyAccessorTokens.length > 0)
propertyAccessorTokens.push(", ");
- if(value[0]==="<" && value[value.length-1]===">") {
+ if(value[0]==="<" && value[value.length-1]===">" && key !== 'about') {
propertyAccessorTokens.push(key + " : function(__ko_value) { __SKO__sc['" + value.slice(1,value.length-1) + "'] = __ko_value; }");
+ } else if(value[0]==="<" && value[value.length-1]===">" && key === 'about') {
+ // nothing here
} else {
propertyAccessorTokens.push(key + " : function(__ko_value) { " + value + " = __ko_value; }");
}
@@ -118,8 +124,10 @@ ko.jsonExpressionRewriting = (function () {
} else {
isFirst = false;
}
- if(value[0]==='<' && value[value.length-1]==='>') {
+ if(value[0]==='<' && value[value.length-1]==='>' && key !== 'about') {
readers = readers+key+": __SKO__sc['"+value.slice(1,value.length-1)+"']";
+ } else if(value[0]==="<" && value[value.length-1]===">" && key === 'about') {
+ readers = readers+key+": '"+value.slice(1,value.length-1)+"'";
} else {
readers = readers+key+": "+value;
}
View
3  test.html
@@ -34,6 +34,7 @@
console.log("*** RESOURCE BUILT");
window['testModel'] = res;
console.log("*** APPLYING BINDINGS");
+ res.testUrl = "http://something.org/hello";
ko.applyBindings(res)
});
@@ -56,7 +57,7 @@
});
</script>
- <p id='theTestP' data-bind="text: <http://test.com/title>"></p>
+ <p id='theTestP' data-bind="text: <http://test.com/title>, about: testUrl"></p>
<!-- <p data-bind="text: testHello"></p> -->
</body>
View
58 test2.html
@@ -0,0 +1,58 @@
+<html>
+<head>
+ <title>test semantic knockout.js</title>
+ <script type='text/javascript' src="./build/output/knockout-latest.debug.js"></script>
+ <script type='text/javascript' src="./deps/rdf_store.js"></script>
+ <script type='text/javascript' src="./deps/sem_ko.js"></script>
+ <script type='text/javascript' src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
+</head>
+<body>
+
+ <script type='text/javascript'>
+ $(document).ready(function(){
+
+ var testData = "INSERT DATA { <http://test.com/a> <http://test.com/title> 'test resource a' ; <http://test.com/count> '1'.\
+ <http://test.com/a> <http://test.com/related> <http://test.com/c>.\
+ <http://test.com/b> <http://test.com/title> 'test resource b' ; <http://test.com/count> '2' . \
+ <http://test.com/b> <http://test.com/related> <http://test.com/d>.\
+ <http://test.com/c> <http://test.com/title> 'test resource c' ; <http://test.com/count> '3' .\
+ <http://test.com/d> <http://test.com/title> 'test resource d' ; <http://test.com/count> '4' . }";
+
+ var test = function() {
+
+ var daModel = {'currentResource': ko.observable("http://test.com/a")};
+ window['daModel'] = daModel;
+
+ sko.traceResources(jQuery("#to-trace")[0],daModel, function(){
+ console.log("*** TRACED RESOURCES!!!");
+ sko.traceRelations(jQuery("#to-trace")[0],daModel, function(){
+ console.log("*** TRACED RELATIONS!!!");
+ ko.applyBindings({});
+
+ window['updateRootResource'] = function(newUri) {
+ sko.about['0'](newUri);
+ };
+ });
+ });
+
+ };
+
+ sko.init(function(){
+ sko.store.execute(testData,
+ function(result, msg){
+ test();
+ });
+ });
+
+ });
+ </script>
+
+ <div id="to-trace" about="currentResource">
+ <p prototype="<http://test.com/title>"></p>
+ <span id='the-related-thing' rel='<http://test.com/related>'>
+ <p prototype="<http://test.com/title>"></p>
+ </span>
+ </div>
+
+</body>
+</html>
Please sign in to comment.
Something went wrong with that request. Please try again.