# HG changeset patch # User Drew Perttula # Date 1373528755 25200 # Node ID 8008ec2fd763434a387cc358a3b8b7ce0ed04270 # Parent a8887fb936765882f0ffd82484772e21df22a236 fix up link page reloading. tried davisjs; may not need it Ignore-this: d0021609f019f0734e779a61e3e73b62 diff -r a8887fb93676 -r 8008ec2fd763 link.py --- 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""" diff -r a8887fb93676 -r 8008ec2fd763 lookup.py --- 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('//') def userSlash(user): bottle.redirect("/%s" % urllib.quote(user)) + +@bottle.route('/.json', method='GET') +def userAllJson(user): + data = recentLinks(user, []) + data['toRoot'] = "." + return json.dumps(data) @bottle.route('/', method='GET') def userAll(user): return userLinks(user, "", toRoot=".") - + + @bottle.route('/', 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('//.json') +def userLinksJson(user, tags): + tags = parseTags(tags) + data = recentLinks(user, tags) + data['toRoot'] = ".." + return json.dumps(data) + @bottle.route('//') 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(): diff -r a8887fb93676 -r 8008ec2fd763 static/add.js --- 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) { diff -r a8887fb93676 -r 8008ec2fd763 static/links.js --- 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); diff -r a8887fb93676 -r 8008ec2fd763 template/linklist.jade.mustache --- /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 diff -r a8887fb93676 -r 8008ec2fd763 template/links.jade.mustache --- 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}} diff -r a8887fb93676 -r 8008ec2fd763 template/tail.jade.mustache --- 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")