Commit 0bce307eabd7924858e0e39293a5fb180ad14885

Fix UI issues

  - Fix the HTML part to fix some UI issues
  - Better handling of error/edge cases
  - Refactor some code
  • swtr/static/css/swtmaker.css 7 ---++++
  • swtr/static/js/swtmaker.js 241 --------------------------------------------------------------------------------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • swtr/templates/index.html 110 ----------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • Diff rendering mode:
  • inline
  • side by side

swtr/static/css/swtmaker.css

39#helpview {39#helpview {
40 font-weight: bold;40 font-weight: bold;
41 font-size: 1.3em;41 font-size: 1.3em;
42 min-height: 60px;
42 min-height: 50px;
43 text-align: center;43 text-align: center;
44}44}
45#app-overlay {45#app-overlay {
46 position: absolute;46 position: absolute;
47 top: 0px;
47 top: 60px;
48 left: 0;
48 display: none;49 display: none;
49 width: 100%;50 width: 100%;
50 height: 100%;
51 height: 800px;
51 z-index: 10000;52 z-index: 10000;
52 background-color: rgba(255, 255, 255, 0.7);53 background-color: rgba(255, 255, 255, 0.7);
53}54}

swtr/static/js/swtmaker.js

26 url: swtr.swtstoreURL()+'/api/users/me?access_token='+26 url: swtr.swtstoreURL()+'/api/users/me?access_token='+
27 swtr.access_token,27 swtr.access_token,
28 success: function(data) {28 success: function(data) {
29 console.log(data.username);
30 swtr.appView.userLoggedIn(data.username);29 swtr.appView.userLoggedIn(data.username);
31 },30 },
32 error: function() {31 error: function() {
44 'how': {}44 'how': {}
45 },45 },
46 initialize: function() {46 initialize: function() {
47 if(!_.has(this, 'id')) {
48 // bad hack to have dates.. FIXIT
49 this.set('created', new Date().toUTCString().substr(0, 25));
50 }
51 }47 }
52 });48 });
5349
66 throw Error('"where" option must be passed to get sweets of a URI');66 throw Error('"where" option must be passed to get sweets of a URI');
67 return false;67 return false;
68 }68 }
69 if(!swtr.access_token) {
69 /*if(!swtr.access_token) {
70 throw new Error('Access Token required to get query that API');70 throw new Error('Access Token required to get query that API');
71 }
71 }*/
72 // setting up params72 // setting up params
73 var where = options.where,73 var where = options.where,
74 who = options.who || null;74 who = options.who || null;
132 new_models.push(model);132 new_models.push(model);
133 }133 }
134 });134 });
135 return new_models;
135 return new_models;
136 },136 },
137 // update part of the collection after a save on the server137 // update part of the collection after a save on the server
138 update: function() {138 update: function() {
157 $('#sweet-list').append(this.template({157 $('#sweet-list').append(this.template({
158 who: swt.get('who'),158 who: swt.get('who'),
159 what: swt.get('what'),159 what: swt.get('what'),
160 where: swt.get('where'),
160 where: swt.get('where'),
161 how: JSON.stringify(swt.get('how').text)161 how: JSON.stringify(swt.get('how').text)
162 }));162 }));
163 }, this);163 }, this);
174 this.collection.remove(notPosted);174 this.collection.remove(notPosted);
175 },175 },
176 postSweets: function() {176 postSweets: function() {
177 swtr.appView.helpview.step(5);
178 swtr.appView.$overlay.show();
179 this.collection.post({
180 success: function(collection, response) {
181 console.log(collection, response);
182 swtr.sweets.update(collection);
183 //TODO: move this to a annotation view or something
184 anno.removeAll();
185 _.each(swtr.sweets.models, function(swt) {
186 if(!_.has(swt.get('how'), 'editable')) {
187 swt.get('how')['editable'] = false;
188 swt.get('how').text += '\n - by ' + swt.get('who');
189 }
190 console.log(swt.get('how'));
191 anno.addAnnotation(swt.get('how'));
192 });
193 //console.log(swtr.sweets.toJSON());
194 swtr.appView.$overlay.hide();
195 swtr.appView.helpview.step(6);
196 },
197 error: function(jqxhr, error, text) {
198 console.log(jqxhr, error, text);
177 var appView = swtr.appView;
178 appView.helpview.step(5);
179 appView.$overlay.show();
180 try {
181 this.collection.post({
182 success: function(collection, response) {
183 console.log(collection, response);
184 swtr.sweets.update(collection);
185 //TODO: move this to a annotation view or something
186 anno.removeAll();
187 _.each(swtr.sweets.models, function(swt) {
188 if(!_.has(swt.get('how'), 'editable')) {
189 swt.get('how')['editable'] = false;
190 swt.get('how').text += '\n - by ' + swt.get('who');
191 }
192 console.log(swt.get('how'));
193 anno.addAnnotation(swt.get('how'));
194 });
195 //console.log(swtr.sweets.toJSON());
196 appView.overlay.hide();
197 appView.helpview.step(6);
198 },
199 error: function(jqxhr, error, text) {
200 console.log(jqxhr, error, text);
201 }
202 });
203 } catch(e) {
204 if(e.message == 'Access Token is required to sweet') {
205 appView.$overlay.hide();
206 appView.helpview.step(9);
199 }207 }
200 });
208 }
201 this.cleanUp();209 this.cleanUp();
202 return false;210 return false;
203 },211 },
221 'click #img-url-load': 'setImage',221 'click #img-url-load': 'setImage',
222 'click #img-url-submit': 'setImage',222 'click #img-url-submit': 'setImage',
223 'click #sweet': 'sweet',223 'click #sweet': 'sweet',
224 'click #signin-credentials': 'getSignInCredentials',
224 'click #sign-in': 'signIn',
225 'click #setbox': 'showHide',225 'click #setbox': 'showHide',
226 'change .form-control': 'button_custom',226 'change .form-control': 'button_custom',
227 'mouseup .annotorious-editor-button-save': 'add_new_anno'227 'mouseup .annotorious-editor-button-save': 'add_new_anno'
228 },228 },
229 initialize: function() {229 initialize: function() {
230 //var allElements = $('body *');
230 // initialize components
231 this.helpview = new HelpView();231 this.helpview = new HelpView();
232 this.sweetsview = new SweetsView({collection: swtr.sweets});232 this.sweetsview = new SweetsView({collection: swtr.sweets});
233
234 //register handlers for annotorious events
233 anno.addHandler('onAnnotationCreated', this.showSwtHelp);235 anno.addHandler('onAnnotationCreated', this.showSwtHelp);
234 anno.addHandler('onannotationupdated', this.showswthelp);
236 anno.addHandler('onAnnotationUpdated', this.showSwtHelp);
235 anno.addHandler('onSelectionStarted', function(annotation) {237 anno.addHandler('onSelectionStarted', function(annotation) {
236 anno.hideAnnotations();});238 anno.hideAnnotations();});
237 anno.addHandler('onSelectionCompleted', function(annotation) {239 anno.addHandler('onSelectionCompleted', function(annotation) {
238 anno.showAnnotations();240 anno.showAnnotations();
239 });241 });
240 anno.addPlugin('CustomFields', this.showSwtHelp);
241 anno.addHandler('onSelectionCompleted', this.setShape);
242 anno.addPlugin('CustomFields', {});
243 anno.addHandler('onSelectionCompleted', this.setShape);
244
245 // cache jquery selected elements which are used frequently
242 this.$overlay = $('#app-overlay');246 this.$overlay = $('#app-overlay');
243 this.$img = $('#annotatable-img');247 this.$img = $('#annotatable-img');
248
249 // attach a load event handler, whenever an image is loaded..
250 this.$img.on('load', this, this.imageLoaded);
251 this.$img.on('error', this, this.onImageLoadError);
252
253 // check if already an image is provided at load time..
244 this.imgURL = this.$img.attr('src');254 this.imgURL = this.$img.attr('src');
245 if(this.imgURL) {255 if(this.imgURL) {
246 this.initImageAnno();256 this.initImageAnno();
260 this.helpview.step(1);260 this.helpview.step(1);
261 }261 }
262262
263 // initialize the oauth stuff
263 this.oauth = new Oauth({264 this.oauth = new Oauth({
264 app_id: swtr.app_id,265 app_id: swtr.app_id,
265 app_secret: swtr.app_secret,266 app_secret: swtr.app_secret,
269 scopes: 'email,sweet'269 scopes: 'email,sweet'
270 });270 });
271 },271 },
272
273 setImage: function() {
274 anno.reset();
272 setImage: function(event) {
273 event.preventDefault();
275 this.imgURL = $('#img-url-input').val();274 this.imgURL = $('#img-url-input').val();
275 if(!this.imgURL) {
276 return false;
277 }
278 anno.reset();
279 var self = this;
276 this.$overlay.show();280 this.$overlay.show();
277 this.helpview.step(7);281 this.helpview.step(7);
278 this.$img.attr('onload', function() {
279 swtr.appView.$overlay.hide();
280 });
281 this.$img.attr('src', this.imgURL);282 this.$img.attr('src', this.imgURL);
282 this.initImageAnno();
283 return false;283 return false;
284 },284 },
285 imageLoaded: function(event) {
286 var self = event.data;
287 console.log('image loaded');
288 self.$overlay.hide();
289 self.initImageAnno();
290 },
291 // when image fails to load - could be because of broken URL or network
292 // issues
293 onImageLoadError: function(event) {
294 var self = event.data;
295 console.log('error while loading image');
296 self.$overlay.hide();
297 self.helpview.step(8);
298 },
285 initImageAnno: function() {299 initImageAnno: function() {
286 // img is a jquery object which annotorious doesn't accept; instead it300 // img is a jquery object which annotorious doesn't accept; instead it
287 // takes the native object returned by a browser API; fortunately, jqeury301 // takes the native object returned by a browser API; fortunately, jqeury
304 this.getExistingAnnotations();304 this.getExistingAnnotations();
305 },305 },
306 getExistingAnnotations: function() {306 getExistingAnnotations: function() {
307 var self = this;
307 this.helpview.step(0);308 this.helpview.step(0);
308 this.$overlay.show();309 this.$overlay.show();
309 //console.log('getting existing annotations of ', this.imgURL);310 //console.log('getting existing annotations of ', this.imgURL);
321 anno.addAnnotation(swt.how);321 anno.addAnnotation(swt.how);
322 console.log('swt.how = ', swt.how);322 console.log('swt.how = ', swt.how);
323 });323 });
324 swtr.appView.$overlay.hide();
325 swtr.appView.helpview.step(2);
324 self.$overlay.hide();
325 self.helpview.step(2);
326 }326 }
327 },327 },
328 error: function(jqxhr, error, statusText) {328 error: function(jqxhr, error, statusText) {
329 if(jqxhr.status === 404) { //annotations don't exist for this image329 if(jqxhr.status === 404) { //annotations don't exist for this image
330 console.log('annotations don\'t exist for this image. Create one!');330 console.log('annotations don\'t exist for this image. Create one!');
331 }331 }
332 swtr.appView.$overlay.hide();
333 swtr.appView.helpview.step(2);
332 self.$overlay.hide();
333 self.helpview.step(2);
334 }334 }
335 });335 });
336 },336 },
369 //$("p").toggle();369 //$("p").toggle();
370 $('.annotorious-item-unfocus').css("opacity", "0");370 $('.annotorious-item-unfocus').css("opacity", "0");
371 }371 }
372
373 },372 },
374//annotorious editor widget - custom with options
375//to obtain shapes object, declaring annotation in global scope - TODO refactor
376//code to find better way to do this.
377
378 setShape: function(annotation) {
373 //annotorious editor widget - custom with options
374 //to obtain shapes object, declaring annotation in global scope - TODO refactor
375 //code to find better way to do this.
376 setShape: function(annotation) {
379 $('.annotorious-editor-text').hide();377 $('.annotorious-editor-text').hide();
380 $('.annotorious-editor').css("width", "100%");378 $('.annotorious-editor').css("width", "100%");
381 window.annotation=annotation;
382 annotation.text = [];
383 },
384//to create new annotation object
379 window.annotation=annotation;
380 annotation.text = [];
381 },
382 //to create new annotation object
385 inputStore: function(opt) {383 inputStore: function(opt) {
386 var temp = opt;384 var temp = opt;
387 var src = $('#img-url-input').val();385 var src = $('#img-url-input').val();
387387
388 },388 },
389389
390//to add the final annotation
390 //to add the final annotation
391391
392//save button - event bind
393 add_new_anno: function(event){
394 var $selected = $('select option:selected');
395 var tempinput = $selected.text()+': '+$('.annotorious-editor textarea').val();
396 this.newanno.text.push(tempinput);
397 var newinput = this.newanno.text.toString();
398 this.newanno.text = newinput;
399 console.log('this.newanno = ', this.newanno);
400 //this.to_Add(this.newanno);
401 var newanno = this.newanno;
402 window.newanno = newanno;
403 },
404//dropdown event
392 //save button - event bind
393 add_new_anno: function(event) {
394 var $selected = $('select option:selected');
395 var tempinput = $selected.text()+': '+$('.annotorious-editor textarea').val();
396 this.newanno.text.push(tempinput);
397 var newinput = this.newanno.text.toString();
398 this.newanno.text = newinput;
399 console.log('this.newanno = ', this.newanno);
400 //this.to_Add(this.newanno);
401 var newanno = this.newanno;
402 window.newanno = newanno;
403 },
404 //dropdown event
405 button_custom: function(event) {405 button_custom: function(event) {
406 $('.annotorious-editor-text').show();406 $('.annotorious-editor-text').show();
407 var $selected = $('select option:selected');407 var $selected = $('select option:selected');
416 $('.annotorious-editor-text:first').val("");416 $('.annotorious-editor-text:first').val("");
417 $('.annotorious-editor-text:first').attr('placeholder', 'Add a '+$selected.text());417 $('.annotorious-editor-text:first').attr('placeholder', 'Add a '+$selected.text());
418 },418 },
419
420
421
422 getSignInCredentials: function(event) {
419 // to sign in the user to swtstore..just make a call to the oauth endpoint
420 signIn: function(event) {
423 event.preventDefault();421 event.preventDefault();
424 this.oauth.authorize();422 this.oauth.authorize();
425 return false;423 return false;
426 },424 },
427 signIn: function(username, password) {
428 this.$overlay.show();
429 $.ajax({
430 url: swtr.swtstoreURL() + swtr.endpoints.auth,
431 type: 'POST',
432 data: {user: username, hash: password},
433 success: function(data) {
434 swtr.appView.$overlay.hide();
435 swtr.who = username;
436 var text = 'You are signed in as <b>' + swtr.who+ '</b>';
437 $('#signinview').html(text);
438 },
439 error: function(jqxhr, status, error) {
440 swtr.appView.$overlay.hide();
441 if(error === 'FORBIDDEN') {
442 $('#signin-msg').html('Error signing in. Please check your username and password. ');
443 }
444 else {
445 }
446 }
447 });
448 },
449 userLoggedIn: function(token) {
450 swtr.who = token;
425 userLoggedIn: function(username) {
426 swtr.who = username;
451 var text = 'You are signed in as <b>' + swtr.who + '</b>';427 var text = 'You are signed in as <b>' + swtr.who + '</b>';
452 $('#signinview').html(text);428 $('#signinview').html(text);
453 },429 },
462 break;462 break;
463 case 7: text = 'Fetching your image..';463 case 7: text = 'Fetching your image..';
464 break;464 break;
465 case 8: text = 'Oops! Seems like the image URL is wrong! Or we couldn\'t fetch the image.';
466 break;
467 case 9: text = 'You have to be <i>signed in</i> to sweet store to post sweets';
468 break;
465 }469 }
466 $(this.el).html(text);470 $(this.el).html(text);
467 $(window).scrollTop(0, 0);471 $(window).scrollTop(0, 0);
476 swtr.utils = {};476 swtr.utils = {};
477477
478 //swtr.AppView = AppView;478 //swtr.AppView = AppView;
479
480 // Persona callbacks
481 /*navigator.id.watch({
482 //when an user logs in
483 onlogin: function(assertion) {
484 //verify assertion and login the user
485 $.ajax({
486 type: 'POST',
487 url: swtr.swtstoreURL() + swtr.endpoints.login,
488 data: {assertion: assertion},
489 success: function(data) {
490 console.log('user logged in', data);
491 swtr.appView.userLoggedIn(data);
492 },
493 error: function() {
494 navigator.id.logout();
495 }
496 });
497 },
498 //when an user logs out
499 onlogout: function() {
500 $.ajax({
501 type: 'POST',
502 //data: {email: swtr.who},
503 url: swtr.swtstoreURL() + swtr.endpoints.logout,
504 success: function() {
505 console.log('user logged out');
506 //swtr.appView.userLoggedOut();
507 },
508 error: function() {
509 }
510 });
511 }
512 });*/
513479
514})(swtr);480})(swtr);

