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(); }); } });