/** * @preserve Copyright © 2018 T. F. Raaion, www.sublunar.space * (MIT): http://www.opensource.org/licenses/MIT * * SUBLUNAR ALMANAC */ var SL = SL || {}; //Defines Calendar Objects and Methods /** * Methods and objects to generate the calendar * @public {function} modules() - loads all plugin modules defined in an Array in index.html and adds their filters to the navbar * @public {function} make() - creates the grid of planetary hours for given coordinates and time in Settings * @public {Array} moments - holds all the JSON Ephemeris objects to calculate the calendar data * @public {function} hourInfo() - creates a temporary DOM object to be displayed as hour info upon clicking a planetary hour object * @public {function} comboFilter() - is a filter function for Isotope to include all selected tags and operations within a filter but exclude between each other * @public {function} getGeoCode() - set latitude, longitude, and UTC-Offset form fields for a given place entered in Settings * @public {function} about() - creates a DOM object with the "About" text for SUBLUNAR ALMANAC. * @public {function} reset() - simulates a resizing of the browser window. Needed hack for a display glitch in the modals. * @public {function} resetFileInput() - resets the file selection button in Settings * @public {function} download() - download a JSON object as a file */ SL.Calendar = (function() { /** * about function creates a DOM object with the "About" text for SUBLUNAR ALMANAC */ function about() { $('
').loadTemplate($("#tpl-modal"), { title : '

SUBLUNAR ALMANAC

', right : '

If you like this tool, please Buy Me a Coffee.
For updates, feedback and discussions, please visit our Facebook Group.
If you are interested in contributing, please check out our GitHub Repo

MIT License © 2018 T. F. Raaion / sublunar space
contact: info@sublunar.space


Astronomical calculations are based on
Swiss Ephemeris API, © 1997-2016 Astrodienst AG, (GPLv2.0+)
and the Table of New Moon Dates by Steve Moshier.

The front-end relies on the following Javascript Libraries:
jQuery 3.2.1, © JS Foundation, (MIT)
Bootstrap 3.3.7, © Twitter, Inc., (MIT)
jQuery.loadTemplate, © 2013 Paul Burgess and other contributors, (MIT)
MomentJS 2.18.1, © Tim Wood, Iskren Chernev, Moment.js contributors, (MIT)
Isotope 3.0.4, © 2017 Metafizzy, (GPLv3)
DefiantJS 1.4.1, © 2013-2017, Hakan Bilgin, (MIT)
NProgress, © 2013, 2014 Rico Sta. Cruz, (MIT)
OsmNamesAutocomplete, © 2016 Klokan Technologies GmbH, (GPLv3)
tzlookup, by The Dark Sky, LLC, (CC0 1.0 Universal)
ics.js, © 2018 Travis Krause, (MIT)

The design is based on
bootswatch v3.3.7, © 2012-2017 Thomas Park, (MIT).

' }) .appendTo('#modals') .attr('id', 'aboutSLC') .attr('tabindex', '-1') .attr('role', 'dialog') .addClass('modal fade autoModal') .find('.col-sm-4').closest('.row') .find('.col-sm-8').attr('class', 'col-sm-12').closest('.row') .find('.col-sm-4').remove(); } /** * resetWindow function simulates a resizing of the browser window. Needed hack for a display glitch in the modals. */ function resetWindow() { var resizeEvent = window.document.createEvent('UIEvents'); resizeEvent .initUIEvent('resize', true, false, window, 0); window.dispatchEvent(resizeEvent); } /** * resetFileInput resets the file selection button in Settings */ function resetFileInput() { $('#fileInput').wrap('
').closest('form').get(0).reset(); $('#fileInput').unwrap(); $('#fileInputLabel').removeClass('btn-danger btn-success').addClass('btn-default').find('span').html('Select File'); } /** * download a JSON object as a file * @param {Object} exportObj - JSON Object to be downloaded * @param {string} exportName - file name for the JSON Object to be downloaded * found here: https://stackoverflow.com/questions/19721439/download-json-object-as-a-file-from-browser */ function download(exportObj, exportName, query = "query"){ var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent('{"'+query+'":'+JSON.stringify(exportObj)+'}'); var downloadAnchorNode = document.createElement('a'); downloadAnchorNode.setAttribute("href", dataStr); downloadAnchorNode.setAttribute("download", exportName + ".json"); document.body.appendChild(downloadAnchorNode); // required for firefox downloadAnchorNode.click(); downloadAnchorNode.remove(); } /** * getGeoCode function to set latitude, longitude, and UTC-Offset form fields for a given place entered in Settings * @param {Object} item - JSON Object returned from kt.OsmNamesAutocomplete (see index.html) */ function getGeoCode(item) { $('#lat').addClass('loading'); $('#lon').addClass('loading'); $('#offset').addClass('loading'); // find timezone with tzlookup for lat and long returned from kt.OsmNamesAutocomplete JSON // and extract UTC offset from a string in the format "(UTC+03:00)": $.getJSON( "https://raw.githubusercontent.com/dmfilipenko/timezones.json/master/timezones.json", function( zones ) { var off = JSON.search( zones, '//*[utc="'+tzlookup(item.lat, item.lon)+'"]'); $('#lat').val(item.lat); $('#lon').val(item.lon); var os; // it's a dirty hack solution... if ( off[0].text ) { if ( off[0].text.split(')')[0] == '(UTC') os = 0; else { if ( item.lon < 0 ) os = parseInt(off[0].text.split(')')[0].split('-')[1].split(':')[0], 10)*-1; if ( item.lon > 0 ) os = parseInt(off[0].text.split(')')[0].split('+')[1].split(':')[0], 10); } } else os = "???"; $('#offset').val(os); $('#lat').removeClass('loading'); $('#lon').removeClass('loading'); $('#offset').removeClass('loading'); }); } function unik(array){ return array.filter(function(el, index, arr) { return index === arr.indexOf(el); }); } /** * Hours object with variable and function to generate the grid of planetary hours * @public {Object} moments - JSON object holding all the data returned from the swiss ephemeris calculations done by pmom.c * @public {function} make() - creates the grid of planetary hours for given coordinates and time */ var Hours = (function() { /** * moments JSON object with all returned moments from pmom.c. Filled by make() */ var moments = []; /** * make function creates the grid of planetary hours for given coordinates and time in Settings * @param {int} days - number of days to be calculated * @param {int} ts - unix timestamp of first day of the calendar to be calculated * @param {float} lat - decimal degrees latitude of place on Earth * @param {float} lon - decimal degrees longitude of place on Earth */ function make(days, ts, lat, lon ) { if ( days > 60 ) { alert("Sorry, the calculation is limited to 60 days at the moment! Calculating the first 60 out of your desired "+days+" days!"); days = 60; } $('#caption .stoke').html('calculating ephemeris...please wait'); $("#grid").empty(); $('#download').remove(); Filter.load(); // check if negative UTC offset will be before the beginning of UTC day and if yes, go back one day. if ( moment.unix(ts).startOf('day') > moment.unix(ts).add( $("#offset").val(), 'hours') ) { ts = moment.unix(ts).subtract(1, 'day').utc().unix(); } var files = document.getElementById('fileInput').files; // check if JSON file is loaded. If not, calculate... if (files.length <= 0) { var url = "pmom.php?days="+days+"&ts="+ts+"&lat="+lat+"&lon="+lon; fetch(url) .then(function(response) { return response.json(); }) .then(function(json) { Hours.moments = json.query; $('#caption .stoke').html('generating planetary hours...please wait'); for (var i = 0, len = Hours.moments.length; i < len; i++) { var everyday = setTimeout( Hour.make, 10, Hours.moments[i], i, Hours.moments.length, everyday); NProgress.set(i/Hours.moments.length); } }); } else { // display calendar data from JSON file var fr = new FileReader(); fr.onloadend = function(e) { try { var result = JSON.parse(e.target.result); } catch (e) { NProgress.done(); $('#settings').modal('show'); $('#fileInputLabel').removeClass('btn-success').addClass('btn-danger').find('span').html('FILE ERROR'); alert("Sorry, wrong file format!"); SL.Calendar.resetFileInput(); return false; } Hours.moments = result.query; $('#caption .stoke').html('generating planetary hours...please wait'); if ( Hours.moments.length > 0 ) { for (var i = 0, len = Hours.moments.length; i < len; i++) { var everyday = setTimeout( Hour.make, 10, Hours.moments[i], i, Hours.moments.length, everyday); NProgress.set(i/Hours.moments.length); } } } fr.readAsText(files.item(0)); } } return { make: make, moments : moments, } }()); /** * Hour object with variable and function to generate a single planetary hour * @public {function} make() - creates a DOM object of a planetary hour based on a template defined in index.html * @public {function} info() - creates a temporary DOM object to be displayed as hour info upon clicking a planetary hour object */ var Hour = (function() { /** * make function creates a DOM object of a planetary hour based on a template defined in index.html * @param {Object} data - a single hour JSON object with all data to build a DOM object * @param {int} j - increment number of hour object * @param {int} all - total number of objects to be created * @param {int} timeout - holds the ID value returned by setTimeout() in Hours.make() */ function make(data, j, all, timeout) { var now = new moment.utc().unix(); var m = data; if ( $("#future").is(':checked') && now > m.ts ) return false; var zodiac = SL.Astro.Logy.getZodiac(m.ephemeris.sun.deg); var moonsign = SL.Astro.Logy.getZodiac(m.ephemeris.sun.deg); var extraData = Module.data(m); $('
').loadTemplate($("#tpl-planetaryhour"), { ruler : SL.Astro.Logy.planet.symbol[(m.planetary.day.no+1)], date : new moment.unix(m.ts).utc().add($('#offset').val(), 'h').format('D/M'), hourstart : new moment.unix(m.planetary.hour.start).utc().add($('#offset').val(), 'h').format('HH:mm'), hourruler : m.planetary.hour.no+1, hournumber : SL.Astro.Logy.planet.symbol[SL.Astro.Logy.planet.order[((m.planetary.hour.no+1+(m.planetary.day.no*24) - 1) % 7 ) + 1]], hourend : new moment.unix(m.planetary.hour.end).utc().add($('#offset').val(), 'h').format('HH:mm'), }) .addClass('col-md-2 centered planetaryhour') .addClass(Module.classes(extraData)) .attr('id', 'moment_'+j) .attr('ts', m.ts) .appendTo('#grid'); if ( m.planetary.hour.no > 11 ) { $('#moment_'+j).addClass('night-hour'); } if ( j == all-1 ) { if ( $('#search').val() != '' && $('#fileInput').val() == '') { $('