Commit 20e0d95087581895c6cce5083d713fce6af3a353

Rename files

  - Rename imganno.js to img_swtr.js
  - Rename swtmaker.js to main.js
  • swtr/static/js/img_swtr.js 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • swtr/static/js/imganno.js 228 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  • swtr/static/js/main.js 772 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • swtr/static/js/swtmaker.js 772 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  • swtr/templates/index.html 4 --++
  • Diff rendering mode:
  • inline
  • side by side

swtr/static/js/img_swtr.js

1(function(swtr) {
2 swtr.ImgAnnoView = Backbone.View.extend({
3 el: $("#img-annotation-wrapper"),
4 events: {
5 'change #custom-dropdown ': 'getFormValue',
6 'click #setbox': 'showHide'
7 },
8 initialize: function(options) {
9 this.listenTo(this.collection, 'add', this.render);
10 // attach event handlers to the anno object
11 anno.addHandler('onAnnotationCreated', this.showSwtHelp);
12 anno.addHandler('onAnnotationCreated', this.updateNewAnno);
13 anno.addHandler('onAnnotationUpdated', this.showSwtHelp);
14 anno.addHandler('onSelectionStarted', function(annotation) {
15 anno.hideAnnotations();
16 });
17 anno.addHandler('onSelectionCompleted', function(annotation) {
18 anno.showAnnotations();
19 });
20 anno.addPlugin('CustomFields', {});
21 anno.addHandler('onSelectionCompleted', this.hideOriginalEditor);
22 if(options.img) {
23 this.img = options.img;
24 this.$img = options.$img;
25 options.$img.on('load', this, this.imageLoaded);
26 options.$img.on('error', this, this.onImageLoadError);
27 this.setImage(options.url);
28 }
29 },
30 render: function(model) {
31 var swt = model.toJSON();
32 swt.how['editable'] = false;
33 swt.how.text = swtr.imgAnnoView.createPopupText(swt.how);
34 swt.how.text += '\n - by ' + swt.who;
35 anno.addAnnotation(swt.how);
36 },
37 renderWith: function() {
38 _.each(this.collection, this.render);
39 },
40 showSwtHelp: function(annotation) {
41 var self = swtr.imgAnnoView;//TODO: figure out how we can bind the scope when this func is called as a callback
42 swtr.appView.helpview.step(3);
43 $('#sweet').show();
44 },
45 updateNewAnno: function(annotation) {
46 console.log('updateNewAnno()');
47 var self = swtr.imgAnnoView;
48 // get the final value/input from the editor
49 var selected = $('select option:selected').text().toLowerCase();
50 var text_input = $('.annotorious-editor-text').val();
51 if( selected === "tags") {
52 self.new_anno[selected] = $('#tags-input').tags().getTags();
53 }
54 else {
55 // update it in our annotation object
56 self.new_anno[selected] = text_input;
57 }
58 // prepare the text field
59 self.new_anno.text = self.createPopupText(self.new_anno);
60 // update the annotorious annotation object with the new values
61 if(self.new_anno.comment) {
62 annotation.comment = self.new_anno.comment;
63 }
64 if(self.new_anno.link) {
65 annotation.link = self.new_anno.link;
66 }
67 if(self.new_anno.tags) {
68 annotation.tags = self.new_anno.tags;
69 }
70 if(self.new_anno.title) {
71 annotation.title = self.new_anno.title;
72 }
73 annotation.text = self.new_anno.text;
74 console.log(self.new_anno, annotation);
75 },
76 // hide the original editor window, when user has completed selecting part
77 // of the image to annotate..
78 hideOriginalEditor: function(annotation) {
79 console.log('hideOriginalEditor()');
80 var self = swtr.imgAnnoView;
81 self.new_anno = {};
82 self.getSuggestionsForTags();
83 //$('.annotorious-editor-text').hide();
84 //$('.annotorious-editor').css('width', '100%');
85 },
86 getFormValue: function(event) {
87 console.log('getFormValue()');
88
89 var self = swtr.imgAnnoView;
90 // show the editor field to input text
91 var $anno_form = $('.annotorious-editor-text');
92 //$anno_form.slideDown();
93 // get the previous item entered
94 var $selected = $('select option:selected');
95 var text_input = $anno_form.val();
96
97 // if there was a input and it was not tags..
98 if(text_input && $selected.prev().text() !== 'Tags') {
99 var field = $selected.prev().text().toLowerCase();
100 // update it in our annotation object
101 self.new_anno[field] = text_input;
102 }
103 // if it was tags..
104 else if ($selected.prev().text() === 'Tags') {
105 // directly save it..
106 self.new_anno['tags'] = $('#tags-input').tags().getTags();
107 }
108
109 // if the current selected is tags
110 if($selected.text() === 'Tags') {
111 $('#tags-input').tags({
112 tagSize: 'md',
113 promptText: 'Type word (and press enter)..',
114 caseInsensitive: true,
115 suggestions: self.tags_suggestions
116 });
117 $('#tags-input').show();
118 $('.annotorious-editor-text').hide();
119 }
120 else {
121 $('#tags-input').hide();
122 $('.annotorious-editor-text').show();
123 }
124 $anno_form.val('');
125 $anno_form.attr('placeholder', 'Add ' + $selected.text());
126 console.log(self.new_anno);
127 },
128 createPopupText: function(annotation) {
129 // title
130 var text = (annotation.title) ? '<h4>' + annotation.title + '</h4>' : '';
131
132 // comment
133 text += (annotation.comment) ? '<p>' + annotation.comment + '</p>' : '';
134
135 // link
136 text += (annotation.link) ? '<a target="blank" href="' +
137 swtr.utils.linkify(annotation.link) + '">' + annotation.link +
138 '</a>' : '';
139
140 // tags
141 text += (annotation.tags) ? '<p>' + annotation.tags + '</p>' : '';
142
143 // if older annotation i.e w/o comment,title etc fields
144 // add text field as text
145 if(!text) {
146 text = annotation.text;
147 }
148 return text;
149 },
150 // load the suggestions for the tag spraying..
151 getSuggestionsForTags: function() {
152 var self = swtr.imgAnnoView;
153 $.ajax({
154 url: '/static/data/tags_suggestions.json',
155 success: function(data) {
156 self.tags_suggestions = data;
157 }
158 });
159 },
160 setImage: function(url) {
161 this.imgURL = url;
162 console.log(url);
163 if(this.$img.attr('src') === this.imgURL) {
164 return;
165 }
166 anno.reset();
167 var self = this;
168 swtr.appView.$overlay.show();
169 swtr.appView.helpview.step(7);
170 this.$img.attr('src', this.imgURL);
171 },
172 imageLoaded: function(event) {
173 var self = event.data;
174 console.log('image loaded', self);
175 swtr.appView.$overlay.hide();
176 // reset the collection
177 swtr.sweets.reset();
178 anno.makeAnnotatable(swtr.imgAnnoView.img);
179 swtr.imgAnnoView.getExistingAnnotations();
180 },
181 // when image fails to load - could be because of broken URL or network
182 // issues
183 onImageLoadError: function(event) {
184 var self = event.data;
185 console.log('error while loading image');
186 swtr.appView.$overlay.hide();
187 swtr.appView.helpview.step(8);
188 },
189 initImageAnno: function() {
190 // img is a jquery object which annotorious doesn't accept; instead it
191 // takes the native object returned by a browser API; fortunately, jqeury
192 // stores a copy of the native object too!
193
194 this.getExistingAnnotations();
195
196 },
197 getExistingAnnotations: function() {
198 var self = this;
199 swtr.appView.helpview.step(0);
200 swtr.appView.$overlay.show();
201 swtr.sweets.getAll({
202 where: this.imgURL,
203 success: function(data) {
204 if(_.isArray(data)) {
205 swtr.sweets.add(data);
206 swtr.appView.$overlay.hide();
207 swtr.appView.helpview.step(2);
208 }
209 },
210 error: function(jqxhr, error, statusText) {
211 if(jqxhr.status === 404) { //annotations don't exist for this image
212 console.log('annotations don\'t exist for this image. Create one!');
213 }
214 swtr.appView.$overlay.hide();
215 swtr.appView.helpview.step(2);
216 }
217 });
218 },
219 showHide: function() {
220 if($("#setbox:checked").length) {
221 $('.annotorious-item-unfocus').css("opacity", "0.5");
222 }
223 else {
224 $('.annotorious-item-unfocus').css("opacity", "0");
225 }
226 }
227 });
228})(swtr);

