Commit 21cc578fca7a843b4e7ca548b198c192f4bf529c
- Diff rendering mode:
- inline
- side by side
swtr/server.py
(76 / 4)
  | |||
6 | 6 | import config | |
7 | 7 | import requests | |
8 | 8 | import json | |
9 | import urllib2 | ||
9 | import StringIO | ||
10 | 10 | import imghdr | |
11 | 11 | ||
12 | 12 | app = flask.Flask(__name__) | |
… | … | ||
53 | 53 | @app.route('/annotate', methods=['GET']) | |
54 | 54 | def annotate(): | |
55 | 55 | print flask.request.args['where'] | |
56 | img = urllib2.urlopen(flask.request.args['where']).read() | ||
57 | if imghdr.what('ignore', img) is None: | ||
58 | root = lxml.html.parse(flask.request.args['where']).getroot() | ||
56 | # img = urllib2.urlopen(flask.request.args['where']).read() | ||
57 | request = requests.get(flask.request.args['where']) | ||
58 | content = request.text | ||
59 | if imghdr.what('ignore', content) is None: | ||
60 | root = lxml.html.parse(StringIO.StringIO(content)).getroot() | ||
59 | 61 | root.make_links_absolute(flask.request.args['where'], | |
60 | 62 | resolve_base_href=True) | |
61 | 63 | ||
… | … | ||
80 | 80 | "css/annotator.min.css")) | |
81 | 81 | annotatorCSS.set("rel", "stylesheet") | |
82 | 82 | annotatorCSS.set("type", "text/css") | |
83 | |||
84 | swtmakerCSS = root.makeelement('link') | ||
85 | root.body.append(swtmakerCSS) | ||
86 | swtmakerCSS.set("href", flask.url_for('static', | ||
87 | filename= | ||
88 | "css/swtmaker.css")) | ||
89 | swtmakerCSS.set("rel", "stylesheet") | ||
90 | swtmakerCSS.set("type", "text/css") | ||
91 | |||
92 | bootstrapCSS = root.makeelement('link') | ||
93 | root.body.append(bootstrapCSS) | ||
94 | bootstrapCSS.set("href", flask.url_for('static', | ||
95 | filename= | ||
96 | "css/bootstrap.min.css")) | ||
97 | bootstrapCSS.set("rel", "stylesheet") | ||
98 | bootstrapCSS.set("type", "text/css") | ||
99 | |||
100 | underscoreJS = root.makeelement('script') | ||
101 | root.body.append(underscoreJS) | ||
102 | underscoreJS.set("src", flask.url_for('static', | ||
103 | filename="js/lib/" + | ||
104 | "underscore-1.5.2.min.js")) | ||
105 | underscoreJS.set("type", "text/javascript") | ||
106 | |||
107 | backboneJS = root.makeelement('script') | ||
108 | root.body.append(backboneJS) | ||
109 | backboneJS.set("src", flask.url_for('static', | ||
110 | filename= | ||
111 | "js/lib/backbone-1.0.0.min.js")) | ||
112 | backboneJS.set("type", "text/javascript") | ||
113 | |||
114 | if 'auth_tok' in session: | ||
115 | auth_tok = session['auth_tok'] | ||
116 | else: | ||
117 | auth_tok = {'access_token': '', 'refresh_token': ''} | ||
118 | |||
119 | configScript = root.makeelement('script') | ||
120 | root.body.append(configScript) | ||
121 | configScript.text = """window.swtr = window.swtr || {}; | ||
122 | swtr.swtstoreURL = {} | ||
123 | swtr.endpoints = {} | ||
124 | 'get': '/api/sweets/q', | ||
125 | 'post': '/api/sweets', | ||
126 | 'auth': '/oauth/authorize', | ||
127 | 'login': '/auth/login', | ||
128 | 'logout': '/auth/logout' | ||
129 | {}; | ||
130 | |||
131 | swtr.access_token = '{}'; | ||
132 | swtr.refresh_token = '{}'; | ||
133 | swtr.app_id = '{}';swtr.app_secret = '{}'; | ||
134 | swtr.oauth_redirect_uri = '{}';""".format( | ||
135 | '{}', 'function() {}return "{}"{}'.format('{', | ||
136 | config.swtstoreURL, '};'), | ||
137 | '{', '}', auth_tok['access_token'], auth_tok['refresh_token'], | ||
138 | config.app_id, config.app_secret, | ||
139 | config.redirect_uri) | ||
140 | configScript.set("type", "text/javascript") | ||
141 | |||
142 | # swtmakerScript = root.makeelement('script') | ||
143 | # root.body.append(swtmakerScript) | ||
144 | # swtmakerScript.set("src", flask.url_for('static', | ||
145 | # filename="js/swtmaker.js")) | ||
146 | # swtmakerScript.set("type", "text/javascript") | ||
147 | |||
148 | oAuthScript = root.makeelement('script') | ||
149 | root.body.append(oAuthScript) | ||
150 | oAuthScript.set("src", flask.url_for('static', | ||
151 | filename="js/oauth.js")) | ||
152 | oAuthScript.set("type", "text/javascript") | ||
83 | 153 | ||
84 | 154 | appScript = root.makeelement('script') | |
85 | 155 | root.body.append(appScript) |
swtr/static/js/app.js
(238 / 6)
  | |||
