您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
The CAT Pimper
// ==UserScript== // @name Pimp My CAT // @namespace http://www.pimpmycat.nothing // @description The CAT Pimper // @include http://training.cat-europe.com/TrainingModeFrame.aspx // @include https://training.cat-europe.com/TrainingModeFrame.aspx // @version 10.7 // @screenshot http://k.min.us/ilyyO4.png // @id pimpmycat@ptntools // @author Axel Bock <[email protected]> // @require http://code.jquery.com/jquery-1.6.min.js // @require https://gf.qytechs.cn/scripts/13081-pimp-my-cat-hotkeys-script/code/Pimp%20My%20CAT%20hotkeys%20script.user.js // ==/UserScript== // ******************************************** // ******* ALSO CHANGE MAIN VERSION HERE ****** // ******************************************** pmc_version = "10.7"; // setInterval() -> setTimeout() in 1 function function getNotAnswered() { return $('tr:not(.hide-row) > td:nth-child(2) > img[src="images/spacer.gif"]', window.frames[1].document).size(); } function getCorrectAnswered() { return $('tr:not(.hide-row) > td:nth-child(2) > img[src="images/frage_richtig.gif"]', window.frames[1].document).size(); } function getWronglyAnswered() { return $('tr:not(.hide-row) > td:nth-child(2) > img[src="images/frage_falsch.gif"]', window.frames[1].document).size(); } function getPinned() { return $('tr:not(.hide-row) > td:nth-child(3) > img[src="images/pin.gif"]', window.frames[1].document).size(); } function updateStatBox() { var ans_ok = getCorrectAnswered(); var ans_no = getWronglyAnswered(); var ans_xx = getNotAnswered(); var pinned = getPinned(); var all_quest = (ans_ok + ans_no + ans_xx); var ans_quest = (ans_ok + ans_no); var perc = (ans_ok / ans_quest * 100).toFixed(2); if (ans_quest == 0) { perc = 0; } // the percentage color and the comment string :) var color = "#cccccc"; var comment = 'Let\'s roll ...'; if (perc == 100) { color = "#FFD700"; if (ans_quest > 10) { comment = '<a target="new" style="color:#FFD700; font-weight: bold;" href="http://www.youtube.com/watch?v=BBRQ8XUJOeI">Shag-a-tastic, baby!!</a>'; } else if (ans_quest > 7) { color = "#ccac00"; comment = 'Yooou <span style="color:#ccac00;">get it </span>baby!!'; } else if (ans_quest > 4) { color = "#b29600"; comment = 'You\'re <a target="new" style="color: #b29600;" href="http://www.youtube.com/watch?v=06kXXJUddHQ">on the roll</a> ...'; } else if (ans_quest > 1) { color = "#806c00"; comment = "Keep it coming ..."; } else if (ans_quest == 1) { color = "#665600"; comment = '<a target="new" style="color: #665600;" href="http://www.youtube.com/watch?v=-8MjBU2sNK4">Great start! :)</a>'; } } else if (perc >= 90) { comment = '<a target="new" href="http://www.youtube.com/watch?v=Ai813WFOtwQ">First class.</a>'; color = "#32CD32"; } else if (perc >= 80) { comment = '<b>you\'re <a target="new" style="color:#32CD32;" href="http://www.youtube.com/watch?v=lbbLT94L-T0">the one</a></span>!</b>'; color = "#32CD32"; } else if (perc >= 75) { color = "#FFB1A3"; comment = 'Close, but failure.'; } else if (perc > 50) { color = "#FF6347"; comment = '<b><a href="http://www.youtube.com/watch?v=1G0aemJnlyU" target="new" style="color:#FF6347;">you suck</a>!!</b>'; } else if ( ans_quest > 3){ color = "red"; comment = '<span style="color:red">Don\'t fly. Please.</span>'; } else if ( ans_quest == 3){ color = "red"; comment = 'Really? Come on!'; } else if ( ans_quest == 2){ color = "red"; comment = 'Bad day?'; } else if ( ans_quest == 1){ color = "red"; comment = 'Ouch.'; } // how many more good answers to be cool if (perc < 80 && ans_quest > 0) { var more = 4*ans_no - ans_ok; more = '<br><span style="font-size:8pt;color:#cccccc;">' + '+<span style="color:#32CD32;">' + more.toFixed(0) + '</span> to be good.</span>'; } else { var more = ""; } // find out about display or question filters // display filters var wf1d = window.frames[1].document; var disp_filter = $('head', wf1d).attr('display_filter'); if (! disp_filter || disp_filter == "0,0,0,0") { disp_filter = ""; } else { disp_filter = "F"; } // question filters ... easier ;) var qstn_filter = dfil_get_filter('set_filter') ? "Q" : ""; // construct the box var innerHTML = '\ <table style="color:#cccccc; font-size:8pt;" width="100%" border="0"><tr>\ <td style="font-size:16pt;color:%COLOR" valign="center"><b>%PERCENT %</b>%MORE%</td> \ <td><b>%DISP_FILTER<br>%QSTN_FILTER</b></td>\ <td align="right"><span style="color:#32CD32">%CORRECT</span> \ + <span style="color:#FF6347">%WRONG</span>, %LEFT left\ <br>\ <span style="color:#fdcb68">%PINNED</span> marked\ <br>\ %COMMENT</td> \ </tr>\ </table>'; innerHTML = innerHTML. replace("%COMMENT", comment). replace("%PERCENT", perc). replace("%COLOR", color). replace("%CORRECT", ans_ok). replace("%WRONG", ans_no). replace("%DISP_FILTER", disp_filter). replace("%QSTN_FILTER", qstn_filter). replace("%LEFT", ans_xx). replace("%PINNED", pinned). replace("%MORE%", more); var box = $('#pimpmyCAT', window.frames[0].document); if (! box.size()) { box = $('<div id="pimpmyCAT"></div>'); $('body > :last-child', window.frames[0].document).after(box); } var hashString = perc + '_' + ans_quest.toString() + '_' + ans_ok.toString() + '_' + ans_xx.toString() + '_' + pinned.toString() + '_' + disp_filter + '_' + qstn_filter; if (! box.attr('hash') || box.attr('hash') != hashString) { box[0].innerHTML = ''; box.attr('hash', hashString); box.html(innerHTML); } } function getQBox() { // this is soooo fucked up. the company behind this should be shot // and burned and banned to hell for all eternity. var idbox = $('form > div > div', window.frames[2].document); var retVal = false; idbox.each( function() { var bg_image = $(this).css('background-image'); if (bg_image != null && bg_image.indexOf('kreis') > -1) { retVal = $(this); return false; } }); return retVal; } // returns FALSE or the number in the question id box function getQuestionNumber() { var retVal = getQBox(); if (retVal) { retVal = parseInt(retVal.find('td').text()); } return retVal; } function clickOn(target, doc) { var evt = doc.createEvent("MouseEvents"); evt.initEvent('click', true, true); var rv = target.dispatchEvent(evt); if (!rv) { alert('clickOn() failed for some reason.'); } return rv; } function goTo(target, framewin) { if (target) { $('html,body', framewin.document).animate( { scrollTop: $(target).offset().top-$(framewin).height()/2 }, { duration: 'medium', easing: 'swing'}); } } function clickNB(no) { var wf0d = window.frames[0].document; target = $('#Table1 td:nth-child(' + no + ')', wf0d)[0]; clickOn(target, wf0d); return false; } function rgb2hex(rgbString) { var parts = rgbString.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); if (!parts) { return rgbString; } // parts now should be ["rgb(0, 70, 255", "0", "70", "255"] delete (parts[0]); for (var i = 1; i <= 3; ++i) { parts[i] = parseInt(parts[i]).toString(16); if (parts[i].length == 1) parts[i] = '0' + parts[i]; } return parts.join(''); // "rrggbb" } // you MUST MUST call filterNeighbors after, cause filterNeighbors // checks for visibility ... function initNeighbors() { var wf1d = window.frames[1].document; var retval = [[], null, []]; var indx = 0; var questions = $('table tr .bg', wf1d); questions.each( function(i) { if (indx == 0 && rgb2hex($(this).css('background-color')) == 'e2e2e2') { indx++; retval[indx] = [$(this)]; indx++; } else { retval[indx].push($(this)); } }); return retval; } function get_matrix_for(element) { var v0 = 1; var v1 = element.find('img[src="images/pin.gif"]').size(); var v2 = element.find('img[src="images/frage_richtig.gif"]').size(); var v3 = element.find('img[src="images/frage_falsch.gif"]').size(); if (v2+v3 > 0) { v0 = 0; } return [v0, v1, v2, v3]; } function keep_element(matrix, filter) { var applid = 0; var failed = 0; for (var k=0; k<4; k++) { if (filter[k]){ applid ++; if (filter[k]-1 != matrix[k]) { failed++; } } } // no applied filter means EVERYTHING passes :) return (applid == 0 || applid > failed); } // PARAM FILTER: // the parameter filter is an array of values of 0..2 // [new / marked / positive_ans / negative_ans] // 0 == do not compare this value. // 1 == DO NOT include those questions // 2 == INCLUDE those questions // the filter values are ORed. // example: get all NEGATIVE and MARKED questions: // [0, 2, 0, 2] // --> do NOT filter for new and positive (array 0 & 2) // --> FILTER for MARKED==TRUE and NEGATIVE==TRUE (array 1 & 3) // PARAM APPLY: // do not only filter, but APPLY the filter to visibility // RETURNS: // returns a 3-array: // all previous visible neigbors (according to filter) // the current item // all following neighbors (according to filter) function filterNeighbors(set, filter, apply) { if (! filter) { filter = dfil_get_filter('display_filter'); if (! filter) { filter = [0,0,0,0]; } } var qset = dfil_get_filter('set_filter'); var invt = parseInt(dfil_get_var('invert_qset')); if (qset) { qset = qset.map( function(n) { return n.toString(); }); } var dbg=0; var retval = [[], [], []]; var matrix, cur, positive; for (var i=0; i<set.length; i++) { for (var j=0; j<set[i].length; j++) { cur = set[i][j]; matrix = get_matrix_for(cur); positive = keep_element(matrix, filter); if (qset) { if (! invt) { positive = positive && (qset.indexOf(cur.text().trim()) != -1); } else { positive = positive && (qset.indexOf(cur.text().trim()) == -1); } } if (apply) { if (positive) { retval[i].push(cur); cur.removeClass('hide-row'); } else { cur.addClass('hide-row'); } } else if (positive && ! cur.hasClass('hide-row')) { retval[i].push(cur); } } } return retval; } // if filter is omitted it is set to [0,0,0,0] automatically // fwd = true if forward. function next_with_filter(fwd, filter) { var wf1d = window.frames[1].document; var set = initNeighbors(); if (!filter) filter = [0, 0, 0, 0]; set = filterNeighbors(set, filter); if (!fwd) { var tmp = set[0].reverse(); set[0] = set[2].reverse(); set[2] = tmp; } var target = null; if (set[2].length) { target = set[2].shift(); } else { if (set[0].length) target = set[0].shift(); } if (target) { clickOn(target[0], wf1d); goTo(target, window.frames[1]); } return (target) ? true : false; } function current() { var wf1d = window.frames[1].document; var set = initNeighbors(); target = set[1].shift(); clickOn(target[0], wf1d); goTo(target, window.frames[1]); return false; } function key_nf(fwd, filter) { logDate('key_nf'); if (!next_with_filter(fwd, filter)) { alert('no matching questions.'); } logDate('key_nf done'); return false; } function key_enter(forward) { if (! next_with_filter(forward, [2,0,0,0])) next_with_filter(forward); return false; } // ========================================================================== // Filter based on question properties (answered/new, marked, etc.) // ========================================================================== function dfil_get_var(varname) { var wf1d = window.frames[1].document; var variable = $('head', wf1d).attr(varname); return variable ? variable : false; } function dfil_save_var(varname, value) { $('head', window.frames[1].document).attr(varname, value); } function dfil_get_filter(attr_name) { var wf1d = window.frames[1].document; var current_filter = $('head', wf1d).attr(attr_name); if (! current_filter) { return false; } else { current_filter = current_filter.split(","); current_filter = current_filter.map( function(n) { return parseInt(n); }); } return current_filter; } function dfil_save_filter(filter_array, filter_name) { var wf1d = window.frames[1].document; if (! filter_name) { filter_name = 'display_filter'; } if (filter_array) { $('head', wf1d).attr(filter_name, filter_array.join(',')); } else { $('head', wf1d).removeAttr(filter_name); } } function dfil_display_filter(f) { var wf2d = window.frames[2].document; var text = [ "all <b>answered</b> questions", "all <b>new</b> questions", "all <b><u>un</u>marked</b>", "all <b>marked</b>", "everything <b><u>not</u> marked correct</b>", "everything <b>marked correct</b>", "everything <b><u>not</u> marked incorrect</b>", "everything <b>marked incorrect</b>"]; var display = '<p>Displaying all of the following:</p><ul>'; var added = false; for (var i=0; i<4; i++) { if (f[i]) { display += "<li>"+text[2*i+f[i]-1]+"</li>"; added = true; } } if (!added) display += "display everything."; display += "</ul>" // now display that :) var box = $('body > div#infobox', wf2d); if (box.size()) box.html(display); else { $('body', wf2d). prepend("<div id='infobox' class='infobox'>"+display+"</div>"); } return false; } filter_wait = false; function dfil_qjump_later(q, click) { if (filter_wait) { clearTimeout(filter_wait); } filter_wait = setTimeout(function() { filter_wait = false; goTo(q, window.frames[1]); if (click) { // remember: clickOn needs a DOM object!! clickOn(q[0], window.frames[1].document); } }, 350); } function dfil_apply_filter(f) { var wf1d = window.frames[1].document; dfil_save_filter(f); var n = initNeighbors(); n = filterNeighbors(n, f, true); dfil_display_filter(f); // if the current question was filtered out - go to the next visible one. if (n[1].length) { dfil_qjump_later(n[1][0]); } else { if (n[2].length) { dfil_qjump_later(n[2][0], true); } else { dfil_qjump_later(n[0][0], true); } } } function dfil(item) { var f = dfil_get_filter('display_filter'); if (!f) { f = [0,0,0,0]; } f[item] = ++f[item] % 3; dfil_apply_filter(f); return false; } function dfil_reapply() { var f = dfil_get_filter('display_filter'); if (!f) { f = [0,0,0,0]; } dfil_apply_filter(f); return false; } // ========================================================================== // common screen methods // ========================================================================== function screen_abort() { var wf2d = window.frames[2].document; if ($('.pmc_screen', wf2d).size()) { $('.pmc_screen', wf2d).remove(); $('body', wf2d).children().each( function() { $(this).removeClass('hide-row'); }); } return false; } function screen_prepare() { var wf2d = window.frames[2].document; $('body', wf2d).children().each( function() { $(this).addClass('hide-row'); }); } function screen_check_already_there(idstr) { // if THIS screen is already there we will simply return "yes" if ($('#'+idstr, window.frames[2].document).size()) { return true; } // otherwise if another screen is there we will kill it if ($('.pmc_screen', window.frames[2].document).size()) { screen_abort(); } return false; } // ========================================================================== // Info screens // ========================================================================== function info_screen() { var wf2d = window.frames[2].document; if (screen_check_already_there('pmc_info_screen')) { return screen_abort(); } screen_prepare(); var ifr; ifr = $('<div style="text-align:center; margin-top:20px;">\ <div id="dismiss">\ <b style="color: #777">Press ESC or click here to dismiss.</b></div>\ <object width="50%" height="220"></object>\ </div>'); $('body > :first-child', wf2d).before(ifr); ifr.attr('id', 'pmc_info_screen'); ifr.addClass('pmc_screen'); ifr.find('object').attr('data', 'http://training.cat-europe.com/ShowQuestionInfo.aspx'); ifr.find('#dismiss').click(screen_abort); return false; } function visi_screen() { var wf2d = window.frames[2].document; var wf1d = window.frames[1].document; if (screen_check_already_there('pmc_einfo_screen')) { return false; } screen_prepare(); var newhtml = '\ <table class="pmc_screen pmc_input_table">\ <tr><td colspan="2">\ Currently <b>visible</b> questions are, depending on display and \ question filters). Press ESC or click "Close" to dismiss.\ </td></tr>\ <tr><td colspan="2">\ <textarea rows=10 cols=50 name="textarea" id="textarea" />\ </td></tr>\ <tr><td colspan="2">\ Note that this is <b>not</b> necessarily the same as the \ question set. The shift-1 ... shift-4 filters are applied here, \ too.\ </td></tr>\ <tr><td colspan="2">\ <input type="submit" id="dismiss" value="Close" />\ </td></tr>\ </table>'; var ifr = $(newhtml); ifr.attr('id', 'pmc_einfo_screen'); // now create the question list var qlist = $('table tr.bg:not(.hide-row)', wf1d); var text = ""; qlist.each( function(i) { text += $(this).text().trim() + " "; }); $('body > :first-child', wf2d).before(ifr); $('#textarea', ifr).val(text); $('#textarea', ifr).bind('keydown', 'esc', screen_abort); $('#dismiss', ifr).click(screen_abort); return false; } // ========================================================================== // Set filter screen // ========================================================================== function set_form() { var wf2d = window.frames[2].document; if (screen_check_already_there('pmc_qset_table')) { return false; } screen_prepare(); var newhtml = '\ <table id="pmc_qset_table" class="pmc_screen pmc_input_table">\ <form>\ <tr><td colspan="2">\ Enter the list of <b>question numbers you want to keep visible</b>. \ Separate by SPACE or ENTER or any combination of those. You can disable \ the filter and make everything visible again later by pressing \ <span style="font-family: courier;">shift-6</span>.\ </td></tr>\ <tr><td colspan="2">\ <textarea rows=10 cols=50 name="textarea" id="question_set" />\ </td></tr>\ <tr><td colspan="2">\ <input type="checkbox" name="invert" id="invert_box"/>Invert\ (hide questions entered here and leave others visible) \ </td></tr>\ <tr>\ <td width="50%">\ <input type="button" id="pmc_submit_set" value="Submit" />\ </td>\ <td width="50%">\ <input type="button" id="pmc_abort_set" value="Abort" />\ </td>\ </tr>\ </form>\ </table>'; $('body > :first-child', wf2d).before(newhtml); $('#question_set').focus(); $('#pmc_submit_set', wf2d).click(set_form_submit); $('#pmc_abort_set', wf2d).click(screen_abort); $('#question_set', wf2d).bind('keydown', 'esc', screen_abort); $('#invert_box', wf2d).prop('checked', dfil_get_var('invert_qset')=='1'); var set = dfil_get_filter('set_filter'); if (set != false) { $('#question_set', wf2d).val(set.join('\n')); } return false; } function set_form_submit() { var wf1d = window.frames[1].document; var wf2d = window.frames[2].document; var list = $('#question_set', wf2d).val(); var invt = $('#invert_box', wf2d).prop('checked'); var num = /\d+/; var splitter = /\s+/; list = list.split(splitter); list = list.filter( function(elem,indx,array) { return num.test(elem.trim()); }); // filter out double entries from the list so you can simply // merge lists in the form ... var list2 = []; for (var j=0; j<list.length; j++) { if (list2.indexOf(list[j]) == -1) { list2.push(list[j]); } } list = list2; list2 = null; // garbage collection? ... hm. // let's restore visibility for everything ;) screen_abort(); // now we have the array of questions we want to see. // now let's apply that stuff :) dfil_save_filter(list, 'set_filter'); dfil_save_var('invert_qset', invt ? 1 : 0); dfil_reapply(); } function set_reset() { dfil_save_filter(false, 'set_filter'); dfil_save_var('invert_qset', 0); dfil_reapply(); return false; } // ========================================================================== // Goto screen // ========================================================================== function goto_screen() { var wf2d = window.frames[2].document; if (screen_check_already_there('pmc_goto')) { return false; } screen_prepare(); var newhtml = '\ <table width="50%" style="padding: 30px;" \ id="pmc_goto" class="pmc_screen pmc_input_table">\ <form>\ <tr><td colspan="2" style="padding: 15px;">\ Input the question number to jump to and press ENTER: \ </td></tr>\ <tr><td colspan="2" style="text-align: center; padding: 15px;">\ <input type="text" size=5 name="qnumber" id="qnumber" />\ </td></tr>\ <tr>\ <td width="50%" style="padding: 15px;">\ <input type="submit" id="pmc_submit_set" value="Go to" />\ </td>\ <td width="50%" style="padding: 15px;">\ <input type="button" id="pmc_abort_set" value="Abort" />\ </td>\ </tr>\ </form>\ </table>'; $('body > :first-child', wf2d).before(newhtml); $('#pmc_submit_set', wf2d).click(goto_screen_submit); $('#pmc_abort_set', wf2d).click(screen_abort); var q = $('#qnumber', wf2d); q.bind('keydown', 'return', goto_screen_submit); q.bind('keydown', 'esc', screen_abort); return false; } function goto_screen_submit() { var wf1d = window.frames[1].document; var wf2d = window.frames[2].document; var qnumber = $('#qnumber', wf2d).val().trim(); var qlist = []; $('table tr .bg', wf1d).each( function(i) { qlist.push($(this)); }); screen_abort(); for (var i=0; i<qlist.length; i++) { var q = qlist[i]; var qtext = q.text().trim(); if (qtext == qnumber) { q.removeClass('hide-row'); var filter_list = dfil_get_filter('set_filter'); if (filter_list) { var found = false; for (var j=0; j<filter_list.length; j++) { if (filter_list[j] == qtext) { found = true; break; } } if (!found) { filter_list.push(qtext); dfil_save_filter(filter_list, 'set_filter'); } } clickOn(q[0], wf1d); goTo(q, window.frames[1]); break; } } // SUBMIT methods MAY NOT RETURN FALSE!!!! } // ========================================================================== // Help screen // ========================================================================== function help_screen() { // <tr><th>key</th><th>function</th></tr>\ var wf2d = window.frames[2].document; if (screen_check_already_there('help_table')) { return screen_abort(); } screen_prepare(); var newhtml = '\ <table width="75%" id="help_table" class="pmc_screen" \ style="margin-left: auto; margin-right: auto; padding:20px; ">\ <tr>\ <td colspan="2" style="text-align:center" id="dismiss">\ <em><b style="color: #777">press ESC or click this text to dismiss help</b></em> \ </td>\ </tr>\ <tr>\ <td>Version</td>\ <td>'+pmc_version+', \ <a href="http://userscripts.org/scripts/show/97267" target="new" \ >check for update</a></td>\ </tr>\ <tr>\ <td>Questions</td>\ <td>Axel <[email protected]></td>\ </tr>\ <tr><th colspan="2"><div style="align:center;">\ Selecting answers\ </div></th></tr> \ <tr>\ <td>1-4</td>\ <td>directly select / unselect a question</td>\ </tr>\ <tr>\ <td>0</td>\ <td>unselect all questions</td>\ </tr>\ <tr>\ <td>shift-left / shift-right</td>\ <td>if there\'s an attachment, switch between attachment and the question itself</td>\ </tr>\ <tr>\ <td>up / down</td>\ <td>mark questions with keyboard</td>\ </tr>\ <tr>\ <td>double-click on answer</td>\ <td>same as marking it and pressing "n"</td>\ </tr>\ <tr><th colspan="2"><div style="align:center;">\ Navigation & answering questions\ </div></th></tr> \ <tr>\ <td><b style="color: red">input boxes</b></td>\ <td>if your cursor is blinking within in input box, you can \ <span style="color:red">use most of the single-key shortcuts,\ but with the CTRL- modifier added.</span> \ Example: <span style="color:red">"s"</span> \ for <u>s</u>how solution \ <span style="color:red">will become CTRL-s</span>. \ ENTER will stay the same. Once the input box \ is no longer active, the normal shortcuts will work again. \ </td>\ </tr>\ <tr>\ <td>ENTER</td>\ <td>either go directly to next unanswered question, OR to next question if all are already answered</td>\ </tr>\ <tr>\ <td>shift-ENTER</td>\ <td>The same as ENTER, just backwards</td>\ </tr>\ <tr>\ <td>g</td>\ <td>go to question directly by number</td>\ </tr>\ <tr>\ <td>s</td>\ <td>show "solution"</td>\ </tr>\ <tr>\ <td>m</td>\ <td>"mark" question</td>\ </tr>\ <tr>\ <td>f</td>\ <td>"finish" session</td>\ </tr>\ <tr>\ <td>n / p</td>\ <td>go to next/previous question directly (without showing solution of answers)</td>\ </tr>\ <tr>\ <td>+ / -</td>\ <td>go to next/previous UNANSWERED question directly</td>\ </tr>\ <tr>\ <td>b / B</td>\ <td>go to previous / next <u>B</u>ADLY ;) \ ANSWERED question.\ </tr>\ <tr>\ <td>left / right</td>\ <td>same as "left"/"right" buttons in navigation bar</td>\ </tr>\ <tr><th colspan="2"><div style="align:center;">\ Filters & functions \ </div></th></tr> \ <tr>\ <td>i</td>\ <td>display question info</td>\ </tr>\ <tr>\ <td>q</td>\ <td>manually enter questions to be shown. \ <span style="color:red;">cool stuff.</span></td>\ </tr>\ <tr>\ <td>shift-1 - shift-4</td>\ <td>set display filters (play with it!)</td>\ </tr>\ <tr>\ <td>v</td>\ <td>display visible questions. useful to copy/paste for later\ use with "q". this takes both the "q"\ and the shift-1..shift-4 filters into account.</td>\ </tr>\ <tr>\ <td>w / W</td>\ <td>go to the question wiki to the page regarding the current \ catalog subject. Just a registration\ is required. The capital \'W\' opens the page directly in \ edit mode. \ <span style="color:red;">potentially very cool stuff.</span>\ </td>\ </tr>\ <tr>\ <td>shift-5</td>\ <td>remove all display filters (the ones from shift-1 to shift-4)</td>\ </tr>\ <tr>\ <td>shift-0</td>\ <td>reapply current display & set filters</td>\ </tr>\ <tr>\ <td>shift-6</td>\ <td>remove the question set filter (the one you activate with \'q\')</td>\ </tr>\ <tr><th colspan="2"><div style="align:center;">\ Well ... \ </div></th></tr> \ <tr>\ <td style="vertical-align: middle;">\ <form action="https://www.paypal.com/cgi-bin/webscr" style="margin: 0px;" method="post">\ <input type="hidden" name="cmd" value="_s-xclick">\ <input type="image" id="donatebutton" src="https://www.paypalobjects.com/WEBSCR-640-20110401-1/en_GB/i/btn/btn_donate_SM.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online.">\ <input type="hidden" name="hosted_button_id" value="57BWCFYLTLLAC">\ <img alt="" border="0" src="https://www.paypalobjects.com/WEBSCR-640-20110401-1/de_DE/i/scr/pixel.gif" width="1" height="1">\ </form>\ </td>\ <td>\ <small>\ The "price" for this little tool used to be a medium \ sized latte macchiato. So if this tool helps you, please consider \ donating one (2,50 EUR) if you really cannot get me in the flesh. \ But I do prefer coffee in person, because this is more of a way to \ let me know I have helped you, which makes me kind of happy. \ </small>\ </td>\ </tr>\ </table>'; $('body > :first-child', wf2d).before(newhtml); $('#dismiss', wf2d).click(screen_abort); return false; } // ========================================================================== // the ajax stuff for the question link :) // ========================================================================== function changeQboxLayout(qbox, color, qhtml) { qbox .attr('id', 'qbox') .addClass('qbox-class') .css('background-image', '') .css('cursor', 'pointer') .css('-moz-border-radius', '9px') .css('-webkit-border-radius', '9px') .css('border-radius', '9px') .css('border', '3px solid ' + color) .click(info_screen) .find('table') .css('height', '100%') .find('td') .html(qhtml); } function pimpAjax(qbox) { // now get the damn question id from that %&§/&! question id page // FIRST, find out whether we're working with HTTPS or HTTP // and use the same ... var my_url = location.href; var my_url = my_url.substr(0, my_url.indexOf('://')) + '://training.cat-europe.com/ShowQuestionInfo.aspx'; // SECOND, do the ajax call $.ajax( { url: my_url, cache: false, dataType: 'html', success: function(data) { // kill all newline & CR in here, otherwise regexp search & // replace will fail. bloody shit. data = data .replace(/\n/g, "") .replace(/\s/g, " "); // extract EVERYTHING INSIDE BODY as regexp // for jquery to be able to parse it. // bloody hellfire fucked up apeshit var body = $(data.match(/<body[^>]*>(.*)<\/body>/)[1]); var editor_id = body.find( 'table > tbody > ' + 'tr:nth-child(2) > td:nth-child(2)') .text() .trim(); var url_base = body.find( 'table > tbody > ' + 'tr:nth-child(5) > td:nth-child(2)') .text() .trim(); var url = url_base .replace(/[^A-Za-z0-9]/g, "_") .replace(/__/g, "_") .replace(/_*$/, ""); var color = '#76c276'; var qnum = qbox.text().trim(); var qhtml = qnum; if (editor_id != qnum) { color = "orange"; qhtml = '<span style="color: #AAA">' + qnum + '</span><br><small>(' + editor_id + ')</small>'; } $('head > title', document).html('CAT Q'+editor_id); changeQboxLayout(qbox, color, qhtml, url); // now add the "wiki-w" var after = $('<div id="wikiClickEnabled"\ alt="' + url_base + '"> \ <table width="100%" height="100%"> \ <tr><td>W</td></tr> \ </table></div>') .attr('url', url) .click(function() { goToWiki(); }); qbox.after(after); }, error: function() { changeQboxLayout(qbox, '#cc6363', qbox.text()); } } ); } function goToWiki(edit) { var wf2d = window.frames[2].document; var target = $('#wikiClickEnabled', wf2d); var url_mid = ""; if (edit) { url_mid = "page/edit/"; } if (target.size()) { var url_end = target.attr('url'); if (url_end != "") { open('http://lbastudentwiki.wikispaces.com/' + url_mid + url_end); } } return false; } // ========================================================================== // Click on attachment tabs // ========================================================================== // test question: MET Q872 function attTab(offset) { var att_cur = $('.a_col_active', window.frames[2].document); if (att_cur.size()) { var n_id = '#a_col' + (parseInt(att_cur.attr('id').substr(5,6))+offset).toFixed(0); att_cur.removeClass('a_col_active'); } else { var n_id = '#a_col1'; } clickOn( $(n_id, window.frames[2].document)[0], window.frames[2].document); $(n_id, window.frames[2].document).addClass('a_col_active'); return false; } // ========================================================================== // Keyboard navigation functions // ========================================================================== lastkeypress = 0; function k() { var c = new Date(); if (c - lastkeypress < 25) { logDate('Aborting keypress. Delta='+(c-lastkeypress).toString()); return false; } lastkeypress = c; return true; } function sel(num) { $('.q-answer[qpos="'+num.toString()+'"]', window.frames[2].document) .addClass('q-sel') .find(':input').prop('checked', true); return false; } function uns() { $('.q-answer', window.frames[2].document) .removeClass('q-sel') .find(':input') .attr('checked', false) .removeProp('checked'); return false; } function advanceCursor(direction) { var q = $('.q-sel', window.frames[2].document); var a = $('.q-answer', window.frames[2].document); var t; if (q.size() == 0) { t = direction>0 ? 0 : a.size()-1; } else { t = parseInt(q.eq(0).attr('qpos')) + direction; } // now simply do it :) uns(); return sel(t); } function scroll_current() { var n = initNeighbors(); n = filterNeighbors(n); if (n[1].length) { goTo(n[1][0], window.frames[1]); } return false; } // ========================================================================== // Keyboard shortcut initializations // ========================================================================== function logDate(logmessage) { console.log(new Date().toLocaleFormat('%Y-%m-%d %H:%M.%S') + ": " + logmessage); } in_pimpKeyboardShortcuts = 0; function pimpKeyboardShortcuts() { var wf2d = window.frames[2].document; // var it = []; // for (var i=0; i<window.frames.length; i++) it.push(window.frames[i].document); // // TODO is it really necessary to push the document itself? // //it.push(document); // for (var i=0; i<it.length; i++) { // curr = it[i]; if (in_pimpKeyboardShortcuts || $('html', wf2d).hasClass('pimped-keyboard')) { return; } in_pimpKeyboardShortcuts = 1; logDate('pimpKeyboardShortcuts()'); // navigation keys - n,p,+,-,r,return var ret = 'return'; var srt = 'shift+return'; var shb = 'shift+b'; $(wf2d).bind('keydown', 'n', function() { return k() && key_nf(1); }); $(wf2d).bind('keydown', 'p', function() { return k() && key_nf(0); }); $(wf2d).bind('keydown', 'r', function() { return k() && current(); }); $(wf2d).bind('keydown', ret, function() { return k() && key_enter(true); }); $(wf2d).bind('keydown', srt, function() { return k() && key_enter(false); }); $(wf2d).bind('keydown', '-', function() { return k() && key_nf(0, [2,0,0,0]); }); $(wf2d).bind('keydown', '+', function() { return k() && key_nf(1, [2,0,0,0]); }); $(wf2d).bind('keydown', 'b', function() { return k() && key_nf(0, [0,0,0,2]); }); $(wf2d).bind('keydown', shb, function() { return k() && key_nf(1, [0,0,0,2]); }); // up / down keyboard navigation $(wf2d).bind('keydown', 'down', function() { return k() && advanceCursor(1); }); $(wf2d).bind('keydown', 'up', function() { return k() && advanceCursor(-1); }); // ESC $(wf2d).bind('keydown', 'esc', function() { return k() && !screen_abort() && uns(); }); // the keys 1-4 for the direct selection for (var j=0; j<4; j++) { // TODO DO NOT LIKE THIS let j_num = j; let j_key = (j+1).toString(); $(wf2d).bind('keydown', j_key, function() { return k() && !uns() && sel(j_num); }); } $(wf2d).bind('keydown', '0', function() { return k() && uns(); }); // Go to wiki :) var shw = "shift+w"; $(wf2d).bind('keydown', 'w', function() { return k() && goToWiki(); }); $(wf2d).bind('keydown', shw, function() { return k() && goToWiki(1); }); // filtering of answers - keys shift-1 .. shift-4, shift-0 $(wf2d).bind('keydown', 'shift+1', function() { return k() && dfil(0); }); $(wf2d).bind('keydown', 'shift+2', function() { return k() && dfil(1); }); $(wf2d).bind('keydown', 'shift+3', function() { return k() && dfil(2); }); $(wf2d).bind('keydown', 'shift+4', function() { return k() && dfil(3); }); $(wf2d).bind('keydown', 'shift+5', function() { dfil_apply_filter([0,0,0,0]); return false; }); $(wf2d).bind('keydown', 'shift+6', function() { return k() && set_reset(); }); $(wf2d).bind('keydown', 'shift+0', function() { return k() && dfil_reapply(); }); // the different screens - help, set filter, go to, info $(wf2d).bind('keydown', 'h', function() { return k() && help_screen(); }); $(wf2d).bind('keydown', 'q', function() { return k() && set_form(); }); $(wf2d).bind('keydown', 'g', function() { return k() && goto_screen(); }); $(wf2d).bind('keydown', 'c', function() { return k() && scroll_current(); }); $(wf2d).bind('keydown', 'i', function() { return k() && info_screen(); }); $(wf2d).bind('keydown', 'v', function() { return k() && visi_screen(); }); // nav bar mappers - left, right, m, s, f $(wf2d).bind('keydown', 'left', function() { return k() && clickNB(1); }); $(wf2d).bind('keydown', 'right', function() { return k() && clickNB(2); }); $(wf2d).bind('keydown', 's', function() { return k() && clickNB(3); }); $(wf2d).bind('keydown', 'm', function() { return k() && clickNB(4); }); $(wf2d).bind('keydown', 'f', function() { return k() && clickNB(9); }); // attachment mappers $(wf2d).bind('keydown', "shift+left", function() { return k() && attTab(-1); }); $(wf2d).bind('keydown', "shift+right", function() { return k() && attTab(1); }); $('html', wf2d).addClass('pimped-keyboard'); in_pimpKeyboardShortcuts = 0; } function pimpInputBoxes(answers) { var ret = "return"; var srt = "shift+return"; var ctrlandl = 'ctrl+left'; var ctrlandr = 'ctrl+right'; answers.each( function(idx) { // make TAB work ... $(this).bind('keydown', 'tab', function() { let let_idx = idx; if (let_idx == answers.length - 1) { answers[0].focus(); } else { answers[let_idx + 1].focus(); } }); // bind nav stuff $(this).bind('keydown', ret, function() { return k() && key_enter(true); }); $(this).bind('keydown', srt, function() { return k() && key_enter(false); }); $(this).bind('keydown', 'ctrl+n', function() { return k() && key_nf(1); }); $(this).bind('keydown', 'ctrl+p', function() { return k() && key_nf(0); }); $(this).bind('keydown', 'ctrl+s', function() { return k() && clickNB(3); }); $(this).bind('keydown', 'ctrl+m', function() { return k() && clickNB(4); }); $(this).bind('keydown', 'ctrl-c', function() { return k() && scroll_current(); }); $(this).bind('keydown', 'ctrl+f', function() { return k() && clickNB(9); }); $(this).bind('keydown', ctrlandl, function() { return k() && clickNB(1); }); $(this).bind('keydown', ctrlandr, function() { return k() && clickNB(2); }); // screens & wiki $(this).bind('keydown', 'ctrl+h', function() { return k() && help_screen(); }); $(this).bind('keydown', 'ctrl+g', function() { return k() && goto_screen(); }); $(this).bind('keydown', 'ctrl+i', function() { return k() && info_screen(); }); $(this).bind('keydown', 'ctrl+v', function() { return k() && visi_screen(); }); $(this).bind('keydown', 'ctrl+q', function() { return k() && set_form(); }); $(this).bind('keydown', 'ctrl+w', function() { return k() && goToWiki(); }); // attachment convenience :) $(this).bind('keydown', "shift+right", function() { return k() && attTab(1); }); $(this).bind('keydown', "shift+left", function() { return k() && attTab(-1); }); }); } function pimpCheckboxes(answers) { var wf2d = window.frames[2].document; // bubblesort var swapped = true; var order = []; for (var i=0; i<answers.size(); i++) { order.push(i); } while (swapped) { swapped = false; for (i=0; i<answers.size()-1; i++) { t0 = parseInt(answers.eq(order[i ]).css('top')); t1 = parseInt(answers.eq(order[i+1]).css('top')); if (t0>t1) { swapped = true; var tmp = order[i]; order[i] = order[i+1]; order[i+1] = tmp; } } } // now answers is sorted, the biggest element should be at the end // ONLY preparation of answers and mouse pimping here ... for (var i=0; i<order.length; i++) { // FORMERLY see here: http://mzl.la/dIqEqA var current = answers.eq(order[i]); // add the question position here :) current .attr('qpos', i.toString()) .addClass('q-answer'); // now add the mouse highlights and clicks current.find('table table').click( function () { var newval = ! $(this).find(':input').attr('checked'); var mypar = $(this).closest('.q-answer'); // uncheck everything else $('.q-sel', window.frames[2].document).removeClass('q-sel'); $('.q-answer :input', window.frames[2].document) .attr('checked', false) .removeProp('checked'); // now check or uncheck :) if (newval) { mypar.addClass('q-sel'); } mypar.find(':input').prop('checked', newval); return false; }); current.find('table table').dblclick( function () { $('.q-sel', window.frames[2].document).removeClass('q-sel'); $(this).addClass('q-sel'); $(this).find(':input').attr('checked', true); key_enter(true); return false; }); current.hover( function() { $(this).addClass('q-high'); }, function () { $(this).removeClass('q-high'); }); // move "cursor" over pre-selected answer if present if (current.find(':input').prop('checked')) { current.addClass('q-sel'); } } } function pimpAnswers() { var wf2d = window.frames[2].document; // get the real question id from the info terminal :) if (! $('html', wf2d).hasClass('id-present')) { qbox = getQBox(); if (qbox) { logDate('pimpAnswers() - pimping AJAX'); pimpAjax(qbox); logDate('pimpAnswers() - pimping AJAX DONE'); $('html', wf2d).addClass('id-present'); } } // if we did that already - just exit. if ($('html', wf2d).hasClass('fully-question-pimped')) { return; } // we can't kill that calculator shit, cause then the annexes // won't work for *some* reason. This is the most incompetent // and fucked up pice of shit work I have ever seen. // MAYBE this was the memory leak source $('#calculator', wf2d).find('input').remove(); // now start. var answers = $('form input:checkbox', wf2d).closest('div'); if (answers.size() == 4) { logDate('pimpAnswers() - pimping checkboxes'); // just assume the answer number is 4 and perform a little bubblesort. pimpCheckboxes(answers); $('html', wf2d).addClass('fully-question-pimped'); } else { answers = $('form div input:text', wf2d); if (answers.size()) { logDate('pimpAnswers() - pimping input boxes'); pimpInputBoxes(answers); $('html', wf2d).addClass('fully-question-pimped'); } } } // ========================================================================== // Core functionality & style injection // ========================================================================== function checkInjectedStyles() { // style for the question's mouseover highlight var wf1d = window.frames[1].document; var wf2d = window.frames[2].document; var h; // first frame - hiding of questions h = $('head', wf1d); if (! h.hasClass('pimp_styles')) { logDate('inject styles wf1d'); s = '\ <style type="text/css"> \ .hide-row { \ display: none; \ } \ </style>'; $(s).appendTo(h); h.addClass('pimp_styles'); } // second frame - styles for the questions (mouseover, keyboard select) h = $('head', wf2d); if (! h.hasClass('pimp_styles')) { logDate('inject styles wf2d'); s1 = '\ <style type="text/css"> \ .infobox { \ position: absolute; \ display: block; \ bottom: 30px; \ margin-left: auto; \ margin-right: auto; \ z-index: 10000; \ font-family: verdana, sans \ } \ </style>'; s2 = '\ <style type="text/css"> \ .q-high { \ background-color: #cccccc; \ -moz-border-radius: 0.5em; \ -webkit-border-radius: 0.5em; \ border-radius: 0.5em; \ } \ .q-sel { \ background-color: #8ee88e !important; \ -moz-border-radius: 0.5em; \ -webkit-border-radius: 0.5em; \ border-radius: 0.5em; \ } \ </style>'; s3 = '\ <style type="text/css"> \ .hide-row { \ display: none; \ } \ </style>'; s4 = '\ <style type="text/css"> \ #help_table { \ font-family: verdana; \ font-size: 0.9em; \ border-spacing: 0px; \ } \ </style>'; s5 = '\ <style type="text/css"> \ #help_table td, #help_table th { \ padding-top: 5px; \ padding-bottom: 5px; \ padding-right: 5px; \ border-bottom: 2px solid #aaaaaa; \ border-collapse: collapse; \ } \ </style>'; s6 = '\ <style type="text/css"> \ #message_table { \ margin-left: auto; \ margin-right: auto; \ font-family: verdana; \ padding-top: 50px; \ padding-bottom: 5px; \ } \ </style>'; s7 = '\ <style type="text/css"> \ .pmc_input_table { \ position: absolute; \ top: 75px; \ margin-left: 12.5%; \ font-family: verdana; \ padding: 15px; \ background-color: #ccc; \ border: 2px solid #666; \ width: 75%; \ } \ </style>'; s75 = '\ <style type="text/css"> \ .pmc_input_table td { \ padding-top: 7px; \ padding-bottom: 7px; \ } \ </style>'; s8 = '\ <style type="text/css"> \ #pmc_set_table td, #pmc_set_table th { \ border-style: none; \ padding: 15px; \ } \ </style>'; s9 = '\ <style type="text/css"> \ #wikiClickEnabled { \ -moz-border-radius: 15px; \ border-radius: 15px; \ background-color: 76c276; \ position: absolute; \ top: 90px; \ left: 21px; \ width: 30px; \ height: 30px; \ cursor: pointer; \ } \ </style>'; s10 = '\ <style type="text/css"> \ #wikiClickEnabled td { \ font-family: Verdana; \ font-weight: bold; \ text-align: center; \ color: white; \ vertical-align: middle; \ align: center; \ } \ </style>'; $(s1).appendTo(h); $(s2).appendTo(h); $(s3).appendTo(h); $(s4).appendTo(h); $(s5).appendTo(h); $(s6).appendTo(h); $(s7).appendTo(h); $(s75).appendTo(h); $(s8).appendTo(h); $(s9).appendTo(h); $(s10).appendTo(h); h.addClass('pimp_styles'); } h = $('head', window.frames[0].document); if (! h.hasClass('pimp_styles')) { logDate('inject styles wf0d'); s01 = '\ <style type="text/css"> \ #pimpmyCAT { \ font-family: Arial; \ color: #cccccc; \ font-size: 8pt; \ position: fixed; \ top: 2px; \ right: 5px; \ width: 250px; \ padding: 2px; \ } \ </style>'; s02 = '\ <style type="text/css"> \ #pimpmyCAT table { \ color: #cccccc; \ font-size: 8pt; \ width: 100%; \ border: 0px; \ padding: 0px; \ margin: 0px; \ } \ </style>'; $(s01).appendTo(h); $(s02).appendTo(h); h.addClass('pimp_styles'); } } // ========================================================================== // THE MAGIC. THE PUMPING HEART. // ========================================================================== function update() { if (! perfUpdate) { return; } perfUpdate = false; withinLoop = true; try { checkInjectedStyles(); updateStatBox(); pimpAnswers(); pimpKeyboardShortcuts(); } catch (e) { alert('ERROR: '+e.message); logDate('ERROR: '+e.message); } withinLoop = false; } withinloop = false; perfUpdate = true; $(document).ready( function() { setInterval(update, 200); $(window.frames[1]).bind('DOMSubtreeModified', function() { if (! withinLoop) { perfUpdate = true; } return false; }); });
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址