swtr/static/js/imganno.js

1(function(swtr) {
2 swtr.ImgAnnoView = Backbone.View.extend({
3 el: $("#img-annotation-wrapper"),
4 events: {
5 'change #custom-dropdown ': 'getFormValue',
6 'click #setbox': 'showHide'
7 },
8 initialize: function(options) {
9 this.listenTo(this.collection, 'add', this.render);
10 // attach event handlers to the anno object
11 anno.addHandler('onAnnotationCreated', this.showSwtHelp);
12 anno.addHandler('onAnnotationCreated', this.updateNewAnno);
13 anno.addHandler('onAnnotationUpdated', this.showSwtHelp);
14 anno.addHandler('onSelectionStarted', function(annotation) {
15 anno.hideAnnotations();
16 });
17 anno.addHandler('onSelectionCompleted', function(annotation) {
18 anno.showAnnotations();
19 });
20 anno.addPlugin('CustomFields', {});
21 anno.addHandler('onSelectionCompleted', this.hideOriginalEditor);
22 if(options.img) {
23 this.img = options.img;
24 this.$img = options.$img;
25 options.$img.on('load', this, this.imageLoaded);
26 options.$img.on('error', this, this.onImageLoadError);
27 this.setImage(options.url);
28 }
29 },
30 render: function(model) {
31 var swt = model.toJSON();
32 swt.how['editable'] = false;
33 swt.how.text = swtr.imgAnnoView.createPopupText(swt.how);
34 swt.how.text += '\n - by ' + swt.who;
35 anno.addAnnotation(swt.how);
36 },
37 renderWith: function() {
38 _.each(this.collection, this.render);
39 },
40 showSwtHelp: function(annotation) {
41 var self = swtr.imgAnnoView;//TODO: figure out how we can bind the scope when this func is called as a callback
42 swtr.appView.helpview.step(3);
43 $('#sweet').show();
44 },
45 updateNewAnno: function(annotation) {
46 console.log('updateNewAnno()');
47 var self = swtr.imgAnnoView;
48 // get the final value/input from the editor
49 var selected = $('select option:selected').text().toLowerCase();
50 var text_input = $('.annotorious-editor-text').val();
51 if( selected === "tags") {
52 self.new_anno[selected] = $('#tags-input').tags().getTags();
53 }
54 else {
55 // update it in our annotation object
56 self.new_anno[selected] = text_input;
57 }
58 // prepare the text field
59 self.new_anno.text = self.createPopupText(self.new_anno);
60 // update the annotorious annotation object with the new values
61 if(self.new_anno.comment) {
62 annotation.comment = self.new_anno.comment;
63 }
64 if(self.new_anno.link) {
65 annotation.link = self.new_anno.link;
66 }
67 if(self.new_anno.tags) {
68 annotation.tags = self.new_anno.tags;
69 }
70 if(self.new_anno.title) {
71 annotation.title = self.new_anno.title;
72 }
73 annotation.text = self.new_anno.text;
74 console.log(self.new_anno, annotation);
75 },
76 // hide the original editor window, when user has completed selecting part
77 // of the image to annotate..
78 hideOriginalEditor: function(annotation) {
79 console.log('hideOriginalEditor()');
80 var self = swtr.imgAnnoView;
81 self.new_anno = {};
82 self.getSuggestionsForTags();
83 //$('.annotorious-editor-text').hide();
84 //$('.annotorious-editor').css('width', '100%');
85 },
86 getFormValue: function(event) {
87 console.log('getFormValue()');
88
89 var self = swtr.imgAnnoView;
90 // show the editor field to input text
91 var $anno_form = $('.annotorious-editor-text');
92 //$anno_form.slideDown();
93 // get the previous item entered
94 var $selected = $('select option:selected');
95 var text_input = $anno_form.val();
96
97 // if there was a input and it was not tags..
98 if(text_input && $selected.prev().text() !== 'Tags') {
99 var field = $selected.prev().text().toLowerCase();
100 // update it in our annotation object
101 self.new_anno[field] = text_input;
102 }
103 // if it was tags..
104 else if ($selected.prev().text() === 'Tags') {
105 // directly save it..
106 self.new_anno['tags'] = $('#tags-input').tags().getTags();
107 }
108
109 // if the current selected is tags
110 if($selected.text() === 'Tags') {
111 $('#tags-input').tags({
112 tagSize: 'md',
113 promptText: 'Type word (and press enter)..',
114 caseInsensitive: true,
115 suggestions: self.tags_suggestions
116 });
117 $('#tags-input').show();
118 $('.annotorious-editor-text').hide();
119 }
120 else {
121 $('#tags-input').hide();
122 $('.annotorious-editor-text').show();
123 }
124 $anno_form.val('');
125 $anno_form.attr('placeholder', 'Add ' + $selected.text());
126 console.log(self.new_anno);
127 },
128 createPopupText: function(annotation) {
129 // title
130 var text = (annotation.title) ? '<h4>' + annotation.title + '</h4>' : '';
131
132 // comment
133 text += (annotation.comment) ? '<p>' + annotation.comment + '</p>' : '';
134
135 // link
136 text += (annotation.link) ? '<a target="blank" href="' +
137 swtr.utils.linkify(annotation.link) + '">' + annotation.link +
138 '</a>' : '';
139
140 // tags
141 text += (annotation.tags) ? '<p>' + annotation.tags + '</p>' : '';
142
143 // if older annotation i.e w/o comment,title etc fields
144 // add text field as text
145 if(!text) {
146 text = annotation.text;
147 }
148 return text;
149 },
150 // load the suggestions for the tag spraying..
151 getSuggestionsForTags: function() {
152 var self = swtr.imgAnnoView;
153 $.ajax({
154 url: '/static/data/tags_suggestions.json',
155 success: function(data) {
156 self.tags_suggestions = data;
157 }
158 });
159 },
160 setImage: function(url) {
161 this.imgURL = url;
162 console.log(url);
163 if(this.$img.attr('src') === this.imgURL) {
164 return;
165 }
166 anno.reset();
167 var self = this;
168 swtr.appView.$overlay.show();
169 swtr.appView.helpview.step(7);
170 this.$img.attr('src', this.imgURL);
171 },
172 imageLoaded: function(event) {
173 var self = event.data;
174 console.log('image loaded', self);
175 swtr.appView.$overlay.hide();
176 // reset the collection
177 swtr.sweets.reset();
178 anno.makeAnnotatable(swtr.imgAnnoView.img);
179 swtr.imgAnnoView.getExistingAnnotations();
180 },
181 // when image fails to load - could be because of broken URL or network
182 // issues
183 onImageLoadError: function(event) {
184 var self = event.data;
185 console.log('error while loading image');
186 swtr.appView.$overlay.hide();
187 swtr.appView.helpview.step(8);
188 },
189 initImageAnno: function() {
190 // img is a jquery object which annotorious doesn't accept; instead it
191 // takes the native object returned by a browser API; fortunately, jqeury
192 // stores a copy of the native object too!
193
194 this.getExistingAnnotations();
195
196 },
197 getExistingAnnotations: function() {
198 var self = this;
199 swtr.appView.helpview.step(0);
200 swtr.appView.$overlay.show();
201 swtr.sweets.getAll({
202 where: this.imgURL,
203 success: function(data) {
204 if(_.isArray(data)) {
205 swtr.sweets.add(data);
206 swtr.appView.$overlay.hide();
207 swtr.appView.helpview.step(2);
208 }
209 },
210 error: function(jqxhr, error, statusText) {
211 if(jqxhr.status === 404) { //annotations don't exist for this image
212 console.log('annotations don\'t exist for this image. Create one!');
213 }
214 swtr.appView.$overlay.hide();
215 swtr.appView.helpview.step(2);
216 }
217 });
218 },
219 showHide: function() {
220 if($("#setbox:checked").length) {
221 $('.annotorious-item-unfocus').css("opacity", "0.5");
222 }
223 else {
224 $('.annotorious-item-unfocus').css("opacity", "0");
225 }
226 }
227 });
228})(swtr);

swtr/static/js/main.js

1(function(swtr) {
2
3 //TODO: find a better way to init.
4 //Find a better way to do closure
5 //Remove script code from the HTML page
6 swtr.init = function() {
7 this.sweets = new ImgAnnoSwts();
8 this.appView = new AppView();
9 this.who = 'Guest';
10
11 this.app_router = new AppRouter();
12 Backbone.history.start();
13 this.app_router.navigate('home');
14
15 $.ajaxSetup({
16 xhrFields: {
17 // we need this to send cookies to cross-domain requests
18 withCredentials: true
19 },
20 //some browsers won't make cross-domain ajax until it is explicitly set
21 crossDomain: true
22 });
23 this.handleOAuth();
24 };
25
26 swtr.handleOAuth = function() {
27 if(swtr.access_token) {
28 $('#signinview').html('Signing you in..');
29 $.ajax({
30 url: swtr.swtstoreURL()+'/api/users/me?access_token='+
31 swtr.access_token,
32 success: function(data) {
33 swtr.appView.userLoggedIn(data.username);
34 },
35 error: function() {
36 $('#signinview').html('Error signing in! Please try again');
37 }
38 });
39 }
40 };
41
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
143 var SweetsView = Backbone.View.extend({
144 el: $('#sweet-list-wrapper'),
145 events: {
146 'click #sweet-cancel': 'cancelSweeting',
147 'click #post-sweet': 'postSweets'
148 },
149 initialize: function() {
150 this.template = _.template($('#sweet-template').html());
151 },
152 render: function() {
153 $('#sweet-list').html('<h4>These are your sweet annotations!</h4>');
154 _.each(this.collection.models, function(swt) {
155 if(swt.has('id')) {
156 return false;
157 }
158 $('#sweet-list').append(this.template({
159 who: swt.get('who'),
160 what: swt.get('what'),
161 where: swt.get('where'),
162 how: JSON.stringify(this.getHumanReadableParts(swt.get('how')))
163 }));
164 }, this);
165 $(this.el).fadeIn(300);
166 },
167 getHumanReadableParts: function(how) {
168 var human_readable_json = {};
169 if(how.comment) {
170 human_readable_json['comment'] = how.comment;
171 }
172 if(how.title) {
173 human_readable_json['title'] = how.title;
174 }
175 if(how.tags) {
176 human_readable_json['tags'] = how.tags;
177 }
178 if(how.link) {
179 human_readable_json['link'] = how.link;
180 }
181 return human_readable_json;
182 },
183 cancelSweeting: function() {
184 this.removeSwtsNotPosted();
185 this.cleanUp();
186 },
187 removeSwtsNotPosted: function() {
188 var notPosted = this.collection.filter(function(model) {
189 return !model.has('id');
190 });
191 this.collection.remove(notPosted);
192 },
193 postSweets: function() {
194 console.log("postSWr");
195 var appView = swtr.appView;
196 appView.helpview.step(5);
197 appView.$overlay.show();
198 try {
199 this.collection.post({
200 success: function(collection, response) {
201 console.log(collection, response);
202 swtr.sweets.set(collection);
203 //TODO: move this to a annotation view or something
204// anno.removeAll();
205 // _.each(swtr.sweets.models, function(swt) {
206 // if(!_.has(swt.get('how'), 'editable')) {
207 // swt.get('how')['editable'] = false;
208 // //console.log(swt.get('how').text.Comment);
209 // swt.get('how').text = swtr.imgAnnoView.createPopupText(swt.get('how'));
210 // //console.log(swt.get('how'));
211 // swt.get('how').text += '\n - by ' + swt.get('who');
212 // }
213 // //console.log(swt.get('how'));
214 // anno.addAnnotation(swt.get('how'));
215 // });
216 //console.log(swtr.sweets.toJSON());
217 swtr.appView.$overlay.hide();
218 swtr.appView.helpview.step(6);
219 },
220 error: function(jqxhr, error, text) {
221 console.log(jqxhr, error, text);
222 swtr.appView.$overlay.hide();
223 swtr.appView.helpview.step(10);
224 }
225 });
226 } catch(e) {
227 if(e.message == 'Access Token is required to sweet') {
228 appView.$overlay.hide();
229 appView.helpview.step(9);
230 }
231 }
232 this.cleanUp();
233 return false;
234 },
235 cleanUp: function() {
236 //console.log('cleaning up');
237 $(this.el).hide();
238 }
239 });
240
241 var FilterView = Backbone.View.extend({
242 el: $('#filter-div'),
243 events: {
244 'click #filter-user-div input': 'filter',
245 'click #filter-tags-div input': 'filter'
246 },
247 initialize: function() {
248 this.filter_users_template = _.template($('#filter-users').html());
249 this.filter_tags_template = _.template($('#filter-tags').html());
250 this.render();
251 },
252 render: function() {
253 //console.log(this.collection);
254 // pluck uniq authors of sweets
255 var authors = _.uniq(this.collection.pluck('who'));
256 // render them as filter controls
257 _.each(authors, function(author) {
258 $('#filter-user-div').append(this.filter_users_template({
259 who: author
260 }));
261 }, this);
262
263 // pluck uniq tags of sweets
264 var tags = _.chain(this.collection.pluck('how')).pluck('tags').flatten().
265 uniq().value();
266 // render them as filter controls
267 _.each(tags, function(tag) {
268 if(tag) {
269 $('#filter-tags-div').append(this.filter_tags_template({
270 tag: tag
271 }));
272 }
273 }, this);
274
275 //this.delegateEvents();
276 },
277 filter: function(event) {
278 // get id of div - parent to parent to the clicked input
279 var target_id = $(event.currentTarget).parent().parent().attr('id');
280 // find out user/tag div
281 var which = target_id.split('-')[1];
282
283 var selected = [];
284 $('#'+target_id + ' input:checked').each(function() {
285 selected.push($(this).attr('name'));
286 });
287
288 if(which === 'user') {
289 this.filterUsers(selected);
290 }
291 else if(which === 'tags') {
292 this.filterTags(selected);
293 }
294 },
295 filterUsers: function(users) {
296 if(!users.length) {
297 return;
298 }
299 var filtered_swts = this.collection.filter(function(model) {
300 if(_.indexOf(users, model.get('who')) > -1) {
301 return model;
302 }
303 });
304 if(filtered_swts.length) {
305 anno.removeAll();
306 _.each(filtered_swts, function(swt) {
307 anno.addAnnotation(swt.get('how'));
308 });
309 }
310 },
311 filterTags: function(tags) {
312 if(!tags.length) {
313 return;
314 }
315 var filtered_swts = this.collection.filter(function(model) {
316 //TODO: find a better way of doing this..
317 var flag = false;
318 _.each(model.get('how').tags, function(tag) {
319 if(_.indexOf(tags, tag) > -1) {
320 flag = true;
321 }
322 });
323 if(flag === true) {
324 return model;
325 }
326 });
327 if(filtered_swts.length) {
328 anno.removeAll();
329 _.each(filtered_swts, function(swt) {
330 anno.addAnnotation(swt.get('how'));
331 });
332 }
333 },
334 filterSweet: function(event) {
335 /*if(!event.currentTarget.checked) {
336 var results = this.collection.filter(function(model) {
337 if(model.get('who') != event.currentTarget.name)
338 return model;
339 });
340 if(results.length) {
341 _.each(results, function(result) {
342 anno.removeAnnotation(result.get('how'));
343 });
344 }
345 else { // if results is empty then remove all anno.
346 anno.removeAll();
347 }
348 }
349 else {
350 results = this.collection.filter(function(model) {
351 if(model.get('who') == event.currentTarget.name)
352 return model;
353 });
354 _.each(results, function(result) {
355 anno.addAnnotation(result.get('how'));
356 });
357
358 }
359 // if(results) {
360 // anno.removeAll();
361 // }
362 // swtr.annoView.collection = results;
363 // swtr.annoView.renderWith();*/
364 }
365 });
366
367 var AppView = Backbone.View.extend({
368 el: $('body'),
369 events: {
370 'click #user-input-submit': 'submitUserInput',
371 'click #sweet': 'sweet',
372 'click #sign-in': 'signIn',
373 'click #ocd-source': 'sourceChanged'
374 //'mouseup .annotorious-editor-button-save': 'addnew_anno'
375 },
376 initialize: function() {
377 // initialize components
378 this.source = 'none';
379 this.helpview = new HelpView();
380 this.sweetsview = new SweetsView({collection: swtr.sweets});
381
382 // cache jquery selected elements which are used frequently
383 this.$overlay = $('#app-overlay');
384 this.$img = $('#annotatable-img');
385
386 this.helpview.step(1);
387 // initialize the oauth stuff
388 this.oauth = new Oauth({
389 app_id: swtr.app_id,
390 endpoint: swtr.swtstoreURL() + swtr.endpoints.auth,
391 redirect_uri: swtr.oauth_redirect_uri,
392 scopes: 'email,sweet'
393 });
394 },
395 submitUserInput: function(event) {
396 event.preventDefault();
397 var input = $('#user-input').val();
398 if(this.source === 'ocd') {
399 this.loadOCDSearch(input);
400 }
401 else if (this.source === 'none') {
402 this.loadURL(input);
403 }
404 },
405 // load a URL for annotation (can be of image or html resource for now)
406 loadURL: function(url, type) {
407 //console.log('loadURL()');
408 if(this.source !== 'ocd') {
409 $('#ocd-results').hide();
410 }
411 $('#img-annotation-wrapper').show();
412 if(!url || !url.match(/http/)) {
413 this.helpview.step(13);
414 return false;
415 }
416 // if type is given explicitly; we load it as such.
417 if(type === 'image') {
418 if(swtr.imgAnnoView) {
419 swtr.imgAnnoView.setImage(url);
420 }
421 else {
422 swtr.imgAnnoView = new swtr.ImgAnnoView({collection:swtr.sweets,
423 img: this.$img[0],
424 $img: this.$img,
425 url: url});
426 }
427 return false;
428 }
429 // else try to find what resource is the URL..
430 // if url has an image extension then load the image annotation
431 if(url.match(/.jpg|.jpeg|.png|.gif|.bmp|.svg/)) {
432 if(swtr.imgAnnoView) {
433 swtr.imgAnnoView.setImage(url);
434 }
435 else {
436 swtr.imgAnnoView = new swtr.ImgAnnoView({collection:swtr.sweets,
437 img: this.$img[0],
438 $img: this.$img,
439 url: url});
440 }
441
442 return false;
443 }
444 // else check with our /media-type endpoint to see what type of resource
445 // it is
446 else {
447 this.helpview.step(12);
448 this.$overlay.show();
449 var self = this;
450 $.get('/media-type', {where: url}, function(response) {
451 //console.log(response);
452 self.$overlay.hide();
453 if(response.type === 'image') {
454 if(swtr.imgAnnoView) {
455 swtr.imgAnnoView.setImage(url);
456 }
457 else {
458 swtr.imgAnnoView = new swtr.ImgAnnoView({collection:swtr.sweets,
459 img: self.$img[0],
460 $img: self.$img,
461 url: url});
462 }
463 }
464 else {
465 window.location.href = '/annotate?where=' + url;
466 }
467 });
468 }
469 },
470 getSweets: function() {
471 var annos = _.filter(anno.getAnnotations(), function(anno) {
472 return (!_.has(anno, 'editable') || anno.editable === true);
473 });
474
475 _.each(annos, function(anno) {
476 swtr.sweets.add({
477 who: swtr.who,
478 where: anno.src,
479 // remove the text field; we don't want to store that in the sweets
480 how: _.omit(anno, 'text')
481 });
482 });
483 },
484 showSweets: function() {
485 this.sweetsview.render();
486 },
487 sweet: function() {
488 this.getSweets();
489 this.showSweets();
490 return false;
491 },
492 signIn: function(event) {
493 event.preventDefault();
494 this.oauth.authorize();
495 return false;
496 },
497 userLoggedIn: function(username) {
498 swtr.who = username;
499 var text = 'Signed in as <b>' + swtr.who + '</b>';
500 $('#signinview').html(text);
501 },
502 userLoggedOut: function() {
503 swtr.who = 'Guest';
504 $('#signinview').html('Logged out');
505 },
506 changeURLInputPlaceholder: function(source) {
507 switch (source) {
508 case 'ocd' : $('#user-input').attr('placeholder', 'Enter search query');
509 break;
510 case 'none' : $('#user-input').attr('placeholder', 'Enter URL of image or web page');
511 break;
512 }
513 },
514 // function to change the source in the application and update the UI
515 changeSource: function(source) {
516 switch (source) {
517 case 'ocd' : this.source = 'ocd';
518 this.helpview.step(11);
519 this.changeURLInputPlaceholder('ocd');
520 break;
521 case 'none' : this.source = 'none';
522 this.helpview.step(1);
523 this.changeURLInputPlaceholder('none');
524 break;
525 }
526 },
527 // event handler to capture control panel UI change of source
528 sourceChanged: function(event) {
529 if($('#ocd-source').is(':checked')) {
530 this.changeSource('ocd');
531 }
532 else {
533 this.changeSource('none');
534 }
535 },
536 loadOCDSearch: function(input) {
537 var self = this;
538 $('#img-annotation-wrapper').hide();
539 $('#ocd-results').show();
540 $('#ocd-results').html('<h4 style="text-align: center;">Loading..</h4>');
541 $.ajax({
542 type: 'GET',
543 url: '/search/ocd',
544 data: {query: input},
545 success: function(data) {
546 self.ocdView = new OCDView({
547 query: input,
548 data: data,
549 model: data.hits.hits
550 });
551 }
552 });
553 }
554 });
555
556 var OCDView = Backbone.View.extend({
557 el: $('#ocd-view'),
558 events: {
559 'click .ocd-item a': 'onImgClick',
560 'click .pager li': 'onPagerClick'
561 },
562 initialize: function(opts) {
563 this.data = opts.data || {};
564 this.query = opts.query || '';
565 this.size = 9; // num of items per page
566 this.page = 0;
567 this.item_template = _.template($('#ocd-item-template').html());
568 this.base_template = _.template($('#ocd-view-base-template').html());
569 this.render();
570 },
571 render: function() {
572 var $row_el;
573 this.$el.html('');
574 if(!this.model.length) {
575 this.$el.html('No results could be found from your query.');
576 return;
577 }
578 this.$el.html(this.base_template());
579 var $el = $('#ocd-results');
580 _.each(this.model, function(item, idx) {
581 // put every 3 items in a row
582 if(idx % 3 === 0) {
583 $row_el = $('<div class="row"></div>');
584 $el.append($row_el);
585 }
586 $row_el.append(this.item_template({
587 title: item._source.title,
588 media_url: item._source.media_urls[0].url,
589 authors: item._source.authors
590 }));
591 }, this);
592 this.resolveOCDURLs();
593 this.appendTotal();
594 },
595 appendTotal: function() {
596 $('#ocd-total-results').html(this.data.hits.total + ' results found.');
597 },
598 // resolve the OCD media URLs
599 resolveOCDURLs: function() {
600 var self = this;
601 $('.ocd-item').each(function(idx, elem) {
602 var temp_arr = self.model[idx]._source.media_urls[0].url.split('/');
603 var media_hash = temp_arr[temp_arr.length - 1];
604 $.get('/resolve-ocd-media', {hash: media_hash}, function(resp) {
605 $(elem).find('img').attr('src', resp.url);
606 });
607 });
608 },
609 rerender: function(data) {
610 this.data = data;
611 this.model = data.hits.hits;
612 this.render();
613 },
614 onPagerClick: function(event) {
615 event.preventDefault();
616 var elem = $(event.currentTarget);
617 var self = this;
618 if(elem.hasClass('next')) {
619 if((this.page + 1) * this.size >= this.data.hits.total) {
620 console.log('no next page to go to');
621 return false;
622 }
623 console.log('clicked next');
624 this.search({
625 query: this.query,
626 from: (this.page + 1) * this.size
627 }, function(resp) {
628 console.log('reached next page');
629 self.page = self.page + 1;
630 self.rerender(resp);
631 });
632 }
633 else if (elem.hasClass('previous')) {
634 if(this.page <= 0) {
635 console.log('no prev page to go to');
636 return false;
637 }
638 console.log('clicked prev');
639 this.search({
640 query: this.query,
641 from: (this.page - 1) * this.size
642 }, function(resp) {
643 console.log('reached prev page');
644 self.page = self.page - 1;
645 self.rerender(resp);
646 });
647 }
648 return false;
649 },
650 onImgClick: function(event) {
651 event.preventDefault();
652 // TODO: init the image anno
653 var url = $(event.currentTarget).find('img').attr('src');
654 swtr.appView.loadURL(url, 'image');
655 return false;
656 },
657 search: function(data, cb) {
658 swtr.appView.$overlay.show();
659 var self = this;
660 $.ajax({
661 type: 'GET',
662 url: '/search/ocd',
663 data: data,
664 success: function(resp) {
665 swtr.appView.$overlay.hide();
666 cb(resp);
667 }
668 });
669 }
670 });
671
672 var HelpView = Backbone.View.extend({
673 el: $('#helpview'),
674 events: {
675 },
676 initialize: function() {
677 this.$text_el = $('#helpview-text');
678 },
679 //TODO: move from number based steps to something else. number based steps
680 //implicitly imply sequential processing..which does not happen in this
681 //case..
682 //following helps can be async..
683 step: function(n) {
684 var text = '';
685 switch (n) {
686 case 0 : text = 'Getting annotations..';
687 break;
688 case 1: text = 'Enter URL of an image or web page below, and start annotating!';
689 break;
690 case 2: text = 'Annotate the image, or see other annotations';
691 break;
692 case 3: text = 'Now you can sweet this annotation, or add more annotations';
693 break;
694 case 4: text = 'Click Sweet button to publish these annotations to the Sweet Store';
695 break;
696 case 5: text = 'Publishing your sweets';
697 break;
698 case 6: text = 'Sweets successfully posted';
699 break;
700 case 7: text = 'Fetching your image..';
701 break;
702 case 8: text = 'Oops! Seems like the image URL is wrong! Or we couldn\'t fetch the image.';
703 break;
704 case 9: text = 'You have to be <i>signed in</i> to sweet store to post sweets';
705 break;
706 case 10: text = 'Oops! Something went wrong. We couldn\'t publish the sweets. Try again.'
707 break;
708 case 11: text = 'Search in <a href="http://www.opencultuurdata.nl/">Open Cuultur Data API</a>';
709 break;
710 case 12: text = 'Analyzing the resource type..';
711 break;
712 case 13: text = 'This does not seem to be a URL. Please enter a valid URL.';
713 break;
714 }
715 $(this.$text_el).html(text);
716 $(window).scrollTop(0, 0);
717 }
718 });
719
720
721 // utilities and helper functions to go here
722 swtr.utils = {
723 linkify: function(link) {
724 if(link.match('http')) {
725 return link;
726 }
727 else {
728 return 'http://' + link;
729 }
730 }
731 };
732 //swtr.AppView = AppView;
733
734 var AppRouter = Backbone.Router.extend({
735 routes: {
736 'home': 'home',
737 'linked-data': 'linkedData',
738 'play': 'play',
739 'search': 'search'
740 },
741 home: function() {
742 this.hideAll();
743 this.show('home-page');
744 },
745 linkedData: function() {
746 this.hideAll();
747 this.show('linked-data-page');
748 },
749 play: function() {
750 this.hideAll();
751 this.show('play-page');
752 },
753 search: function() {
754 this.hideAll();
755 this.show('search-page');
756 },
757 hideAll: function() {
758 $('.page').hide();
759 },
760 show: function(id) {
761 $('#' + id).show();
762 this.highlight(id);
763 },
764 highlight: function(id) {
765 $('#swtr-navbar-collapse li').removeClass('active');
766 var href = id.split('-page')[0];
767 var selector = '#swtr-navbar-collapse a[href="#/' + href + '"]';
768 $(selector).parent('li').addClass('active');
769 }
770 });
771
772})(swtr);

