/*
* 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.1_beta';
function updateCheck(){
var request = $.ajax({
url: 'http://klimaag.***REMOVED***/app/www/knockout.html'
});
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 'info':
$('#versioninfo').text('Ein Update ist verfügbar.').show();
break;
case 'warn':
$('#versioninfo').text('Ein wichtiges Update ist verfügbar.').show();
break;
case 'force':
$('#versioninfo').text('Ein wichtiges Update ist 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.').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('ui/tab/plan');
track('plan/' + vdate.format('YYYY/MM/DD'));
}
$('#settingspanel').on('panelclose', function(){
localStorage.setItem('dntrack', $('#dntflip').val());
localStorage.setItem('classes', $('#addclass').val().toUpperCase());
localStorage.setItem('notifyday', $('#notifydayflip').val());
localStorage.setItem('notifydate', $('#notifydate').val());
localStorage.setItem('apptheme', $('#themeselect').val());
});
document.addEventListener('pause', function(){
track('event/pause');
});
document.addEventListener('resume', function(){
track('event/resume');
});
document.addEventListener('offline', function offline(){
$('#loginsubmit').prop('disabled', true);
$('#error_noconn').show('fast');
}, false);
document.addEventListener('online', function online(){
$('#loginsubmit').prop('disabled', false);
$('#error_noconn').hide('fast');
}, false);
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 notifyDate = localStorage.getItem('notifydate');
var apptheme = localStorage.getItem('apptheme');
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(notifyDate === null || notifyDate.length === 0){
notifyDate = '18:00';
}
$('#notifydate').val(notifyDate).textinput('refresh');
if(apptheme === null || apptheme.length === 0){
apptheme = 'a';
}
$('#themeselect').val(apptheme).selectmenu('refresh');
/* Piwik initialisieren */
_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('event/load');
var dntrack = localStorage.getItem('dntrack');
if(dntrack === null || dntrack.length === 0){
setTimeout(function(){
$('#lnkdisclaimer').click();
}, 2000);
_paq.push(['setDoNotTrack', false]);
localStorage.setItem('dntrack', false);
$('#dntflip').val(dntrack);
}else{
fillForm();
updateCheck();
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');
});
$('#disclaimerpopup').on('popupafterclose', function(){
fillForm();
updateCheck();
window.plugin.notification.local.cancelAll();
load();
});
/* 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, msg, $div){
var title = 'Vertretung für ' + vdate.format('dddd');
vdate = vdate.subtract('days', 1).add('days', $('#notifydayflip').val()); // Bei Bedarf einen Tag vorher
if(moment().isBefore(vdate)){
window.plugin.notification.local.add({
date: vdate.toDate(),
message: msg,
title: title,
autoCancel: true,
json: JSON.stringify({id: $div.attr('id')}),
//badge: 0 //TODO
});
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: ' + vdate.format('DD.MM. HH:mm') + '; ' + msg);
}
}
/* zeigt einen Button an, mit dem die Vertretung geteilt werden kann */
function addSocialButton(vdate, msg, $div){
if(moment().isBefore(vdate)){
var datestr = vdate.format('dddd');
var btn = $('')
.text('Teile die Vertretungen')
.click(function(){
window.plugins.socialsharing.share('Hey Leute! Laut der WvS-VPlan-App gibt es folgende Vertretungen am ' + datestr + ': ' + msg);
console.log('Vertretungsplan geteilt: ' + datestr);
track('event/share');
})
.addClass('ui-btn ui-corner-all ui-btn-inline ui-btn-icon-left ui-icon-share-square-o');
$div.prepend(btn);
console.log('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;
}
/* konvertiert die extrahierten Strings in ein lesbares Format und filtert nach relevanten Vertretungen */
function parseVertretungen(str, username){
//Klasse Stunde Entfall Vertreter Fach Raum Art Lehrer Info
var vertretungenRe = new RegExp('(\\S+)\\s+(\\d(?:\\s+-\\s+\\d)?)\\s+((?:x\\s+)?)(\\S+)\\s+(\\w+|---)\\s+(\\w+|---)\\s+(\\S*)\\s+(\\S+)(?:\\s+)?(\\S*)');
var res;
var vertretungen = [];
var nutzerklasse = username.match(/(\w+)_.*/)[1].toUpperCase();
str.split('\n').forEach(function(vertretungsstr){
res = vertretungsstr.match(vertretungenRe);
if(res !== null){
var klasse = res[1];
var stunde = res[2].replace(/ /g, '');
var entfall = res[3].indexOf('x') > -1;
var vertreter = res[4];
var fach = res[5];
var raum = res[6];
var art = res[7];
var lehrer = res[8];
var info = res[9];
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;
}
});
}
if(wichtig === false){
return;
}
if(entfall === true){
if(art.length <= 1){
art = "";
}else{
art = " " + art;
}
if(info.length <= 1){
info = "";
}else{
info = " Info:" + info;
}
vertretungen.push(klasse + " " + stunde + ". Stunde Entfall statt " + lehrer + art + info);
}else{
if(art.length <= 1){
art = "";
}else{
art = art + " in ";
}
if(info.length <= 1){
info = "";
}else{
info = " Info:" + info;
}
var nlehrer;
if(lehrer !== vertreter){
nlehrer = " bei " + vertreter + " (statt " + lehrer + ")";
}
vertretungen.push(klasse + " " + stunde + ". Stunde " + art + fach + nlehrer + " in Raum " + raum + 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;
}
/* 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.length; j++){
var block = textContent[j];
if(lastBlock !== null && lastBlock.str[lastBlock.str.length - 1] !== ' '){
if(block.x < lastBlock.x){
pageText += '\n';
}else if(lastBlock.y !== block.y && (lastBlock.str.match(/^(\s?[a-zA-Z])$|^(.+\s[a-zA-Z])$/) === null )){
pageText += '\n';
}
}
pageText += block.str;
lastBlock = block;
}
var username = getUserData();
if(username === null){
return;
}else{
username = username[0];
}
var vertretungen = parseVertretungen(pageText, username);
if(vertretungen.length > 0){
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]);
addSocialButton(vdate, vertretungen.join(', '), $div);
addNotification(vdate, vertretungen.join('\n'), $div); //FIXME addNotification ändert vdate
}
});
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');
console.log('Fehler: ' + text.toString() + ' / ' + text.code);
track('error/general_' + text.toString() + '_' + text.code);
}
/* entfernt das Logintab */
function removeLoginTab(){
$('#vplannavbar').find('*').andSelf().each(function(){
$(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 li:first').remove();
$('#vplannavbar').navbar();
$('#login').remove();
$('#vplantabs').tabs('refresh');
var vdate = moment($('#vplantabs ul li:first').data('date'), 'YYYY-MM-DD');
trackPlan(vdate);
}
function replaceUmlauts(umlautstr){
tr = {'ä': 'ae', 'ü': 'ue', 'ö': 'oe', 'ß': 'ss'};
return umlautstr.replace(/[äöüß]/g, function($0) { return tr[$0]; });
}
/* fügt die lokale URL des Plans ein und ruft die Seite zum Rendering auf */
function addPlanAttr(fileEntry, wasdownloaded){
var sPath = fileEntry.toURL();
var name = decodeURIComponent(fileEntry.name);
console.log('Dateiname: ' + name);
var fnameRe = new RegExp('.*?(\\d{4}-\\d{2}-\\d{2})(.*?)\\.pdf');
var results = name.match(fnameRe);
var 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]);
if(!$(name).length){
$('#vplannavbar').find('*').andSelf().each(function(){
$(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('
' + name.replace(/_/g, '.') + '');
$('#vplannavbar ul li:last').click(function(){
trackPlan(vdate);
});
$('#vplannavbar').navbar();
$('#vplantabs').append('');
$('#vplantabs').tabs('refresh');
$('#' + name).data('date', results[1]);
if($('#login').length && wasdownloaded){
removeLoginTab();
}
}
renderPdf(sPath, name);
}
/* lädt ein PDF anhand eines Links herunter, benötigt ID zur Sortierung */
function downloadPdf(link){
var filename = link.substring(link.lastIndexOf("/") + 1);
window.requestFileSystem(
LocalFileSystem.PERSISTENT, 0,
function(fileSystem){
fileSystem.root.getDirectory('WvSVPlan', {create: true, exclusive: false},
function(dirEntry){
dirEntry.getFile(filename, {create: false, exclusive: false},
function(fileEntry){
// Datei ist schon heruntergeladen
console.log('Datei schon heruntergeladen');
if($('#login').length){
removeLoginTab();
$.mobile.loading('hide'); // Loader verstecken
}
},
function(err){
// Datei existiert noch nicht
dirEntry.getFile(filename, {create: true, exclusive: false},
function(fileEntry){
var fileTransfer = new FileTransfer();
fileTransfer.download(
link,
fileEntry.toURL(),
function(theFile){
console.log('Download fertig: ' + theFile.toURL());
addPlanAttr(theFile, true);
}, fail);
}, fail);
});
}, fail);
});
}
/* sucht den Downloadlink eines Plans mithilfe eines Links aus der Kursübersicht */
function getPlan(link){
var request = $.ajax({
url: link
});
request.done(function gotPlan(resultHtml){
var pdflink = $('.resourceworkaround a', resultHtml).attr('href');
if(pdflink === undefined){
return 0;
}
console.log('Plan gefunden: ' + pdflink);
downloadPdf(pdflink);
return 1;
});
request.fail(fail);
}
/* holt die Kursübersicht - bei Verbindungsfehlern wird onfail aufgerufen */
function getOverview(link){
var request = $.ajax({
url: link
});
console.log('Hole Übersicht');
request.done(function gotOverview(resultHtml){
/* ruft getPlan für jeden Link in der Kursübersicht auf */
var as = $('.activityinstance a', resultHtml);
if(as.length === 0){
$('#error_passwd').show('fast');
onfail();
return 0;
}
as.each(function(index, element){
console.log('suche Plan ' + element);
getPlan(element);
});
console.log('Übersichts-Request erfolgreich');
return 1;
});
request.fail(fail);
}
/* such den Link zur Übersicht von der Startseite aus */
function loggedIn(resultHtml){
var istLehrer = new RegExp(/\d/).test(getUserData()[0]) === false; // wenn eine Zahl vorhanden ist, ist es ein Schüler
var link;
if(istLehrer === false){
link = $('.coursebox .info .coursename a:contains("Vertretungsplan")', resultHtml).attr('href');
}else{
link = $('.coursebox .info .coursename a:contains("Vertretungsplan (Lehrer)")', resultHtml).attr('href');
}
if(link === undefined){
fail();
}
localStorage.setItem('overviewlink', link);
console.log('Eingeloggt');
getOverview(link);
}
/* sendet einen HTTPost-Request mit Logindaten */
function sendLogin(username, password){
var params = 'username=' + username + '&password=' + password;
$('#error_general').hide('fast');
$('#error_passwd').hide('fast');
var request = $.ajax({
type: 'POST',
url: 'https://***REMOVED***/moodle/login/index.php',
data: {username: username, password: password}
});
console.log('Logge ein');
request.done(loggedIn);
request.fail(fail);
}
/* lädt Benutzerdaten 1) aus den Userinterfaces oder 2) aus dem HTML5 Local Storage */
/* oder 3) gibt null zurück */
function getUserData(){
var username = encodeURIComponent($('#username').val());
if(username === ''){
username = localStorage.getItem('username');
}else{
localStorage.setItem('username', username);
}
var password = encodeURIComponent($('#password').val());
if(password === ''){
password = localStorage.getItem('password');
}else{
localStorage.setItem('password', password);
}
console.log('Benutzerdaten geladen');
if(username !== null && username.length !== 0 && password !== null && password.length !== 0){
return [username, password];
}else{
return null;
}
}
/* loggt ein oder zeigt Loginform */
function login(){
if(navigator.connection.type === Connection.NONE){
return;
}
$.mobile.loading('show', {theme: 'a'});
userdata = getUserData();
if(userdata){
sendLogin(userdata[0], userdata[1]);
$('#loginpopup').popup('close');
return 1;
}else{
$('#lnklogin').click();
return 0;
}
}
/* loggt automatisch ein */
function tryLogin(){
$('#loginpopup').popup('close');
console.log('Teste Login');
login();
}
/* gibt aktuelle Pläne aus entries zurück */
function getActual(entries){
var fnameRe = new RegExp('.*?(\\d{4}-\\d{2}-\\d{2}).*?\\.pdf');
var today = moment().startOf('day');
var actual = [];
entries.forEach(function(entry){
console.log(entry.name);
var results = decodeURIComponent(entry.name).match(fnameRe);
var fdate = moment(results[1], 'YYYY-MM-DD');
if(today.isBefore(fdate) || today.isSame(fdate, 'day')){
actual.push(entry);
}
});
return actual;
}
/* lädt Vertretungspläne aus dem Speicher [1) SD-Karte oder 2) dem internen Speicher] */
function load(){
$.mobile.loading('show', {theme: 'a'});
window.requestFileSystem(
LocalFileSystem.PERSISTENT, 0,
function(fileSystem){
fileSystem.root.getDirectory('WvSVPlan', {create: true, exclusive: false},
function(dirEntry){
var dirReader = dirEntry.createReader();
dirReader.readEntries(
function(entries){
var actual = getActual(entries);
console.log('Im Speicher: ' + actual.length + ' Pläne');
if(actual.length > 0){
$('#noplans').hide('slow');
for(j = 0; j < actual.length; j++){
addPlanAttr(actual[j], false);
}
}else{
$('#noplans').show('fast');
$.mobile.loading('hide');
}
}, fail);
}, fail);
});
}
/* löscht alle Pläne im Speicher */
function purge(){
track('event/purge');
$('#purgebtn').hide('fast');
window.requestFileSystem(
LocalFileSystem.PERSISTENT, 0,
function(fileSystem){
fileSystem.root.getDirectory('WvSVPlan', {create: true, exclusive: false},
function(dirEntry){
var dirReader = dirEntry.createReader();
dirReader.readEntries(
function(entries){
var actual = getActual(entries);
actual.forEach(function(entry){ // akuelle aus entries löschen
var index = entries.indexOf(entry);
if(index !== -1){
entries.splice(index, 1);
}
});
console.log('Lösche ' + entries.length + ' alte Pläne');
entries.forEach(function(entry){
entry.remove(null, fail); // Datei löschen
});
}, fail);
}, fail);
}, fail);
}