hippie/source/code/hippie/app.js

723 lines
17 KiB
JavaScript

// TODO: Inhalte angleichen nach Zusammenfassung von app.js und function.js.
// Benennung und Beschreibungen verbessern.
// This is called everytime
function setup() {
'use strict';
console.group('Document information');
console.info('\n', HIPPIE.brand, '\n\n');
console.info('HTML:', hippie.screen, '\nBODY:', hippie.body);
console.groupEnd();
if (debugOn) {
console.group('Debug information');
console.dir(hippie);
console.groupEnd();
}
}
// MODULE Scroll navigation
// Using constructor function
function HippieScroll($tp, $dn) {
'use strict';
// this.$tp = $tp;
// Define initial situation
let initLeft = false;
const initY = hippie.screen.vh;
$tp.addClass('di_none');
// Check scroll position and toggle element
this.check = function () {
hippie.screen.y = Math.min($(document).scrollTop(), document.documentElement.scrollTop);
if (hippie.screen.y > initY) {
if (!initLeft) {
$tp.removeClass('di_none');
console.info('Initial viewport left');
}
initLeft = true;
} else {
if (initLeft) {
$tp.addClass('di_none');
console.info('Initial viewport entered');
}
initLeft = false;
}
};
// Add events to navigation elements
$tp.click(function (event) {
event.preventDefault();
$('html, body').stop().animate({
scrollTop: 0
}, basicEase);
// console.log('Scrolled to top');
});
$dn.click(function (event) {
event.preventDefault();
const pos = Math.max(hippie.screen.dh, hippie.body.h) - hippie.screen.vh;
$('html').scrollTop(pos);
// document.documentElement.scrollTop = pos;
console.info('Scrolled down to', pos);
});
}
function HippieFade(toggleElement, initState) {
'use strict';
const fragment = document.createDocumentFragment();
const overlay = document.createElement('div');
overlay.id = 'mouse-overlay';
if (initState) {
overlay.classList.add('active');
}
toggleElement.addEventListener('click', function () {
overlay.classList.toggle('active');
});
fragment.appendChild(overlay);
document.body.style.position = 'relative';
document.body.prepend(fragment);
}
// MODULE Meta elements
function HippieMeta($ma, $pp) {
'use strict';
let metaOn = false;
$ma.click(function () {
let $wrap, $pop;
// if (metaOn !== true) {
if (!metaOn) {
metaOn = true;
$pp.each(function () {
// if ($(this).css('position') === 'static') {
// $(this).addClass('js_changed_pos');
// $(this).css('position', 'relative');
// }
// $pop = $(this).next('.exp_pop').detach();
// $wrap = $(this).wrap('<span class="exp_wrap"></span>').parent().prepend('<span class="exp_overlay"></span>').prepend('<span class="exp_marker_pop"></span>');
// $wrap.after($pop);
$('<div></div>').addClass('exp_overlay').css({
position: 'absolute',
width: '100%',
height: '100%',
top: 0,
left: 0
}).appendTo($(this).addClass('exp_wrap'));
// Displays explanation popup following the mouse
$(this).on({
mouseenter: function () {
// if ($(this).attr('emmet')) {
//
// }
$(this).next('.exp_pop').show();
},
mouseleave: function () {
$(this).next('.exp_pop').hide();
},
mousemove: function (event) {
$(this).next('.exp_pop').css({
'top': event.pageY - $(this).next('.exp_pop').outerHeight() - 4,
'left': event.pageX + 8
// 'left': event.pageX - $(this).offset().left + 8
});
}
});
});
} else {
$pp.each(function () {
$(this).off('mouseenter mouseleave mousemove');
$(this).removeClass('exp_wrap').find('.exp_overlay').remove();
// $wrap = $(this).parent('.exp_wrap');
// $pop = $wrap.next('.exp_pop').detach();
// $wrap.find('.exp_marker_pop').remove();
// $(this).unwrap('.exp_wrap');
// $(this).after($pop);
// if ($(this).hasClass('js_changed_pos')) {
// $(this).css('position', '');
// if ($(this).attr('style') === '') {
// $(this).removeAttr('style');
// }
// $(this).removeClass('js_changed_pos');
// }
});
metaOn = false;
}
console.log('Explanation mode', metaOn);
});
}
// Sets the href attribute to mailto: with given information
function composeMail(tag, name, prov, suffix, text, topic) {
'use strict';
let trigger = tag.indexOf('.');
let mailString = name + '@' + prov + '.' + suffix;
let textString = mailString.replace(/@/g, '(at)');
let descString = 'Nachricht an ' + mailString;
if (!text) {
text = mailString;
} else if (text === 'at') {
text = textString;
} else if (text === 'to') {
text = descString;
}
if (topic) {
topic = '?subject=' + topic;
} else {
topic = '';
}
if (trigger === -1) {
const el = document.getElementById(tag);
const elContent = el.innerHTML;
el.innerHTML = elContent + text;
el.setAttribute('href', 'mailto:' + mailString + topic);
} else {
const els = document.getElementsByClassName(tag.slice(1));
for (let el of els) {
const elContent = el.innerHTML;
el.innerHTML = elContent + text;
el.setAttribute('href', 'mailto:' + mailString + topic);
}
}
}
// get document coordinates of the element
// function getCoords (elem) {
// let box = elem.getBoundingClientRect();
//
// return {
// top: box.top + pageYOffset,
// left: box.left + pageXOffset
// };
// }
// https://stackoverflow.com/a/488073/1444149
// function Utils () {}
//
// Utils.prototype = {
// constructor: Utils,
// isElementInView: function (element, fullyInView) {
// var pageTop = $(window).scrollTop();
// var pageBottom = pageTop + $(window).height();
// var elementTop = $(element).offset().top;
// var elementBottom = elementTop + $(element).height();
//
// if (fullyInView === true) {
// return ((pageTop < elementTop) && (pageBottom > elementBottom));
// } else {
// return ((elementTop <= pageBottom) && (elementBottom >= pageTop));
// }
// }
// };
//
// var Utils = new Utils();
class TimeDisplay {
constructor(element, options, interval) {
this.element = element;
this.options = options || {hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false};
this.interval = interval || 1000;
this.isPaused = false;
this.locale = navigator.language || 'en-US';
this.updateTime();
console.group('Time information');
console.info('\nOptions:', this.options, '\n\n');
console.info('Interval:', this.interval);
console.groupEnd();
}
formatTime(time) {
return time.toLocaleTimeString(this.locale, this.options);
}
// TODO: Zeit nur im Sekundentakt aktualisieren wenn Sekunden angezeigt werden
async updateTime() {
while (true) {
if (!this.isPaused) {
const now = new Date();
this.element.textContent = this.formatTime(now);
}
await new Promise(resolve => setTimeout(resolve, this.interval));
}
}
pause() {
this.isPaused = true;
}
resume() {
this.isPaused = false;
}
}
class DateDisplay {
constructor(element, options, direction) {
this.element = element;
this.options = options || {year: 'numeric', month: 'long', day: 'numeric'};
this.direction = direction || 0;
this.updateDate();
this.checkForDateChange();
console.group('Date information');
console.info('\nOptions:', this.options, '\n\n');
console.info('Remaining minutes:', Math.floor(this.getTimeUntilNextMidnight() / 3600));
console.groupEnd();
}
formatDate(date) {
const formatter = new Intl.DateTimeFormat(navigator.language, this.options);
switch (this.direction) {
case 1:
const dateString = formatter
.formatToParts(date)
.map(({type, value}) => {
// if (type === 'day' || type === 'month') {
if (type === 'literal') {
return `${value}<br>`;
} else {
return value;
}
})
.join('');
return dateString;
case 0:
default:
return formatter.format(date);
}
}
updateDate() {
const now = new Date();
this.element.innerHTML = this.formatDate(now);
}
changeFormat(format, direction) {
this.options = format;
this.direction = direction;
this.updateDate();
}
getTimeUntilNextMidnight() {
const now = new Date();
const nextMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
return nextMidnight - now;
}
checkForDateChange() {
const timeUntilNextMidnight = this.getTimeUntilNextMidnight();
setTimeout(() => {
this.updateDate();
this.checkForDateChange();
}, timeUntilNextMidnight);
}
}
function checkButtonAndTarget(event, element, button = 0) {
return (
event.button === button &&
event.target === element
);
}
function getClosestEdgeToElement(element) {
'use strict';
const bounding = element.getBoundingClientRect();
const distances = {
top: bounding.top,
right: window.innerWidth - bounding.right,
bottom: window.innerHeight - bounding.bottom,
left: bounding.left
};
return Object.keys(distances).reduce((a, b) => distances[a] < distances[b] ? a : b);
}
function getClosestEdgeToMouse(event) {
'use strict';
const mouseX = event.clientX;
const mouseY = event.clientY;
const distances = {
left: mouseX,
right: window.innerWidth - mouseX,
top: mouseY,
bottom: window.innerHeight - mouseY
};
return Object.keys(distances).reduce((a, b) =>
distances[a] < distances[b] ? a : b
);
}
function centerElementUnderCursor(event, element) {
'use strict';
const offsetX = element.getBoundingClientRect().width / 2;
const offsetY = element.getBoundingClientRect().height / 2;
const x = event.clientX - offsetX;
const y = event.clientY - offsetY;
element.style.left = `${x}px`;
element.style.top = `${y}px`;
}
function setAttributesAccordingToPosition(element, position, attributes) {
'use strict';
element.classList.remove(...Object.values(attributes).map(pos => pos.className));
Object.keys(attributes[position].styles).forEach(key => {
element.style[key] = '';
});
element.classList.add(attributes[position].className);
Object.entries(attributes[position].styles).forEach(([key, value]) => {
element.style[key] = value;
});
}
/**
* Gibt eine Zahl zwischen <min> und <max> aus.
* Die Werte <min> und <max> sind dabei mit eingeschlossen.
* Mit <pos> kann der Exponent für eine 10er-Teilung angegeben werden.
*
* @param {number} min
* @param {number} max
* @param {number} pos
* @returns {number}
*/
function randomIntFrom(min, max, pos = 0) {
pos = Math.pow(10, pos);
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor((Math.random() * (max - min + 1) + min) / pos) * pos;
}
function randomFloatFrom(min, max, dec = 0) {
dec = Math.pow(10, dec);
return Math.round((Math.random() * (max - min + 1) + min) * dec) / dec;
}
/**
* Ersetzt \n durch <br>.
*
* @param {string} text
* @returns {string}
*/
function replaceLineBreaks(text) {
if (text === '' || !text.includes('\n')) {
return text;
}
return text.split('\n').join('<br>');
}
/**
* Gibt eine zufällige Farbe als HEX-Wert aus.
*
* @returns {string}
*/
function getRandomColor() {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
function getRandomFormattedString(chars = 2, digits = 6, separator = '-') {
const getRandomUppercase = () => String.fromCharCode(Math.floor(Math.random() * 26) + 65);
const getRandomDigit = () => Math.floor(Math.random() * 10);
let string = '';
for (let i = 0; i < chars; i++) {
string += getRandomUppercase();
}
string += separator;
for (let i = 0; i < digits; i++) {
string += getRandomDigit();
}
return string;
}
function toggleColumn(table, index) {
const rows = table.rows;
const isHidden = rows[0].cells[index].classList.contains('di_none');
for (let i = 0; i < rows.length; i++) {
const cell = rows[i].cells[index];
if (isHidden) {
cell.classList.remove('di_none');
} else {
cell.classList.add('di_none');
}
}
}
function convertToRomanNumeral(num) {
const romanNumeralMap = [
{value: 1000, numeral: 'M'},
{value: 900, numeral: 'CM'},
{value: 500, numeral: 'D'},
{value: 400, numeral: 'CD'},
{value: 100, numeral: 'C'},
{value: 90, numeral: 'XC'},
{value: 50, numeral: 'L'},
{value: 40, numeral: 'XL'},
{value: 10, numeral: 'X'},
{value: 9, numeral: 'IX'},
{value: 5, numeral: 'V'},
{value: 4, numeral: 'IV'},
{value: 1, numeral: 'I'}
];
let result = '';
for (let i = 0; i < romanNumeralMap.length; i++) {
while (num >= romanNumeralMap[i].value) {
result += romanNumeralMap[i].numeral;
num -= romanNumeralMap[i].value;
}
}
return result;
}
function capitalizeFirstLetter(text) {
return text.charAt(0).toUpperCase() + text.slice(1);
}
function mapRange(value, inMin, inMax, outMin, outMax, reverse = false, clamp = false) {
let min = outMin;
let max = outMax;
if (reverse) {
[min, max] = [max, min];
}
const mapped = (value - inMin) * (max - min) / (inMax - inMin) + min;
if (clamp) {
return Math.max(Math.min(min, max), Math.min(Math.max(min, max), mapped));
}
return mapped;
}
// CONCEPTS
// NOTE: Benutzt private Zuweisungen
class elementBinder {
#element;
constructor(element) {
this.#setElement(element);
}
#setElement(value) {
if (!value) {
throw new Error('No element found');
}
this.#element = value;
this.#element.style.background = "hotpink";
}
get element() {
return this.#element;
}
set element(value) {
this.#setElement(value);
}
}
//OLD
function Clock(id) {
'use strict';
this.id = id;
const that = this;
setInterval(function () {
that.updateClock();
}, 1000);
this.updateClock();
}
Clock.prototype.updateClock = function () {
'use strict';
const date = new Date();
const clock = document.getElementById(this.id);
//console.log(this);
clock.innerHTML = this.formatDigits(date.getHours()) + ':' + this.formatDigits(date.getMinutes()) + ":" + this.formatDigits(date.getSeconds());
};
Clock.prototype.formatDigits = function (val) {
'use strict';
if (val < 10) val = '0' + val;
return val;
};
function ongoing() {
var now = new Date();
var w = Math.floor(now.getDay());
var D = ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'];
var DNumb = Math.floor(now.getDate());
var MNumb = Math.floor(now.getMonth());
var M = ['Januar', 'Februar', 'M&auml;rz', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'MaiOktober', 'November', 'Dezember'];
var y = Math.floor(now.getYear());
if (y < 999) y += 1900;
var ms = Math.floor(now.getMilliseconds());
var s = Math.floor(now.getSeconds());
var m = Math.floor(now.getMinutes() + s / 60);
var h = Math.floor(now.getHours() + m / 60);
var j2000 = new Date(); // Bezugspunkt ist der 1.1.2000 0:00 UT (entspricht JD 2451544,5)
j2000.setUTCFullYear(2000, 0, 1);
j2000.setUTCHours(0, 0, 0, 0);
var utc = new Date();
utc.setUTCFullYear(y, MNumb, DNumb); // Monate müssen im Wertebereich 0...11 übergeben werden
utc.setUTCHours(h, m, s, ms);
var utc0 = new Date();
utc0.setUTCFullYear(y, MNumb, DNumb);
utc0.setUTCHours(0, 0, 0, 0);
var jd = 2451544.5 + (utc - j2000) / 86400000; // Zählung erfolgt in Millisekunden, 1 Tag = 86.400.000 ms
var jdUTC0 = 2451544.5 + (utc0 - j2000) / 86400000;
var N = jd - 2451545.0;
var L = 280.460 + 0.9856474 * N; // mittlere ekliptikale Länge der Sonne
var g = 357.528 + 0.9856003 * N; // mittlere Anomalie
var el = L + 1.915 * Math.sin(g) + 0.020 * Math.sin(2 * g);
var e = 23.439 - 0.0000004 * N;
var rektaszension = Math.atan((Math.cos(e) * Math.sin(el)) / Math.cos(el));
var T = (jdUTC0 - 2451545.0) / 36525;
var stGMT = (((6 * 3600) + (41 * 60) + 50.54841) + (8640184.812866 * T) + (0.093104 * Math.pow(T, 2)) - (0.0000062 * Math.pow(T, 3))) / 3600;
var stGMT2 = 6.697376 + 2400.05134 * T + 1.002738 * T;
var hWGMT = stGMT2 * 15;
var hW = hWGMT + 11.9566185772;
var st = (stGMT + (now.getUTCHours() * 1.00273790935)) + (11.9566185772 / 15); // Sommerzeit muss noch berücksichtigt werden
var st24 = Math.abs(st - (Math.round(st / 24) * 24));
var stH = Math.floor(st24);
var stM = Math.floor((st24 % 1) * 60);
var stS = zeroFill(Math.floor((((st24 % 1) * 60) % 1) * 60), 2);
var travelWidth = document.body.clientWidth;
var travelHeight = document.body.clientHeight;
var sunPosX = 0;
var sunPosY = 0;
var moonPosX = 0;
var moonPosY = 0;
var sun = $('#sun').css({
'left': (s / 60) * travelWidth,
'top': (m / 60) * travelHeight
});
$('#day').text(D[w]);
$('#dayNumb').text(DNumb);
$('#month').text(M[MNumb]);
$('#year').text(y);
$('#time').text('' + zeroFill(h, 2) + ':' + zeroFill(m, 2) + ':' + zeroFill(s, 2));
$('#julian').text(jd.toFixed(6));
//$('#star').text(stH + ':' + stM + ':' + stS);
$('#star').text(stH + ':' + stM);
$('#star1').text(stGMT);
$('#star2').text(stGMT2);
}
function zeroFill(number, width) {
width -= number.toString().length;
if (width > 0) {
return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number;
}
return number + ''; // always return a string
}
//Länge der Balken im Diagram berechnen
function barwidth(size, G, W) {
var s = size;
var g = G;
var w = W;
var p = (w / g) * 100;
var newW = s * (p / 100);
return newW;
}
//String Element erweitern
String.prototype.transform = function () {
return parseFloat(this.replace(',', '.'));
}
//Array Element erweitern
Array.prototype.arrayAdd = function () {
return eval(this.join('+'));
}
//Speicherplatz in Prozent berechnen
function percentage(total, gigs, round) {
var totalSpace = total;
var singleSpace = gigs;
var z = round;
var p = singleSpace / (totalSpace / 100);
return p;
}
//Speicherplatz in GB berechnen
function gigabytes(percent, total, round) {
var occupiedPercent = percent;
var singleSpace = total;
var z = round;
var g = (singleSpace / 100) * occupiedPercent;
return g;
}