[READ-ONLY] Mirror of https://github.com/probablykasper/LaRSS. RSS Generator for iTunes
larss.kasper.space
rss
website
1window.xhr = function(req, options) {
2 // handle options passed as first argument
3 if (options === undefined) {
4 options = req;
5 if (options.req) req = options.req;
6 else if (options.json) req = JSON.stringify(options.json);
7 }
8
9 // init xhr
10 if (!options.url) return "url required";
11 if (!options.type) options.type = "POST";
12 if (!options.contentType) options.contentType = "application/x-www-form-urlencoded";
13
14 // initiate & send request
15 var xhr = new XMLHttpRequest();
16 xhr.open(options.type, options.url, true);
17 xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
18 if (options.auth) xhr.setRequestHeader("Authorization", options.auth);
19 if (options.headers) {
20 for (var i = 0; i < options.headers.length; i++) {
21 xhr.setRequestHeader(options.headers[i][0], options.headers[i][1]);
22 }
23 }
24 xhr.send(req);
25
26 // handle response
27 xhr.onreadystatechange = function() {
28 if (this.readyState == 4) {
29 if (String(this.status).startsWith(2)) {
30 if (options.callback) options.callback(this.responseText, false);
31 if (options.onSuccess) options.onSuccess(this.responseText, this.status);
32 } else {
33 if (options.callback) options.callback(this.responseText, this.status);
34 if (options.onError) options.onError(this.responseText, this.status);
35 }
36 }
37 };
38};
39
40(function items() {
41 document.addEventListener("click", function(e) {
42 // collapse
43 if (e.target.classList.contains("collapse-icon")) {
44 var item = e.target.parentElement.parentElement;
45 item.classList.toggle("collapsed");
46 }
47 // add
48 if (e.target.classList.contains("add-icon")) {
49 var item = document.querySelector(".container-sample .item").cloneNode(true);
50 var container = document.querySelector(".container");
51 container.insertBefore(item, document.querySelector(".container .item"));
52 var textareas = item.querySelectorAll("textarea");
53 for (var i = 0; i < textareas.length; i++) {
54 resizeTextarea(textareas[i]);
55 }
56 }
57 // delete
58 if (e.target.classList.contains("delete-icon")) {
59 var item = e.target.parentElement.parentElement;
60 item.parentElement.removeChild(item);
61 }
62 });
63 document.addEventListener("input", function(e) {
64 if (e.target.classList.contains("title")) {
65 var item = e.target.parentElement.parentElement.parentElement;
66 if (item.classList.contains("item")) {
67 item.querySelector(".item-title").innerHTML = e.target.value;
68 }
69 }
70 });
71})();
72
73(function textareaResize() {
74 window.resizeTextarea = function(textarea) {
75 textarea.style.height = "auto";
76 textarea.parentElement.style.height = "auto";
77 var newHeight = textarea.scrollHeight;
78 var cs = window.getComputedStyle(textarea); // cs == computedStyles
79 var padding = Number(cs.paddingTop.slice(0, -2)) + Number(cs.paddingBottom.slice(0, -2));
80 textarea.style.height = newHeight+"px";
81 }
82 window.resizeTextareas = function() {
83 var textareas = document.querySelectorAll("textarea");
84 for (var i = 0; i < textareas.length; i++) {
85 resizeTextarea(textareas[i]);
86 }
87 }
88 resizeTextareas();
89 document.addEventListener("input", function(e) {
90 if (e.target.tagName == "TEXTAREA") {
91 resizeTextarea(e.target);
92 }
93 });
94})();
95
96var pat = localStorage.getItem("pat");
97var gistId = localStorage.getItem("gistId");
98function getFilename(pat) {
99 return {
100 json: "LaRSS-"+pat.slice(0,4)+".json",
101 rss: "LaRSS-"+pat.slice(0,4)+".rss"
102 };
103}
104
105load(pat);
106function load(pat, callback) {
107 if (pat) {
108 // if gistId is already saved, load it. Otherwise, find the gistId
109 if (gistId) {
110 findGist(pat, gistId, function(content, rssURL) {
111 // if the gist was found, load it. Otherwise
112 if (content) {
113 gistFound(content, rssURL);
114 if (callback) callback();
115 } else {
116 findGistId(pat, function(id) {
117 if (id) {
118 findGist(pat, id, function(content, rssURL) {
119 gistFound(content, rssURL);
120 if (callback) callback();
121 resizeTextareas();
122 });
123 } else {
124 createGist(pat, function(id, content, rssURL) {
125 localStorage.setItem("gistId", id);
126 gistId = id;
127 gistFound(content, rssURL);
128 if (callback) callback();
129 });
130 }
131 });
132 }
133 });
134 } else {
135 findGistId(pat, function(id) {
136 if (id) {
137 findGist(pat, id, function(content, rssURL) {
138 gistFound(content, rssURL);
139 if (callback) callback();
140 resizeTextareas();
141 });
142 } else {
143 createGist(pat, function(id, content, rssURL) {
144 localStorage.setItem("gistId", id);
145 gistId = id;
146 gistFound(content, rssURL);
147 if (callback) callback();
148 });
149 }
150 });
151 }
152 } else {
153 showPATdialog();
154 }
155}
156function getGistPermaLink(old) {
157 var start = old.indexOf("/raw/") + 4;
158 var end = old.indexOf("/LaRSS");
159 return old.slice(0, start)+old.slice(end);
160}
161function gistFound(json, rssURL) {
162 rssURL = getGistPermaLink(rssURL);
163 var container = document.querySelector(".container");
164 container.innerHTML = '<button class="save-button">Save</button>';
165 container.innerHTML += '<a target="_blank" href="'+rssURL+'"><button class="rss-link">RSS Link</button></a>';
166 container.innerHTML += '<img src="logo.png" class="logo">';
167 var gi = document.querySelector(".container-sample .general-info").cloneNode(true); // generalInfo
168 var gio = json.generalInfo; // generalInfoObject
169 var ia = json.items; // itemsArray
170 container.appendChild(gi);
171 gi.querySelector("input.title").value = gio.title;
172 gi.querySelector("input.author").value = gio.author;
173 gi.querySelector("input.subtitle").value = gio.subtitle;
174 gi.querySelector("textarea.description").value = gio.description;
175 gi.querySelector("input.link").value = gio.link;
176 gi.querySelector("input.imageURL").value = gio.imageURL;
177 gi.querySelector('select.language').value = gio.language;
178 gi.querySelector('select.category').value = gio.category;
179 gi.querySelector('select.explicit').value = gio.explicit;
180 gi.querySelector("input.copyright").value = gio.copyright;
181 gi.querySelector("input.name").value = gio.name;
182 gi.querySelector("input.email").value = gio.email;
183 for (var i = 0; i < json.items.length; i++) {
184 var item = document.querySelector(".container-sample .item").cloneNode(true);
185 container.appendChild(item);
186 item.querySelector("input.guid").value = ia[i].guid;
187 item.querySelector("input.title").value = ia[i].title;
188 item.querySelector("h3.item-title").innerHTML = ia[i].title;
189 item.querySelector("input.audioURL").value = ia[i].audioURL;
190 item.querySelector("input.imageURL").value = ia[i].imageURL;
191 item.querySelector('select.explicit').value = ia[i].explicit;
192 item.querySelector("input.date").value = ia[i].date;
193 item.querySelector("input.duration").value = ia[i].duration;
194 item.querySelector("textarea.summary").value = ia[i].summary;
195 }
196 resizeTextareas();
197}
198
199function save(callback) {
200 var container = document.querySelector(".container");
201 var gi = container.querySelector(".general-info"); // generalInfo
202 var gio = {}; // generalInfoObject
203 gio.title = gi.querySelector("input.title").value;
204 gio.author = gi.querySelector("input.author").value;
205 gio.subtitle = gi.querySelector("input.subtitle").value;
206 gio.description = gi.querySelector("textarea.description").value;
207 gio.link = gi.querySelector("input.link").value;
208 gio.imageURL = gi.querySelector("input.imageURL").value;
209 gio.language = gi.querySelector('select.language').value;
210 gio.category = gi.querySelector('select.category').value;
211 gio.explicit = gi.querySelector('select.explicit').value;
212 gio.copyright = gi.querySelector("input.copyright").value;
213 gio.name = gi.querySelector("input.name").value;
214 gio.email = gi.querySelector("input.email").value;
215
216 var items = container.querySelectorAll(".item"); // generalInfo
217 var ia = []; // itemsArray
218 for (var i = 0; i < items.length; i++) {
219 ia[i] = {};
220 ia[i].guid = items[i].querySelector("input.guid").value
221 ia[i].title = items[i].querySelector("input.title").value
222 ia[i].title = items[i].querySelector("h3.item-title").innerHTML
223 ia[i].audioURL = items[i].querySelector("input.audioURL").value
224 ia[i].imageURL = items[i].querySelector("input.imageURL").value
225 ia[i].explicit = items[i].querySelector('select.explicit').value
226 ia[i].date = items[i].querySelector("input.date").value
227 ia[i].duration = items[i].querySelector("input.duration").value
228 ia[i].summary = items[i].querySelector("textarea.summary").value
229 }
230 var json = {
231 generalInfo: gio,
232 items: ia
233 };
234 var xml = generateXML(json);
235 updateGist(gistId, json, xml, function(id) {
236 if (id) {
237 console.log("saved");
238 if (callback) callback(true);
239 } else {
240 console.log("not saved");
241 if (callback) callback(false);
242 }
243 });
244}
245
246function updateGist(gistId, json, xml, callback) {
247 var req = {
248 files: {}
249 };
250 req.files[getFilename(pat).json] = {
251 content: JSON.stringify(json)
252 };
253 req.files[getFilename(pat).rss] = {
254 content: xml
255 };
256 xhr({
257 url: "https://api.github.com/gists/"+gistId,
258 auth: "token "+pat,
259 type: "POST",
260 json: req,
261 onSuccess: function(res) {
262 res = JSON.parse(res);
263 console.log("----------------------------------- updateGist suc");
264 console.log(res);
265 callback(res.id, new Date(res.updated_at));
266 },
267 onError: function(res, code) {
268 res = JSON.parse(res);
269 console.log("----------------------------------- updateGist err");
270 console.log(code);
271 console.log(res);
272 callback(false);
273 }
274 });
275}
276
277document.addEventListener("click", function(e) {
278 if (e.target.classList.contains("save-button")) {
279 e.target.classList.add("saving");
280 e.target.innerHTML = "Saving...";
281 save(function(success) {
282 if (success) {
283 e.target.classList.remove("saving");
284 e.target.classList.add("saved");
285 e.target.innerHTML = "Saved";
286 setTimeout(function() {
287 e.target.classList.remove("saved");
288 e.target.innerHTML = "Save";
289 },3000);
290 } else {
291 e.target.classList.remove("saving");
292 e.target.classList.add("error");
293 e.target.innerHTML = "Error saving :l";
294 }
295 });
296 }
297});
298
299function showPATdialog() {
300 var dialog = document.querySelector(".pat-dialog");
301 dialog.classList.add("visible");
302}
303(function patDialog() {
304 var button = document.querySelector(".pat-settings");
305 var dialog = document.querySelector(".pat-dialog");
306 var patInput = dialog.querySelector("input.pat");
307 if (pat) patInput.value = pat;
308 button.addEventListener("click", function(e) {
309 showPATdialog();
310 });
311 var saveButton = dialog.querySelector("button.save-pat");
312 saveButton.addEventListener("click", function() {
313 console.log("saving access token");
314 // save personal access token
315 pat = patInput.value;
316 localStorage.setItem("pat", pat);
317 jsonGistId = null;
318 rssGistId = null;
319 localStorage.removeItem("gistId");
320 dialog.classList.add("saving");
321 load(pat, function() {
322 dialog.classList.remove("saving");
323 dialog.classList.remove("visible");
324 });
325 });
326 document.addEventListener("click", function(e) {
327 if (e.target.classList.contains("dialog-container")) {
328 e.target.classList.remove("visible");
329 }
330 });
331})();
332
333function findGist(pat, gistId, callback) {
334 xhr({
335 url: "https://api.github.com/gists/"+gistId,
336 auth: "token "+pat,
337 type: "GET",
338 req: "",
339 onSuccess: function(gist) {
340 gist = JSON.parse(gist);
341 console.log("----------------------------------- findGist suc");
342 console.log(gist);
343 var jsonFile = gist.files[getFilename(pat).json];
344 var rssFile = gist.files[getFilename(pat).rss];
345 callback(JSON.parse(jsonFile.content), rssFile.raw_url);
346 },
347 onError: function(res, code) {
348 res = JSON.parse(res);
349 console.log("----------------------------------- findGist err");
350 console.log(code);
351 console.log(res);
352 callback(false);
353 }
354 });
355}
356
357function generateXML(json) {
358 var xml = '';
359 xml += '<?xml version="1.0" encoding="UTF-8"?>\n';
360 xml += '<rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">\n';
361 xml += ' <channel>\n';
362 xml += ' <title>' +json.generalInfo.title+ '</title>\n';
363 xml += ' <link>' +json.generalInfo.link+ '</link>\n';
364 xml += ' <language>' +json.generalInfo.language+ '</language>\n';
365 xml += ' <copyright>' +json.generalInfo.copyright+ '</copyright>\n';
366 xml += ' <itunes:subtitle>' +json.generalInfo.subtitle+ '</itunes:subtitle>\n';
367 xml += ' <itunes:author>' +json.generalInfo.author+ '</itunes:author>\n';
368 xml += ' <description>' +json.generalInfo.description+ '</description>\n';
369 xml += ' <itunes:owner>\n';
370 xml += ' <itunes:name>' +json.generalInfo.name+ '</itunes:name>\n';
371 xml += ' <itunes:email>' +json.generalInfo.email+ '</itunes:email>\n';
372 xml += ' </itunes:owner>\n';
373 xml += ' <itunes:image href="' +json.generalInfo.imageURL+ '"/>\n';
374 xml += ' <itunes:category text="'+json.generalInfo.category+ '"/>\n';
375 xml += ' <itunes:explicit>' +json.generalInfo.explicit+ '</itunes:explicit>\n';
376
377 for (var i = 0; i < json.items.length; i++) {
378 xml += ' <item>\n';
379 xml += ' <title>' +json.items[i].title+ '</title>\n';
380 xml += ' <itunes:summary>' +json.items[i].summary+ '</itunes:summary>\n';
381 xml += ' <itunes:image href="' +json.items[i].imageURL+ '"/>\n';
382 xml += ' <enclosure type="audio/mpeg" url="' +json.items[i].audioURL+ '" length="0"/>\n';
383 xml += ' <guid>' +json.items[i].guid+ '</guid>\n';
384 xml += ' <pubDate>' +json.items[i].date+ '</pubDate>\n';
385 xml += ' <itunes:duration>' +json.items[i].duration+ '</itunes:duration>\n';
386 xml += ' <itunes:explicit>' +json.items[i].explicit+ '</itunes:explicit>\n';
387 xml += ' </item>\n';
388 }
389
390 xml += ' </channel>\n';
391 xml += '</rss>\n';
392 return xml;
393}
394
395function createGist(pat, callback) {
396 var req = {
397 description: "RSS Gist created by LaRSS",
398 files: {}
399 };
400 var defaultJSON = {
401 generalInfo: {
402 title: "",
403 author: "",
404 subtitle: "",
405 description: "",
406 link: "",
407 imageURL: "",
408 explicit: "no",
409 language: "en-us",
410 category: "Music",
411 copyright: "",
412 name: "",
413 email: ""
414 },
415 items: [
416 {
417 guid: "",
418 title: "New Item",
419 audioURL: "",
420 imageURL: "",
421 explicit: "no",
422 date: "Wed, 25 Oct 2017 22:00:00 CET",
423 duration: "",
424 summary: ""
425 }
426 ]
427 };
428 req.files[getFilename(pat).json] = {
429 content: JSON.stringify(defaultJSON)
430 };
431 req.files[getFilename(pat).rss] = {
432 content: "empty rn"
433 };
434 xhr({
435 url: "https://api.github.com/gists",
436 auth: "token "+pat,
437 type: "POST",
438 json: req,
439 onSuccess: function(gist) {
440 gist = JSON.parse(gist);
441 console.log("----------------------------------- createGist suc");
442 console.log(gist);
443 var rssFile = gist.files[getFilename(pat).rss];
444 callback(gist.id, defaultJSON, rssFile.raw_url);
445 },
446 onError: function(res, code) {
447 res = JSON.parse(res);
448 console.log("----------------------------------- createGist err");
449 console.log(code);
450 console.log(res);
451 }
452 });
453}
454
455function findGistId(pat, callback) {
456 xhr({
457 url: "https://api.github.com/gists",
458 auth: "token "+pat,
459 type: "GET",
460 req: "",
461 onSuccess: function(gists) {
462 gists = JSON.parse(gists);
463 console.log("----------------------------------- findgistid suc");
464 console.log(gists);
465 for (var i = 0; i < gists.length; i++) {
466 if (gists[i].files[getFilename(pat).json]) {
467 callback(gists[i].id);
468 localStorage.setItem("gistId", gists[i].id);
469 return;
470 }
471 }
472 callback(false);
473 },
474 onError: function(res, code) {
475 res = JSON.parse(res);
476 console.log("----------------------------------- findgistid err");
477 console.log(code);
478 console.log(res);
479 callback(false);
480 }
481 });
482}