├── docs ├── .nojekyll ├── images │ ├── avatar │ │ └── avatar.jpg │ └── icons │ │ └── tampermonkey.png ├── index.html └── js │ ├── iframe.js │ ├── iframe-content.js │ └── jquery.min.js ├── .gitattributes ├── 主要数据来源.pdf ├── README.md └── src └── wcc.js /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /主要数据来源.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wr786/What-Class-Classification/HEAD/主要数据来源.pdf -------------------------------------------------------------------------------- /docs/images/avatar/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wr786/What-Class-Classification/HEAD/docs/images/avatar/avatar.jpg -------------------------------------------------------------------------------- /docs/images/icons/tampermonkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wr786/What-Class-Classification/HEAD/docs/images/icons/tampermonkey.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What-Class-Classification 2 | 便捷地辨别北京大学通选课程类别! 3 | 4 | 效果展示:[树洞#1673516](http://pkuhelper.pku.edu.cn/hole/#1673516) 5 | 6 | ### Update 7 | 8 | 2. 增加了F类通选。([#2](https://github.com/wr786/What-Class-Classification/issues/2)) 9 | 1. 修复了《中俄文化交流史》的课程类别。([#1](https://github.com/wr786/What-Class-Classification/issues/1)) 10 | 11 | ### Usage 12 | 13 | - 详见[该推送](https://mp.weixin.qq.com/s/pey6Y2QyboGn8gdHnADnpg) 14 | 15 | -------------------------------------------------------------------------------- /src/wcc.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name 在成绩查询中直接显示通选课的类别! 3 | // @namespace https://wr786.github.io/ 4 | // @version 0.1.2 5 | // @description upd patch for new score page 6 | // @author wr786 7 | // @match *://pkuhelper.pku.edu.cn/my_score/* 8 | // @grant none 9 | // @license MIT 10 | // @run-at document-start 11 | // ==/UserScript== 12 | 13 | function getElementsByClass(Parent,Class){ 14 | var Result = []; 15 | var Node = Parent.getElementsByTagName("*"); 16 | for(var i=0; i 2 | 3 | 4 | 5 | 6 | 7 | What Class Classification | wr786 8 | 9 | 10 | 11 | 12 | 13 | 14 | 31 | 32 | 33 |
34 |
35 |
36 |

What Class Classification

37 |
38 | 39 |
40 | wr786 41 |
42 | 43 |
44 | 点击获取脚本 45 |
46 | 47 | 54 |
55 | 56 |
57 |
58 | 59 | 330 | 331 | -------------------------------------------------------------------------------- /docs/js/iframe.js: -------------------------------------------------------------------------------- 1 | /* 2 | * File: iframeResizer.js 3 | * Desc: Force iframes to size to content. 4 | * Requires: iframeResizer.contentWindow.js to be loaded into the target frame. 5 | * Author: David J. Bradshaw - dave@bradshaw.net 6 | * Contributor: Jure Mav - jure.mav@gmail.com 7 | * Contributor: Reed Dadoune - reed@dadoune.com 8 | */ 9 | ;(function() { 10 | 'use strict'; 11 | 12 | var 13 | count = 0, 14 | firstRun = true, 15 | logEnabled = false, 16 | msgHeader = 'message', 17 | msgHeaderLen = msgHeader.length, 18 | msgId = '[iFrameSizer]', //Must match iframe msg ID 19 | msgIdLen = msgId.length, 20 | pagePosition = null, 21 | requestAnimationFrame = window.requestAnimationFrame, 22 | resetRequiredMethods = {max:1,scroll:1,bodyScroll:1,documentElementScroll:1}, 23 | settings = {}, 24 | timer = null, 25 | 26 | defaults = { 27 | autoResize : true, 28 | bodyBackground : null, 29 | bodyMargin : null, 30 | bodyMarginV1 : 8, 31 | bodyPadding : null, 32 | checkOrigin : true, 33 | enableInPageLinks : false, 34 | enablePublicMethods : false, 35 | heightCalculationMethod : 'offset', 36 | interval : 32, 37 | log : false, 38 | maxHeight : Infinity, 39 | maxWidth : Infinity, 40 | minHeight : 0, 41 | minWidth : 0, 42 | resizeFrom : 'parent', 43 | scrolling : false, 44 | sizeHeight : true, 45 | sizeWidth : false, 46 | tolerance : 0, 47 | closedCallback : function(){}, 48 | initCallback : function(){}, 49 | messageCallback : function(){}, 50 | resizedCallback : function(){}, 51 | scrollCallback : function(){return true;} 52 | }; 53 | 54 | function addEventListener(obj,evt,func){ 55 | if ('addEventListener' in window){ 56 | obj.addEventListener(evt,func, false); 57 | } else if ('attachEvent' in window){//IE 58 | obj.attachEvent('on'+evt,func); 59 | } 60 | } 61 | 62 | function setupRequestAnimationFrame(){ 63 | var 64 | vendors = ['moz', 'webkit', 'o', 'ms'], 65 | x; 66 | 67 | // Remove vendor prefixing if prefixed and break early if not 68 | for (x = 0; x < vendors.length && !requestAnimationFrame; x += 1) { 69 | requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; 70 | } 71 | 72 | if (!(requestAnimationFrame)){ 73 | log(' RequestAnimationFrame not supported'); 74 | } 75 | } 76 | 77 | function getMyID(){ 78 | var retStr = 'Host page'; 79 | 80 | if (window.top!==window.self){ 81 | if (window.parentIFrame){ 82 | retStr = window.parentIFrame.getId(); 83 | } else { 84 | retStr = 'Nested host page'; 85 | } 86 | } 87 | 88 | return retStr; 89 | } 90 | 91 | function formatLogMsg(msg){ 92 | return msgId + '[' + getMyID() + ']' + msg; 93 | } 94 | 95 | function log(msg){ 96 | if (logEnabled && ('object' === typeof window.console)){ 97 | console.log(formatLogMsg(msg)); 98 | } 99 | } 100 | 101 | function warn(msg){ 102 | if ('object' === typeof window.console){ 103 | console.warn(formatLogMsg(msg)); 104 | } 105 | } 106 | 107 | function iFrameListener(event){ 108 | function resizeIFrame(){ 109 | function resize(){ 110 | setSize(messageData); 111 | setPagePosition(); 112 | settings[iframeID].resizedCallback(messageData); 113 | } 114 | 115 | ensureInRange('Height'); 116 | ensureInRange('Width'); 117 | 118 | syncResize(resize,messageData,'resetPage'); 119 | } 120 | 121 | function closeIFrame(iframe){ 122 | var iframeID = iframe.id; 123 | 124 | log(' Removing iFrame: '+iframeID); 125 | iframe.parentNode.removeChild(iframe); 126 | settings[iframeID].closedCallback(iframeID); 127 | delete settings[iframeID]; 128 | log(' --'); 129 | } 130 | 131 | function processMsg(){ 132 | var data = msg.substr(msgIdLen).split(':'); 133 | 134 | return { 135 | iframe: document.getElementById(data[0]), 136 | id: data[0], 137 | height: data[1], 138 | width: data[2], 139 | type: data[3] 140 | }; 141 | } 142 | 143 | function ensureInRange(Dimension){ 144 | var 145 | max = Number(settings[iframeID]['max'+Dimension]), 146 | min = Number(settings[iframeID]['min'+Dimension]), 147 | dimension = Dimension.toLowerCase(), 148 | size = Number(messageData[dimension]); 149 | 150 | if (min>max){ 151 | throw new Error('Value for min'+Dimension+' can not be greater than max'+Dimension); 152 | } 153 | 154 | log(' Checking '+dimension+' is in range '+min+'-'+max); 155 | 156 | if (sizemax) { 162 | size=max; 163 | log(' Set '+dimension+' to max value'); 164 | } 165 | 166 | messageData[dimension]=''+size; 167 | } 168 | 169 | 170 | function isMessageFromIFrame(){ 171 | function checkAllowedOrigin(){ 172 | function checkList(){ 173 | log(' Checking connection is from allowed list of origins: ' + checkOrigin); 174 | var i; 175 | for (i = 0; i < checkOrigin.length; i++) { 176 | if (checkOrigin[i] === origin) { 177 | return true; 178 | } 179 | } 180 | return false; 181 | } 182 | 183 | function checkSingle(){ 184 | log(' Checking connection is from: '+remoteHost); 185 | return origin == remoteHost; 186 | } 187 | 188 | return checkOrigin.constructor === Array ? checkList() : checkSingle(); 189 | } 190 | 191 | var 192 | origin = event.origin, 193 | checkOrigin = settings[iframeID].checkOrigin, 194 | remoteHost = messageData.iframe.src.split('/').slice(0,3).join('/'); 195 | 196 | if (checkOrigin) { 197 | if ((''+origin !== 'null') && !checkAllowedOrigin()) { 198 | throw new Error( 199 | 'Unexpected message received from: ' + origin + 200 | ' for ' + messageData.iframe.id + 201 | '. Message was: ' + event.data + 202 | '. This error can be disabled by setting the checkOrigin: false option or by providing of array of trusted domains.' 203 | ); 204 | } 205 | } 206 | 207 | return true; 208 | } 209 | 210 | function isMessageForUs(){ 211 | return msgId === ('' + msg).substr(0,msgIdLen); //''+Protects against non-string msg 212 | } 213 | 214 | function isMessageFromMetaParent(){ 215 | //test if this message is from a parent above us. This is an ugly test, however, updating 216 | //the message format would break backwards compatibility. 217 | var retCode = messageData.type in {'true':1,'false':1,'undefined':1}; 218 | 219 | if (retCode){ 220 | log(' Ignoring init message from meta parent page'); 221 | } 222 | 223 | return retCode; 224 | } 225 | 226 | function getMsgBody(offset){ 227 | return msg.substr(msg.indexOf(':')+msgHeaderLen+offset); 228 | } 229 | 230 | function forwardMsgFromIFrame(msgBody){ 231 | log(' MessageCallback passed: {iframe: '+ messageData.iframe.id + ', message: ' + msgBody + '}'); 232 | settings[iframeID].messageCallback({ 233 | iframe: messageData.iframe, 234 | message: JSON.parse(msgBody) 235 | }); 236 | log(' --'); 237 | } 238 | 239 | function checkIFrameExists(){ 240 | if (null === messageData.iframe) { 241 | warn(' IFrame ('+messageData.id+') not found'); 242 | return false; 243 | } 244 | return true; 245 | } 246 | 247 | function getElementPosition(target){ 248 | var 249 | iFramePosition = target.getBoundingClientRect(); 250 | 251 | getPagePosition(); 252 | 253 | return { 254 | x: parseInt(iFramePosition.left, 10) + parseInt(pagePosition.x, 10), 255 | y: parseInt(iFramePosition.top, 10) + parseInt(pagePosition.y, 10) 256 | }; 257 | } 258 | 259 | function scrollRequestFromChild(addOffset){ 260 | function reposition(){ 261 | pagePosition = newPosition; 262 | 263 | scrollTo(); 264 | 265 | log(' --'); 266 | } 267 | 268 | function calcOffset(){ 269 | return { 270 | x: Number(messageData.width) + offset.x, 271 | y: Number(messageData.height) + offset.y 272 | }; 273 | } 274 | 275 | var 276 | offset = addOffset ? getElementPosition(messageData.iframe) : {x:0,y:0}, 277 | newPosition = calcOffset(); 278 | 279 | log(' Reposition requested from iFrame (offset x:'+offset.x+' y:'+offset.y+')'); 280 | 281 | if(window.top!==window.self){ 282 | if (window.parentIFrame){ 283 | if (addOffset){ 284 | parentIFrame.scrollToOffset(newPosition.x,newPosition.y); 285 | } else { 286 | parentIFrame.scrollTo(messageData.width,messageData.height); 287 | } 288 | } else { 289 | warn(' Unable to scroll to requested position, window.parentIFrame not found'); 290 | } 291 | } else { 292 | reposition(); 293 | } 294 | 295 | } 296 | 297 | function scrollTo(){ 298 | if (false !== settings[iframeID].scrollCallback(pagePosition)){ 299 | setPagePosition(); 300 | } 301 | } 302 | 303 | function findTarget(location){ 304 | var hash = location.split("#")[1] || ""; 305 | var hashData = decodeURIComponent(hash); 306 | 307 | function jumpToTarget(target){ 308 | var jumpPosition = getElementPosition(target); 309 | 310 | log(' Moving to in page link (#'+hash+') at x: '+jumpPosition.x+' y: '+jumpPosition.y); 311 | pagePosition = { 312 | x: jumpPosition.x, 313 | y: jumpPosition.y 314 | }; 315 | 316 | scrollTo(); 317 | log(' --'); 318 | } 319 | 320 | var target = document.getElementById(hashData) || document.getElementsByName(hashData)[0]; 321 | 322 | if(window.top!==window.self){ 323 | if (window.parentIFrame){ 324 | parentIFrame.moveToAnchor(hash); 325 | } else { 326 | log(' In page link #'+hash+' not found and window.parentIFrame not found'); 327 | } 328 | } else if (target){ 329 | jumpToTarget(target); 330 | } else { 331 | log(' In page link #'+hash+' not found'); 332 | } 333 | } 334 | 335 | function actionMsg(){ 336 | switch(messageData.type){ 337 | case 'close': 338 | closeIFrame(messageData.iframe); 339 | break; 340 | case 'message': 341 | forwardMsgFromIFrame(getMsgBody(6)); 342 | break; 343 | case 'scrollTo': 344 | scrollRequestFromChild(false); 345 | break; 346 | case 'scrollToOffset': 347 | scrollRequestFromChild(true); 348 | break; 349 | case 'inPageLink': 350 | findTarget(getMsgBody(9)); 351 | break; 352 | case 'reset': 353 | resetIFrame(messageData); 354 | break; 355 | case 'init': 356 | resizeIFrame(); 357 | settings[iframeID].initCallback(messageData.iframe); 358 | break; 359 | default: 360 | resizeIFrame(); 361 | } 362 | } 363 | 364 | function hasSettings(iframeID){ 365 | var retBool = true; 366 | 367 | if (!settings[iframeID]){ 368 | retBool = false; 369 | warn(messageData.type + ' No settings for ' + iframeID + '. Message was: ' + msg); 370 | } 371 | 372 | return retBool; 373 | } 374 | 375 | var 376 | msg = event.data, 377 | messageData = {}, 378 | iframeID = null; 379 | 380 | if (isMessageForUs()){ 381 | messageData = processMsg(); 382 | iframeID = messageData.id; 383 | 384 | if (!isMessageFromMetaParent() && hasSettings(iframeID)){ 385 | logEnabled = settings[iframeID].log; 386 | log(' Received: '+msg); 387 | 388 | if ( checkIFrameExists() && isMessageFromIFrame() ){ 389 | actionMsg(); 390 | firstRun = false; 391 | } 392 | } 393 | } 394 | } 395 | 396 | 397 | function getPagePosition (){ 398 | if(null === pagePosition){ 399 | pagePosition = { 400 | x: (window.pageXOffset !== undefined) ? window.pageXOffset : document.documentElement.scrollLeft, 401 | y: (window.pageYOffset !== undefined) ? window.pageYOffset : document.documentElement.scrollTop 402 | }; 403 | log(' Get page position: '+pagePosition.x+','+pagePosition.y); 404 | } 405 | } 406 | 407 | function setPagePosition(){ 408 | if(null !== pagePosition){ 409 | window.scrollTo(pagePosition.x,pagePosition.y); 410 | log(' Set page position: '+pagePosition.x+','+pagePosition.y); 411 | pagePosition = null; 412 | } 413 | } 414 | 415 | function resetIFrame(messageData){ 416 | function reset(){ 417 | setSize(messageData); 418 | trigger('reset','reset',messageData.iframe,messageData.id); 419 | } 420 | 421 | log(' Size reset requested by '+('init'===messageData.type?'host page':'iFrame')); 422 | getPagePosition(); 423 | syncResize(reset,messageData,'init'); 424 | } 425 | 426 | function setSize(messageData){ 427 | function setDimension(dimension){ 428 | messageData.iframe.style[dimension] = messageData[dimension] + 'px'; 429 | log( 430 | ' IFrame (' + iframeID + 431 | ') ' + dimension + 432 | ' set to ' + messageData[dimension] + 'px' 433 | ); 434 | } 435 | var iframeID = messageData.iframe.id; 436 | if( settings[iframeID].sizeHeight) { setDimension('height'); } 437 | if( settings[iframeID].sizeWidth ) { setDimension('width'); } 438 | } 439 | 440 | function syncResize(func,messageData,doNotSync){ 441 | if(doNotSync!==messageData.type && requestAnimationFrame){ 442 | log(' Requesting animation frame'); 443 | requestAnimationFrame(func); 444 | } else { 445 | func(); 446 | } 447 | } 448 | 449 | function trigger(calleeMsg,msg,iframe,id){ 450 | if(iframe && iframe.contentWindow){ 451 | log('[' + calleeMsg + '] Sending msg to iframe ('+msg+')'); 452 | iframe.contentWindow.postMessage( msgId + msg, '*' ); 453 | } else { 454 | warn('[' + calleeMsg + '] IFrame not found'); 455 | if(settings[id]) delete settings[id]; 456 | } 457 | } 458 | 459 | 460 | function setupIFrame(options){ 461 | function setLimits(){ 462 | function addStyle(style){ 463 | if ((Infinity !== settings[iframeID][style]) && (0 !== settings[iframeID][style])){ 464 | iframe.style[style] = settings[iframeID][style] + 'px'; 465 | log(' Set '+style+' = '+settings[iframeID][style]+'px'); 466 | } 467 | } 468 | 469 | addStyle('maxHeight'); 470 | addStyle('minHeight'); 471 | addStyle('maxWidth'); 472 | addStyle('minWidth'); 473 | } 474 | 475 | function ensureHasId(iframeID){ 476 | if (''===iframeID){ 477 | iframe.id = iframeID = 'iFrameResizer' + count++; 478 | logEnabled = (options || {}).log; 479 | log(' Added missing iframe ID: '+ iframeID +' (' + iframe.src + ')'); 480 | } 481 | 482 | return iframeID; 483 | } 484 | 485 | function setScrolling(){ 486 | log(' IFrame scrolling ' + (settings[iframeID].scrolling ? 'enabled' : 'disabled') + ' for ' + iframeID); 487 | iframe.style.overflow = false === settings[iframeID].scrolling ? 'hidden' : 'auto'; 488 | iframe.scrolling = false === settings[iframeID].scrolling ? 'no' : 'yes'; 489 | } 490 | 491 | //The V1 iFrame script expects an int, where as in V2 expects a CSS 492 | //string value such as '1px 3em', so if we have an int for V2, set V1=V2 493 | //and then convert V2 to a string PX value. 494 | function setupBodyMarginValues(){ 495 | if (('number'===typeof(settings[iframeID].bodyMargin)) || ('0'===settings[iframeID].bodyMargin)){ 496 | settings[iframeID].bodyMarginV1 = settings[iframeID].bodyMargin; 497 | settings[iframeID].bodyMargin = '' + settings[iframeID].bodyMargin + 'px'; 498 | } 499 | } 500 | 501 | function createOutgoingMsg(){ 502 | return iframeID + 503 | ':' + settings[iframeID].bodyMarginV1 + 504 | ':' + settings[iframeID].sizeWidth + 505 | ':' + settings[iframeID].log + 506 | ':' + settings[iframeID].interval + 507 | ':' + settings[iframeID].enablePublicMethods + 508 | ':' + settings[iframeID].autoResize + 509 | ':' + settings[iframeID].bodyMargin + 510 | ':' + settings[iframeID].heightCalculationMethod + 511 | ':' + settings[iframeID].bodyBackground + 512 | ':' + settings[iframeID].bodyPadding + 513 | ':' + settings[iframeID].tolerance + 514 | ':' + settings[iframeID].enableInPageLinks + 515 | ':' + settings[iframeID].resizeFrom; 516 | } 517 | 518 | function init(msg){ 519 | //We have to call trigger twice, as we can not be sure if all 520 | //iframes have completed loading when this code runs. The 521 | //event listener also catches the page changing in the iFrame. 522 | addEventListener(iframe,'load',function(){ 523 | var fr = firstRun; // Reduce scope of var to function, because IE8's JS execution 524 | // context stack is borked and this value gets externally 525 | // changed midway through running this function. 526 | trigger('iFrame.onload',msg,iframe); 527 | if (!fr && settings[iframeID].heightCalculationMethod in resetRequiredMethods){ 528 | resetIFrame({ 529 | iframe:iframe, 530 | height:0, 531 | width:0, 532 | type:'init' 533 | }); 534 | } 535 | }); 536 | trigger('init',msg,iframe); 537 | } 538 | 539 | function checkOptions(options){ 540 | if ('object' !== typeof options){ 541 | throw new TypeError('Options is not an object.'); 542 | } 543 | } 544 | 545 | function processOptions(options){ 546 | options = options || {}; 547 | settings[iframeID] = {}; 548 | 549 | checkOptions(options); 550 | 551 | for (var option in defaults) { 552 | if (defaults.hasOwnProperty(option)){ 553 | settings[iframeID][option] = options.hasOwnProperty(option) ? options[option] : defaults[option]; 554 | } 555 | } 556 | 557 | logEnabled = settings[iframeID].log; 558 | } 559 | 560 | var 561 | /*jshint validthis:true */ 562 | iframe = this, 563 | iframeID = ensureHasId(iframe.id); 564 | 565 | processOptions(options); 566 | setScrolling(); 567 | setLimits(); 568 | setupBodyMarginValues(); 569 | init(createOutgoingMsg()); 570 | } 571 | 572 | function throttle(fn,time){ 573 | if (null === timer){ 574 | timer = setTimeout(function(){ 575 | timer = null; 576 | fn(); 577 | }, time); 578 | } 579 | } 580 | 581 | function winResize(){ 582 | throttle(function(){ 583 | for (var iframeId in settings){ 584 | if('parent' === settings[iframeId].resizeFrom){ 585 | trigger('Window resize','resize',document.getElementById(iframeId),iframeId); 586 | } 587 | } 588 | },66); 589 | } 590 | 591 | function factory(){ 592 | 593 | setupRequestAnimationFrame(); 594 | addEventListener(window,'message',iFrameListener); 595 | addEventListener(window,'resize', winResize); 596 | 597 | function init(element, options){ 598 | if(!element.tagName) { 599 | throw new TypeError('Object is not a valid DOM element'); 600 | } else if ('IFRAME' !== element.tagName.toUpperCase()) { 601 | throw new TypeError('Expected