swtr/static/js/swtmaker.js

1(function(swtr) {
2
3 //TODO: find a better way to init.
4 //Find a better way to do closure
5 //Remove script code from the HTML page
6 swtr.init = function() {
7 this.sweets = new ImgAnnoSwts();
8 this.appView = new AppView();
9 this.who = 'Guest';
10
11 this.app_router = new AppRouter();
12 Backbone.history.start();
13 this.app_router.navigate('home');
14
15 $.ajaxSetup({
16 xhrFields: {
17 // we need this to send cookies to cross-domain requests
18 withCredentials: true
19 },
20 //some browsers won't make cross-domain ajax until it is explicitly set
21 crossDomain: true
22 });
23 this.handleOAuth();
24 };
25
26 swtr.handleOAuth = function() {
27 if(swtr.access_token) {
28 $('#signinview').html('Signing you in..');
29 $.ajax({
30 url: swtr.swtstoreURL()+'/api/users/me?access_token='+
31 swtr.access_token,
32 success: function(data) {
33 swtr.appView.userLoggedIn(data.username);
34 },
35 error: function() {
36 $('#signinview').html('Error signing in! Please try again');
37 }
38 });
39 }
40 };
41
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
143 var SweetsView = Backbone.View.extend({
144 el: $('#sweet-list-wrapper'),
145 events: {
146 'click #sweet-cancel': 'cancelSweeting',
147 'click #post-sweet': 'postSweets'
148 },
149 initialize: function() {
150 this.template = _.template($('#sweet-template').html());
151 },
152 render: function() {
153 $('#sweet-list').html('<h4>These are your sweet annotations!</h4>');
154 _.each(this.collection.models, function(swt) {
155 if(swt.has('id')) {
156 return false;
157 }
158 $('#sweet-list').append(this.template({
159 who: swt.get('who'),
160 what: swt.get('what'),
161 where: swt.get('where'),
162 how: JSON.stringify(this.getHumanReadableParts(swt.get('how')))
163 }));
164 }, this);
165 $(this.el).fadeIn(300);
166 },
167 getHumanReadableParts: function(how) {
168 var human_readable_json = {};
169 if(how.comment) {
170 human_readable_json['comment'] = how.comment;
171 }
172 if(how.title) {
173 human_readable_json['title'] = how.title;
174 }
175 if(how.tags) {
176 human_readable_json['tags'] = how.tags;
177 }
178 if(how.link) {
179 human_readable_json['link'] = how.link;
180 }
181 return human_readable_json;
182 },
183 cancelSweeting: function() {
184 this.removeSwtsNotPosted();
185 this.cleanUp();
186 },
187 removeSwtsNotPosted: function() {
188 var notPosted = this.collection.filter(function(model) {
189 return !model.has('id');
190 });
191 this.collection.remove(notPosted);
192 },
193 postSweets: function() {
194 console.log("postSWr");
195 var appView = swtr.appView;
196 appView.helpview.step(5);
197 appView.$overlay.show();
198 try {
199 this.collection.post({
200 success: function(collection, response) {
201 console.log(collection, response);
202 swtr.sweets.set(collection);
203 //TODO: move this to a annotation view or something
204// anno.removeAll();
205 // _.each(swtr.sweets.models, function(swt) {
206 // if(!_.has(swt.get('how'), 'editable')) {
207 // swt.get('how')['editable'] = false;
208 // //console.log(swt.get('how').text.Comment);
209 // swt.get('how').text = swtr.imgAnnoView.createPopupText(swt.get('how'));
210 // //console.log(swt.get('how'));
211 // swt.get('how').text += '\n - by ' + swt.get('who');
212 // }
213 // //console.log(swt.get('how'));
214 // anno.addAnnotation(swt.get('how'));
215 // });
216 //console.log(swtr.sweets.toJSON());
217 swtr.appView.$overlay.hide();
218 swtr.appView.helpview.step(6);
219 },
220 error: function(jqxhr, error, text) {
221 console.log(jqxhr, error, text);
222 swtr.appView.$overlay.hide();
223 swtr.appView.helpview.step(10);
224 }
225 });
226 } catch(e) {
227 if(e.message == 'Access Token is required to sweet') {
228 appView.$overlay.hide();
229 appView.helpview.step(9);
230 }
231 }
232 this.cleanUp();
233 return false;
234 },
235 cleanUp: function() {
236 //console.log('cleaning up');
237 $(this.el).hide();
238 }
239 });
240
241 var FilterView = Backbone.View.extend({
242 el: $('#filter-div'),
243 events: {
244 'click #filter-user-div input': 'filter',
245 'click #filter-tags-div input': 'filter'
246 },
247 initialize: function() {
248 this.filter_users_template = _.template($('#filter-users').html());
249 this.filter_tags_template = _.template($('#filter-tags').html());
250 this.render();
251 },
252 render: function() {
253 //console.log(this.collection);
254 // pluck uniq authors of sweets
255 var authors = _.uniq(this.collection.pluck('who'));
256 // render them as filter controls
257 _.each(authors, function(author) {
258 $('#filter-user-div').append(this.filter_users_template({
259 who: author
260 }));
261 }, this);
262
263 // pluck uniq tags of sweets
264 var tags = _.chain(this.collection.pluck('how')).pluck('tags').flatten().
265 uniq().value();
266 // render them as filter controls
267 _.each(tags, function(tag) {
268 if(tag) {
269 $('#filter-tags-div').append(this.filter_tags_template({
270 tag: tag
271 }));
272 }
273 }, this);
274
275 //this.delegateEvents();
276 },
277 filter: function(event) {
278 // get id of div - parent to parent to the clicked input
279 var target_id = $(event.currentTarget).parent().parent().attr('id');
280 // find out user/tag div
281 var which = target_id.split('-')[1];
282
283 var selected = [];
284 $('#'+target_id + ' input:checked').each(function() {
285 selected.push($(this).attr('name'));
286 });
287
288 if(which === 'user') {
289 this.filterUsers(selected);
290 }
291 else if(which === 'tags') {
292 this.filterTags(selected);
293 }
294 },
295 filterUsers: function(users) {
296 if(!users.length) {
297 return;
298 }
299 var filtered_swts = this.collection.filter(function(model) {
300 if(_.indexOf(users, model.get('who')) > -1) {
301 return model;
302 }
303 });
304 if(filtered_swts.length) {
305 anno.removeAll();
306 _.each(filtered_swts, function(swt) {
307 anno.addAnnotation(swt.get('how'));
308 });
309 }
310 },
311 filterTags: function(tags) {
312 if(!tags.length) {
313 return;
314 }
315 var filtered_swts = this.collection.filter(function(model) {
316 //TODO: find a better way of doing this..
317 var flag = false;
318 _.each(model.get('how').tags, function(tag) {
319 if(_.indexOf(tags, tag) > -1) {
320 flag = true;
321 }
322 });
323 if(flag === true) {
324 return model;
325 }
326 });
327 if(filtered_swts.length) {
328 anno.removeAll();
329 _.each(filtered_swts, function(swt) {
330 anno.addAnnotation(swt.get('how'));
331 });
332 }
333 },
334 filterSweet: function(event) {
335 /*if(!event.currentTarget.checked) {
336 var results = this.collection.filter(function(model) {
337 if(model.get('who') != event.currentTarget.name)
338 return model;
339 });
340 if(results.length) {
341 _.each(results, function(result) {
342 anno.removeAnnotation(result.get('how'));
343 });
344 }
345 else { // if results is empty then remove all anno.
346 anno.removeAll();
347 }
348 }
349 else {
350 results = this.collection.filter(function(model) {
351 if(model.get('who') == event.currentTarget.name)
352 return model;
353 });
354 _.each(results, function(result) {
355 anno.addAnnotation(result.get('how'));
356 });
357
358 }
359 // if(results) {
360 // anno.removeAll();
361 // }
362 // swtr.annoView.collection = results;
363 // swtr.annoView.renderWith();*/
364 }
365 });
366
367 var AppView = Backbone.View.extend({
368 el: $('body'),
369 events: {
370 'click #user-input-submit': 'submitUserInput',
371 'click #sweet': 'sweet',
372 'click #sign-in': 'signIn',
373 'click #ocd-source': 'sourceChanged'
374 //'mouseup .annotorious-editor-button-save': 'addnew_anno'
375 },
376 initialize: function() {
377 // initialize components
378 this.source = 'none';
379 this.helpview = new HelpView();
380 this.sweetsview = new SweetsView({collection: swtr.sweets});
381
382 // cache jquery selected elements which are used frequently
383 this.$overlay = $('#app-overlay');
384 this.$img = $('#annotatable-img');
385
386 this.helpview.step(1);
387 // initialize the oauth stuff
388 this.oauth = new Oauth({
389 app_id: swtr.app_id,
390 endpoint: swtr.swtstoreURL() + swtr.endpoints.auth,
391 redirect_uri: swtr.oauth_redirect_uri,
392 scopes: 'email,sweet'
393 });
394 },
395 submitUserInput: function(event) {
396 event.preventDefault();
397 var input = $('#user-input').val();
398 if(this.source === 'ocd') {
399 this.loadOCDSearch(input);
400 }
401 else if (this.source === 'none') {
402 this.loadURL(input);
403 }
404 },
405 // load a URL for annotation (can be of image or html resource for now)
406 loadURL: function(url, type) {
407 //console.log('loadURL()');
408 if(this.source !== 'ocd') {
409 $('#ocd-results').hide();
410 }
411 $('#img-annotation-wrapper').show();
412 if(!url || !url.match(/http/)) {
413 this.helpview.step(13);
414 return false;
415 }
416 // if type is given explicitly; we load it as such.
417 if(type === 'image') {
418 if(swtr.imgAnnoView) {
419 swtr.imgAnnoView.setImage(url);
420 }
421 else {
422 swtr.imgAnnoView = new swtr.ImgAnnoView({collection:swtr.sweets,
423 img: this.$img[0],
424 $img: this.$img,
425 url: url});
426 }
427 return false;
428 }
429 // else try to find what resource is the URL..
430 // if url has an image extension then load the image annotation
431 if(url.match(/.jpg|.jpeg|.png|.gif|.bmp|.svg/)) {
432 if(swtr.imgAnnoView) {
433 swtr.imgAnnoView.setImage(url);
434 }
435 else {
436 swtr.imgAnnoView = new swtr.ImgAnnoView({collection:swtr.sweets,
437 img: this.$img[0],
438 $img: this.$img,
439 url: url});
440 }
441
442 return false;
443 }
444 // else check with our /media-type endpoint to see what type of resource
445 // it is
446 else {
447 this.helpview.step(12);
448 this.$overlay.show();
449 var self = this;
450 $.get('/media-type', {where: url}, function(response) {
451 //console.log(response);
452 self.$overlay.hide();
453 if(response.type === 'image') {
454 if(swtr.imgAnnoView) {
455 swtr.imgAnnoView.setImage(url);
456 }
457 else {
458 swtr.imgAnnoView = new swtr.ImgAnnoView({collection:swtr.sweets,
459 img: self.$img[0],
460 $img: self.$img,
461 url: url});
462 }
463 }
464 else {
465 window.location.href = '/annotate?where=' + url;
466 }
467 });
468 }
469 },
470 getSweets: function() {
471 var annos = _.filter(anno.getAnnotations(), function(anno) {
472 return (!_.has(anno, 'editable') || anno.editable === true);
473 });
474
475 _.each(annos, function(anno) {
476 swtr.sweets.add({
477 who: swtr.who,
478 where: anno.src,
479 // remove the text field; we don't want to store that in the sweets
480 how: _.omit(anno, 'text')
481 });
482 });
483 },
484 showSweets: function() {
485 this.sweetsview.render();
486 },
487 sweet: function() {
488 this.getSweets();
489 this.showSweets();
490 return false;
491 },
492 signIn: function(event) {
493 event.preventDefault();
494 this.oauth.authorize();
495 return false;
496 },
497 userLoggedIn: function(username) {
498 swtr.who = username;
499 var text = 'Signed in as <b>' + swtr.who + '</b>';
500 $('#signinview').html(text);
501 },
502 userLoggedOut: function() {
503 swtr.who = 'Guest';
504 $('#signinview').html('Logged out');
505 },
506 changeURLInputPlaceholder: function(source) {
507 switch (source) {
508 case 'ocd' : $('#user-input').attr('placeholder', 'Enter search query');
509 break;
510 case 'none' : $('#user-input').attr('placeholder', 'Enter URL of image or web page');
511 break;
512 }
513 },
514 // function to change the source in the application and update the UI
515 changeSource: function(source) {
516 switch (source) {
517 case 'ocd' : this.source = 'ocd';
518 this.helpview.step(11);
519 this.changeURLInputPlaceholder('ocd');
520 break;
521 case 'none' : this.source = 'none';
522 this.helpview.step(1);
523 this.changeURLInputPlaceholder('none');
524 break;
525 }
526 },
527 // event handler to capture control panel UI change of source
528 sourceChanged: function(event) {
529 if($('#ocd-source').is(':checked')) {
530 this.changeSource('ocd');
531 }
532 else {
533 this.changeSource('none');
534 }
535 },
536 loadOCDSearch: function(input) {
537 var self = this;
538 $('#img-annotation-wrapper').hide();
539 $('#ocd-results').show();
540 $('#ocd-results').html('<h4 style="text-align: center;">Loading..</h4>');
541 $.ajax({
542 type: 'GET',
543 url: '/search/ocd',
544 data: {query: input},
545 success: function(data) {
546 self.ocdView = new OCDView({
547 query: input,
548 data: data,
549 model: data.hits.hits
550 });
551 }
552 });
553 }
554 });
555
556 var OCDView = Backbone.View.extend({
557 el: $('#ocd-view'),
558 events: {
559 'click .ocd-item a': 'onImgClick',
560 'click .pager li': 'onPagerClick'
561 },
562 initialize: function(opts) {
563 this.data = opts.data || {};
564 this.query = opts.query || '';
565 this.size = 9; // num of items per page
566 this.page = 0;
567 this.item_template = _.template($('#ocd-item-template').html());
568 this.base_template = _.template($('#ocd-view-base-template').html());
569 this.render();
570 },
571 render: function() {
572 var $row_el;
573 this.$el.html('');
574 if(!this.model.length) {
575 this.$el.html('No results could be found from your query.');
576 return;
577 }
578 this.$el.html(this.base_template());
579 var $el = $('#ocd-results');
580 _.each(this.model, function(item, idx) {
581 // put every 3 items in a row
582 if(idx % 3 === 0) {
583 $row_el = $('<div class="row"></div>');
584 $el.append($row_el);
585 }
586 $row_el.append(this.item_template({
587 title: item._source.title,
588 media_url: item._source.media_urls[0].url,
589 authors: item._source.authors
590 }));
591 }, this);
592 this.resolveOCDURLs();
593 this.appendTotal();
594 },
595 appendTotal: function() {
596 $('#ocd-total-results').html(this.data.hits.total + ' results found.');
597 },
598 // resolve the OCD media URLs
599 resolveOCDURLs: function() {
600 var self = this;
601 $('.ocd-item').each(function(idx, elem) {
602 var temp_arr = self.model[idx]._source.media_urls[0].url.split('/');
603 var media_hash = temp_arr[temp_arr.length - 1];
604 $.get('/resolve-ocd-media', {hash: media_hash}, function(resp) {
605 $(elem).find('img').attr('src', resp.url);
606 });
607 });
608 },
609 rerender: function(data) {
610 this.data = data;
611 this.model = data.hits.hits;
612 this.render();
613 },
614 onPagerClick: function(event) {
615 event.preventDefault();
616 var elem = $(event.currentTarget);
617 var self = this;
618 if(elem.hasClass('next')) {
619 if((this.page + 1) * this.size >= this.data.hits.total) {
620 console.log('no next page to go to');
621 return false;
622 }
623 console.log('clicked next');
624 this.search({
625 query: this.query,
626 from: (this.page + 1) * this.size
627 }, function(resp) {
628 console.log('reached next page');
629 self.page = self.page + 1;
630 self.rerender(resp);
631 });
632 }
633 else if (elem.hasClass('previous')) {
634 if(this.page <= 0) {
635 console.log('no prev page to go to');
636 return false;
637 }
638 console.log('clicked prev');
639 this.search({
640 query: this.query,
641 from: (this.page - 1) * this.size
642 }, function(resp) {
643 console.log('reached prev page');
644 self.page = self.page - 1;
645 self.rerender(resp);
646 });
647 }
648 return false;
649 },
650 onImgClick: function(event) {
651 event.preventDefault();
652 // TODO: init the image anno
653 var url = $(event.currentTarget).find('img').attr('src');
654 swtr.appView.loadURL(url, 'image');
655 return false;
656 },
657 search: function(data, cb) {
658 swtr.appView.$overlay.show();
659 var self = this;
660 $.ajax({
661 type: 'GET',
662 url: '/search/ocd',
663 data: data,
664 success: function(resp) {
665 swtr.appView.$overlay.hide();
666 cb(resp);
667 }
668 });
669 }
670 });
671
672 var HelpView = Backbone.View.extend({
673 el: $('#helpview'),
674 events: {
675 },
676 initialize: function() {
677 this.$text_el = $('#helpview-text');
678 },
679 //TODO: move from number based steps to something else. number based steps
680 //implicitly imply sequential processing..which does not happen in this
681 //case..
682 //following helps can be async..
683 step: function(n) {
684 var text = '';
685 switch (n) {
686 case 0 : text = 'Getting annotations..';
687 break;
688 case 1: text = 'Enter URL of an image or web page below, and start annotating!';
689 break;
690 case 2: text = 'Annotate the image, or see other annotations';
691 break;
692 case 3: text = 'Now you can sweet this annotation, or add more annotations';
693 break;
694 case 4: text = 'Click Sweet button to publish these annotations to the Sweet Store';
695 break;
696 case 5: text = 'Publishing your sweets';
697 break;
698 case 6: text = 'Sweets successfully posted';
699 break;
700 case 7: text = 'Fetching your image..';
701 break;
702 case 8: text = 'Oops! Seems like the image URL is wrong! Or we couldn\'t fetch the image.';
703 break;
704 case 9: text = 'You have to be <i>signed in</i> to sweet store to post sweets';
705 break;
706 case 10: text = 'Oops! Something went wrong. We couldn\'t publish the sweets. Try again.'
707 break;
708 case 11: text = 'Search in <a href="http://www.opencultuurdata.nl/">Open Cuultur Data API</a>';
709 break;
710 case 12: text = 'Analyzing the resource type..';
711 break;
712 case 13: text = 'This does not seem to be a URL. Please enter a valid URL.';
713 break;
714 }
715 $(this.$text_el).html(text);
716 $(window).scrollTop(0, 0);
717 }
718 });
719
720
721 // utilities and helper functions to go here
722 swtr.utils = {
723 linkify: function(link) {
724 if(link.match('http')) {
725 return link;
726 }
727 else {
728 return 'http://' + link;
729 }
730 }
731 };
732 //swtr.AppView = AppView;
733
734 var AppRouter = Backbone.Router.extend({
735 routes: {
736 'home': 'home',
737 'linked-data': 'linkedData',
738 'play': 'play',
739 'search': 'search'
740 },
741 home: function() {
742 this.hideAll();
743 this.show('home-page');
744 },
745 linkedData: function() {
746 this.hideAll();
747 this.show('linked-data-page');
748 },
749 play: function() {
750 this.hideAll();
751 this.show('play-page');
752 },
753 search: function() {
754 this.hideAll();
755 this.show('search-page');
756 },
757 hideAll: function() {
758 $('.page').hide();
759 },
760 show: function(id) {
761 $('#' + id).show();
762 this.highlight(id);
763 },
764 highlight: function(id) {
765 $('#swtr-navbar-collapse li').removeClass('active');
766 var href = id.split('-page')[0];
767 var selector = '#swtr-navbar-collapse a[href="#/' + href + '"]';
768 $(selector).parent('li').addClass('active');
769 }
770 });
771
772})(swtr);

swtr/templates/index.html

179 <script src="{{ url_for('static', filename='js/lib/custom-fields-plugin.js') }}"></script>179 <script src="{{ url_for('static', filename='js/lib/custom-fields-plugin.js') }}"></script>
180 <script src="{{ url_for('static', filename='js/oauth.js') }}"></script>180 <script src="{{ url_for('static', filename='js/oauth.js') }}"></script>
181 <script type="text/javascript" src="{{ url_for('static', filename='js/lib/bootstrap-tags.min.js') }}"></script>181 <script type="text/javascript" src="{{ url_for('static', filename='js/lib/bootstrap-tags.min.js') }}"></script>
182 <script src="{{ url_for('static', filename='js/imganno.js') }}"></script>
183 <script src="{{ url_for('static', filename='js/swtmaker.js') }}"></script>
182 <script src="{{ url_for('static', filename='js/img_swtr.js') }}"></script>
183 <script src="{{ url_for('static', filename='js/main.js') }}"></script>
184184
185 <script type="text/template" id="sweet-template">185 <script type="text/template" id="sweet-template">
186 <li class="sweet">186 <li class="sweet">