uhr/src/uhr.ts

431 lines
16 KiB
TypeScript

/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import {WidgetPrototype} from "./widget/widget-prototype";
import {Globals} from "./domain/globals";
import {UhrRenderer} from "./renderer";
import {EMPTY_LAYOUT, Layout} from "./domain/layout";
import * as Cookies from "js-cookie";
export class Uhr {
private timer: number = null;
private currentMinute: number = null;
constructor(private widgetInstance: WidgetPrototype) {
const userTime = this.widgetInstance.options.time;
if (this.widgetInstance.options.time === undefined) {
this.widgetInstance.options.time = new Date();
}
this.parseHash();
this.setupHTML();
this.wireFunctionality();
if (userTime !== undefined) {
this.setTime(userTime);
}
}
destroy(): void {
if (!!this.timer) {
window.clearInterval(this.timer);
this.timer = null;
}
this.widgetInstance.element
.removeAttr('style')
.removeAttr('class')
.empty();
$(`#uhr-configlink${this.widgetInstance.uuid}`).remove();
$(`#uhr-controlpanel${this.widgetInstance.uuid}`).remove();
}
start(): void {
if (!this.isOn()) {
this.timer = window.setInterval(() => {
this.widgetInstance.options.time = new Date();
this.update();
}, 1000);
this.update();
this.setCookie('uhr-status', 'on');
}
}
stop(): void {
if (this.isOn()) {
window.clearInterval(this.timer);
this.timer = null;
this.update();
this.setCookie('uhr-status', 'off');
}
}
toggle(): void {
if (this.isOn()) {
this.stop();
} else {
this.start();
}
}
setLanguage(key: string): void {
if (key !== this.widgetInstance.options.language) {
this.widgetInstance.options.language = key;
const renderer = new UhrRenderer(this.getCurrentLayout(), this.widgetInstance.element.find('.letterarea'));
renderer.render(() => {
this.currentMinute = -1;
this.update();
});
this.setCookie('uhr-language', key);
this.update();
}
}
setTheme(styleClass: string): void {
if (styleClass !== this.widgetInstance.options.theme) {
this.widgetInstance.element.removeClass(this.widgetInstance.options.theme).addClass(styleClass);
$(`#uhr-onoffswitch${this.widgetInstance.uuid}`).removeClass(this.widgetInstance.options.theme).addClass(styleClass);
this.widgetInstance.options.theme = styleClass;
this.setCookie('uhr-theme', styleClass);
}
}
setTime(time: Date): void {
this.currentMinute = null;
if (time === null) {
this.widgetInstance.options.time = new Date();
} else {
if (this.timer !== null) {
window.clearInterval(this.timer);
}
this.widgetInstance.options.time = time;
}
this.update();
}
setMode(mode: string): void {
this.widgetInstance.options.mode = mode;
this.currentMinute = null;
this.update();
this.setCookie('uhr-mode', mode);
}
setWidth(width: string): void {
const e = this.widgetInstance.element;
e.css('width', width);
const realWidth = e.width();
e.width(realWidth);
e.height(realWidth);
e.css('font-size', (realWidth / 40) + 'px');
}
private setupHTML(): void {
const e: JQuery<HTMLElement> = this.widgetInstance.element;
// Base clock area
e.addClass('uhr')
.empty()
.append('<span class="item dot dot1"></span>')
.append('<span class="item dot dot2"></span>')
.append('<span class="item dot dot3"></span>')
.append('<span class="item dot dot4"></span>')
.append('<div class="letterarea"></div>')
.append('<div class="reflection"></div>');
this.setWidth(this.widgetInstance.options.width);
if (this.widgetInstance.options.controls) {
const controlpanel = $(`<div class="uhr-controlpanel" id="uhr-controlpanel${this.widgetInstance.uuid}"></div>`);
const content = $('<div class="content"></div>');
controlpanel.append(content);
// on/off switch
const toggleSwitch = $(`<div class="onoffswitch" id="uhr-onoffswitch${this.widgetInstance.uuid}"></div>`);
toggleSwitch.append(`<input type="checkbox" class="onoffswitch-checkbox" id="uhr-onoffswitch-checkbox${this.widgetInstance.uuid}" checked="checked" />`);
toggleSwitch.append(`<label class="onoffswitch-label" for="uhr-onoffswitch-checkbox${this.widgetInstance.uuid}"><div class="onoffswitch-inner"></div><div class="onoffswitch-switch"></div></label>`);
content.append(toggleSwitch);
// time mode switch
const modeSwitch = $(`<div class="onoffswitch" id="uhr-modeswitch${this.widgetInstance.uuid}"></div>`);
modeSwitch.append(`<input type="checkbox" class="onoffswitch-checkbox" id="uhr-modeswitch-checkbox${this.widgetInstance.uuid}" checked="checked" />`);
modeSwitch.append(`<label class="onoffswitch-label" for="uhr-modeswitch-checkbox${this.widgetInstance.uuid}"><div class="modeswitch-inner"></div><div class="onoffswitch-switch"></div></label>`);
content.append(modeSwitch);
// language chooser
if (Globals.hasMultipleLayouts()) {
const languageChooser = $(`<select id="uhr-languagechooser${this.widgetInstance.uuid}"></select>`);
Globals.getLayouts().forEach(layout => {
languageChooser.append(`<option value="${layout.code}">${layout.prettyName}</option>`);
});
content.append(languageChooser);
}
// theme chooser
if (Globals.hasMultipleThemes()) {
const themeChooser = $(`<select id="uhr-themechooser${this.widgetInstance.uuid}"></select>`);
Globals.getThemes().forEach(theme => {
themeChooser.append(`<option value="${theme.styleClass}">${theme.name}</option>`);
});
content.append(themeChooser);
}
const closebutton = $(`<a class="uhr-closecontrolpanel" id="uhr-closecontrolpanel${this.widgetInstance.uuid}"></a>`);
//FIXME deprecated?!
closebutton.on('click', () => $(`#uhr-controlpanel${this.widgetInstance.uuid}`).hide('fast'));
content.append(closebutton);
e.after(controlpanel);
controlpanel.hide();
const configlink = $(`<a class="uhr-configlink" id="uhr-configlink${this.widgetInstance.uuid}"></a>`);
// FIXME deprecated!?
configlink.on('click', () => this.toggleConfigScreen());
e.after(configlink);
}
};
private wireFunctionality(): void {
// on/off switch
const toggleSwitch = $(`#uhr-onoffswitch-checkbox${this.widgetInstance.uuid}`);
// FIXME deprecated!?
toggleSwitch.on('click', () => this.toggle());
let status = this.getCookie('uhr-status');
if (status === undefined || this.widgetInstance.options.force) {
status = this.widgetInstance.options.status;
}
toggleSwitch.prop('checked', status === 'on');
if (status === 'on') {
this.start();
} else {
this.stop();
}
// time mode switch
const modeSwitch = $(`#uhr-modeswitch-checkbox${this.widgetInstance.uuid}`);
// FIXME deprecated!?
modeSwitch.on('click', () => {
if (this.widgetInstance.options.mode === 'seconds') {
this.setMode('normal');
} else {
this.setMode('seconds');
}
});
let mode = this.getCookie('uhr-mode');
if (mode === undefined || this.widgetInstance.options.force) {
mode = this.widgetInstance.options.mode;
}
modeSwitch.prop('checked', mode !== 'seconds');
if (mode === 'seconds') {
this.setMode('seconds');
} else {
this.setMode('normal');
}
// language chooser
const languageChooser = $(`#uhr-languagechooser${this.widgetInstance.uuid}`);
// FIXME deprecated!?
languageChooser.on('change', () => {
const languageKey = $(`#uhr-languagechooser${this.widgetInstance.uuid}`).val() as string;
this.setLanguage(languageKey);
});
let selectedLayout = this.getCookie('uhr-language');
if (selectedLayout === undefined || this.widgetInstance.options.force) {
selectedLayout = this.widgetInstance.options.language;
}
let found = Globals.getLayouts().some(item => selectedLayout === item.code);
if (!found) {
let fallbackLanguage;
if (Globals.hasLayouts()) {
fallbackLanguage = Globals.getFirstLayout().code;
} else {
fallbackLanguage = '';
}
console.warn(`Language '${selectedLayout}' not found! Using fallback '${fallbackLanguage}'.`);
selectedLayout = fallbackLanguage;
}
languageChooser.val(selectedLayout);
this.widgetInstance.options.language = "";
this.setLanguage(selectedLayout);
// theme chooser
const themeChooser = $(`#uhr-themechooser${this.widgetInstance.uuid}`);
// FIXME deprecated!?
themeChooser.on('change', () => {
const themeKey = $(`#uhr-themechooser${this.widgetInstance.uuid}`).val() as string;
this.setTheme(themeKey);
});
let selectedTheme = this.getCookie('uhr-theme');
if (selectedTheme === undefined || this.widgetInstance.options.force) {
selectedTheme = this.widgetInstance.options.theme;
}
found = Globals.getThemes().some(item => selectedTheme === item.styleClass);
if (!found) {
const fallbackTheme = Globals.getFirstTheme().styleClass;
console.warn(`Theme '${selectedTheme}' not found! Using fallback '${fallbackTheme}'.`);
selectedTheme = fallbackTheme;
}
themeChooser.val(selectedTheme);
this.widgetInstance.options.theme = "";
this.setTheme(selectedTheme);
if (this.widgetInstance.options.autoresize) {
// FIXME deprecated!?
$(window).on('resize', () => {
const $e = this.widgetInstance.element;
const $parent = $e.parent();
const $window = $(window);
const parentWidth = $parent.width();
const parentHeight = $parent.height();
const windowWidth = $window.width();
const windowHeight = $window.height();
const size = `${Math.min(parentWidth, parentHeight, windowWidth, windowHeight)}px`;
this.setWidth(size);
});
}
}
private isOn(): boolean {
return this.timer !== null;
}
private getCookie(cookieName: string): string {
return Cookies.get(cookieName + this.widgetInstance.uuid);
}
private setCookie(cookieName: string, cookieValue: string): void {
let options;
if (this.widgetInstance.options.cookiePath !== undefined) {
options = {expires: 365, path: this.widgetInstance.options.cookiePath};
} else {
options = {expires: 365};
}
Cookies.set(cookieName + this.widgetInstance.uuid, cookieValue, options);
}
private update(): void {
if (this.isOn()) {
const time = this.widgetInstance.options.time;
if (!this.getCurrentLayout().hasOwnProperty('seconds') && this.widgetInstance.options.mode !== 'seconds') {
if (time.getMinutes() === this.currentMinute) {
return;
}
this.currentMinute = time.getMinutes();
}
this.show(time);
} else {
this.clear();
this.currentMinute = -1;
}
}
private show(time: Date): void {
const second = this.getSecond(time);
const dotMinute = this.getDotMinute(time);
const hour = this.getHour(time);
const coarseMinute = this.getCoarseMinute(time);
this.clear();
if (this.widgetInstance.options.mode === 'seconds') {
this.highlight(`second${second}`);
} else {
this.highlight('on');
for (let i = 1; i <= dotMinute; i++) {
this.highlight(`dot${i}`);
}
this.highlight(`minute${coarseMinute}`);
this.highlight(`hour${hour}`);
}
}
private clear(): void {
this.widgetInstance.element.find('.item').removeClass('active');
}
private highlight(itemClass: string): void {
this.widgetInstance.element.find(`.item.${itemClass}`).addClass('active');
}
private getSecond(time: Date): number {
if (typeof this.getCurrentLayout().getSeconds === 'function') {
return this.getCurrentLayout().getSeconds(time);
}
return time.getSeconds();
};
private getDotMinute(date: Date): number {
if (typeof this.getCurrentLayout().getDotMinute === 'function') {
return this.getCurrentLayout().getDotMinute(date);
}
return date.getMinutes() % 5;
};
private getCoarseMinute(date: Date): number {
if (typeof this.getCurrentLayout().getCoarseMinute === 'function') {
return this.getCurrentLayout().getCoarseMinute(date);
}
return date.getMinutes();
};
private getHour(date: Date): number {
if (typeof this.getCurrentLayout().getHour === 'function') {
return this.getCurrentLayout().getHour(date);
}
const hour = date.getHours();
if (date.getMinutes() >= 25) {
return (hour + 1) % 24;
}
return hour;
};
private toggleConfigScreen() {
$(`#uhr-controlpanel${this.widgetInstance.uuid}`).toggle('fast');
};
private parseHash(): void {
let hash: string = window.location.hash;
if (hash !== undefined && hash.charAt(0) === '#') {
hash = hash.substring(1);
hash = decodeURIComponent(hash);
const params: string[] = hash.split('&');
params.forEach(element => {
const pair: string[] = element.split('=');
const key = pair[0];
const value = pair[1];
switch (key) {
case 'l':
case 'language':
this.widgetInstance.options.language = value;
this.widgetInstance.options.force = true;
break;
case 't':
case 'theme':
this.widgetInstance.options.theme = value;
this.widgetInstance.options.force = true;
break;
case 'm':
case 'mode':
this.widgetInstance.options.mode = value;
this.widgetInstance.options.force = true;
break;
case 's':
case 'status':
this.widgetInstance.options.status = value;
this.widgetInstance.options.force = true;
break;
}
});
}
}
private getCurrentLayout(): Layout {
const matchingLanguages: Layout[] = Globals.getLayouts().filter(element => element.code === this.widgetInstance.options.language, this);
if (matchingLanguages.length > 0) {
return matchingLanguages[0];
}
// fallback: return empty object
return EMPTY_LAYOUT;
};
}