swtr/templates/index.html

8 <meta name="description" content="Sweet Maker, Social, Semantic, Web, Decentralized, Makes Sweet">8 <meta name="description" content="Sweet Maker, Social, Semantic, Web, Decentralized, Makes Sweet">
9 <link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">9 <link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
10 <link href="{{ url_for('static', filename='css/bootstrap-theme.min.css') }}" rel="stylesheet">10 <link href="{{ url_for('static', filename='css/bootstrap-theme.min.css') }}" rel="stylesheet">
11 <link href="{{ url_for('static', filename='css/swtmaker.css') }}" rel="stylesheet">
12 <link href="{{ url_for('static', filename='css/annotorious.css') }}" rel="stylesheet">11 <link href="{{ url_for('static', filename='css/annotorious.css') }}" rel="stylesheet">
13 <link href="{{ url_for('static', filename='css/swtmaker.css') }}" rel="stylesheet">12 <link href="{{ url_for('static', filename='css/swtmaker.css') }}" rel="stylesheet">
14 </head>13 </head>
15 <body>
1614
15 <body>
17 <div id="swt-maker" class="container">16 <div id="swt-maker" class="container">
18 <div id="helpview" class="alert alert-info col-md-8"></div>
19 <div class="row" id="signinview">
20 <form class="form-inline" role="form">
21 <span id="signin-msg">You are not signed in. </span>
22 <button class="btn btn-sm btn-primary" id="signin-credentials">
23 Sign In
24 </button>
25 </form>
26 </div>
27 <div id="img-input" class="row">
28 <div class="form-group col-md-8">
29 <input type="text" placeholder="Enter URL of the image"
30 id="img-url-input" class="form-control">
17 <!-- first row: helpview and sign in button -->
18 <div class="row">
19 <!-- helpview column of width md-8 -->
20 <div class="col-md-8">
21 <div id="helpview" class="alert alert-info"></div>
31 </div>22 </div>
32 <div class="col-md-2">
33 <button class="btn btn-default" id="img-url-submit">Load</button>
23 <!-- signin view column of width md-4 -->
24 <div class="col-md-4">
25 <div id="signinview">
26 <span id="signin-msg">You are not signed in.</span>
27 <button class="btn btn-sm btn-primary" id="sign-in">
28 Sign In
29 </button>
30 </div>
34 </div>31 </div>
32 </div> <!-- end first row -->
33 <!-- second row: image URL input box and Load button -->
34 <div class="row">
35 <div class="col-md-8">35 <div class="col-md-8">
36 <button class="btn btn-default" id="img-url-load">Annotate</button>
37 <button class="btn btn-default" id="sweet">Sweet</button>
38 <input id="setbox" type="checkbox"/ > <label
39 id="setcontrol"><p>Show annotated areas</p><p style="display: none">Hide
40 annotated areas</p></label>
36 <form class="form-inline" role="form">
37 <div class="form-group col-md-11">
38 <label class="sr-only" for="img-url-input">Enter URL of the image</label>
39 <input class="form-control" type="text"
40 placeholder="Enter URL of the image" id="img-url-input">
41 </div>
42 <button type="submit" class="btn btn-primary" id="img-url-load">Load</button>
43 </form>
41 </div>44 </div>
42 </div>
43 <div id="img-annotation-wrapper" class="col-md-12 col-xs-12 col-lg-12 col-sm-12 well">
45 <div class="col-md-4">
46 </div>
47 </div> <!-- end second row -->
48 <!-- third row - control panel? -->
49 <div class="row">
50 <div class="col-md-3">
51 <input id="setbox" type="checkbox"/>
52 <label id="setcontrol" for="setbox">Show annotated areas</label>
53 </div>
54 <div class="col-md-1">
55 <button class="btn btn-default" id="sweet">Sweet</button>
56 </div>
57 </div> <!-- end third row -- >
58 <!-- fourth row: the image annotation window -->
59 <div class="row">
60 <div id="img-annotation-wrapper" class="col-md-12 col-xs-12 col-lg-12 col-sm-12 well">
44 {% if url %}61 {% if url %}
45 <img src="{{ url }}" id="annotatable-img">
62 <img src="{{ url }}" id="annotatable-img" class="img-responsive">
46 {% else %}63 {% else %}
47 <img src="" id="annotatable-img">
64 <img src="" id="annotatable-img" class="img-responsive">
48 {% endif %}65 {% endif %}
49 </div>
66 </div>
67 </div> <!-- end fourth row -->
68
50 <div id="sweet-list-wrapper">69 <div id="sweet-list-wrapper">
51 <ul id="sweet-list"></ul>70 <ul id="sweet-list"></ul>
52 <div class="btn-grp">71 <div class="btn-grp">
102 <script src="{{ url_for('static', filename='js/lib/annotorious.debug.js') }}"></script>102 <script src="{{ url_for('static', filename='js/lib/annotorious.debug.js') }}"></script>
103 <script src="{{ url_for('static', filename='js/lib/custom-fields-plugin.js') }}"></script>103 <script src="{{ url_for('static', filename='js/lib/custom-fields-plugin.js') }}"></script>
104 <script src="{{ url_for('static', filename='js/lib/label.js') }}"></script>104 <script src="{{ url_for('static', filename='js/lib/label.js') }}"></script>
105 <!--script src="https://login.persona.org/include.js"></script-->
106 <!--script src="{{ url_for('static',
107 filename='js/lib/mozilla.persona.include.js') }}"></script-->
108 <script src="{{ url_for('static', filename='js/oauth.js') }}"></script>105 <script src="{{ url_for('static', filename='js/oauth.js') }}"></script>
109 <script src="{{ url_for('static', filename='js/swtmaker.js') }}"></script>106 <script src="{{ url_for('static', filename='js/swtmaker.js') }}"></script>
110107
112 <%= how %>112 <%= how %>
113 </li>113 </li>
114 </script>114 </script>
115 <script type="text/template" id="signin-credentials-template">
116 <div class="form-inline col-md-3">
117 <label class="sr-only" for="username">Username</label>
118 <input type="text" class="form-control" id="username" placeholder="Enter username">
119 </div>
120 <div class="form-inline col-md-3">
121 <label class="sr-only" for="password">Password</label>
122 <input type="password" class="form-control" id="password" placeholder="Password">
123 </div>
124 </script>
125 <script type="text/template" id="button-template">
126 <select class="form-control">
127 <option id="">Choose an Option</option>
128 <option id="comment" >Comment</option>
129 <option id="label" >Label</option>
130 <option id="tags">Tags</option>
131 <option id="links">Links</option>
132 </select>
133 </script>
134 <script type="text/template" id="popup-template">
135 <span class="annotorious-popup-text"> </span>
136 </script>
115 <script type="text/template" id="button-template">
116 <select class="form-control">
117 <option id="">Choose an Option</option>
118 <option id="comment" >Comment</option>
119 <option id="label" >Label</option>
120 <option id="tags">Tags</option>
121 <option id="links">Links</option>
122 </select>
123 </script>
124 <script type="text/template" id="popup-template">
125 <span class="annotorious-popup-text"> </span>
126 </script>
137 </body>127 </body>
138</html>128</html>