From 21cc578fca7a843b4e7ca548b198c192f4bf529c Mon Sep 17 00:00:00 2001 From: Arvind Date: Mon, 7 Jul 2014 17:58:44 +0530 Subject: [PATCH] Integrating annotator with the application. /annotate is the API endpoint for getting a web page with annotator loaded. --- swtr/server.py | 80 +++++++++++++++- swtr/static/js/app.js | 244 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 314 insertions(+), 10 deletions(-) diff --git a/swtr/server.py b/swtr/server.py index 87609bf..f3d257e 100644 --- a/swtr/server.py +++ b/swtr/server.py @@ -6,7 +6,7 @@ import lxml.html import config import requests import json -import urllib2 +import StringIO import imghdr app = flask.Flask(__name__) @@ -53,9 +53,11 @@ def index(): @app.route('/annotate', methods=['GET']) def annotate(): print flask.request.args['where'] - img = urllib2.urlopen(flask.request.args['where']).read() - if imghdr.what('ignore', img) is None: - root = lxml.html.parse(flask.request.args['where']).getroot() + # img = urllib2.urlopen(flask.request.args['where']).read() + request = requests.get(flask.request.args['where']) + content = request.text + if imghdr.what('ignore', content) is None: + root = lxml.html.parse(StringIO.StringIO(content)).getroot() root.make_links_absolute(flask.request.args['where'], resolve_base_href=True) @@ -79,6 +81,76 @@ def annotate(): annotatorCSS.set("rel", "stylesheet") annotatorCSS.set("type", "text/css") + swtmakerCSS = root.makeelement('link') + root.body.append(swtmakerCSS) + swtmakerCSS.set("href", flask.url_for('static', + filename= + "css/swtmaker.css")) + swtmakerCSS.set("rel", "stylesheet") + swtmakerCSS.set("type", "text/css") + + bootstrapCSS = root.makeelement('link') + root.body.append(bootstrapCSS) + bootstrapCSS.set("href", flask.url_for('static', + filename= + "css/bootstrap.min.css")) + bootstrapCSS.set("rel", "stylesheet") + bootstrapCSS.set("type", "text/css") + + underscoreJS = root.makeelement('script') + root.body.append(underscoreJS) + underscoreJS.set("src", flask.url_for('static', + filename="js/lib/" + + "underscore-1.5.2.min.js")) + underscoreJS.set("type", "text/javascript") + + backboneJS = root.makeelement('script') + root.body.append(backboneJS) + backboneJS.set("src", flask.url_for('static', + filename= + "js/lib/backbone-1.0.0.min.js")) + backboneJS.set("type", "text/javascript") + + if 'auth_tok' in session: + auth_tok = session['auth_tok'] + else: + auth_tok = {'access_token': '', 'refresh_token': ''} + + configScript = root.makeelement('script') + root.body.append(configScript) + configScript.text = """window.swtr = window.swtr || {}; + swtr.swtstoreURL = {} + swtr.endpoints = {} + 'get': '/api/sweets/q', + 'post': '/api/sweets', + 'auth': '/oauth/authorize', + 'login': '/auth/login', + 'logout': '/auth/logout' + {}; + + swtr.access_token = '{}'; + swtr.refresh_token = '{}'; + swtr.app_id = '{}';swtr.app_secret = '{}'; + swtr.oauth_redirect_uri = '{}';""".format( + '{}', 'function() {}return "{}"{}'.format('{', + config.swtstoreURL, '};'), + '{', '}', auth_tok['access_token'], auth_tok['refresh_token'], + config.app_id, config.app_secret, + config.redirect_uri) + configScript.set("type", "text/javascript") + + # swtmakerScript = root.makeelement('script') + # root.body.append(swtmakerScript) + # swtmakerScript.set("src", flask.url_for('static', + # filename="js/swtmaker.js")) + # swtmakerScript.set("type", "text/javascript") + + oAuthScript = root.makeelement('script') + root.body.append(oAuthScript) + oAuthScript.set("src", flask.url_for('static', + filename="js/oauth.js")) + oAuthScript.set("type", "text/javascript") + appScript = root.makeelement('script') root.body.append(appScript) appScript.set("src", flask.url_for('static', diff --git a/swtr/static/js/app.js b/swtr/static/js/app.js index 0a5f5a5..68d195c 100644 --- a/swtr/static/js/app.js +++ b/swtr/static/js/app.js @@ -1,7 +1,239 @@ -(function() { - $(document).ready(function() { - var annotator = new Annotator(document.body); - annotator.addPlugin("Tags"); - annotator.addPlugin("Filter"); +window.swtr = window.swtr || {}; +(function(swtr) { + + swtr.handleOAuth = function() { + if(swtr.access_token) { + $('#signinview').html('Signing you in..'); + $.ajax({ + url: swtr.swtstoreURL()+'/api/users/me?access_token='+ + swtr.access_token, + success: function(data) { + $('#signinview').html('Signed in as ' + data.username); + swtr.who = data.username; + $("#sign-in").hide(); + }, + error: function() { + // $('#signinview').html('Error signing in! Please try again'); + $("#sign-in").show(); + } + }); + } else { + $('#signinview').html('Please sign in.'); + } + }; + + var TxtAnnoSwt = Backbone.Model.extend({ + defaults: { + 'who': '', + 'what': 'txt-anno', + 'where': '', + 'how': {} + }, + initialize: function(options) { + this.set(options); + } }); -})(); + + var TxtAnnoSwts = Backbone.Collection.extend({ + model: TxtAnnoSwt, + url: function() { + return swtr.swtstoreURL() + '/sweets'; + }, + // get all sweets/annotations of type #img-anno for a particular URI + // (where) + // @options is a javascript object, + // @options.where : URI of the resource for which swts to be fetched + // @options.who: optional username to filter sweets + // @options.success: success callback to call + // @options.error: error callback to call + getAll: function(options) { + // error checking + if(!options.where) { + throw Error('"where" option must be passed to get sweets of a URI'); + return false; + } + /*if(!swtr.access_token) { + throw new Error('Access Token required to get query that API'); + }*/ + // setting up params + var where = options.where, + who = options.who || null; + url = swtr.swtstoreURL() + swtr.endpoints.get + '?where=' + + encodeURIComponent(where) + '&access_token=' + swtr.access_token; + if(who) { + url += '&who=' + who; + } + // get them! + this.sync('read', this, { + url: url, + success: function() { + if(typeof options.success === 'function') { + options.success.apply(this, arguments); + } + }, + error: function() { + if(typeof options.error === 'function') { + options.error.apply(this, arguments); + } + } + }); + }, + // post newly created sweets to a sweet store + // @options is a javascript object, + // @options.where : URI of the resource for which swts to be fetched + // @options.who: optional username to filter sweets + // @options.success: success callback to call + // @options.error: error callback to call, + post: function(options) { + var new_sweets = this.getNew(); + var dummy_collection = new Backbone.Collection(new_sweets); + + if(!swtr.access_token) { + throw new Error('Access Token is required to sweet'); + return; + } + + var url = swtr.swtstoreURL() + swtr.endpoints.post + + '?access_token=' + swtr.access_token; + + this.sync('create', dummy_collection, { + url: url, + success: function() { + if(typeof options.success === 'function') { + options.success.apply(this, arguments); + } + }, + error: function() { + if(typeof options.error === 'function') { + options.error.apply(this, arguments); + } + } + }); + }, + // return newly created models from the collection + getNew: function() { + var new_models = []; + this.each(function(model) { + if(model.isNew()) { + new_models.push(model); + } + }); + return new_models; + }, + // update part of the collection after a save on the server + update: function() { + } + }); + + var TxtAnnoView = Backbone.View.extend({ + initialize: function() { + this.annotator = new Annotator(document.body); + swtr.anno = this.annotator; + this.annotator.addPlugin("Tags"); + this.listenTo(this.collection, "add", this.loadAnno); + this.annotator.subscribe("annotationCreated", this.storeAnno); + }, + storeAnno: function(annotation) { + var pageURL = window.location.search.split("=")[1]; + if(!(swtr.who)) { + swtr.who = "Guest"; + } + swtr.TxtAnnoSwts.add(new TxtAnnoSwt({how: annotation, + where: pageURL, + who: swtr.who})); + if($("#show-sweets").attr('disabled')) { + $("#show-sweets").removeAttr('disabled'); + } + }, + loadAnno: function(annotation) { + if(!(annotation.isNew())) { + swtr.anno.createAnnotation(annotation.get('how')); + swtr.anno.setupAnnotation(annotation.get('how')); + } + } + }); + + var AppView = Backbone.View.extend({ + el: document.body, + events: { + "click #show-sweets": 'showSweets', + "click #sweet-cancel": 'cancelSweet', + "click #post-sweet": 'postSweet', + "click #sign-in": 'signIn' + }, + oauth: new Oauth({ + app_id: swtr.app_id, + app_secret: swtr.app_secret, + endpoint: swtr.swtstoreURL() + swtr.endpoints.auth, + redirect_uri: swtr.oauth_redirect_uri, + scopes: 'email,sweet' + }), + sweetTemplate: '
'+ + '

