nmhive.py: Return 400 errors for data-less POSTs
[nmhive.git] / nmbug.js
1 var nmbug_server = 'http://localhost:5000';
2
3 nmbug = {
4         show: function (message_id, frame) {
5                 var _this = this;
6                 if (frame === undefined) {
7                         frame = window;
8                 }
9                 this._get_available_tags(function (available_tags) {
10                         _this._get_tags(
11                                 message_id,
12                                 _this._edit_tags.bind(_this, frame, available_tags, message_id));
13                 });
14         },
15         _get_available_tags: function (callback) {
16                 var url = [
17                         nmbug_server,
18                         'tags',
19                         ].join('/');
20                 console.log('nmbug: get available tags from ' + url);
21                 var request = new XMLHttpRequest();
22                 request.onload = function () {
23                         if (this.status == 200) {
24                                 var available_tags = JSON.parse(this.response);
25                                 console.log('nmbug: got available tags', available_tags);
26                                 callback(available_tags);
27                         } else {
28                                 throw 'Error fetching ' + url + ' (status ' + this.status + ')';
29                         }
30                 };
31                 request.open('get', url, true);
32                 request.send();
33         },
34         _get_tags: function (message_id, callback) {
35                 var url = [
36                         nmbug_server,
37                         'mid',
38                         encodeURIComponent(message_id),
39                         ].join('/');
40                 console.log('nmbug: get tags from ' + url);
41                 var request = new XMLHttpRequest();
42                 request.onload = function () {
43                         if (this.status == 200) {
44                                 var tags = JSON.parse(this.response);
45                                 console.log('nmbug: got tags', tags);
46                                 callback(tags);
47                         } else {
48                                 throw 'Error fetching ' + url + ' (status ' + this.status + ')';
49                         }
50                 };
51                 request.open('get', url, true);
52                 request.send();
53         },
54         _edit_tags: function (frame, available_tags, message_id, tags) {
55                 var have_dialog_polyfill;
56                 try {
57                         have_dialog_polyfill = frame.dialogPolyfill !== undefined;
58                 } catch (error) {
59                         if (error.name == 'ReferenceError') {
60                                 have_dialog_polyfill = false;
61                         }
62                 }
63                 if (frame.document.createElement('dialog').show || have_dialog_polyfill) {
64                         this._x_edit_tags(frame, available_tags, message_id, tags);
65                 } else {
66                         var script = frame.document.createElement('script');
67                         script.type = 'text/javascript';
68                         script.src = nmbug_server + '/static/dialog-polyfill/dialog-polyfill.js';
69                         script.async = false;
70                         console.log('nmbug: loading dialog-polyfill.js');
71                         frame.document.head.appendChild(script);
72
73                         var link = frame.document.createElement('link');
74                         link.rel = 'stylesheet';
75                         link.type = 'text/css';
76                         link.href = nmbug_server + '/static/dialog-polyfill/dialog-polyfill.css';
77                         link.async = false;
78                         console.log('nmbug: loading dialog-polyfill.css');
79                         frame.document.head.appendChild(link);
80
81                         var _this = this;
82                         function edit_tags_after_dialog_polyfill () {
83                                 try {
84                                         have_dialog_polyfill = frame.dialogPolyfill !== undefined;
85                                         console.log('have dialogPolyfill');
86                                         window.setTimeout(
87                                                         _this._x_edit_tags.bind(_this), 200,
88                                                         frame, available_tags, message_id, tags);
89                                 } catch (error) {
90                                         if (error.name == 'ReferenceError') {
91                                                 console.log('waiting for dialogPolyfill');
92                                                 window.setTimeout(edit_tags_after_dialog_polyfill, 200);
93                                         }
94                                 }
95                         }
96                         edit_tags_after_dialog_polyfill();
97                 }
98         },
99         _x_edit_tags: function (frame, available_tags, message_id, tags) {
100                 var dialog = frame.document.createElement('dialog');
101                 if (!frame.document.createElement('dialog').show) {
102                         frame.dialogPolyfill.registerDialog(dialog);
103                 }
104
105                 dialog.style.border = '1px solid rgba(0, 0, 0, 0.3)';
106                 dialog.style.borderRadius = '6px';
107                 dialog.style.boxShadow = '0 3px 7px rgba(0, 0, 0, 0.3)';
108
109                 var content = frame.document.createElement('p');
110                 content.innerHTML = 'Edit tags for ' + message_id;
111                 dialog.appendChild(content);
112
113                 var ul = frame.document.createElement('ul');
114                 dialog.appendChild(ul);
115                 for (var i = 0; i < available_tags.length; i++) {
116                         var li = frame.document.createElement('li');
117                         li.innerHTML = available_tags[i];
118                         li.style.cursor = 'pointer';
119                         if (tags.indexOf(available_tags[i]) >= 0) {
120                                 li.style.backgroundColor = 'lime';
121                         }
122                         li.onclick = this._toggle_tag.bind(
123                                 this, message_id, available_tags[i], li);
124                         ul.appendChild(li);
125                 }
126                 var close = frame.document.createElement('button');
127                 close.innerHTML = 'Close';
128                 close.onclick = function () {
129                         dialog.close();
130                 };
131                 dialog.appendChild(close);
132
133                 frame.document.body.insertBefore(dialog, frame.document.body.firstChild);
134
135                 dialog.show();
136         },
137         _toggle_tag: function (message_id, tag, li) {
138                 var prefix;
139                 if (li.style.backgroundColor == 'lime') {
140                         prefix = '-';  /* unset */
141                         li.style.backgroundColor = null;
142                 } else {
143                         prefix = '+';  /* set */
144                         li.style.backgroundColor = 'lime';
145                 }
146                 var url = [
147                         nmbug_server,
148                         'mid',
149                         encodeURIComponent(message_id),
150                         ].join('/');
151                 console.log('nmbug: alter tags via ' + url);
152                 var request = new XMLHttpRequest();
153                 request.onload = function () {
154                         if (this.status == 200) {
155                                 var tags = JSON.parse(this.response);
156                                 console.log('nmbug: got tags', tags);
157                         } else {
158                                 throw 'Error posting to ' + url + ' (status ' + this.status + ')';
159                         }
160                 };
161                 request.open('post', url, true);
162                 request.setRequestHeader(
163                         'Content-Type', 'application/json; charset=UTF-8');
164                 request.send(JSON.stringify([prefix + tag]));
165         },
166 };
167
168 var _gmane_handler = {
169         regexp: /gmane[.]org/,
170         handle: function (callback) {
171                 var frame = this._get_frame();
172                 var article = this._article_from_url(frame.document.URL);
173                 this._get_message_id(article, function (message_id) {
174                         callback(message_id, frame);
175                 });
176         },
177         _article_from_url: function (url) {
178                 var regexp = new RegExp('http://article.gmane.org/([^/]+)/([0-9]+)');
179                 var match = regexp.exec(url);
180                 console.log('nmbug: get article from ' + url, match);
181                 if (match) {
182                         return {'group': match[1], 'id': parseInt(match[2])};
183                 }
184         },
185         _get_frame: function () {
186                 var frame = window;
187                 var article = this._article_from_url(frame.document.URL);
188                 var i = 0;
189                 for (var i = 0; !article && i < window.frames.length; i++) {
190                         frame = window.frames[i];
191                         article = this._article_from_url(frame.document.URL);
192                 }
193                 if (!article) {
194                         throw "Cannot extract an article from Gmane's " + document.URL;
195                 }
196                 return frame;
197         },
198         _get_message_id: function (article, callback) {
199                 var url = [
200                         nmbug_server,
201                         'gmane',
202                         article.group,
203                         article.id,
204                 ].join('/');
205                 console.log('nmbug: get Message-ID from ' + url);
206                 var request = new XMLHttpRequest();
207                 request.onload = function () {
208                         var message_id = this.responseText;
209                         callback(message_id);
210                 };
211                 request.open('get', url, true);
212                 request.send();
213         },
214 };
215
216 var handlers = [
217         _gmane_handler,
218 ];
219
220 function _check_handler(handler) {
221         var match = handler.regexp.test(document.URL);
222         console.log('nmbug: testing', handler, match);
223         if (match) {
224                 console.log('nmbug: matched', handler);
225                 handler.handle(nmbug.show.bind(nmbug));
226         }
227         return match;  /* break after the first match */
228 }
229
230 function run() {
231         var matched = handlers.some(_check_handler);
232         if (!matched) {
233                 throw 'No handler for ' + document.URL;
234         }
235 }