NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name lessons-availability
// @namespace http://namal.ovh
// @version 3.0
// @description Adds availabilities of every lesson.
// @author Namal
// @match http://www.polymtl.ca/etudes/cs/*/maitrise.php
// @grant none
// @require https://code.jquery.com/jquery-2.1.4.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/pnotify/2.1.0/pnotify.core.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/pnotify/2.1.0/pnotify.buttons.min.js
// @require https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js
// @require https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular-sanitize.min.js
// ==/UserScript==
/**
Author: Namal
**/
jQuery( document ).ready(function( $ ) {
// progress notice
var notice = new PNotify({
title : 'Progress',
text : '{{progress()}}%',
hide: false
});
var tables = $('table'); /* get all tables */
/* filter tables about program structure */
for(var i=0;i<tables.length;i++){
if($(tables[i]).find('a').length){
tables = tables.slice(i);
break;
}
}
var tableAll=[];
var nbLessons=0;
if(tables.length){
$('<link href="https://cdnjs.cloudflare.com/ajax/libs/pnotify/2.1.0/pnotify.core.min.css" media="all" rel="stylesheet" type="text/css" />').appendTo('head');
$('<link href="https://cdnjs.cloudflare.com/ajax/libs/pnotify/2.1.0/pnotify.brighttheme.min.css" media="all" rel="stylesheet" type="text/css" />').appendTo('head');
$('<link href="https://cdnjs.cloudflare.com/ajax/libs/pnotify/2.1.0/pnotify.buttons.min.css" media="all" rel="stylesheet" type="text/css" />').appendTo('head');
var lessons={};
var body = $('body');
body.attr('ng-app','PolyApp');
body.attr('ng-controller','PolyCtrl');
//add inputs before the tables
$($(tables[0]).siblings('h1')[0]).before('<select ng-model="yearSelected" ng-change="updateYear()"ng-options="yr as yr.name for yr in years" ng-disabled="selectDisable"></select><span ng-repeat="sess in sessions" ng-if="yearSelected.value!=\'all\'"><input type="checkbox" ng-model="sess.value" ng-disabled="selectDisable"/>{{sess.name}}</span>');
tables.each(function(tableIndex){// for each table (sub domains of a speciality)
$(this).find('tr').each(function(index) {//for each row of a table
if (!index) { //first row -> column names
$(this).append('<th ng-repeat="yr in year">{{yr.name}}<br/>AHE</th>');
$(tables[tableIndex]).find('tbody').append('<tr ng-repeat="sigle in tables['+tableIndex+']|available:this" ng-class-even="\'tableAlternateRow\'"><td ng-repeat="html in lessons[sigle].html track by $index" ng-bind-html="html"></td><td ng-repeat="yr in year">{{lessons[sigle].av[yr.value]}}</td></tr>');
tableAll[tableIndex]=[];
}else{
var line=[];
var sigle="";
$(this).find('td').each(function(index){ //for each column
line[index] = $(this).html();
if(index==1) // 2nd column is the course ID
sigle=$(this).find('a').attr('href').slice(1);
});
if(!lessons[sigle]){
lessons[sigle]={};
lessons[sigle].html=line;
lessons[sigle].av=[];
nbLessons++;
}
$(this).remove();
tableAll[tableIndex].push(sigle);
}
});//end row
}); // end table
angular.module('PolyApp',['ngSanitize'])
.filter('available',function(){
return function(sigles,$scope){
return sigles.filter(function(sigle){//filter out unavailable lessons
var yr =$scope.yearSelected;
if(yr.value=="all"){
return true;
}
var sess = $scope.sessions;
for(var i=0;i<3;i++){
if(sess[i].value && lessons[sigle].av[yr.value].charAt(i)!="-"){
return true;
}
}
return false;
});
}
})
.controller('PolyCtrl',function($scope,$http){
//variables
$scope.years=[{value:"all",name:"Toutes les années"}];
$scope.yearSelected = $scope.years[0];
$scope.year=[];
$scope.lessons=lessons;
$scope.tables = tableAll;
$scope.completed=0;
$scope.nbLessons=nbLessons;
$scope.selectDisable=false;
$scope.sessions=[{value:true,name:"Automne"},{value:true,name:"Hiver"},{value:true,name:"Ete"}];
//callback functions
$scope.updateYear=function(){
var yr = $scope.yearSelected;
if(yr.value=="all"){
$scope.year=$scope.years.slice(1);
}else{
$scope.year=[yr];
}
};
$scope.progress=function(){
if($scope.completed==$scope.nbLessons){
notice.update({type:'success'});
setTimeout(function(){
notice.remove();
},3000);
$scope.selectDisable=false;
}
return Math.floor($scope.completed/$scope.nbLessons*100);
};
$scope.selectDisable=true;
// get availabilities !
for(var sigle in lessons){
(function(sigle){
$http.get('http://www.polymtl.ca/etudes/cours/details.php?sigle='+sigle).then(
function(res){
var table = $(res.data).find('table');
var len = table.length;
var available = $(table[len-1]).find('tr'); //rows of table with availabilities
if(!available.length){ //some lessons don't even exist ! Genius !
delete lessons[sigle];
tableAll.forEach(function(e,i){
if(e.indexOf(sigle) !== -1) {
e.splice(e.indexOf('c'), 1);
}
});
new PNotify({
title : 'Cours supprimé',
type :'error',
text : '<a href="http://www.polymtl.ca/etudes/cours/details.php?sigle='+sigle+'">'+sigle+'</a> ne semble pas être un cours valide ! Il a été supprimé des listes',
hide: false
});
$scope.nbLessons--;
return;
}
$scope.completed++;
available.each(function(index){ // for each row
if($scope.years.length==1 && !index){//JS is not multithread so this works
$(this).find('th').each(function() {//for each column -> a year
var tmp = {value: $(this).text().substring(0, 4), name: $(this).text()};
$scope.years.push(tmp);
$scope.updateYear();
});
}else if(index==2){ // finally, we process the availabilities
var td = $(this).find('td');
var data = "";
for (var i=0;i<3*($scope.years.length-1);i++){
data+=$(td[i]).text().charAt(0);
if(i && (i+1)%3==0){
var yr = $scope.years[(i+1)/3].value;
lessons[sigle].av[yr]=data;
data="";
}
}
}
})
});
})(sigle);
}
});
// create an injector
var $injector = angular.injector(['ng','PolyApp']);
// use the injector to kick off the application
$injector.invoke(function($rootScope, $compile, $document) {
$compile($document)($rootScope);
$rootScope.$digest();
});
}
});