/*
* Copyright (C) 2014
* This file is part of WvS-VPlan.
*
* WvS-VPlan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
version = '0.9.9_rc8';
function istPremium(benutzer, key){ // Supersicherer Keygenerator :P Inspiriert von kryptochef.de
sollKey = '';
switch(benutzer.length % 10){ // Anzahl Zeichen ausschreiben
case 0: sollKey = 'NULL'; break;
case 1: sollKey = 'EINS'; break;
case 2: sollKey = 'ZWEI'; break;
case 3: sollKey = 'DREI'; break;
case 4: sollKey = 'VIER'; break;
case 5: sollKey = 'FUNF'; break;
case 6: sollKey = 'SECHS'; break;
case 7: sollKey = 'SIEBEN'; break;
case 8: sollKey = 'ACHT'; break;
case 9: sollKey = 'NEUN'; break;
}
sollKey = sollKey.replace(/[A-Z]/g, function(s){
return String.fromCharCode(s.charCodeAt(0) + (s < 'N' ? 13 : -13)); // ROT13
});
sollKey = sollKey + Math.round(benutzer.length * 3.14); // Eine Zahl anfügen
if(sollKey === key){
return true;
}else{
return false;
}
}
function updateCheck(){
var request = $.ajax({
url: 'http://klimaag.***REMOVED***/app/www/knockout.html',
cache: false
});
request.done(function(resultHtml){
var appStatus = $('li[id="' + version + '"]', resultHtml).text().trim();
var loginOk = true;
console.log('Appstatus: ' + appStatus);
switch(appStatus){
case 'ok':
break;
case 'hangon':
$('#versioninfo').text('Es besteht ein Problem mit der aktuellen Version. Ein Update ist in Arbeit.');
break;
case 'info':
$('#versioninfo').text('Ein Update ist im Appstore verfügbar.').show();
break;
case 'warn':
$('#versioninfo').text('Ein wichtiges Update ist im Appstore verfügbar.').show();
break;
case 'force':
$('#versioninfo').text('Ein wichtiges Update ist im Appstore verfügbar. Du musst es installieren, um dich weiterhin einloggen zu können.').show();
$('#loginsubmit').prop('disabled', true);
loginOk = false;
break;
default:
$('#versioninfo').text('Diese Appversion wird nicht mehr unterstützt, bitte aktualisiere auf die neueste Version aus dem Appstore.').show();
$('#loginsubmit').prop('disabled', true);
loginOk = false;
}
if(loginOk === true){
tryLogin(); // nur wenn alles in Ordnung ist, einloggen lassen
}
});
request.fail(fail);
}
function track(what){
_paq.push(['setCustomUrl', what]);
_paq.push(['trackPageView', what]);
}
function trackPlan(vdate){
track('plan/' + vdate.format('YYYY/MM/DD'));
}
$('#settingspanel').change(function(){
localStorage.setItem('dntrack', $('#dntflip').val());
localStorage.setItem('classes', $('#addclass').val().toUpperCase());
localStorage.setItem('notifyday', $('#notifydayflip').val());
localStorage.setItem('notifynull', $('#notifynullflip').val());
localStorage.setItem('notifydate', $('#notifydate').val());
localStorage.setItem('apptheme', $('#themeselect').val());
});
$('#saveflip').change(function(){
localStorage.setItem('savepass', $('#saveflip').val());
if($('#saveflip').val() === 'false'){
localStorage.removeItem('password');
}
});
document.addEventListener('offline', function(){
$('#loginsubmit').prop('disabled', true);
$('#error_noconn').show('fast');
}, false);
document.addEventListener('online', function(){
$('#loginsubmit').prop('disabled', false);
$('#error_noconn').hide('fast');
}, false);
$('#mainpage').on('swiperight', function(){
$('#settingspanel').panel('open');
});
var _paq = _paq || [];
$('#mainpage').on('pagebeforeshow', function(){
/* Versionsnummer zeigen */
$('.version').text(version);
/* Sprache setzen */
moment.lang('de');
/* Nutzungsstatistiken */
_paq.push(['setTrackerUrl', 'http://klimaag.***REMOVED***/piwik/piwik.php']); // TODO Https wäre schön.
_paq.push(['setSiteId', 5]);
console.log('Piwik initialisiert');
/* gespeicherte Daten laden */
var dntrack = localStorage.getItem('dntrack');
var additionalClasses = localStorage.getItem('classes');
var notifyDay = localStorage.getItem('notifyday');
var notifyNull = localStorage.getItem('notifynull');
var notifyDate = localStorage.getItem('notifydate');
var premium = localStorage.getItem('premium');
var easteregg = localStorage.getItem('easteregg');
var apptheme = localStorage.getItem('apptheme');
var savepass = localStorage.getItem('savepass');
if(dntrack === null || dntrack.length === 0){
dntrack = 'false';
}
$('#dntflip').val(dntrack).slider('refresh');
if(additionalClasses === null || additionalClasses.length === 0){
additionalClasses = '';
}
$('#addclass').val(additionalClasses).textinput('refresh');
if(notifyDay === null || notifyDay.length === 0){
notifyDay = 0;
}
$('#notifydayflip').val(notifyDay).slider('refresh');
if(notifyNull === null || notifyNull.length === 0){
notifyNull = 0;
}
$('#notifynullflip').val(notifyNull).slider('refresh');
if(notifyDate === null || notifyDate.length === 0){
notifyDate = '18:00';
}
$('#notifydate').val(notifyDate).textinput('refresh');
if(premium === null || premium.length === 0){
premium = 'false';
}
if(premium === 'true'){
$('#themeselect').append('').selectmenu('refresh');
console.log('Premium geladen');
}
if(easteregg === null || easteregg.length === 0){
easteregg = 'false';
}
if(easteregg === 'true'){
$('#themeselect').append('').selectmenu('refresh');
console.log('Easteregg geladen');
}
if(apptheme === null || apptheme.length === 0){
apptheme = 'a';
}
$('#themeselect').val(apptheme).selectmenu('refresh');
if(savepass === null || savepass.length === 0){
savepass = 'true';
}
$('#saveflip').val(savepass).slider('refresh');
/* Piwik initialisieren */
_paq.push(['setCustomVariable', 4, 'DNT', dntrack, 'visit']);
_paq.push(['setDoNotTrack', dntrack]);
});
// Login-Popup beim Start
document.addEventListener('deviceready', function(){
/* iOS fix */
if(window.device && parseFloat(window.device.version) >= 7.0) {
$('body').addClass('iOS7');
}
/* los geht's */
_paq.push(['setCustomVariable', 1, 'Modell', device.model, 'visit']);
_paq.push(['setCustomVariable', 2, 'Version', device.version, 'visit']);
_paq.push(['setCustomVariable', 3, 'Appversion', version, 'visit']);
track('load');
fillForm();
if(navigator.connection.type !== Connection.NONE){ // falls Netz da ist
updateCheck(); // checkt ob aktuellste Version und loggt ein
}
window.plugin.notification.local.cancelAll(); // alle Benachrichtigungen canceln, werden während des Renderings neu gesetzt
load();
}, false);
/* der Loader wird nur angezeigt, wenn das Popup zu ist */
$('#loginpopup').on('popupafteropen', function(){
$.mobile.loading('hide');
});
/* füllt die Loginform mit gespeicherten Werten */
function fillForm(){
var username = localStorage.getItem('username');
if(username !== null && username.length !== 0){
$('#username').val(username);
}
var password = localStorage.getItem('password');
if(password !== null && password.length !== 0){
$('#password').val(password);
}
}
/* benachrichtigt den Benutzer an vdate mit msg */
function addNotification(vdate, msgarr, $div){
var msg = msgarr.join('\n');
var title = 'Vertretungen für ' + vdate.format('dddd');
var ndate = vdate.subtract('days', 1).add('days', $('#notifydayflip').val()); // Bei Bedarf einen Tag vorher
if(moment().isBefore(ndate)){
window.plugin.notification.local.add({
date: ndate.toDate(),
message: msg,
title: title,
autoCancel: true,
json: JSON.stringify({id: $div.attr('id')})
});
window.plugin.notification.local.onclick = function(id, state, json){
var div = JSON.parse(json).id;
location.href = '#' + div; // TODO hoffentlich funktioniert das auch immer...
};
console.log('Benachrichtigung hinzugefügt: ' + ndate.format('DD.MM. HH:mm') + '; ' + msg);
}
}
/* zeigt einen Button an, mit dem die Vertretung geteilt werden kann */
function addSocialButton(vdate, msgarr, $div){
var msg = msgarr.join(', ');
if(moment().isBefore(vdate)){
var datestr = vdate.format('dddd');
var sharebtns = $('button', $div);
if(sharebtns.length === 0){
var btn = $('')
.text('Teile die Vertretungen')
.data('msg', msg)
.click(function(){
window.plugins.socialsharing.share('Hey Leute! Laut der WvS-VPlan-App gibt es folgende Vertretungen am ' + datestr + ': ' + $(this).data('msg'));
console.log('Vertretungsplan geteilt: ' + datestr);
track('share');
})
.addClass('ui-btn ui-corner-all ui-btn-inline ui-btn-icon-left ui-icon-share-square-o');
var btnholder = $('
');
btnholder.append(btn);
$div.prepend(btnholder);
console.log('Teilen-Button hinzugefügt: ' + datestr + ' ' + msg);
}else{
sharebtns.first().data('msg', sharebtns.first().data('msg') + ', ' + msg);
console.log('Zum Teilen-Button hinzugefügt: ' + datestr + ' ' + msg);
}
}
}
function checkeKlasse(nutzerklasse, klasse){
var subklassen = nutzerklasse.match(/(\d{1,2})([ABCD])/);
var wichtig = true;
if(nutzerklasse.length === 0){
wichtig = false;
}
if(klasse.indexOf(nutzerklasse) == -1){
if(subklassen !== null){
for(j = 1; j < subklassen.length; j++){ // Wenn alle Buchstaben der Klasse in der Vertretung vorkommen
if(klasse.indexOf(subklassen[j]) == -1){
wichtig = false;
}
}
}else{
wichtig = false;
}
}
return wichtig;
}
function entkuerzeleLehrer(kuerzel){ // wenn vorhanden, Langform zurückgeben
if(window.lehrerKuerzel[kuerzel] !== undefined){
return window.lehrerKuerzel[kuerzel];
}else{
return kuerzel;
}
}
function entkuerzeleFach(kuerzel){ // wenn vorhanden, Langform zurückgeben
if(window.fachKuerzel[kuerzel] !== undefined){
return window.fachKuerzel[kuerzel];
}else{
return kuerzel;
}
}
/* konvertiert die extrahierten Strings in ein lesbares Format und filtert nach relevanten Vertretungen */
function parseVertretungen(str, username){
/*
* Abstandhalter [ ]*
*
* ([\wäöü-]+)
* Klasse
*
* TODO offensichtlich kommt auch KlasseAlt -> KlasseNeu vor :T
*
* (\d+(?:[ ]*-[ ]*\d+)?)
* (Stunde x bis)? Stunde y
*
* (x?)
* Entfall ja/nein
*
* (?:(?:([\wäöü]+|\+)[ ]*(?:→|->)[ ]*([\wäöü]+|\+))|(?:()([\wäöü]+|\+)))
* Altes Fach -> Neues Fach | Neues Fach
*
* (?:(?:(\d+|---)[ ]*(?:→|->)[ ]*(\d+|---))|(?:()(\d+|---)))
* Alter Raum -> neuer Raum | neuer Raum
*
* ([\wäöü-]+|---|[+.])
* Art-Text
*
* (?:(?:([\wäöü]+|\+)[ ]*(?:→|->)[ ]*([\wäöü]+|\+))|(?:()([\wäöü]+|\+)))
* Alter Lehrer -> Neuer Lehrer | Neuer Lehrer
*
* ([\wäöü -]*)
* Info-Text
*/
var vertretungenRe = /([\wäöü-]+)[ ]+(\d+(?:[ ]*-[ ]*\d+)?)[ ]+(x?)[ ]*(?:(?:([\wäöü]+|\+)[ ]*(?:→|->)[ ]*([\wäöü]+|\+))|(?:()([\wäöü]+|\+)))[ ]+(?:(?:(\d+|---)[ ]*(?:→|->)[ ]*(\d+|---))|(?:()(\d+|---)))[ ]+([\wäöü-]+|---|[+.])[ ]+(?:(?:([\wäöü]+|\+)[ ]*(?:→|->)[ ]*([\wäöü]+|\+))|(?:()([\wäöü]+|\+)))[ ]*([\wäöü -]*)/i;
var res;
var vertretungen = [];
var nutzerklasse;
if(/\w+_.*/.test(username)){
nutzerklasse = username.match(/(\w+)_.*/)[1].toUpperCase();
}else{
return [];
}
str.split('\n').forEach(function(vertretungsstr){
result = vertretungsstr.match(vertretungenRe);
if(result !== null){
// aufräumen
var res = [];
for(var j = 0; j < result.length; j++){
if(result[j] != null){ // jshint ignore:line
res.push(result[j]);
}
}
var klasse = res[1].toUpperCase();
var stunde = res[2].trim();
var entfall = res[3].indexOf('x') > -1;
var altFach = res[4];
var fach = res[5];
var altRaum = res[6];
var raum = res[7];
var art = res[8];
var lehrer = res[9];
var vertreter = res[10];
var info = res[11];
var wichtig = false;
if(checkeKlasse(nutzerklasse, klasse) === true){
wichtig = true;
}else{
var additionalClasses = $('#addclass').val().split('\n'); // alle benutzerdefinierten Klassen durchgehen
additionalClasses.forEach(function(addCls){
if(checkeKlasse(addCls, klasse) === true){
wichtig = true;
}
});
}
/* entkürzeln :) */
lehrer = entkuerzeleLehrer(lehrer);
vertreter = entkuerzeleLehrer(vertreter);
altFach = entkuerzeleFach(altFach);
fach = entkuerzeleFach(fach);
if(wichtig === false){
return;
}
if(entfall === false){
if(info.length <= 1){
info = "";
}else{
info = " Info: " + info;
}
if(art.length <= 1){
art = "";
}else{
art = art + " in ";
}
var nlehrer;
var nraum;
var nfach;
if(lehrer != vertreter && lehrer !== '' && vertreter !== ''){
nlehrer = "mit " + vertreter + " (statt " + lehrer + ") ";
}else{
nlehrer = "mit " + vertreter + " ";
}
if(raum != altRaum && raum !== '' && altRaum !== ''){
nraum = "in Raum " + raum + " (statt " + altRaum + ") ";
}else{
nraum = "in Raum " + raum + " ";
}
if(fach != altFach && fach !== '' && altFach !== ''){
nfach = fach + " (statt " + altFach + ") ";
}else{
nfach = fach + " ";
}
vertretungen.push($.trim(klasse + " " + stunde + ". Stunde " + art + nfach + nlehrer + nraum + info));
}else{
var nstr = " (statt " + fach + " mit " + vertreter + ")";
if(info.length <= 1){
info = "";
}else{
info = " Info: " + info;
}
vertretungen.push($.trim(klasse + " " + stunde + ". Stunde Entfall" + nstr + info));
}
}else{
if(vertretungsstr.indexOf('Unterrichtsschluss') > -1){
vertretungen.push(vertretungsstr);
}
}
});
vertretungen = vertretungen.filter(function(elem, pos){ // doppelte Einträge filtern
return vertretungen.indexOf(elem) == pos;
});
console.log(JSON.stringify(vertretungen));
return vertretungen;
}
/* schreibt ein Bild und eine Textdatei mit dem Inhalt der PDF-Seite */
function schreibeCache($div, pageNumber, canvasUrl, extrakt){
var prefix = $div.data('file').replace(/\.pdf/gi, '') + '-' + pageNumber;
var bildName = prefix + '.png';
window.requestFileSystem(
LocalFileSystem.PERSISTENT, 0,
function(fileSystem){
fileSystem.root.getDirectory('WvS-VPlan', {create: true, exclusive: false},
function(dirEntry){
dirEntry.getFile(bildName, {create: true, exclusive: false},
function(fileEntry){
var fileTransfer = new FileTransfer();
fileTransfer.download(
canvasUrl,
fileEntry.toURL(),
function(theFile){
// Stille.
}, fail);
});
}, fail);
});
var textName = prefix + '.txt';
window.requestFileSystem(
LocalFileSystem.PERSISTENT, 0,
function(fileSystem){
fileSystem.root.getDirectory('WvS-VPlan', {create: true, exclusive: false},
function(dirEntry){
dirEntry.getFile(textName, {create: true, exclusive: false},
function(fileEntry){
fileEntry.createWriter(
function(fileWriter){
var blob;
try{
blob = new Blob([extrakt], {type: 'text/plain'}); // Kein 'new Blob' für Android, obwohl BlobBuilder obsolet ist
}catch(e){
window.BlobBuilder = window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;
if(e.name == 'TypeError' && window.BlobBuilder){
var bb = new BlobBuilder();
bb.append(extrakt);
blob = bb.getBlob('text/plain');
}
}
fileWriter.write(blob);
}, fail);
});
}, fail);
});
console.log($div.attr('id') + '-' + pageNumber + ' gecached');
}
function benutzeVertretungen(pageText, $div){
var userdata = getUserData();
if(userdata === null){
return;
}
var username = userdata[0];
var vertretungen = parseVertretungen(pageText, username);
var vdate = moment($div.data('date'), 'YYYY-MM-DD').startOf('second');
var time = $('#notifydate').val().split(':');
vdate.set('hour', time[0]);
vdate.set('minute', time[1]);
if(vertretungen.length > 0){
addSocialButton(vdate, vertretungen, $div);
addNotification(vdate, vertretungen, $div);
var $holder = $('');
vertretungen.forEach(function(vertretung){
var $text = $('');
$text.text(vertretung);
$holder.append($text);
});
$div.prepend($holder);
}else{
if($('#notifynullflip').val() == 'true'){
addNotification(vdate, ['Keine weiteren Vertretungen'], $div); // TODO FIXME Problem bei mehreren Seiten!
}
}
}
/* PDF Rendering */
function renderPage($div, pdf, pageNumber, callback){
pdf.getPage(pageNumber).then(function(page){
var scale = 1;
var viewport = page.getViewport(scale);
var pageDisplayWidth = viewport.width;
var pageDisplayHeight = viewport.height;
var $canvas = $('');
var canvas = $canvas.get(0);
canvas.className = 'pdfcanvas';
var context = canvas.getContext('2d');
canvas.width = pageDisplayWidth;
canvas.height = pageDisplayHeight;
$div.append($canvas);
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext).promise.then(function(pdf){
page.getTextContent().then(function(textContent){ // Text extrahieren
var pageText = "";
var lastBlock = null;
for(j = 0; j < textContent.items.length; j++){
var block = textContent.items[j];
if(lastBlock !== null && lastBlock.str[lastBlock.str.length - 1] !== ' '){
if(block.transform[4] < lastBlock.transform[4]){
pageText += '\n';
}else if(lastBlock.transform[5] !== block.transform[5] && (lastBlock.str.match(/^(\s?[a-zA-Z])$|^(.+\s[a-zA-Z])$/) === null )){
pageText += '\n';
}
}
pageText += block.str + ' ';
lastBlock = block;
}
benutzeVertretungen(pageText, $div);
/* Als Bild und Text speichern */
schreibeCache($div, pageNumber, canvas.toDataURL(), pageText);
});
callback(pdf);
});
});
}
function renderPdf(url, divstr){
console.log('Rendere PDF: ' + url + ' (' + divstr + ')');
PDFJS.getDocument(url).then(function(pdf){
var $viewer = $('#' + divstr);
var pageNumber = 1;
renderPage($viewer, pdf, pageNumber++, function pageRenderingComplete(){
if(pageNumber > pdf.numPages){
$.mobile.loading('hide'); // nach dem Rendern des ersten PDFs Loader verstecken
return;
}
renderPage($viewer, pdf, pageNumber++, pageRenderingComplete);
});
});
}
/* wird bei Verbindungsfehlern oder anderen Fehlern aufgerufen */
function fail(text){
$('#lnklogin').click();
$('#error_general').show('fast');
}
function replaceUmlauts(umlautstr){
tr = {'ä': 'ae', 'ü': 'ue', 'ö': 'oe', 'ß': 'ss'};
return umlautstr.replace(/[äöüß]/g, function($0) { return tr[$0]; });
}
function getPlanTabName(fname){
var name, vdate;
var fnameRe = /.*?(\d{4}-\d{2}-\d{2})(.*?)\.(?:pdf|png|txt)/;
var results = fname.match(fnameRe);
if(results !== null){
vdate = moment(results[1], 'YYYY-MM-DD').startOf('day');
if(results[2] !== null){
results[2] = results[2].replace(/[ ,]/g, '-').replace(/-{2}/g, '-');
}
name = vdate.format('DD_MM_YYYY') + replaceUmlauts(results[2]);
}else{
return null;
}
return [name, vdate, results[1]]; // TODO aua, das geht aber schöner
}
/* fügt ein neues Tab in die Navigationsleiste ein und gibt den Namen des Tab-Divs zurück */
function neuesPlanTab(fname){
var dname = decodeURIComponent(fname);
var name = "";
var vdate, rdate;
console.log('Dateiname: ' + dname);
name = getPlanTabName(dname)[0];
vdate = getPlanTabName(dname)[1];
rdate = getPlanTabName(dname)[2];
if(name !== null){
if(!$(name).length){
$('#vplannavbar').find('*').andSelf().each(function(){ // Navbar neuzeichnen; hier hat jQuery Mobile eine Schwäche :(
$(this).removeClass(function(i, cn){
var matches = cn.match(/ui-[\w\-]+/g) || [];
return (matches.join(' '));
});
if($(this).attr('class') === ''){
$(this).removeAttr('class');
}
});
$('#vplannavbar').navbar('destroy');
$('#vplannavbar ul').append('