Skip to content

Instantly share code, notes, and snippets.

@BigBlueHat
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BigBlueHat/f47e6cfda27afccc03be to your computer and use it in GitHub Desktop.
Save BigBlueHat/f47e6cfda27afccc03be to your computer and use it in GitHub Desktop.
Open Annotation RDFa built from Hypothes.is JSON
/**
* @copyright 2014 The Hypothes.is Project
* @license MIT
* @author BigBlueHat <bigbluehat@hypothes.is>
**/
var mustache = require('mustache');
var request = require('superagent');
var style = '<style>\n\
aside {border: 1px solid darkgray;margin: 2em;padding:1em;background: #efefef;}\n\
aside blockquote {border-left: 5px solid #CCC;padding:1em;margin:0 0 1em}\n\
</style>';
var annotation = {};
var template = ' \
<aside vocab="http://www.w3.org/ns/oa#" typeof="Annotation" about="https://hypothes.is/a/{{id}}">\n\
<header>\n\
<a property="annotatedBy" href="https://hypothes.is/u/{{__username}}">{{__username}}</a>\n\
on <a property="hasTarget" typeof="SpecificResource" href="{{{document.link.0.href}}}">\n\
<span property="hasSelector" typeof="TextQuoteSelector" resource="#quote"></span>\n\
{{document.title}}\n\
</a>\n\
</header>\n\
<blockquote id="quote" about="#quote" cite="{{{document.link.0.href}}}"><mark property="exact">{{quote}}</mark></blockquote>\n\
<section property="hasBody">\n\
<p>{{text}}</p>\n\
</section>\n\
</aside>\n\
';
function renderAnnotation() {
var rendered = mustache.to_html(template, annotation);
var div = document.createElement('div');
div.innerHTML = style + rendered;
var textarea = document.createElement('textarea');
textarea.style.width = "100%";
textarea.style.height = "30em";
textarea.value = rendered;
document.body.appendChild(div);
document.body.appendChild(textarea);
}
request
// API URL of an annotation
.get('https://hypothes.is/api/annotations/Gk_TW9d_SyCG5cFH4UCy9A')
.end(function(res) {
// res.body is the parsed JSON response
annotation = res.body;
// this bit splits out the actual username from the user URI
annotation.__username = function() {
return annotation.user.split('@')[0].split(':')[1];
}
// output the rendered HTML+RDFa
renderAnnotation();
});
require=function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({mustache:[function(require,module,exports){(function(root,factory){if(typeof exports==="object"&&exports){factory(exports)}else{var mustache={};factory(mustache);if(typeof define==="function"&&define.amd){define(mustache)}else{root.Mustache=mustache}}})(this,function(mustache){var RegExp_test=RegExp.prototype.test;function testRegExp(re,string){return RegExp_test.call(re,string)}var nonSpaceRe=/\S/;function isWhitespace(string){return!testRegExp(nonSpaceRe,string)}var Object_toString=Object.prototype.toString;var isArray=Array.isArray||function(object){return Object_toString.call(object)==="[object Array]"};function isFunction(object){return typeof object==="function"}function escapeRegExp(string){return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;"};function escapeHtml(string){return String(string).replace(/[&<>"'\/]/g,function(s){return entityMap[s]})}function escapeTags(tags){if(!isArray(tags)||tags.length!==2){throw new Error("Invalid tags: "+tags)}return[new RegExp(escapeRegExp(tags[0])+"\\s*"),new RegExp("\\s*"+escapeRegExp(tags[1]))]}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){tags=tags||mustache.tags;template=template||"";if(typeof tags==="string"){tags=tags.split(spaceRe)}var tagRes=escapeTags(tags);var scanner=new Scanner(template);var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length){delete tokens[spaces.pop()]}}else{spaces=[]}hasTag=false;nonSpace=false}var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(tagRes[0]);if(value){for(var i=0,len=value.length;i<len;++i){chr=value.charAt(i);if(isWhitespace(chr)){spaces.push(tokens.length)}else{nonSpace=true}tokens.push(["text",chr,start,start+1]);start+=1;if(chr==="\n"){stripSpace()}}}if(!scanner.scan(tagRes[0]))break;hasTag=true;type=scanner.scan(tagRe)||"name";scanner.scan(whiteRe);if(type==="="){value=scanner.scanUntil(equalsRe);scanner.scan(equalsRe);scanner.scanUntil(tagRes[1])}else if(type==="{"){value=scanner.scanUntil(new RegExp("\\s*"+escapeRegExp("}"+tags[1])));scanner.scan(curlyRe);scanner.scanUntil(tagRes[1]);type="&"}else{value=scanner.scanUntil(tagRes[1])}if(!scanner.scan(tagRes[1])){throw new Error("Unclosed tag at "+scanner.pos)}token=[type,value,start,scanner.pos];tokens.push(token);if(type==="#"||type==="^"){sections.push(token)}else if(type==="/"){openSection=sections.pop();if(!openSection){throw new Error('Unopened section "'+value+'" at '+start)}if(openSection[1]!==value){throw new Error('Unclosed section "'+openSection[1]+'" at '+start)}}else if(type==="name"||type==="{"||type==="&"){nonSpace=true}else if(type==="="){tagRes=escapeTags(tags=value.split(spaceRe))}}openSection=sections.pop();if(openSection){throw new Error('Unclosed section "'+openSection[1]+'" at '+scanner.pos)}return nestTokens(squashTokens(tokens))}function squashTokens(tokens){var squashedTokens=[];var token,lastToken;for(var i=0,len=tokens.length;i<len;++i){token=tokens[i];if(token){if(token[0]==="text"&&lastToken&&lastToken[0]==="text"){lastToken[1]+=token[1];lastToken[3]=token[3]}else{squashedTokens.push(token);lastToken=token}}}return squashedTokens}function nestTokens(tokens){var nestedTokens=[];var collector=nestedTokens;var sections=[];var token,section;for(var i=0,len=tokens.length;i<len;++i){token=tokens[i];switch(token[0]){case"#":case"^":collector.push(token);sections.push(token);collector=token[4]=[];break;case"/":section=sections.pop();section[5]=token[2];collector=sections.length>0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function(){return this.tail===""};Scanner.prototype.scan=function(re){var match=this.tail.match(re);if(match&&match.index===0){var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string}return""};Scanner.prototype.scanUntil=function(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view==null?{}:view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function(view){return new Context(view,this)};Context.prototype.lookup=function(name){var value;if(name in this.cache){value=this.cache[name]}else{var context=this;while(context){if(name.indexOf(".")>0){value=context.view;var names=name.split("."),i=0;while(value!=null&&i<names.length){value=value[names[i++]]}}else{value=context.view[name]}if(value!=null)break;context=context.parent}this.cache[name]=value}if(isFunction(value)){value=value.call(this.view)}return value};function Writer(){this.cache={}}Writer.prototype.clearCache=function(){this.cache={}};Writer.prototype.parse=function(template,tags){var cache=this.cache;var tokens=cache[template];if(tokens==null){tokens=cache[template]=parseTemplate(template,tags)}return tokens};Writer.prototype.render=function(template,view,partials){var tokens=this.parse(template);var context=view instanceof Context?view:new Context(view);return this.renderTokens(tokens,context,partials,template)};Writer.prototype.renderTokens=function(tokens,context,partials,originalTemplate){var buffer="";var self=this;function subRender(template){return self.render(template,context,partials)}var token,value;for(var i=0,len=tokens.length;i<len;++i){token=tokens[i];switch(token[0]){case"#":value=context.lookup(token[1]);if(!value)continue;if(isArray(value)){for(var j=0,jlen=value.length;j<jlen;++j){buffer+=this.renderTokens(token[4],context.push(value[j]),partials,originalTemplate)}}else if(typeof value==="object"||typeof value==="string"){buffer+=this.renderTokens(token[4],context.push(value),partials,originalTemplate)}else if(isFunction(value)){if(typeof originalTemplate!=="string"){throw new Error("Cannot use higher-order sections without the original template")}value=value.call(context.view,originalTemplate.slice(token[3],token[5]),subRender);if(value!=null)buffer+=value}else{buffer+=this.renderTokens(token[4],context,partials,originalTemplate)}break;case"^":value=context.lookup(token[1]);if(!value||isArray(value)&&value.length===0){buffer+=this.renderTokens(token[4],context,partials,originalTemplate)}break;case">":if(!partials)continue;value=isFunction(partials)?partials(token[1]):partials[token[1]];if(value!=null)buffer+=this.renderTokens(this.parse(value),context,partials,value);break;case"&":value=context.lookup(token[1]);if(value!=null)buffer+=value;break;case"name":value=context.lookup(token[1]);if(value!=null)buffer+=mustache.escape(value);break;case"text":buffer+=token[1];break}}return buffer};mustache.name="mustache.js";mustache.version="0.8.1";mustache.tags=["{{","}}"];var defaultWriter=new Writer;mustache.clearCache=function(){return defaultWriter.clearCache()};mustache.parse=function(template,tags){return defaultWriter.parse(template,tags)};mustache.render=function(template,view,partials){return defaultWriter.render(template,view,partials)};mustache.to_html=function(template,view,partials,send){var result=mustache.render(template,view,partials);if(isFunction(send)){send(result)}else{return result}};mustache.escape=escapeHtml;mustache.Scanner=Scanner;mustache.Context=Context;mustache.Writer=Writer})},{}]},{},[]);require=function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){module.exports=Emitter;function Emitter(obj){if(obj)return mixin(obj)}function mixin(obj){for(var key in Emitter.prototype){obj[key]=Emitter.prototype[key]}return obj}Emitter.prototype.on=Emitter.prototype.addEventListener=function(event,fn){this._callbacks=this._callbacks||{};(this._callbacks[event]=this._callbacks[event]||[]).push(fn);return this};Emitter.prototype.once=function(event,fn){var self=this;this._callbacks=this._callbacks||{};function on(){self.off(event,on);fn.apply(this,arguments)}on.fn=fn;this.on(event,on);return this};Emitter.prototype.off=Emitter.prototype.removeListener=Emitter.prototype.removeAllListeners=Emitter.prototype.removeEventListener=function(event,fn){this._callbacks=this._callbacks||{};if(0==arguments.length){this._callbacks={};return this}var callbacks=this._callbacks[event];if(!callbacks)return this;if(1==arguments.length){delete this._callbacks[event];return this}var cb;for(var i=0;i<callbacks.length;i++){cb=callbacks[i];if(cb===fn||cb.fn===fn){callbacks.splice(i,1);break}}return this};Emitter.prototype.emit=function(event){this._callbacks=this._callbacks||{};var args=[].slice.call(arguments,1),callbacks=this._callbacks[event];if(callbacks){callbacks=callbacks.slice(0);for(var i=0,len=callbacks.length;i<len;++i){callbacks[i].apply(this,args)}}return this};Emitter.prototype.listeners=function(event){this._callbacks=this._callbacks||{};return this._callbacks[event]||[]};Emitter.prototype.hasListeners=function(event){return!!this.listeners(event).length}},{}],2:[function(require,module,exports){module.exports=function(arr,fn,initial){var idx=0;var len=arr.length;var curr=arguments.length==3?initial:arr[idx++];while(idx<len){curr=fn.call(null,curr,arr[idx],++idx,arr)}return curr}},{}],superagent:[function(require,module,exports){var Emitter=require("emitter");var reduce=require("reduce");var root="undefined"==typeof window?this:window;function noop(){}function isHost(obj){var str={}.toString.call(obj);switch(str){case"[object File]":case"[object Blob]":case"[object FormData]":return true;default:return false}}function getXHR(){if(root.XMLHttpRequest&&("file:"!=root.location.protocol||!root.ActiveXObject)){return new XMLHttpRequest}else{try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(e){}}return false}var trim="".trim?function(s){return s.trim()}:function(s){return s.replace(/(^\s*|\s*$)/g,"")};function isObject(obj){return obj===Object(obj)}function serialize(obj){if(!isObject(obj))return obj;var pairs=[];for(var key in obj){if(null!=obj[key]){pairs.push(encodeURIComponent(key)+"="+encodeURIComponent(obj[key]))}}return pairs.join("&")}request.serializeObject=serialize;function parseString(str){var obj={};var pairs=str.split("&");var parts;var pair;for(var i=0,len=pairs.length;i<len;++i){pair=pairs[i];parts=pair.split("=");obj[decodeURIComponent(parts[0])]=decodeURIComponent(parts[1])}return obj}request.parseString=parseString;request.types={html:"text/html",json:"application/json",xml:"application/xml",urlencoded:"application/x-www-form-urlencoded",form:"application/x-www-form-urlencoded","form-data":"application/x-www-form-urlencoded"};request.serialize={"application/x-www-form-urlencoded":serialize,"application/json":JSON.stringify};request.parse={"application/x-www-form-urlencoded":parseString,"application/json":JSON.parse};function parseHeader(str){var lines=str.split(/\r?\n/);var fields={};var index;var line;var field;var val;lines.pop();for(var i=0,len=lines.length;i<len;++i){line=lines[i];index=line.indexOf(":");field=line.slice(0,index).toLowerCase();val=trim(line.slice(index+1));fields[field]=val}return fields}function type(str){return str.split(/ *; */).shift()}function params(str){return reduce(str.split(/ *; */),function(obj,str){var parts=str.split(/ *= */),key=parts.shift(),val=parts.shift();if(key&&val)obj[key]=val;return obj},{})}function Response(req,options){options=options||{};this.req=req;this.xhr=this.req.xhr;this.text=this.xhr.responseText;this.setStatusProperties(this.xhr.status);this.header=this.headers=parseHeader(this.xhr.getAllResponseHeaders());this.header["content-type"]=this.xhr.getResponseHeader("content-type");this.setHeaderProperties(this.header);this.body=this.req.method!="HEAD"?this.parseBody(this.text):null}Response.prototype.get=function(field){return this.header[field.toLowerCase()]};Response.prototype.setHeaderProperties=function(header){var ct=this.header["content-type"]||"";this.type=type(ct);var obj=params(ct);for(var key in obj)this[key]=obj[key]};Response.prototype.parseBody=function(str){var parse=request.parse[this.type];return parse&&str&&str.length?parse(str):null};Response.prototype.setStatusProperties=function(status){var type=status/100|0;this.status=status;this.statusType=type;this.info=1==type;this.ok=2==type;this.clientError=4==type;this.serverError=5==type;this.error=4==type||5==type?this.toError():false;this.accepted=202==status;this.noContent=204==status||1223==status;this.badRequest=400==status;this.unauthorized=401==status;this.notAcceptable=406==status;this.notFound=404==status;this.forbidden=403==status};Response.prototype.toError=function(){var req=this.req;var method=req.method;var url=req.url;var msg="cannot "+method+" "+url+" ("+this.status+")";var err=new Error(msg);err.status=this.status;err.method=method;err.url=url;return err};request.Response=Response;function Request(method,url){var self=this;Emitter.call(this);this._query=this._query||[];this.method=method;this.url=url;this.header={};this._header={};this.on("end",function(){try{var res=new Response(self);if("HEAD"==method)res.text=null;self.callback(null,res)}catch(e){var err=new Error("Parser is unable to parse the response");err.parse=true;err.original=e;self.callback(err)}})}Emitter(Request.prototype);Request.prototype.use=function(fn){fn(this);return this};Request.prototype.timeout=function(ms){this._timeout=ms;return this};Request.prototype.clearTimeout=function(){this._timeout=0;clearTimeout(this._timer);return this};Request.prototype.abort=function(){if(this.aborted)return;this.aborted=true;this.xhr.abort();this.clearTimeout();this.emit("abort");return this};Request.prototype.set=function(field,val){if(isObject(field)){for(var key in field){this.set(key,field[key])}return this}this._header[field.toLowerCase()]=val;this.header[field]=val;return this};Request.prototype.unset=function(field){delete this._header[field.toLowerCase()];delete this.header[field];return this};Request.prototype.getHeader=function(field){return this._header[field.toLowerCase()]};Request.prototype.type=function(type){this.set("Content-Type",request.types[type]||type);return this};Request.prototype.accept=function(type){this.set("Accept",request.types[type]||type);return this};Request.prototype.auth=function(user,pass){var str=btoa(user+":"+pass);this.set("Authorization","Basic "+str);return this};Request.prototype.query=function(val){if("string"!=typeof val)val=serialize(val);if(val)this._query.push(val);return this};Request.prototype.field=function(name,val){if(!this._formData)this._formData=new FormData;this._formData.append(name,val);return this};Request.prototype.attach=function(field,file,filename){if(!this._formData)this._formData=new FormData;this._formData.append(field,file,filename);return this};Request.prototype.send=function(data){var obj=isObject(data);var type=this.getHeader("Content-Type");if(obj&&isObject(this._data)){for(var key in data){this._data[key]=data[key]}}else if("string"==typeof data){if(!type)this.type("form");type=this.getHeader("Content-Type");if("application/x-www-form-urlencoded"==type){this._data=this._data?this._data+"&"+data:data}else{this._data=(this._data||"")+data}}else{this._data=data}if(!obj)return this;if(!type)this.type("json");return this};Request.prototype.callback=function(err,res){var fn=this._callback;if(2==fn.length)return fn(err,res);if(err)return this.emit("error",err);fn(res)};Request.prototype.crossDomainError=function(){var err=new Error("Origin is not allowed by Access-Control-Allow-Origin");err.crossDomain=true;this.callback(err)};Request.prototype.timeoutError=function(){var timeout=this._timeout;var err=new Error("timeout of "+timeout+"ms exceeded");err.timeout=timeout;this.callback(err)};Request.prototype.withCredentials=function(){this._withCredentials=true;return this};Request.prototype.end=function(fn){var self=this;var xhr=this.xhr=getXHR();var query=this._query.join("&");var timeout=this._timeout;var data=this._formData||this._data;this._callback=fn||noop;xhr.onreadystatechange=function(){if(4!=xhr.readyState)return;if(0==xhr.status){if(self.aborted)return self.timeoutError();return self.crossDomainError()}self.emit("end")};if(xhr.upload){xhr.upload.onprogress=function(e){e.percent=e.loaded/e.total*100;self.emit("progress",e)}}if(timeout&&!this._timer){this._timer=setTimeout(function(){self.abort()},timeout)}if(query){query=request.serializeObject(query);this.url+=~this.url.indexOf("?")?"&"+query:"?"+query}xhr.open(this.method,this.url,true);if(this._withCredentials)xhr.withCredentials=true;if("GET"!=this.method&&"HEAD"!=this.method&&"string"!=typeof data&&!isHost(data)){var serialize=request.serialize[this.getHeader("Content-Type")];if(serialize)data=serialize(data)}for(var field in this.header){if(null==this.header[field])continue;xhr.setRequestHeader(field,this.header[field])}this.emit("request",this);xhr.send(data);return this};request.Request=Request;function request(method,url){if("function"==typeof url){return new Request("GET",method).end(url)}if(1==arguments.length){return new Request("GET",method)}return new Request(method,url)}request.get=function(url,data,fn){var req=request("GET",url);if("function"==typeof data)fn=data,data=null;if(data)req.query(data);if(fn)req.end(fn);return req};request.head=function(url,data,fn){var req=request("HEAD",url);if("function"==typeof data)fn=data,data=null;if(data)req.send(data);if(fn)req.end(fn);return req};request.del=function(url,fn){var req=request("DELETE",url);if(fn)req.end(fn);return req};request.patch=function(url,data,fn){var req=request("PATCH",url);if("function"==typeof data)fn=data,data=null;if(data)req.send(data);if(fn)req.end(fn);return req};request.post=function(url,data,fn){var req=request("POST",url);if("function"==typeof data)fn=data,data=null;if(data)req.send(data);if(fn)req.end(fn);return req};request.put=function(url,data,fn){var req=request("PUT",url);if("function"==typeof data)fn=data,data=null;if(data)req.send(data);if(fn)req.end(fn);return req};module.exports=request},{emitter:1,reduce:2}]},{},[]);var mustache=require("mustache");var request=require("superagent");var style="<style>\naside {border: 1px solid darkgray;margin: 2em;padding:1em;background: #efefef;}\naside blockquote {border-left: 5px solid #CCC;padding:1em;margin:0 0 1em}\n</style>";var annotation={};var template=' <aside vocab="http://www.w3.org/ns/oa#" typeof="Annotation" about="https://hypothes.is/a/{{id}}">\n <header>\n <a property="annotatedBy" href="https://hypothes.is/u/{{__username}}">{{__username}}</a>\non <a property="hasTarget" typeof="SpecificResource" href="{{{document.link.0.href}}}">\n <span property="hasSelector" typeof="TextQuoteSelector" resource="#quote"></span>\n {{document.title}}\n </a>\n </header>\n<blockquote id="quote" about="#quote" cite="{{{document.link.0.href}}}"><mark property="exact">{{quote}}</mark></blockquote>\n <section property="hasBody">\n <p>{{text}}</p>\n </section>\n</aside>\n';function renderAnnotation(){var rendered=mustache.to_html(template,annotation);var div=document.createElement("div");div.innerHTML=style+rendered;var textarea=document.createElement("textarea");textarea.style.width="100%";textarea.style.height="30em";textarea.value=rendered;document.body.appendChild(div);document.body.appendChild(textarea)}request.get("https://hypothes.is/api/annotations/Gk_TW9d_SyCG5cFH4UCy9A").end(function(res){annotation=res.body;annotation.__username=function(){return annotation.user.split("@")[0].split(":")[1]};renderAnnotation()});
{
"name": "requirebin-sketch",
"version": "1.0.0",
"dependencies": {
"mustache": "0.8.2",
"superagent": "0.20.0"
}
}
<style type='text/css'>html, body { margin: 0; padding: 0; border: 0; }
body, html { height: 100%; width: 100%; }</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment