long list of timezones

This commit is contained in:
AJ ONeal 2019-06-22 13:28:03 -06:00
parent 4c986119f9
commit 0c6003a894
5 changed files with 530 additions and 9 deletions

13
public/ajquery.js Normal file
View File

@ -0,0 +1,13 @@
'use strict';
var $ = function(sel, el) {
return (el || window.document).querySelector(sel);
};
$.create = function(html) {
var div = document.createElement('div');
div.innerHTML = html;
return div;
};
var $$ = function(sel, el) {
return (el || window.document).querySelectorAll(sel);
};

301
public/hooks.js Normal file
View File

@ -0,0 +1,301 @@
(function() {
'use strict';
// AJ Query
var $ = window.$;
var $$ = window.$$;
var state = {};
var $grantTpl;
var $devTpl;
var $updateTpl;
var $headerTpl;
var $webhookTpl;
var $webhookHeaderTpl;
// TODO add offset based on date selected (i.e. MDT -0500)
var tzdb = [
'America/New_York',
'America/Chicago',
'America/Denver',
'America/Phoenix',
'America/Los_Angeles',
'America/Sao_Paulo',
'Europe/London',
'Europe/Berlin',
'Europe/Moscow',
'Asia/Dubai',
'Asia/Kolkata',
'Asia/Hong_Kong',
'Asia/Tokyo',
'Pacific/Auckland',
'Australia/Sydney'
];
function run() {
$headerTpl = $('.js-new-webhook .js-header').outerHTML;
$webhookHeaderTpl = $('.js-schedule .js-webhook .js-header').outerHTML;
$('.js-schedule .js-webhooks .js-headers').innerHTML = '';
$webhookTpl = $('.js-schedule .js-webhook').outerHTML;
$('.js-schedule .js-webhooks').innerHTML = '';
// after blanking all inner templates
$devTpl = $('.js-schedule').outerHTML;
console.log('hello');
$('body').addEventListener('click', function(ev) {
if (ev.target.matches('.js-new-header')) {
newWebhookHeader(ev.target);
} else if (ev.target.matches('.js-rm-header')) {
rmWebhookHeader(ev.target);
} else if (
ev.target.matches('.js-delete') &&
ev.target.closest('.js-grant')
) {
deleteGrant(ev.target.closest('.js-grant'));
} else if (
ev.target.matches('.js-delete') &&
ev.target.closest('.js-webhook')
) {
deleteWebhook(ev.target.closest('.js-webhook'));
} else {
return;
}
ev.preventDefault();
ev.stopPropagation();
});
$('body').addEventListener('submit', function(ev) {
if (ev.target.matches('.js-new-schedule')) {
newSchedule(ev.target);
} else {
return;
}
ev.preventDefault();
ev.stopPropagation();
});
}
function newSchedule() {
var $hook = $('.js-new-webhook');
var deviceId = $hook.closest('.js-schedule').querySelector('.js-id').value;
var hook = {
comment: $('.js-comment', $hook).value,
method: $('.js-method', $hook).value,
url: $('.js-url', $hook).value,
headers: {}
};
$$('.js-header', $hook).forEach(function($head) {
var key = $('.js-key', $head).value;
var val = $('.js-value', $head).value;
if (key && val) {
hook.headers[key] = val;
}
});
hook.body = $('.js-body-template', $hook).value;
// TODO update on template change and show preview
var opts = {
method: 'POST',
headers: {
Accept: 'application/json',
Authorization: JSON.parse(localStorage.getItem('session')).access_token,
'Content-Type': 'application/json'
},
body: JSON.stringify(hook),
cors: true
};
/*
state.account.devices
.filter(function(d) {
return d.accessToken == deviceId;
})[0]
.webhooks.push(hook);
displayAccount(state.account);
return;
*/
window
.fetch('/api/iot/devices/' + deviceId + '/webhooks', opts)
.then(function(resp) {
return resp
.json()
.then(function(data) {
if (!data.webhook) {
throw new Error('something bad happened');
return;
}
state.account.devices
.filter(function(d) {
return d.accessToken == deviceId;
})[0]
.webhooks.push(resp.data.webhook);
displayAccount(state.account);
})
.catch(function(e) {
window.alert(e.message);
});
});
}
function newWebhookHeader($newHeader) {
var $hs = $newHeader.closest('.js-headers');
var $h = $newHeader.closest('.js-header');
var $div = document.createElement('div');
$div.innerHTML = $headerTpl;
$hs.append($('.js-header', $div));
$newHeader.hidden = true;
$('.js-rm-header', $h).hidden = false;
$('.js-key', $h).required = 'required';
$('.js-value', $h).required = 'required';
}
function rmWebhookHeader($rmHeader) {
var $h = $rmHeader.closest('.js-header');
$h.parentElement.removeChild($h);
}
function deleteWebhook($hook) {
var deviceId = $hook.closest('.js-schedule').querySelector('.js-id').value;
var id = $('.js-id', $hook).innerText;
var opts = {
method: 'DELETE',
headers: {
Accept: 'application/json',
Authorization: JSON.parse(localStorage.getItem('session')).access_token
},
cors: true
};
window
.fetch('/api/iot/devices/' + deviceId + '/webhooks/' + id, opts)
.then(function(resp) {
return resp.json().then(function(result) {
if (!result.webhook) {
console.error(result);
window.alert('something went wrong: ' + JSON.stringify(result));
return;
}
var index = -1;
var dev = state.account.devices.filter(function(d, i) {
return d.accessToken == deviceId;
})[0];
dev.webhooks.some(function(g, i) {
if (g.id === id) {
index = i;
return true;
}
});
if (index > -1) {
dev.webhooks.splice(index, 1);
displayAccount(state.account);
}
});
});
}
function displayAccount(data) {
state.account = data;
console.log('[debug] Display Account:');
console.log(data);
var $devs = $('.js-schedules');
$devs.innerHTML = '';
data.devices.forEach(function(d) {
var $dev = $.create($devTpl);
$('.js-ieme', $dev).innerText = d.id;
$('.js-id', $dev).value = d.accessToken;
$('.js-update-url', $dev).innerText = d.updateUrl;
d.webhooks.forEach(function(h) {
var $hook = $.create($webhookTpl);
$('.js-id', $hook).innerText = h.id;
$('.js-comment', $hook).innerText = h.comment;
$('.js-method', $hook).innerText = h.method;
$('.js-url', $hook).innerText = h.url;
Object.keys(h.headers).forEach(function(k) {
var $header = $.create($webhookHeaderTpl);
var v = h.headers[k];
$('.js-key', $header).innerText = k;
$('.js-value', $header).innerText = v;
$('.js-headers', $hook).innerHTML += $header.innerHTML;
});
$('.js-body-template', $hook).innerText = h.body;
$('.js-webhooks', $dev).innerHTML += $hook.innerHTML;
});
d.grants.forEach(function(g) {
var $grant = $.create($grantTpl);
$('.js-id', $grant).innerText = g.id;
$('.js-comment', $grant).innerText = g.comment;
$('.js-token', $grant).innerText = g.token;
//TODO Math.floor(Date.now() / 1000);
var url =
'https://test.therootcompany.com/api/v1/devices/' +
d.accessToken +
'/updates?since=' +
0;
$('.js-example-curl .js-example-url', $grant).innerText = url;
$('.js-example-curl .js-example-token', $grant).innerText = g.token;
$('.js-example-js .js-example-url', $grant).innerText = url;
$('.js-example-js .js-example-id', $grant).innerText = d.accessToken;
$('.js-example-js .js-example-token', $grant).innerText = g.token;
$('.js-grants', $dev).innerHTML += $grant.innerHTML;
});
d.updates.slice(0, 10).forEach(function(u) {
var $update = $.create($updateTpl);
$('.js-update-details', $update).innerText = JSON.stringify(u);
$('.js-updates', $dev).innerHTML += $update.innerHTML;
});
$devs.innerHTML += $dev.innerHTML;
});
}
console.info('[tzdb] requesting');
window.fetch('./tzdb.json').then(function(resp) {
return resp.json().then(function(tzdb) {
console.info('[tzdb] received');
var tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
var options = $$('.js-schedule-tz option');
var valOpt = options[0].outerHTML; // UTC
var spaceOpt = options[1].outerHTML; // ----
var innerHTML = $('.js-schedule-tz').innerHTML;
innerHTML =
'<option selected value="' +
tz +
'">&nbsp;&nbsp;&nbsp;&nbsp;' +
tz +
'</option>' +
spaceOpt +
innerHTML.replace(/>UTC/, '>&nbsp;&nbsp;&nbsp;&nbsp;UTC');
//$('.js-schedule-tz').innerHTML += spaceOpt;
//$('.js-schedule-tz').innerHTML += valOpt.replace(/UTC/g, 'custom');
Object.keys(tzdb)
.sort()
.forEach(function(k) {
var parts = k.split(' ');
//var sep = '── ' + parts[0];
var sep = parts[0];
if (parts[0] !== parts[1]) {
sep += ' / ' + parts[1] + ' (DST)';
}
//innerHTML += '<option disabled>' + sep + '</option>';
innerHTML += '<optgroup label="' + sep + '">';
var areas = tzdb[k];
areas.forEach(function(_tz) {
if (tz !== _tz) {
innerHTML += valOpt.replace(/UTC/g, _tz);
}
});
innerHTML += '</optgroup>';
});
$('.js-schedule-tz').innerHTML = innerHTML;
console.info('[tzdb] loaded');
run();
});
});
//window.addEventListener('load', run);
})();

View File

@ -4,7 +4,9 @@
<title>Go Again</title>
</head>
<body>
<h1>Hello, World!</h1>
<h1>Go Again</h1>
<h2>Webhooks, on time!</h2>
<form class="js-schedules-list">
<label
>Token:
@ -12,33 +14,153 @@
</label>
<button>See Schedules</button>
</form>
<pre><code class="js-schedules"> </code></pre>
<pre><code class="js-schedules-output"> </code></pre>
<div class="js-schedules">
<h2>Schedules</h2>
<div class="js-schedule">
<form class="js-new-schedule">
<label>Date: <input type="date" required/></label>
<br />
<label>Time: <input type="time" step="60" required/></label>
<br />
<!-- TODO combo box -->
<select class="js-schedule-tz">
<option value="UTC">UTC</option>
<option disabled>──────────</option>
</select>
<br />
<div class="doc-webhooks-container">
<h3>Webhook</h3>
<div class="js-webhooks">
<div class="js-webhook">
<h4>
<span class="js-comment"></span
><button class="js-delete" type="button">Delete</button>
</h4>
<span class="js-id" hidden></span>
<span class="js-method"></span>
<span class="js-url"></span>
<br />
<div class="js-headers">
<div class="js-header">
<span class="js-key"></span>
<span class="js-value"></span>
</div>
</div>
<pre><code class="js-body-template"></code></pre>
</div>
</div>
<div class="js-new-webhook">
<select class="js-template">
<option value="" selected>Custom</option>
<option value="dweet-v2">Dweet v2</option>
</select>
<br />
<input
class="js-comment"
type="text"
placeholder="Webhook Name"
required
/>
<br />
<select class="js-method">
<option value="POST" selected>POST</option>
<option value="PUT">PUT</option>
</select>
<input
placeholder="https://example.com/api/v1/updates"
class="js-url"
type="url"
required
/>
<div class="js-headers">
<div class="js-header">
<input placeholder="Header" class="js-key" type="text" />
<input placeholder="Value" class="js-value" type="text" />
<button type="button" class="js-rm-header" hidden>[x]</button>
<button type="button" class="js-new-header">[+]</button>
</div>
</div>
<div class="js-body">
<textarea
placeholder="Body template, use '{{ keyname }}' for template values."
class="js-body-template"
></textarea>
<!-- TODO preview template -->
</div>
</div>
</div>
<br />
<button class="js-create">Save Schedule</button>
</form>
</div>
</div>
<script src="./ajquery.js"></script>
<script>
'use strict';
var allSchedules = [];
document.body.addEventListener('submit', function(ev) {
if (ev.target.matches('.js-schedules-list')) {
ev.preventDefault();
ev.stopPropagation();
getSchedules();
return;
} else if (ev.target.matches('.js-schedules-new')) {
ev.preventDefault();
ev.stopPropagation();
scheduleTask();
return;
}
});
function getToken() {
return document.querySelector('.js-auth-token').value;
}
function getSchedules() {
var token = document.querySelector('.js-auth-token').value;
window
return window
.fetch('/api/schedules', {
headers: { Authorization: token }
headers: { Authorization: getToken() }
})
.then(function(resp) {
return resp.json().then(function(schedules) {
allSchedules = schedules;
renderSchedules(schedules);
});
});
}
function renderSchedules(schedules) {
document.querySelector(
'.js-schedules'
'.js-schedules-output'
).innerText = JSON.stringify(schedules, null, 2);
}
function scheduleTask() {
return window
.fetch('/api/schedules/new', {
method: 'POST',
headers: {
Authorization: getToken(),
'Content-Type': 'application/json'
},
body: JSON.stringify(task)
})
.then(function(resp) {
return resp.json().then(function(schedule) {
console.log('New Schedule:', schedule);
allSchedules.push(schedule);
renderSchedules(allSchedules);
});
});
return;
}
</script>
<script src="./hooks.js"></script>
</body>
</html>

54
public/tzdb.json Normal file
View File

@ -0,0 +1,54 @@
{"+00:00 +00:00":["Africa/Abidjan","Africa/Accra","Africa/Bissau","Africa/Monrovia","America/Danmarkshavn","Atlantic/Reykjavik"],
"+01:00 +01:00":["Africa/Algiers","Africa/Casablanca","Africa/Lagos","Africa/Ndjamena","Africa/Tunis"],
"+02:00 +02:00":["Africa/Cairo","Africa/Johannesburg","Africa/Khartoum","Africa/Maputo","Africa/Tripoli","Africa/Windhoek","Asia/Famagusta","Europe/Kaliningrad"],
"+01:00 +02:00":["Africa/Ceuta","Europe/Amsterdam","Europe/Andorra","Europe/Belgrade","Europe/Berlin","Europe/Brussels","Europe/Budapest","Europe/Copenhagen","Europe/Gibraltar","Europe/Luxembourg","Europe/Madrid","Europe/Malta","Europe/Monaco","Europe/Oslo","Europe/Paris","Europe/Prague","Europe/Rome","Europe/Stockholm","Europe/Tirane","Europe/Vienna","Europe/Warsaw","Europe/Zurich"],
"+00:00 +01:00":["Africa/El_Aaiun","Atlantic/Canary","Atlantic/Faroe","Atlantic/Madeira","Europe/Dublin","Europe/Lisbon","Europe/London"],
"+03:00 +03:00":["Africa/Juba","Africa/Nairobi","Antarctica/Syowa","Asia/Baghdad","Asia/Qatar","Asia/Riyadh","Europe/Istanbul","Europe/Kirov","Europe/Minsk","Europe/Moscow","Europe/Simferopol"],
"10:00 09:00":["America/Adak"],
"09:00 08:00":["America/Anchorage","America/Juneau","America/Metlakatla","America/Nome","America/Sitka","America/Yakutat"],
"03:00 03:00":["America/Araguaina","America/Argentina/Buenos_Aires","America/Argentina/Catamarca","America/Argentina/Cordoba","America/Argentina/Jujuy","America/Argentina/La_Rioja","America/Argentina/Mendoza","America/Argentina/Rio_Gallegos","America/Argentina/Salta","America/Argentina/San_Juan","America/Argentina/San_Luis","America/Argentina/Tucuman","America/Argentina/Ushuaia","America/Bahia","America/Belem","America/Cayenne","America/Fortaleza","America/Maceio","America/Montevideo","America/Paramaribo","America/Punta_Arenas","America/Recife","America/Santarem","Antarctica/Palmer","Antarctica/Rothera","Atlantic/Stanley"],
"04:00 03:00":["America/Asuncion","America/Campo_Grande","America/Cuiaba","America/Glace_Bay","America/Goose_Bay","America/Halifax","America/Moncton","America/Santiago","America/Thule","Atlantic/Bermuda"],
"05:00 05:00":["America/Atikokan","America/Bogota","America/Cancun","America/Eirunepe","America/Guayaquil","America/Jamaica","America/Lima","America/Panama","America/Rio_Branco"],
"06:00 05:00":["America/Bahia_Banderas","America/Chicago","America/Indiana/Knox","America/Indiana/Tell_City","America/Matamoros","America/Menominee","America/Merida","America/Mexico_City","America/Monterrey","America/North_Dakota/Beulah","America/North_Dakota/Center","America/North_Dakota/New_Salem","America/Rainy_River","America/Rankin_Inlet","America/Resolute","America/Winnipeg","Pacific/Easter"],
"04:00 04:00":["America/Barbados","America/Blanc-Sablon","America/Boa_Vista","America/Caracas","America/Curacao","America/Guyana","America/La_Paz","America/Manaus","America/Martinique","America/Port_of_Spain","America/Porto_Velho","America/Puerto_Rico","America/Santo_Domingo"],
"06:00 06:00":["America/Belize","America/Costa_Rica","America/El_Salvador","America/Guatemala","America/Managua","America/Regina","America/Swift_Current","America/Tegucigalpa","Pacific/Galapagos"],
"07:00 06:00":["America/Boise","America/Cambridge_Bay","America/Chihuahua","America/Denver","America/Edmonton","America/Inuvik","America/Mazatlan","America/Ojinaga","America/Yellowknife"],
"07:00 07:00":["America/Creston","America/Dawson_Creek","America/Fort_Nelson","America/Hermosillo","America/Phoenix"],
"08:00 07:00":["America/Dawson","America/Los_Angeles","America/Tijuana","America/Vancouver","America/Whitehorse"],
"05:00 04:00":["America/Detroit","America/Grand_Turk","America/Havana","America/Indiana/Indianapolis","America/Indiana/Marengo","America/Indiana/Petersburg","America/Indiana/Vevay","America/Indiana/Vincennes","America/Indiana/Winamac","America/Iqaluit","America/Kentucky/Louisville","America/Kentucky/Monticello","America/Nassau","America/New_York","America/Nipigon","America/Pangnirtung","America/Port-au-Prince","America/Thunder_Bay","America/Toronto"],
"03:00 02:00":["America/Godthab","America/Miquelon","America/Sao_Paulo"],
"02:00 02:00":["America/Noronha","Atlantic/South_Georgia"],
"01:00 +00:00":["America/Scoresbysund","Atlantic/Azores"],
"03:30 02:30":["America/St_Johns"],
"+11:00 +11:00":["Antarctica/Casey","Antarctica/Macquarie","Asia/Magadan","Asia/Sakhalin","Asia/Srednekolymsk","Pacific/Bougainville","Pacific/Efate","Pacific/Guadalcanal","Pacific/Kosrae","Pacific/Norfolk","Pacific/Noumea","Pacific/Pohnpei"],
"+07:00 +07:00":["Antarctica/Davis","Asia/Bangkok","Asia/Barnaul","Asia/Ho_Chi_Minh","Asia/Hovd","Asia/Jakarta","Asia/Krasnoyarsk","Asia/Novokuznetsk","Asia/Novosibirsk","Asia/Pontianak","Asia/Tomsk","Indian/Christmas"],
"+10:00 +10:00":["Antarctica/DumontDUrville","Asia/Ust-Nera","Asia/Vladivostok","Australia/Brisbane","Australia/Lindeman","Pacific/Chuuk","Pacific/Guam","Pacific/Port_Moresby"],
"+05:00 +05:00":["Antarctica/Mawson","Asia/Aqtau","Asia/Aqtobe","Asia/Ashgabat","Asia/Atyrau","Asia/Dushanbe","Asia/Karachi","Asia/Oral","Asia/Qyzylorda","Asia/Samarkand","Asia/Tashkent","Asia/Yekaterinburg","Indian/Kerguelen","Indian/Maldives"],
"+00:00 +02:00":["Antarctica/Troll"],
"+06:00 +06:00":["Antarctica/Vostok","Asia/Almaty","Asia/Bishkek","Asia/Dhaka","Asia/Omsk","Asia/Thimphu","Asia/Urumqi","Indian/Chagos"],
"+02:00 +03:00":["Asia/Amman","Asia/Beirut","Asia/Damascus","Asia/Gaza","Asia/Hebron","Asia/Jerusalem","Europe/Athens","Europe/Bucharest","Europe/Chisinau","Europe/Helsinki","Europe/Kiev","Asia/Nicosia","Europe/Riga","Europe/Sofia","Europe/Tallinn","Europe/Uzhgorod","Europe/Vilnius","Europe/Zaporozhye"],
"+12:00 +12:00":["Asia/Anadyr","Asia/Kamchatka","Pacific/Funafuti","Pacific/Kwajalein","Pacific/Majuro","Pacific/Nauru","Pacific/Tarawa","Pacific/Wake","Pacific/Wallis"],
"+04:00 +04:00":["Asia/Baku","Asia/Dubai","Asia/Tbilisi","Asia/Yerevan","Europe/Astrakhan","Europe/Samara","Europe/Saratov","Europe/Ulyanovsk","Europe/Volgograd","Indian/Mahe","Indian/Mauritius","Indian/Reunion"],
"+08:00 +08:00":["Asia/Brunei","Asia/Choibalsan","Asia/Hong_Kong","Asia/Irkutsk","Asia/Kuala_Lumpur","Asia/Kuching","Asia/Macau","Asia/Makassar","Asia/Manila","Asia/Shanghai","Asia/Singapore","Asia/Taipei","Asia/Ulaanbaatar","Australia/Perth"],
"+09:00 +09:00":["Asia/Chita","Asia/Dili","Asia/Jayapura","Asia/Khandyga","Asia/Pyongyang","Asia/Seoul","Asia/Tokyo","Asia/Yakutsk","Pacific/Palau"],
"+05:30 +05:30":["Asia/Colombo","Asia/Kolkata"],
"+04:30 +04:30":["Asia/Kabul"],
"+05:45 +05:45":["Asia/Kathmandu"],
"+03:30 +04:30":["Asia/Tehran"],
"+06:30 +06:30":["Asia/Yangon","Indian/Cocos"],
"01:00 01:00":["Atlantic/Cape_Verde"],
"+09:30 +10:30":["Australia/Adelaide","Australia/Broken_Hill"],
"+10:00 +11:00":["Australia/Currie","Australia/Hobart","Australia/Melbourne","Australia/Sydney"],
"+09:30 +09:30":["Australia/Darwin"],
"+08:45 +08:45":["Australia/Eucla"],
"+10:30 +11:00":["Australia/Lord_Howe"],
"+13:00 +14:00":["Pacific/Apia","Pacific/Tongatapu"],
"+12:00 +13:00":["Pacific/Auckland","Pacific/Fiji"],
"+12:45 +13:45":["Pacific/Chatham"],
"+13:00 +13:00":["Pacific/Enderbury","Pacific/Fakaofo"],
"09:00 09:00":["Pacific/Gambier"],
"10:00 10:00":["Pacific/Honolulu","Pacific/Rarotonga","Pacific/Tahiti"],
"+14:00 +14:00":["Pacific/Kiritimati"],
"09:30 09:30":["Pacific/Marquesas"],
"11:00 11:00":["Pacific/Niue","Pacific/Pago_Pago"],
"08:00 08:00":["Pacific/Pitcairn"]}

View File

@ -0,0 +1,31 @@
// https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
var zones = [];
var zoneMap = {};
var all = document.body
.querySelector('.wikitable.sortable.jquery-tablesorter')
.querySelectorAll('tr');
all = [].slice.call(all, 1); // remove header
all.forEach(function(el) {
if (/Alias|Deprecated|Etc\//.test(el.outerText)) {
$(el).remove();
return;
}
var fields = [].slice.call(el.querySelectorAll('td'));
var f = fields.map(function(td) {
return td.innerText.trim();
});
var id = f[5] + ' ' + f[6];
if (!zoneMap[id]) {
zones.push([f[2], f[5], f[6]]);
}
zoneMap[id] = zoneMap[id] || [];
zoneMap[id].push(f[2]);
});
// console.log(JSON.stringify(zones));
console.log('Total:', all.length);
console.log('Unique:', Object.keys(zoneMap).length);
console.log(zoneMap);