changeset 21:8008ec2fd763

fix up link page reloading. tried davisjs; may not need it Ignore-this: d0021609f019f0734e779a61e3e73b62
author Drew Perttula <drewp@bigasterisk.com>
date Thu, 11 Jul 2013 00:45:55 -0700
parents a8887fb93676
children fa55f4439977
files link.py lookup.py static/add.js static/links.js template/linklist.jade.mustache template/links.jade.mustache template/tail.jade.mustache
diffstat 7 files changed, 208 insertions(+), 114 deletions(-) [+]
line wrap: on
line diff
--- a/link.py	Sun Mar 17 01:03:43 2013 -0700
+++ b/link.py	Thu Jul 11 00:45:55 2013 -0700
@@ -32,6 +32,9 @@
             raise ValueError("%s docs found for href %s" % (len(docs), uri))
         else:
             return docs[0]
+
+    def filter(self, user, startTime):
+        return self.coll.find({'user' : user, 't': {'$gte': startTime}})
             
     def forDisplay(self, doc):
         """return a mustache-ready dict for this db doc"""
--- a/lookup.py	Sun Mar 17 01:03:43 2013 -0700
+++ b/lookup.py	Thu Jul 11 00:45:55 2013 -0700
@@ -37,7 +37,7 @@
 def server_static(path):
     return static_file(path, root='static')
 
-def recentTags(user, tags=None):
+def recentLinks(user, tags=None):
     out = {'links':[]}
     t1 = time.time()
     spec = {'user':user}
@@ -125,11 +125,18 @@
 @bottle.route('/<user>/')
 def userSlash(user):
     bottle.redirect("/%s" % urllib.quote(user))
+
+@bottle.route('/<user>.json', method='GET')
+def userAllJson(user):
+    data = recentLinks(user, [])
+    data['toRoot'] = "."
+    return json.dumps(data)
     
 @bottle.route('/<user>', method='GET')
 def userAll(user):
     return userLinks(user, "", toRoot=".")
-
+    
+   
 @bottle.route('/<user>', method='POST')
 def userAddLink(user):
     if getUser()[0] != user:
@@ -143,11 +150,23 @@
     print "notify about sharing to", repr(doc['shareWith'])
         
     bottle.redirect(siteRoot + '/' + user)
+
+def parseTags(tagComponent):
+    # the %20 is coming from davis.js, not me :(
+    return filter(None, tagComponent.replace("%20", "+").split('+'))
+    
+@bottle.route('/<user>/<tags:re:.*>.json')
+def userLinksJson(user, tags):
+    tags = parseTags(tags)
+    data = recentLinks(user, tags)
+    data['toRoot'] = ".."
+    return json.dumps(data)
+
     
 @bottle.route('/<user>/<tags>')
 def userLinks(user, tags, toRoot=".."):
-    tags = filter(None, tags.split('+'))
-    data = recentTags(user, tags)
+    tags = parseTags(tags)
+    data = recentLinks(user, tags)
     data['loginBar'] = getLoginBar()
     data['desc'] = ("%s's recent links" % user) + (" tagged %s"  % (tags,) if tags else "")
     data['toRoot'] = toRoot
@@ -158,6 +177,10 @@
     data['pageTags'] = [{"word":t} for t in tags]
     data['stats']['template'] = 'TEMPLATETIME'
     return renderWithTime('links.jade', data)
+
+@bottle.route('/templates')
+def templates():
+    return json.dumps({'linklist': renderer.load_template("linklist.jade")})
     
 @bottle.route('/')
 def root():
--- a/static/add.js	Sun Mar 17 01:03:43 2013 -0700
+++ b/static/add.js	Thu Jul 11 00:45:55 2013 -0700
@@ -69,10 +69,24 @@
             }
             console.log("from model", uris)
 
