Commit 8a12443449fbb147a3b575b8b4de6affefbb4f7c

Some fixes and improvements

  - Move more image anno related code to img_swtr.js
  - Made respective components to load for different routes. Also logic for
    mounting/unmounting components in AppRouter.
    Each component should define a destroy function to unmount it properly.
  
1818 margin-left: 0;
1919}
2020
21#signinview {
22 margin 0 20px;
23 padding: 10px;
24 text-align: center;
25 z-index: 100001;
26}
27
2821#img-annotation-wrapper, #ocd-results {
2922 margin: 30px auto 0 auto;
3023}
9393 background-color: rgba(255, 255, 255, 0.7);
9494}
9595
96#user-input-row .form-inline .form-control {
96#user-input-row .form-inline .form-control,
97#search-user-input-row .form-inline .form-control {
9798 width: 100%;
9899}
99100
  
11(function(swtr) {
2
3 /* Model for Image Annotation Sweets */
4 var ImgAnnoSwt = Backbone.Model.extend({
5 defaults: {
6 'who': '',
7 'what': 'img-anno',
8 'where': '',
9 'how': {}
10 },
11 initialize: function() {
12 }
13 });
14
15 /* Collection to hold all multiple ImgAnnoSwt */
16 swtr.ImgAnnoSwts = Backbone.Collection.extend({
17 model: ImgAnnoSwt,
18 url: function() {
19 return swtr.swtstoreURL() + '/sweets';
20 },
21 // get all sweets/annotations of type #img-anno for a particular URI
22 // (where)
23 // @options is a javascript object,
24 // @options.where : URI of the resource for which swts to be fetched
25 // @options.who: optional username to filter sweets
26 // @options.success: success callback to call
27 // @options.error: error callback to call
28 getAll: function(options) {
29 // error checking
30 if(!options.where) {
31 throw Error('"where" option must be passed to get sweets of a URI');
32 return false;
33 }
34 // setting up params
35 var where = options.where,
36 who = options.who || null;
37 url = swtr.swtstoreURL() + swtr.endpoints.get + '?where=' +
38 encodeURIComponent(where) + '&access_token=' + swtr.access_token;
39 if(who) {
40 url += '&who=' + who;
41 }
42 // get them!
43 this.sync('read', this, {
44 url: url,
45 success: function() {
46 if(typeof options.success === 'function') {
47 options.success.apply(this, arguments);
48 }
49 },
50 error: function() {
51 if(typeof options.error === 'function') {
52 options.error.apply(this, arguments);
53 }
54 }
55 });
56 },
57 // post newly created sweets to a sweet store
58 // @options is a javascript object,
59 // @options.where : URI of the resource for which swts to be fetched
60 // @options.who: optional username to filter sweets
61 // @options.success: success callback to call
62 // @options.error: error callback to call,
63 post: function(options) {
64 var new_sweets = this.getNew();
65 var dummy_collection = new Backbone.Collection(new_sweets);
66
67 if(!swtr.access_token) {
68 throw new Error('Access Token is required to sweet');
69 return;
70 }
71
72 var url = swtr.swtstoreURL() + swtr.endpoints.post +
73 '?access_token=' + swtr.access_token;
74
75 this.sync('create', dummy_collection, {
76 url: url,
77 success: function() {
78 if(typeof options.success === 'function') {
79 options.success.apply(this, arguments);
80 }
81 },
82 error: function() {
83 if(typeof options.error === 'function') {
84 options.error.apply(this, arguments);
85 }
86 }
87 });
88 },
89 // return newly created models from the collection
90 getNew: function() {
91 var new_models = [];
92 this.each(function(model) {
93 if(model.isNew()) {
94 new_models.push(model);
95 }
96 });
97 return new_models;
98 },
99 // update part of the collection after a save on the server
100 update: function() {
101 }
102 });
2103 swtr.ImgAnnoView = Backbone.View.extend({
3104 el: $("#img-annotation-wrapper"),
4105 events: {
  
44 //Find a better way to do closure
55 //Remove script code from the HTML page
66 swtr.init = function() {
7 this.sweets = new ImgAnnoSwts();
7 this.sweets = new swtr.ImgAnnoSwts();
88 this.appView = new AppView();
99 this.who = 'Guest';
1010
1111 this.app_router = new AppRouter();
1212 Backbone.history.start();
13 this.app_router.navigate('home');
13 this.app_router.start();
1414
1515 $.ajaxSetup({
1616 xhrFields: {
3939 }
4040 };
4141
42 /* Model for Image Annotation Sweets */
43 var ImgAnnoSwt = Backbone.Model.extend({
44 defaults: {
45 'who': '',
46 'what': 'img-anno',
47 'where': '',
48 'how': {}
49 },
50 initialize: function() {
51 }
52 });
53
54 /* Collection to hold all multiple ImgAnnoSwt */
55 var ImgAnnoSwts = Backbone.Collection.extend({
56 model: ImgAnnoSwt,
57 url: function() {
58 return swtr.swtstoreURL() + '/sweets';
59 },
60 // get all sweets/annotations of type #img-anno for a particular URI
61 // (where)
62 // @options is a javascript object,
63 // @options.where : URI of the resource for which swts to be fetched
64 // @options.who: optional username to filter sweets
65 // @options.success: success callback to call
66 // @options.error: error callback to call
67 getAll: function(options) {
68 // error checking
69 if(!options.where) {
70 throw Error('"where" option must be passed to get sweets of a URI');
71 return false;
72 }
73 // setting up params
74 var where = options.where,
75 who = options.who || null;
76 url = swtr.swtstoreURL() + swtr.endpoints.get + '?where=' +
77 encodeURIComponent(where) + '&access_token=' + swtr.access_token;
78 if(who) {
79 url += '&who=' + who;
80 }
81 // get them!
82 this.sync('read', this, {
83 url: url,
84 success: function() {
85 if(typeof options.success === 'function') {
86 options.success.apply(this, arguments);
87 }
88 },
89 error: function() {
90 if(typeof options.error === 'function') {
91 options.error.apply(this, arguments);
92 }
93 }
94 });
95 },
96 // post newly created sweets to a sweet store
97 // @options is a javascript object,
98 // @options.where : URI of the resource for which swts to be fetched
99 // @options.who: optional username to filter sweets
100 // @options.success: success callback to call
101 // @options.error: error callback to call,
102 post: function(options) {
103 var new_sweets = this.getNew();
104 var dummy_collection = new Backbone.Collection(new_sweets);
105
106 if(!swtr.access_token) {
107 throw new Error('Access Token is required to sweet');
108 return;
109 }
110
111 var url = swtr.swtstoreURL() + swtr.endpoints.post +
112 '?access_token=' + swtr.access_token;
113
114 this.sync('create', dummy_collection, {
115 url: url,
116 success: function() {
117 if(typeof options.success === 'function') {
118 options.success.apply(this, arguments);
119 }
120 },
121 error: function() {
122 if(typeof options.error === 'function') {
123 options.error.apply(this, arguments);
124 }
125 }
126 });
127 },
128 // return newly created models from the collection
129 getNew: function() {
130 var new_models = [];
131 this.each(function(model) {
132 if(model.isNew()) {
133 new_models.push(model);
134 }
135 });
136 return new_models;
137 },
138 // update part of the collection after a save on the server
139 update: function() {
140 }
141 });
142
14342 var SweetsView = Backbone.View.extend({
14443 el: $('#sweet-list-wrapper'),
14544 events: {
269269 'click #user-input-submit': 'submitUserInput',
270270 'click #sweet': 'sweet',
271271 'click #sign-in': 'signIn',
272 'click #ocd-source': 'sourceChanged'
273272 //'mouseup .annotorious-editor-button-save': 'addnew_anno'
274273 },
275274 initialize: function() {
276275 // initialize components
277276 this.source = 'none';
278 this.helpview = new HelpView();
277 //this.helpview = new HelpView();
279278 this.sweetsview = new SweetsView({collection: swtr.sweets});
280279
281280 // cache jquery selected elements which are used frequently
282281 this.$overlay = $('#app-overlay');
283282 this.$img = $('#annotatable-img');
284283
285 this.helpview.step(1);
284 //this.helpview.step(1);
286285 // initialize the oauth stuff
287286 this.oauth = new Oauth({
288287 app_id: swtr.app_id,
317317 swtr.imgAnnoView.setImage(url);
318318 }
319319 else {
320 swtr.imgAnnoView = new swtr.ImgAnnoView({collection:swtr.sweets,
321 img: this.$img[0],
322 $img: this.$img,
323 url: url});
320 swtr.imgAnnoView = new swtr.ImgAnnoView({
321 collection:swtr.sweets,
322 img: this.$img[0],
323 $img: this.$img,
324 url: url
325 });
324326 }
325327 return false;
326328 }
396396 },
397397 userLoggedIn: function(username) {
398398 swtr.who = username;
399 var text = 'Signed in as <b>' + swtr.who + '</b>';
399 var text = 'Signed in as <b><u>' + swtr.who + '</u></b>';
400400 $('#signinview').html(text);
401401 },
402402 userLoggedOut: function() {
403403 swtr.who = 'Guest';
404404 $('#signinview').html('Logged out');
405 }
406 });
407
408
409 var PlayAreaView = Backbone.View.extend({
410 el: $('#play-page'),
411 events: {
405412 },
406 changeURLInputPlaceholder: function(source) {
407 switch (source) {
408 case 'ocd' : $('#user-input').attr('placeholder', 'Enter search query');
409 break;
410 case 'none' : $('#user-input').attr('placeholder', 'Enter URL of image or web page');
411 break;
412 }
413 initialize: function() {
414 this.render();
413415 },
414 // function to change the source in the application and update the UI
415 changeSource: function(source) {
416 switch (source) {
417 case 'ocd' : this.source = 'ocd';
418 this.helpview.step(11);
419 this.changeURLInputPlaceholder('ocd');
420 break;
421 case 'none' : this.source = 'none';
422 this.helpview.step(1);
423 this.changeURLInputPlaceholder('none');
424 break;
425 }
416 render: function() {
417 }
418 });
419
420 var SearchView = Backbone.View.extend({
421 id: 'search-page-container',
422 events: {
423 'click #search-user-input-submit': 'userInputSubmit'
426424 },
427 // event handler to capture control panel UI change of source
428 sourceChanged: function(event) {
429 if($('#ocd-source').is(':checked')) {
430 this.changeSource('ocd');
431 }
432 else {
433 this.changeSource('none');
434 }
425 initialize: function() {
426 this.template = _.template($('#search-page-template').html());
427 this.helpview = new HelpView();
428 this.render();
429 this.helpview.step(11);
435430 },
436 loadOCDSearch: function(input) {
431 render: function() {
432 this.$el.html(this.template());
433 $('#search-page').html(this.$el);
434 },
435 userInputSubmit: function(event) {
436 event.preventDefault();
437 var input = $('#search-user-input').val();
438 this.loadSearch(input);
439 return false;
440 },
441 loadSearch: function(input) {
437442 var self = this;
438 $('#img-annotation-wrapper').hide();
439 $('#ocd-results').show();
440 $('#ocd-results').html('<h4 style="text-align: center;">Loading..</h4>');
443 this.$el.append('<div id="ocd-view"></div>');
444 $('#ocd-view').html('<h4 style="text-align: center;">Loading..</h4>');
441445 $.ajax({
442446 type: 'GET',
443447 url: '/search/ocd',
444448 data: {query: input},
445449 success: function(data) {
446 self.ocdView = new OCDView({
450 self.ocd_view = new OCDView({
451 el: $('#ocd-view'),
447452 query: input,
448453 data: data,
449454 model: data.hits.hits
450455 });
451456 }
452457 });
453 }
458 },
459 destroy: function() {
460 this.helpview.remove();
461 this.remove();
462 },
454463 });
455464
456465 var OCDView = Backbone.View.extend({
560560 event.preventDefault();
561561 // TODO: init the image anno
562562 var url = $(event.currentTarget).find('img').attr('src');
563 swtr.appView.loadURL(url, 'image');
563 //swtr.appView.loadURL(url, 'image');
564564 return false;
565565 },
566566 search: function(data, cb) {
579579 });
580580
581581 var HelpView = Backbone.View.extend({
582 el: $('#helpview'),
582 id: 'helpview-wrapper',
583583 events: {
584584 },
585585 initialize: function() {
586 this.template = _.template($('#helpview-template').html());
587 this.render();
586588 this.$text_el = $('#helpview-text');
587589 },
590 render: function() {
591 $('#helpview-container').html(this.$el);
592 this.$el.html(this.template({}));
593 },
588594 //TODO: move from number based steps to something else. number based steps
589595 //implicitly imply sequential processing..which does not happen in this
590596 //case..
627627 case 13: text = 'This does not seem to be a URL. Please enter a valid URL.';
628628 break;
629629 }
630 $(this.$text_el).html(text);
630 this.$text_el.html(text);
631631 $(window).scrollTop(0, 0);
632632 }
633633 });
634634
635 var DummyView = Backbone.View.extend({
636 intialize: function() {
637 this.render();
638 },
639 render: function() {
640 $('#swtr-root').append('dummy view');
641 },
642 destroy: function() {
643 this.remove();
644 this.undelegateEvents();
645 }
646 });
635647
648
636649 // utilities and helper functions to go here
637650 swtr.utils = {
638651 linkify: function(link) {
657657 }
658658 }
659659 };
660 //swtr.AppView = AppView;
661660
662661 var AppRouter = Backbone.Router.extend({
663662 routes: {
665665 'play': 'play',
666666 'search': 'search'
667667 },
668 components: {
669 'linked-data': DummyView,
670 'play': DummyView,
671 'search': SearchView
672 },
668673 home: function() {
669674 this.hideAll();
670675 this.show('home-page');
690690 $('.page').hide();
691691 },
692692 show: function(id) {
693 if(this.mounted_component) {
694 this.mounted_component.destroy();
695 }
696 if(id !== 'home-page') {
697 var component = id.split('-page')[0];
698 this.mounted_component = new this.components[component];
699 }
693700 $('#' + id).show();
694701 this.highlight(id);
695702 },
705705 var href = id.split('-page')[0];
706706 var selector = '#swtr-navbar-collapse a[href="#/' + href + '"]';
707707 $(selector).parent('li').addClass('active');
708 },
709 start: function() {
710 var fragment = window.location.hash.split('#')[1];
711 if(!fragment) {
712 this.navigate('home');
713 return;
714 }
715 var route = fragment.split('/')[1];
716 if(_.indexOf(_.keys(this.routes), route) > -1) {
717 this.navigate(fragment);
718 }
708719 }
709720 });
710721
  
5656 </header>
5757
5858 <div id="swtr-root" class="container">
59 <div id="helpview-container"></div>
60
5961 <div id="home-page" class="page">
6062 <div class="jumbotron">
6163 <h1> CLink: Community Linked Open Cultuur Data </h1>
8787 </div>
8888
8989 <div id="play-page" class="page">
90 <!-- first row: helpview and sign in button -->
91 <div class="row">
92 <!-- helpview column of width md-8 -->
93 <div class="col-md-8">
94 <div id="helpview" class="bg-info">
95 <span class="label label-primary">Heads Up</span>
96 <span id="helpview-text"></span>
97 <a class="close">&times;</a>
98 </div>
99 </div>
100 <!-- signin view column of width md-4 -->
101 <div class="col-md-4"></div>
102 </div> <!-- end first row -->
10390 <!-- second row: image URL input box and Load button -->
10491 <div class="row" id="user-input-row">
10592 <div class="col-md-8">
131131 <img src="" id="annotatable-img" class="img-responsive" alt="Annotation Workbench">
132132 {% endif %}
133133 </div>
134 <div id="ocd-view"></div>
135134 </div> <!-- end fourth row -->
136 <!-- <\!-- fifth row: options for filter -\-> -->
137 <!-- <div id="filter-div" class="row"> -->
138 <!-- <div id="filter-user-div"> -->
139 <!-- <p><span>SWeeTs by</span></p> -->
140 <!-- </div> -->
141 <!-- <div id="filter-tags-div"> -->
142 <!-- <p><span>Tags</span></p> -->
143 <!-- </div> -->
144 <!-- </div> <\!-- end fifth row -\-> -->
145135
146136 <div id="sweet-list-wrapper">
147137 <ul id="sweet-list"></ul>
143143
144144 </div>
145145
146 <div id="linked-data-page" class="page"></div>
146 <!-- the search page -->
147147 <div id="search-page" class="page"></div>
148148
149 <div id="linked-data-page" class="page"></div>
150
149151 </div>
150152 <div id="app-overlay"></div>
151153
176176 <script src="{{ url_for('static', filename='js/lib/annotorious.debug.js') }}"></script>
177177 <script src="{{ url_for('static', filename='js/lib/custom-fields-plugin.js') }}"></script>
178178 <script src="{{ url_for('static', filename='js/oauth.js') }}"></script>
179 <script type="text/javascript" src="{{ url_for('static', filename='js/lib/bootstrap-tags.min.js') }}"></script>
179 <script src="{{ url_for('static', filename='js/lib/bootstrap-tags.min.js') }}"></script>
180180 <script src="{{ url_for('static', filename='js/img_swtr.js') }}"></script>
181181 <script src="{{ url_for('static', filename='js/main.js') }}"></script>
182182
183 <!-- underscore.js templates -->
184 <script type="text/template" id="helpview-template">
185 <div class="row">
186 <div class="col-md-8">
187 <div id="helpview" class="bg-info">
188 <span class="label label-primary">Heads Up</span>
189 <span id="helpview-text"></span>
190 <a class="close">&times;</a>
191 </div>
192 </div>
193 <div class="col-md-4"></div>
194 </div>
195 </script>
183196 <script type="text/template" id="sweet-template">
184197 <li class="sweet">
185198 <a href="#">@<%= who %></a> <strong>#<%= what %></strong>
219219 </ul>
220220 </span>
221221 </script>
222 <script type="text/template" id="search-page-template">
223 <div class="row" id="search-user-input-row">
224 <div class="col-md-8">
225 <form class="form-inline" role="form">
226 <div class="form-group col-md-11">
227 <label class="sr-only" for="search-user-input">Enter search query</label>
228 <input class="form-control" type="text"
229 placeholder="Enter search query" id="search-user-input">
230 </div>
231 <button type="submit" class="btn btn-info" id="search-user-input-submit">
232 Go
233 </button>
234 </form>
235 </div>
236 <div class="col-md-4">
237 </div>
238 </div>
239 </script>
222240 <script type="text/template" id="ocd-view-base-template">
223241 <div id="ocd-hud">
224242 <span id="ocd-total-results"></span>
260260 </script>
261261 <script type="text/template" id="filter-users">
262262 <ul>
263 <input type="checkbox" name=<%= who %> checked="checked">
264 <label><%= who %></label>
263 <label>
264 <input type="checkbox" name="<%= who %>" checked="checked">
265 <%= who %>
266 </label>
265267 </ul>
266268 </script>
267269 <script type="text/template" id="filter-tags">