1 | (function() { | ||
2 | $(document).ready(function() { | ||
3 | var annotator = new Annotator(document.body); | ||
4 | annotator.addPlugin("Tags"); | ||
5 | annotator.addPlugin("Filter"); | ||
1 | window.swtr = window.swtr || {}; | ||
2 | (function(swtr) { | ||
3 | |||
4 | swtr.handleOAuth = function() { | ||
5 | if(swtr.access_token) { | ||
6 | $('#signinview').html('Signing you in..'); | ||
7 | $.ajax({ | ||
8 | url: swtr.swtstoreURL()+'/api/users/me?access_token='+ | ||
9 | swtr.access_token, | ||
10 | success: function(data) { | ||
11 | $('#signinview').html('Signed in as ' + data.username); | ||
12 | swtr.who = data.username; | ||
13 | $("#sign-in").hide(); | ||
14 | }, | ||
15 | error: function() { | ||
16 | // $('#signinview').html('Error signing in! Please try again'); | ||
17 | $("#sign-in").show(); | ||
18 | } | ||
19 | }); | ||
20 | } else { | ||
21 | $('#signinview').html('Please sign in.'); | ||
22 | } | ||
23 | }; | ||
24 | |||
25 | var TxtAnnoSwt = Backbone.Model.extend({ | ||
26 | defaults: { | ||
27 | 'who': '', | ||
28 | 'what': 'txt-anno', | ||
29 | 'where': '', | ||
30 | 'how': {} | ||
31 | }, | ||
32 | initialize: function(options) { | ||
33 | this.set(options); | ||
34 | } | ||
6 | 35 | }); | |
7 | })(); | ||
36 | |||
37 | var TxtAnnoSwts = Backbone.Collection.extend({ | ||
38 | model: TxtAnnoSwt, | ||
39 | url: function() { | ||
40 | return swtr.swtstoreURL() + '/sweets'; | ||
41 | }, | ||
42 | // get all sweets/annotations of type #img-anno for a particular URI | ||
43 | // (where) | ||
44 | // @options is a javascript object, | ||
45 | // @options.where : URI of the resource for which swts to be fetched | ||
46 | // @options.who: optional username to filter sweets | ||
47 | // @options.success: success callback to call | ||
48 | // @options.error: error callback to call | ||
49 | getAll: function(options) { | ||
50 | // error checking | ||
51 | if(!options.where) { | ||
52 | throw Error('"where" option must be passed to get sweets of a URI'); | ||
53 | return false; | ||
54 | } | ||
55 | /*if(!swtr.access_token) { | ||
56 | throw new Error('Access Token required to get query that API'); | ||
57 | }*/ | ||
58 | // setting up params | ||
59 | var where = options.where, | ||
60 | who = options.who || null; | ||
61 | url = swtr.swtstoreURL() + swtr.endpoints.get + '?where=' + | ||
62 | encodeURIComponent(where) + '&access_token=' + swtr.access_token; | ||
63 | if(who) { | ||
64 | url += '&who=' + who; | ||
65 | } | ||
66 | // get them! | ||
67 | this.sync('read', this, { | ||
68 | url: url, | ||
69 | success: function() { | ||
70 | if(typeof options.success === 'function') { | ||
71 | options.success.apply(this, arguments); | ||
72 | } | ||
73 | }, | ||
74 | error: function() { | ||
75 | if(typeof options.error === 'function') { | ||
76 | options.error.apply(this, arguments); | ||
77 | } | ||
78 | } | ||
79 | }); | ||
80 | }, | ||
81 | // post newly created sweets to a sweet store | ||
82 | // @options is a javascript object, | ||
83 | // @options.where : URI of the resource for which swts to be fetched | ||
84 | // @options.who: optional username to filter sweets | ||
85 | // @options.success: success callback to call | ||
86 | // @options.error: error callback to call, | ||
87 | post: function(options) { | ||
88 | var new_sweets = this.getNew(); | ||
89 | var dummy_collection = new Backbone.Collection(new_sweets); | ||
90 | |||
91 | if(!swtr.access_token) { | ||
92 | throw new Error('Access Token is required to sweet'); | ||
93 | return; | ||
94 | } | ||
95 | |||
96 | var url = swtr.swtstoreURL() + swtr.endpoints.post + | ||
97 | '?access_token=' + swtr.access_token; | ||
98 | |||
99 | this.sync('create', dummy_collection, { | ||
100 | url: url, | ||
101 | success: function() { | ||
102 | if(typeof options.success === 'function') { | ||
103 | options.success.apply(this, arguments); | ||
104 | } | ||
105 | }, | ||
106 | error: function() { | ||
107 | if(typeof options.error === 'function') { | ||
108 | options.error.apply(this, arguments); | ||
109 | } | ||
110 | } | ||
111 | }); | ||
112 | }, | ||
113 | // return newly created models from the collection | ||
114 | getNew: function() { | ||
115 | var new_models = []; | ||
116 | this.each(function(model) { | ||
117 | if(model.isNew()) { | ||
118 | new_models.push(model); | ||
119 | } | ||
120 | }); | ||
121 | return new_models; | ||
122 | }, | ||
123 | // update part of the collection after a save on the server | ||
124 | update: function() { | ||
125 | } | ||
126 | }); | ||
127 | |||
128 | var TxtAnnoView = Backbone.View.extend({ | ||
129 | initialize: function() { | ||
130 | this.annotator = new Annotator(document.body); | ||
131 | swtr.anno = this.annotator; | ||
132 | this.annotator.addPlugin("Tags"); | ||
133 | this.listenTo(this.collection, "add", this.loadAnno); | ||
134 | this.annotator.subscribe("annotationCreated", this.storeAnno); | ||
135 | }, | ||
136 | storeAnno: function(annotation) { | ||
137 | var pageURL = window.location.search.split("=")[1]; | ||
138 | if(!(swtr.who)) { | ||
139 | swtr.who = "Guest"; | ||
140 | } | ||
141 | swtr.TxtAnnoSwts.add(new TxtAnnoSwt({how: annotation, | ||
142 | where: pageURL, | ||
143 | who: swtr.who})); | ||
144 | if($("#show-sweets").attr('disabled')) { | ||
145 | $("#show-sweets").removeAttr('disabled'); | ||
146 | } | ||
147 | }, | ||
148 | loadAnno: function(annotation) { | ||
149 | if(!(annotation.isNew())) { | ||
150 | swtr.anno.createAnnotation(annotation.get('how')); | ||
151 | swtr.anno.setupAnnotation(annotation.get('how')); | ||
152 | } | ||
153 | } | ||
154 | }); | ||
155 | |||
156 | var AppView = Backbone.View.extend({ | ||
157 | el: document.body, | ||
158 | events: { | ||
159 | "click #show-sweets": 'showSweets', | ||
160 | "click #sweet-cancel": 'cancelSweet', | ||
161 | "click #post-sweet": 'postSweet', | ||
162 | "click #sign-in": 'signIn' | ||
163 | }, | ||
164 | oauth: new Oauth({ | ||
165 | app_id: swtr.app_id, | ||
166 | app_secret: swtr.app_secret, | ||
167 | endpoint: swtr.swtstoreURL() + swtr.endpoints.auth, | ||
168 | redirect_uri: swtr.oauth_redirect_uri, | ||
169 | scopes: 'email,sweet' | ||
170 | }), | ||
171 | sweetTemplate: '<div id="sweet-list-wrapper">'+ | ||
172 | '<h4>These are your sweet annotations!</h4>'+ | ||
173 | '<ul id="sweet-list"></ul>'+ | ||
174 | '<div class="btn-grp">'+ | ||
175 | '<button class="btn btn-default" id="sweet-cancel">Cancel</button>'+ | ||
176 | '<button class="btn btn-primary" id="post-sweet">Sweet</button>'+ | ||
177 | '</div>'+ | ||
178 | '</div>'+ | ||
179 | '</div>', | ||
180 | initialize: function() { | ||
181 | swtr.TxtAnnoSwts = new TxtAnnoSwts(); | ||
182 | var txtAnnoView = new TxtAnnoView({collection: swtr.TxtAnnoSwts}); | ||
183 | swtr.TxtAnnoSwts.getAll({where: window.location.search.split("=")[1], | ||
184 | success: function(data) { | ||
185 | swtr.TxtAnnoSwts.add(data); | ||
186 | }}); | ||
187 | swtr.handleOAuth(); | ||
188 | $(this.el).append(this.sweetTemplate); | ||
189 | this.loadOverlayBar(); | ||
190 | }, | ||
191 | loadOverlayBar: function() { | ||
192 | var template = "<div id='overlayBar' class='navbar' style='background:grey;position:fixed; top:0px; left:0px;width:100%;'><div class='navbar-inner'><div class='container-fluid'>"+ | ||
193 | "<a href='/' >Try a different website.</a>"+ | ||
194 | "<button id='show-sweets' disabled='enabled' class='btn btn-default'>Sweets"+ | ||
195 | "</button><button id='sign-in' class='btn btn-sm btn-primary'>Sign In</button><span id='signinview'></span></div></div></div>"; | ||
196 | $(document.body).append(template); | ||
197 | }, | ||
198 | signIn: function(e) { | ||
199 | this.oauth.authorize(); | ||
200 | swtr.handleOAuth(); | ||
201 | }, | ||
202 | showSweets: function(e) { | ||
203 | $("#sweet-list-wrapper").show(); | ||
204 | swtr.TxtAnnoSwts.each(function(model){ | ||
205 | var templateStr ='<li class="sweet">'+ | ||
206 | '<a href="#">@<%= who %></a> <strong>#<%= what %></strong> '+ | ||
207 | '<a href="<%= where %>"><%= where.substr(0, 30) + "..." %></a> '+ | ||
208 | '<p><%= how %></p>'+ | ||
209 | '</li>'; | ||
210 | var template = _.template(templateStr); | ||
211 | if(model.isNew()) { | ||
212 | $("#sweet-list").append(template({ | ||
213 | 'who': swtr.who, | ||
214 | 'what': 'txt-anno', | ||
215 | 'where': window.location.search.split("=")[1], | ||
216 | 'how': JSON.stringify(model.get('how')) | ||
217 | })); | ||
218 | } | ||
219 | }, this); | ||
220 | $("#sweet-list-wrapper").focus(); | ||
221 | |||
222 | }, | ||
223 | cancelSweet: function(e) { | ||
224 | $("#sweet-list-wrapper").hide(); | ||
225 | $("#sweet-list").html(''); | ||
226 | }, | ||
227 | postSweet: function(e) { | ||
228 | swtr.TxtAnnoSwts.post({success: function(data) { | ||
229 | alert("Your SWeets are posted!!"); | ||
230 | }, | ||
231 | error: function(data) { | ||
232 | alert("Failed to post your SWeets, please try again."); | ||
233 | } | ||
234 | }); | ||
235 | } | ||
236 | }); | ||
237 | new AppView; | ||
238 | |||
239 | })(swtr); |