-            inputElem.select2("data", uris.map(
-                function (uri) {
-                    return {id: uri, text: "("+uri+")"};
-                }));
+            async.map(uris,
+                      function (uri, cb) {
+                          $.ajax({
+                              url: uri.replace(/^http:/, "https:"),
+                              dataType: "text",
+                              success: function (page) {
+                                  pp = page
+                                  d = $(page).rdfa().databank;
+                                  console.log("from", uri, "extracted", d.size(), "triples");
+                                  "trying to get the rdfa out of this page to attempt prettier label/icon for the listed person"
+                                  console.log(JSON.stringify(d.dump()));
+                              }
+                          });
+                          cb(null, {id: uri, text: "("+uri+")"});
+                      },
+                      function (err, selections) {
+                          inputElem.select2("data", selections);
+                      });
         });
 
     function setModelFromShares(n) {
--- a/static/links.js	Sun Mar 17 01:03:43 2013 -0700
+++ b/static/links.js	Thu Jul 11 00:45:55 2013 -0700
@@ -1,100 +1,147 @@
-
 $("#filterTag").focus();
 
 var model = {
-    filterTags: ko.observableArray(currentFilter())
+    filterTags: ko.observableArray(tagsFromWindowLocation())
 };
 
-function currentFilter() {
+function tagsFromWindowLocation() {
     var p = window.location.pathname;
     var comps = p.split("/");
     if (toRoot == ".") {
         return [];
     } else {
-        return (comps[comps.length-1] || "").split("+");
+        return (comps[comps.length-1] || "").replace("%20", "+").split("+");
     }
 }
 
 function toggleTag(tag) {
-    var selected = currentFilter();
+    var selected = model.filterTags();
 
     if (selected.indexOf(tag) == -1) {
         selected.push(tag);
     } else {
         selected.splice(selected.indexOf(tag), 1);
     }
-    setPageTags(selected);
-}
-
-function setPageTags(tags) {
-    
-    var newPath = window.location.pathname;
-    if (toRoot == ".") {
-        newPath += "/";
-    } else {
-        newPath = newPath.replace(
-                /(.*\/)[^\/]*$/, "$1")
-    }
-    console.log("user root", newPath);
-    if (tags.length) {
-        newPath += tags.join("+")
-    } else {
-        newPath = newPath.substr(0, newPath.length - 1);
-    }
-    console.log("done", newPath);
-    
-    window.location.pathname = newPath;
-}
-
-function backspaceLastTag() {
-    var p = window.location.pathname;
-    var comps = p.split("/");
-    var selected = (comps[comps.length-1] || "").split("+");
-    if (selected.length == 0) {
-        return;
-    }
-    toggleTag(selected[selected.length-1]);
+    model.filterTags(selected);
 }
 
-$("a.tag").click(function () {
-    var tag = $(this).text();
-    toggleTag(tag);
-    return false;
-});
-
-$("#filterTag").change(function () {
-    var tags = $(this).val().split(",");
-    setPageTags(tags);
-    return false;
+function initSpecialLinkBehavior() {
+    // clicking tag links doesn't go to them (they're not
+    // user-specific); it toggles their presence in our page's current
+    // filter list   
+    $(document).on("click", "a.tag", function (ev) {
+        var tag = $(this).text();
+        toggleTag(tag);
+        ev.stopPropagation()
+        ev.preventDefault()
+        return false;
+    });
+}
+            
+var linklist = null;
+// unsure how to use toRoot- can it change?
+$.getJSON("/href/templates", function (result) {
+    linklist = result.linklist;
 });
 
-var filterCompleteWords = "";
-$("#filterTag").select2({
-    allowClear: true,
-    multiple: true,
-    tokenSeparators: [' ', ','],
-    query: function (opts) {
-        $.ajax({
-            url: toRoot + "/tags",
-            data: {user: user, have: opts.element.val()},
-            success: function (data) {
-                opts.callback({results: data.tags});
+function initUrlSync(model) {
+    // tag changes push url history; and url edits freshen the page
+
+    ko.computed(function () {
+        var tags = model.filterTags();
+        var newPath = window.location.pathname;
+        console.log("currently", newPath, toRoot);
+        if (toRoot == ".") {
+            newPath += "/";
+            toRoot = "..";
+        } else {
+            newPath = newPath.replace(
+                    /(.*\/)[^\/]*$/, "$1")
+        }
+        console.log("user root", newPath);
+        if (tags.length) {
+            newPath += tags.join("+")
+        } else {
+            newPath = newPath.substr(0, newPath.length - 1);
+            toRoot = ".";
+        }
+        
+        changePage(newPath);
+    });
+
+    function changePage(newPath) {
+        console.log("changePage", newPath);
+        if (window.location.pathname != newPath) {
+            window.history.pushState({}, "", newPath);
+
+            function updateLinklist(fullPath) {
+                var t0 = +new Date();
+                if (linklist === null) {
+                    console.log("too soon- templates aren't loaded");
+                    return;
+                }
+                $(".linklist").text("Loading...");
+                $.getJSON(fullPath + ".json", function (result) {
+                    var t1 = +new Date();
+                    var rendered = Mustache.render(linklist, result)
+                    var t2 = +new Date();
+                    $(".linklist").html(rendered);
+                    var t3 = +new Date();
+                    $(".stats").text(JSON.stringify({
+                        "getMs": t1 - t0,
+                        "mustacheRenderMs": t2 - t1,
+                        "setHtmlMs": t3 - t2
+                    }));
+                });
             }
-        });
-    },
-    change: function (ev) {
-        console.log("ch", ev.val);
-    },
-    initSelection: function (element, callback) {
-        var data = [];
-        $(element.val().split(",")).each(function () {
-            if (this != "") {
-                data.push({id: this, text: this});
-            }
-        });
-        callback(data);
-    }
-});
-$("#filterTag").select2("val", model.filterTags());
+            updateLinklist(newPath);
+        }    
+    }   
+}
+
+function initFilterTag(elem, model) {
+    // sync the entry box and the model
+    
+    elem.change(function () {
+        var tags = $(this).val().split(",");
+        model.filterTags(tags);
+        return false;
+    });
+
+    var filterCompleteWords = "";
+    elem.select2({
+        allowClear: true,
+        multiple: true,
+        tokenSeparators: [' ', ','],
+        query: function (opts) {
+            $.ajax({
+                url: toRoot + "/tags",
+                data: {user: user, have: opts.element.val()},
+                success: function (data) {
+                    opts.callback({results: data.tags});
+                }
+            });
+        },
+        change: function (ev) {
+            console.log("ch", ev.val);
+        },
+        initSelection: function (element, callback) {
+            var data = [];
+            $(element.val().split(",")).each(function () {
+                if (this != "") {
+                    data.push({id: this, text: this});
+                }
+            });
+            callback(data);
+        }
+    });
+    ko.computed(function () {
+        elem.select2("val", model.filterTags());
+    });
+}
+
+initFilterTag($("#filterTag"), model);
+initSpecialLinkBehavior();
+initUrlSync(model);
 
 ko.applyBindings(model);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/template/linklist.jade.mustache	Thu Jul 11 00:45:55 2013 -0700
@@ -0,0 +1,31 @@
+{{#links}}
+.link
+  h3
+    //span.favicon(style="background-image: url(//www.google.com/s2/favicons?domain={{domain}})")
+    a(href="{{href}}") {{displayDescription}}
+  {{#extended}}
+  .notes
+    | {{extended}}
+  {{/extended}}
+  .tags
+    | Tags:
+    {{#tagWords}}
+    a.tag(href="{{toRoot}}/tag/{{word}}", title="add/remove this tag from the current filter") {{word}}
+    | 
+    {{/tagWords}}
+  .hrefShown
+    a(href="{{href}}") {{href}}
+  .modified
+    | Modified: {{t}}
+  {{#showPrivateData}}
+  .shareWith
+    | Share with:
+    {{#shareWith}}
+    span.person
+      | {{label}}
+    {{/shareWith}}
+  {{/showPrivateData}}
+  .edit
+    a(href="{{toRoot}}/{{editLink}}") Edit
+    
+{{/links}}
\ No newline at end of file
--- a/template/links.jade.mustache	Sun Mar 17 01:03:43 2013 -0700
+++ b/template/links.jade.mustache	Thu Jul 11 00:45:55 2013 -0700
@@ -17,40 +17,11 @@
     
     | Filter to: 
     input#filterTag(type='hidden', style="width: 300px")
-    
-    {{#links}}
-    .link
-      h3
-        span.favicon(style="background-image: url(//www.google.com/s2/favicons?domain={{domain}})")
-        a(href="{{href}}") {{displayDescription}}
-      {{#extended}}
-      .notes
-        | {{extended}}
-      {{/extended}}
-      .tags
-        | Tags:
-        {{#tagWords}}
-        a.tag(href="{{toRoot}}/tag/{{word}}", title="add/remove this tag from the current filter") {{word}}
-        | 
-        {{/tagWords}}
-      .hrefShown
-        a(href="{{href}}") {{href}}
-      .modified
-        | Modified: {{t}}
-      {{#showPrivateData}}
-      .shareWith
-        | Share with:
-        {{#shareWith}}
-        span.person
-          | {{label}}
-        {{/shareWith}}
-      {{/showPrivateData}}
-      .edit
-        a(href="{{toRoot}}/{{editLink}}") Edit
-        
-    {{/links}}
 
-    p {{stats}}
+    .linklist
+      {{> linklist.jade}}
+
+    p.stats {{stats}}
     script
       var toRoot = "{{toRoot}}", user = "{{user}}";
     {{> tail.jade}}
--- a/template/tail.jade.mustache	Sun Mar 17 01:03:43 2013 -0700
+++ b/template/tail.jade.mustache	Thu Jul 11 00:45:55 2013 -0700
@@ -1,4 +1,9 @@
 script(src='{{toRoot}}/static/lib/jquery-1.9.1.min.js')
 script(src='{{toRoot}}/static/lib/select2/select2.js')
 script(src="{{toRoot}}/static/lib/knockout-2.2.1.js")
+script(src="{{toRoot}}/static/lib/async-2013-03-17.js")
+script(src="{{toRoot}}/static/lib/jquery.rdfquery.rdfa.min-1.0.js")
+script(src="{{toRoot}}/static/lib/RDFa.min.0.21.0.js")
+script(src="{{toRoot}}/static/lib/RDFa-from-jsonld.js")
+script(src="{{toRoot}}/static/lib/mustache.js")
 script(src="{{toRoot}}/static/gui.js")