Advertisement
Guest User

Calendar To Ical

a guest
Nov 18th, 2019
334
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <head>
  2.     <meta charset="utf-8">
  3.     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  4.  
  5.     <meta name="description" content="KULoket to iCal script">
  6.     <meta name="keywords" content="KUL,KULoket,Toledo,uurrooster,roster,schedule,ical,google,calendar">
  7.     <meta name="author" content="Dries007">
  8.     <title>KUL2iCal</title>
  9.  
  10.     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
  11.     <style>
  12.         body {
  13.             margin-bottom: 80px;
  14.         }
  15.  
  16.         html {
  17.             position: relative;
  18.             min-height: 100%;
  19.         }
  20.  
  21.         footer {
  22.             position: absolute;
  23.             bottom: 0;
  24.             width: 100%;
  25.             /* Set the fixed height of the footer here */
  26.             height: 60px;
  27.             background-color: #f5f5f5;
  28.         }
  29.  
  30.         footer p {
  31.             margin: 20px 0;
  32.         }
  33.  
  34.         label {
  35.             margin-bottom: 0;
  36.         }
  37.  
  38.         .string { color: green; }
  39.         .number { color: darkorange; }
  40.         .boolean { color: blue; }
  41.         .null { color: magenta; }
  42.         .key { color: darkmagenta; }
  43.     </style>
  44.  
  45.     <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
  46.     <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
  47.     <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
  48.     <script src="download.js"></script>
  49.     <!--suppress EqualityComparisonWithCoercionJS -->
  50.     <script>
  51.         'use strict';
  52.  
  53.         var dateRegex = /^\/Date\((\d+)\)\/$/;
  54.         var timeRegex = /^PT(\d{2})H(\d{2})M\d{2}S$/;
  55.         var baseurl = "https://webwsp.aps.kuleuven.be/sap/opu/odata/sap/zc_ep_schedule_srv/Events?$format=json&$filter=";
  56.         var output;
  57.         var steps;
  58.         var data;
  59.  
  60.         $(function ()
  61.         {
  62.             steps = [$('#step1'), $('#step2'), $('#step3'), $('#step4')];
  63.             step(1);
  64.         });
  65.  
  66.         /**
  67.          * Json syntax highlighting
  68.          * Give this guy all the credit: http://stackoverflow.com/questions/4810841/how-can-i-pretty-print-json-using-javascript/7220510#7220510
  69.          */
  70.         function syntaxHighlight(json)
  71.         {
  72.             if (typeof json != 'string') json = JSON.stringify(json, undefined, 2);
  73.             json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  74.             return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match)
  75.             {
  76.                 var cls = 'number';
  77.                 if (/^"/.test(match))
  78.                 {
  79.                     if (/:$/.test(match)) cls = 'key';
  80.                     else cls = 'string';
  81.                 }
  82.                 else if (/true|false/.test(match)) cls = 'boolean';
  83.                 else if (/null/.test(match)) cls = 'null';
  84.                 return '<span class="' + cls + '">' + match + '</span>';
  85.             });
  86.         }
  87.  
  88.         function makeUrl()
  89.         {
  90.             try
  91.             {
  92.                 var start = $("#start").val();
  93.                 var end = $("#end").val();
  94.                 var teachers = $("#teachers").prop("checked");
  95.                 var locations = $("#locations").prop("checked");
  96.                 if (start == "" || end == "") return false;
  97.                 var filter = "date ge datetime'" + start + "T00:00:00' and date le datetime'" + end + "T00:00:00'";
  98.                 var url = baseurl + encodeURIComponent(filter);
  99.                 if (teachers || locations)
  100.                 {
  101.                     url += "&$expand=";
  102.                     var expand = [];
  103.                     if (teachers) expand.push("EventTeacherSet");
  104.                     if (locations) expand.push("EventLocationSet");
  105.                     url += encodeURIComponent(expand.join(','));
  106.                 }
  107.                 $("#link").html('<a href="' + url + '" target="_blank">' + url + '</a>');
  108.                 step(2)
  109.             }
  110.             catch (e)
  111.             {
  112.                 console.log(e);
  113.             }
  114.             return false;
  115.         }
  116.  
  117.         function getLocation(result)
  118.         {
  119.             return result.roomNumber + ' ' + result.roomName + " in " + result.buildingName;
  120.         }
  121.  
  122.         function getLocations(results)
  123.         {
  124.             var n = results.length;
  125.             if (n == 0) return '???';
  126.             if (n == 1) return getLocation(results[0]);
  127.             var a = [];
  128.             for (var i = 0; i < n; i++) a.push(getLocation(results[i]));
  129.             return a.join(', ');
  130.         }
  131.  
  132.         function getTeachers(results)
  133.         {
  134.             var n = results.length;
  135.             if (n == 0) return '???';
  136.             if (n == 1) return results[0].name;
  137.             var a = [];
  138.             for (var i = 0; i < n; i++) a.push(results[i].name);
  139.             return a.join(', ');
  140.         }
  141.  
  142.         /**
  143.          * @param date {Date}
  144.          * @returns {string}
  145.          */
  146.         function getIcalFormat(date)
  147.         {
  148.             return date.getFullYear() + ("0" + (date.getMonth() + 1)).slice(-2) + ("0" + date.getDate()).slice(-2) + "T" + ("0" + date.getHours()).slice(-2) + ("0" + date.getMinutes()).slice(-2) + "00";
  149.         }
  150.  
  151.         function downloadIcal()
  152.         {
  153.             download(output, "kul2ical.ics", "text/calendar; charset=UTF-8");
  154.         }
  155.  
  156.         function showIcal()
  157.         {
  158.             var generator = window.open('', '_blank', '');
  159.             generator.document.write('<html><head><title>kul2ical.ics</title></head><body><pre>');
  160.             generator.document.write(output);
  161.             generator.document.write('</pre></body></html>');
  162.             generator.document.close();
  163.         }
  164.  
  165.         function parseJson()
  166.         {
  167.             try
  168.             {
  169.                 var json = $("#json").val();
  170.  
  171.                 json = JSON.parse(json, function (k, v) {
  172.                     if (k === "__metadata") return;
  173.                     return v;
  174.                 }).d.results;
  175.  
  176. //                console.debug(json);
  177. //                $("#jsonOut").html(syntaxHighlight(JSON.stringify(json, null, 2)));
  178.  
  179.                 var teachers = json[0].hasOwnProperty('EventTeacherSet');
  180.                 var locations = json[0].hasOwnProperty('EventLocationSet');
  181.  
  182.                 data = {};
  183.  
  184.                 json.forEach(function (event_json)
  185.                 {
  186.                     var date = parseInt(dateRegex.exec(event_json['date'])[1]);
  187.                     var start = new Date(date);
  188.                     var end = new Date(date);
  189.  
  190.                     var temp = timeRegex.exec(event_json['start_time']);
  191.                     start.setHours(temp[1], temp[2], 0, 0);
  192.  
  193.                     temp = timeRegex.exec(event_json['end_time']);
  194.                     end.setHours(temp[1], temp[2], 0, 0);
  195.  
  196.                     var event = {
  197.                         location: locations ? getLocations(event_json['EventLocationSet']['results']) : '',
  198.                         teachers: teachers ? getTeachers(event_json['EventTeacherSet']['results']) : '',
  199.                         start: getIcalFormat(start),
  200.                         end: getIcalFormat(end)
  201.                     };
  202.  
  203.                     var group = {
  204.                         group: event_json['groupinfo_general'],
  205.                         group_extra: event_json['groupinfo_detail'],
  206.                         events: [event],
  207.                         checkbox: null
  208.                     };
  209.  
  210.                     var common = {
  211.                         ects: event_json['ects_code'],
  212.                         name: event_json['ola_description'],
  213.                         groups: [group]
  214.                     };
  215.  
  216. //                    console.debug(event_json);
  217. //                    console.debug(event);
  218.  
  219.                     if (!data.hasOwnProperty(event_json['eventID']))
  220.                     {
  221.                         data[event_json['eventID']] = [common];
  222.                     }
  223.                     else // eventID already present
  224.                     {
  225.                         var found_common = data[event_json['eventID']].filter(function (t)
  226.                         {
  227.                             return t.ects == common.ects && t.name == common.name
  228.                         });
  229.                         if (found_common.length == 0) // New 'common', push it to the eventID array
  230.                         {
  231.                             data[event_json['eventID']].push(common);
  232.                         }
  233.                         else // Common already present
  234.                         {
  235.                             found_common = found_common[0];
  236.                             var found_group = found_common.groups.filter(function (t)
  237.                             {
  238.                                 return t.group == group.group && t.group_extra == group.group_extra
  239.                             });
  240.                             if (found_group.length == 0) // New 'group', push it to the common's groups array
  241.                             {
  242.                                 found_common.groups.push(group);
  243.                             }
  244.                             else // Group already present, push event to groups's events
  245.                             {
  246.                                 found_group = found_group[0];
  247.                                 found_group.events.push(event);
  248.                             }
  249.                         }
  250.                     }
  251.                 });
  252.  
  253. //                console.debug(data);
  254.  
  255.                 var courses = $('#courses').empty();
  256.                 Object.keys(data).forEach(function (id)
  257.                 {
  258.                     data[id].forEach(function (common)
  259.                     {
  260.                         var common_e = $('<li><b><small>' + common.ects + '</small> ' + common.name + '</b></li>');
  261.                         var list = $('<ul>').appendTo(common_e);
  262.                         common.groups.forEach(function (group)
  263.                         {
  264.                             group.checkbox = $('<label class="custom-control custom-checkbox"><input type="checkbox" class="custom-control-input"/><span class="custom-control-indicator"></span><span class="custom-control-description">' + group.group + group.group_extra + '</span></label>');
  265.                             group.checkbox.find('input').prop('checked', true);
  266.                             list.append($('<li>').append(group.checkbox));
  267.                         });
  268.                         courses.append(common_e);
  269.                     });
  270.                 });
  271.  
  272.                 step(3);
  273.             }
  274.             catch (e)
  275.             {
  276.                 console.log(e);
  277.             }
  278.             return false;
  279.         }
  280.  
  281.         function selectGroups()
  282.         {
  283.             try
  284.             {
  285. //                console.info(data);
  286.                 var icalLines = ["BEGIN:VCALENDAR", "VERSION:2.0"];
  287.                 Object.keys(data).forEach(function (id)
  288.                 {
  289.                     data[id].forEach(function (common)
  290.                     {
  291. //                        console.debug('common');
  292. //                        console.debug(common);
  293.                         common.groups.forEach(function (group)
  294.                         {
  295. //                            console.debug('group');
  296. //                            console.debug(group);
  297.                             if (group.checkbox.find('input').prop('checked'))
  298.                             {
  299.                                 var events = group.events;
  300.                                 events.forEach(function (event) {
  301. //                                    console.debug('event');
  302. //                                    console.debug(event);
  303.                                     icalLines.push(
  304.                                         "BEGIN:VEVENT",
  305.                                         "CLASS:PUBLIC",
  306.                                         "DESCRIPTION:By " + event.teachers,
  307.                                         "DTSTART;VALUE=DATE-TIME:" + event.start,
  308.                                         "DTEND;VALUE=DATE-TIME:" + event.end,
  309.                                         "LOCATION:" + event.location,
  310.                                         "SUMMARY:" + common.name + ' (' + group.group + group.group_extra + ')',
  311.                                         "TRANSP:TRANSPARENT",
  312.                                         "END:VEVENT");
  313.                                 });
  314.                             }
  315.                         });
  316.                     });
  317.                 });
  318.                 icalLines.push("END:VCALENDAR");
  319.                 output = icalLines.join("\r\n");
  320.  
  321.                 $("#download").html('<div class="btn-group">' +
  322.                     '<button class="btn btn-primary" onclick="downloadIcal();">Download!</butt>' +
  323.                     '<button class="btn btn-secondary" onclick="showIcal();">Show iCal as text</butt>' +
  324.                     '</div>');
  325.  
  326.                 step(4);
  327.             }
  328.             catch (e)
  329.             {
  330.                 console.log(e);
  331.             }
  332.             return false;
  333.         }
  334.  
  335.         function step(id)
  336.         {
  337.             id = id - 1;
  338.             steps.forEach(function (t, i)
  339.             {
  340.                 if (id == i) t.show();
  341.                 else t.hide();
  342.             });
  343.         }
  344.     </script>
  345. </head>
  346. <body>
  347.     <div class="container">
  348.         <h1>KUL2iCal</h1>
  349.         <p class="lead">Be careful with this tool, the iCal file doesn't magically update when changes are made in KULoket!<br>
  350.            I recommend you use <a href="https://quivr.be/en/">quivr</a> or setup <a href="https://admin.kuleuven.be/icts/config/android/tablet/mail-config-android-t">account synchronisation</a> on your phone.</p>
  351.        <p>Follow the instructions step by step. You can go back by clicking on the grey button with the step number.</p>
  352.        <h2><button type="button" class="btn btn-outline-secondary btn-sm" onclick="step(1)">Step 1</button> Options</h2>
  353.        <div id="step1">
  354.            <p>This page can get slow if the range of data is too big. You can import multiple iCal files into 1 calender in Google Calender.</p>
  355.            <form onsubmit="return makeUrl();">
  356.                <div class="form-group">
  357.                    <label for="start" class="control-label">Start date</label>
  358.                    <input class="form-control" type="date" name="start" id="start" placeholder="YYYY-MM-DD">
  359.                </div>
  360.                <div class="form-group">
  361.                    <label for="end" class="control-label">End date</label>
  362.                    <input class="form-control" type="date" name="end" id="end" placeholder="YYYY-MM-DD">
  363.                </div>
  364.                <div class="checkbox">
  365.                    <label>
  366.                        <input type="checkbox" id="locations" value="" checked="">
  367.                        Include Locations
  368.                    </label>
  369.                </div>
  370.                <div class="checkbox">
  371.                    <label>
  372.                        <input type="checkbox" id="teachers" value="" checked="">
  373.                        Include Teachers
  374.                    </label>
  375.                </div>
  376.                <div class="form-group">
  377.                    <button type="submit" class="btn btn-primary">Next</button>
  378.                </div>
  379.            </form>
  380.        </div>
  381.        <h2><button type="button" class="btn btn-outline-secondary btn-sm" onclick="step(2)">Step 2</button> Get the data</h2>
  382.        <div id="step2" style="display: none;">
  383.            <p>Click the link below. It will open in a new tab.<br>
  384.                You will be prompted to log in if you are not already.</p>
  385.            <p id="link"><em>The link will appear here when you click 'Go' in step 1.</em></p>
  386.            <p>If the data looks way too short (something like <code>{"d":{"results":[]}}</code>) pick a later start date or sooner end date.<br>
  387.                This happens when one of the dates are in a different semester (according to KU Loket).</p>
  388.            <form onsubmit="return parseJson();">
  389.                <label for="json">Click link above &amp; copy ALL of the data into the textbox below.</label>
  390.                <div class="form-group">
  391.                    <textarea id="json" class="form-control" rows="10"></textarea>
  392.                </div>
  393.                <div class="form-group">
  394.                    <button type="submit" class="btn btn-primary">Next</button>
  395.                </div>
  396.            </form>
  397.        </div>
  398.        <h2><button type="button" class="btn btn-outline-secondary btn-sm" onclick="step(3)">Step 3</button> Select wanted groups</h2>
  399.        <div id="step3" style="display: none;">
  400.            <form onsubmit="return selectGroups();">
  401.                <label for="courses">Select the events you'd like to export:</label>
  402.                 <div class="form-group">
  403.                     <ul id="courses">
  404.                         <li style="list-style: none"><em>Data will appear after adding data in step 3.</em></li>
  405.                     </ul>
  406.                 </div>
  407.                 <div class="form-group">
  408.                     <button type="submit" class="btn btn-primary">Next</button>
  409.                 </div>
  410.             </form>
  411.         </div>
  412.         <h2><button type="button" class="btn btn-outline-secondary btn-sm" onclick="step(4)">Step 4</button> Download the iCal file</h2>
  413.         <div id="step4" style="display: none;">
  414.             <p><b>Thank you for using Dries007's KUL2iCal. Have a good semester!</b></p>
  415.            <p id="download"><em>The download button will appear here when you click 'Go' in step 2.</em></p>
  416.            <p>If the download button doesn't work, copy and save all of the iCal text as 'KUL.ics'.</p>
  417.         </div>
  418.     </div>
  419.     <footer>
  420.         <div class="container">
  421.             <p class="text-muted">
  422.                 © 2015-2017 <a href="http://dries007.net">Dries007.net</a> See source for more info —
  423.                 This script works entirely in your browser. No data is send back to my server.
  424.             </p>
  425.         </div>
  426.     </footer>
  427.  
  428. </body>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement