static/article.png: Add a screen-shot with an article.gmane.org/... page
[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                 dialog.style.marginLeft = '10em';
109                 dialog.style.marginRight = '10em';
110
111                 var content = frame.document.createElement('p');
112                 content.innerHTML = 'Edit tags for ' + message_id;
113                 dialog.appendChild(content);
114
115                 var tag_list = frame.document.createElement('p');
116                 dialog.appendChild(tag_list);
117                 for (var i = 0; i < available_tags.length; i++) {
118                         var tag = frame.document.createElement('a');
119                         tag.innerHTML = available_tags[i];
120                         tag.style.cursor = 'pointer';
121                         if (tags.indexOf(available_tags[i]) >= 0) {
122                                 tag.style.backgroundColor = 'lime';
123                         }
124                         tag.onclick = this._toggle_tag.bind(
125                                 this, message_id, available_tags[i], tag);
126                         tag_list.appendChild(tag);
127                         tag_list.appendChild(frame.document.createTextNode(' '));
128                 }
129                 var close = frame.document.createElement('button');
130                 close.innerHTML = 'Close';
131                 close.onclick = function () {
132                         dialog.close();
133                 };
134                 dialog.appendChild(close);
135
136                 frame.document.body.insertBefore(dialog, frame.document.body.firstChild);
137
138                 dialog.show();
139         },
140         _toggle_tag: function (message_id, tag, element) {
141                 var prefix;
142                 if (element.style.backgroundColor == 'lime') {
143                         prefix = '-';  /* unset */
144                         element.style.backgroundColor = null;
145                 } else {
146                         prefix = '+';  /* set */
147                         element.style.backgroundColor = 'lime';
148                 }
149                 var url = [
150                         nmbug_server,
151                         'mid',
152                         encodeURIComponent(message_id),
153                         ].join('/');
154                 console.log('nmbug: alter tags via ' + url);
155                 var request = new XMLHttpRequest();
156                 request.onload = function () {
157                         if (this.status == 200) {
158                                 var tags = JSON.parse(this.response);
159                                 console.log('nmbug: got tags', tags);
160                         } else {
161                                 throw 'Error posting to ' + url + ' (status ' + this.status + ')';
162                         }
163                 };
164                 request.open('post', url, true);
165                 request.setRequestHeader(
166                         'Content-Type', 'application/json; charset=UTF-8');
167                 request.send(JSON.stringify([prefix + tag]));
168         },
169 };
170
171 var _gmane_handler = {
172         regexp: /gmane[.]org/,
173         handle: function (callback) {
174                 var frame = this._get_frame();
175                 var article = this._article_from_url(frame.document.URL);
176                 this._get_message_id(article, function (message_id) {
177                         callback(message_id, frame);
178                 });
179         },
180         _article_from_url: function (url) {
181                 var regexp = new RegExp('http://article.gmane.org/([^/]+)/([0-9]+)');
182                 var match = regexp.exec(url);
183                 console.log('nmbug: get article from ' + url, match);
184                 if (match) {
185                         return {'group': match[1], 'id': parseInt(match[2])};
186                 }
187         },
188         _get_frame: function () {
189                 var frame = window;
190                 var article = this._article_from_url(frame.document.URL);
191                 var i = 0;
192                 for (var i = 0; !article && i < window.frames.length; i++) {
193                         frame = window.frames[i];
194                         article = this._article_from_url(frame.document.URL);
195                 }
196                 if (!article) {
197                         throw "Cannot extract an article from Gmane's " + document.URL;
198                 }
199                 return frame;
200         },
201         _get_message_id: function (article, callback) {
202                 var url = [
203                         nmbug_server,
204                         'gmane',
205                         article.group,
206                         article.id,
207                 ].join('/');
208                 console.log('nmbug: get Message-ID from ' + url);
209                 var request = new XMLHttpRequest();
210                 request.onload = function () {
211                         var message_id = this.responseText;
212                         callback(message_id);
213                 };
214                 request.open('get', url, true);
215                 request.send();
216         },
217 };
218
219 var handlers = [
220         _gmane_handler,
221 ];
222
223 function _check_handler(handler) {
224         var match = handler.regexp.test(document.URL);
225         console.log('nmbug: testing', handler, match);
226         if (match) {
227                 console.log('nmbug: matched', handler);
228                 handler.handle(nmbug.show.bind(nmbug));
229         }
230         return match;  /* break after the first match */
231 }
232
233 function run() {
234         var matched = handlers.some(_check_handler);
235         if (!matched) {
236                 throw 'No handler for ' + document.URL;
237         }
238 }