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