These are your sweet annotations!

'+ + ''+ + '
'+ + ''+ + ''+ + '
'+ + '
'+ + '', + initialize: function() { + swtr.TxtAnnoSwts = new TxtAnnoSwts(); + var txtAnnoView = new TxtAnnoView({collection: swtr.TxtAnnoSwts}); + swtr.TxtAnnoSwts.getAll({where: window.location.search.split("=")[1], + success: function(data) { + swtr.TxtAnnoSwts.add(data); + }}); + swtr.handleOAuth(); + $(this.el).append(this.sweetTemplate); + this.loadOverlayBar(); + }, + loadOverlayBar: function() { + var template = ""; + $(document.body).append(template); + }, + signIn: function(e) { + this.oauth.authorize(); + swtr.handleOAuth(); + }, + showSweets: function(e) { + $("#sweet-list-wrapper").show(); + swtr.TxtAnnoSwts.each(function(model){ + var templateStr ='
  • '+ + '@<%= who %> #<%= what %> '+ + '<%= where.substr(0, 30) + "..." %> '+ + '

    <%= how %>

    '+ + '
  • '; + var template = _.template(templateStr); + if(model.isNew()) { + $("#sweet-list").append(template({ + 'who': swtr.who, + 'what': 'txt-anno', + 'where': window.location.search.split("=")[1], + 'how': JSON.stringify(model.get('how')) + })); + } + }, this); + $("#sweet-list-wrapper").focus(); + + }, + cancelSweet: function(e) { + $("#sweet-list-wrapper").hide(); + $("#sweet-list").html(''); + }, + postSweet: function(e) { + swtr.TxtAnnoSwts.post({success: function(data) { + alert("Your SWeets are posted!!"); + }, + error: function(data) { + alert("Failed to post your SWeets, please try again."); + } + }); + } + }); + new AppView; + +})(swtr); -- 1.7.10.4