/ Published in: JavaScript
Because WKW is a german social community, wird auch deutsch Kommentiert: Sobald der Kalender-Bereich aufgerufen wird, gesellt sich oben in der Menüzeile ein neuer Punkt hinzu "Grab BDdays!". Ein klick darauf listet alle Buddies mitsamt Geburtstagen auf. Das Script ist noch nicht fertig. Das Kernfeature - der Export als VCS-Datei - fehlt noch, die Codedokumentation ist nicht sauber, genauso wie der Code an sich. Ein funktionierender Prototype quasi.
Expand |
Embed | Plain Text
Copy this code and paste it in your HTML
// ==UserScript== // @name WKW-ElemGrabber // @namespace http://dev.3muskeeters.com // @description Grabs birthday dates of all your buddies // @include http://www.wer-kennt-wen.de/events/calendar/year/* // ==/UserScript== /* - Grabs birthday dates, saves them in an Array ( key: BDAY00000 -> value: SURNAME NAME--X--DD-MM ) */ //-------------------------------------------------------------------------------------- // Some obvious prototyping //-------------------------------------------------------------------------------------- // // // @method Checks wether a key already exists (using the double equal as log. op.) // Array.prototype.contains = function(strKey) { for (key in this) { if (key == strKey) { return true; } } return false; } // // @method Counts all contained (associative) keys // Array.prototype.getLength = function() { var i = 0; for each (k in this) { if (typeof(k) != "function") { i++; } } return i; } // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // // @name CElementGrabber() // @class Baseclass for the whole grabbing thing. // function CElementGrabber() { var aTempNodes = new Array(); // holds all requested resources var addElement, getElement, dumpElements; // Dummy declaration, used by following closure ( // BEGIN GETTER/SETTER CLOSURE function() { var grabbedElements = new Array(); // // @name addElement(key, value) // @method Setter for array of grabbed elements // @params nothing to explain; only key has to be != null/undefined // addElement = function(key, value) { try { if (key) { //GM_log("addElement( " + key + " , " + value + " );"); grabbedElements[key] = value; } else { throw new TypeError("Invalid element was returned from XPath query"); } } catch (error) { alert(error.name + ": " + error.message); } }; // // @name getElement(index) // @method Getter for array of grabbed elements // @params key: the key that should be returned; if key is omitted, // a whole "copy" of grabbedElements is being returned // getElement = function(key) { // no index passed => return complete "copy" of array if (key == undefined) { return ( function(tmp) { //GM_log(" getElement(undefined): WHOLE ARRAY"); return tmp; } )(grabbedElements); } // valid index passed else if ((typeof(key) == "string") && key) { //GM_log("getElement( " + key + " ): " + grabbedElements[key]); return grabbedElements[key]; } // invalid parameter else { return null; } }; dumpElements = function() { //GM_log("Listing the content of grabbedElements:\n\n"); for (key in grabbedElements) { GM_log("grabbedElements[ " + key + "] = " + grabbedElements[key]) } }; } )() // END GETTER/SETTER CLOSURE // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // // @name dump() // @method Privileged method, lists all grabbed Elements in error console // this.dump = function() { dumpElements(); } // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // // @name grab(strRule, strURL) // @method privileged method; Executes a XPath query to the current document, if nodes are found // the given callback function is called with argsCallback as argument // @params strRule: string, xpath query to execute // aUrlList: array, contains all urls that should be filtered // this.grab = function(strXPathExpr, aUrlList) { //GM_log("ENTER grab"); try { if (!!aUrlList) // when a url list is passed { for (var i = 0; i < aUrlList.length; i++) { if (!load(aUrlList[i], filterElements, [strXPathExpr])) // when load() wasn't successfull { throw new Error("Error occured while loading url: " + aUrlList[i]); } } //alert("All urls in aUrlList loaded"); } else // only the current document should be filtered { filterElements(strXPathExpr); } } catch (error) { alert("grab()->try-statement: " + error.name + ": " + error.message); return false; } //GM_log("LEAVE grab"); } // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // // @name filterElements(strURL, strExpression) // @method callback function; Gets called through load(), applies a passed xpath to the // current document and passes each matched node to getBDayDetails() // @params strURL: string, the current url // strExpression: string, xpath to apply // function filterElements(strURL, aArgs) { //GM_log("ENTER filterElements"); var result = document.evaluate( aArgs[0], aTempNodes[aArgs[1]], null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ); var currentNode = { count: 1, node: result.iterateNext() }; while (currentNode.node) { // there are nodes in result to iterate through try { getBDayDetails(currentNode.node, currentNode.count, strURL); currentNode.count++; currentNode.node = result.iterateNext(); } catch (error) { alert("filterElements()->try-statement: " + error.name + ": " + error.message); return false; } } if (aArgs[1] == 11) { startInjecting(); } return true; //GM_log("LEAVE filterElements"); } // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // // @name getBDayDetails(nodeCapture, numIndex, strURL) // @method Callback function; Gets captured node, index of that node and some optionally passed // in arguments in a string. // @params nodeCapture: dom-node, the captured node // numIndex: number, the index of the node inside the whole captured collection // aPassedArgs: array, the optionally passed in arguments // function getBDayDetails(nodeCapture, numIndex, strURL) { var aBirthday = new Array(); var nodecollSpans = nodeCapture.parentNode.parentNode.parentNode.getElementsByTagName("span"); for (var i = 0; i < nodecollSpans.length; i++) { if (nodecollSpans[i].getAttribute("class") == "day") { aBirthday["day"] = nodecollSpans[i].childNodes[0].nodeValue; i = nodecollSpans.length; } } var aREReturn = /^http:\/\/www\.wer-kennt-wen\.de\/events\/calendar\/year\/(\d{4,4})\/month\/(\d{1,2})/.exec(strURL); // month aBirthday["month"] = aREReturn[2]; // year aBirthday["year"] = aREReturn[1]; // name of birthday child if (nodeCapture.hasAttribute("title")) // birthday of a buddy { if (nodeCapture.getAttribute("title").length) { // using unicode-ranges cause some dumbasses are using "funny" symbols // i am wondering why the wkw-team permitted those characters aBirthday["who"] = /([\u0000-\uFFFF]+)\swird\s\d{2,3}/.exec(nodeCapture.getAttribute("title"))[1]; } } else // thats my own birthday, returning { return false; } //GM_log(" Capture #" + numIndex + "/" + getElement("BDAY-COUNTER") + ": " + aBirthday["who"] + " -> " + aBirthday["day"] + "-" + aBirthday["month"] + "-" + aBirthday["year"]); if (parseInt(getElement("BDAY-COUNTER")) >= 0) { // bday counter already exists, so we increment him by one addElement("BDAY-COUNTER", parseInt(getElement("BDAY-COUNTER")) + 1); } else { // bday coutner doesnt exist, so we create him addElement("BDAY-COUNTER", 1); } //GM_log(" Current BDay-Counter: " + getElement("BDAY-COUNTER")); addElement(("BDAY" + decorateNumber(getElement("BDAY-COUNTER"), 5)), (aBirthday["who"] + "--X--" + aBirthday["month"] + "-" + aBirthday["day"])); return true; } // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // // @name decorateNumber(numInput, templength) // @method private method, takes a string and "fills" him up with zeros up to a legth // specified by numLength. If the passed in string is already longer than numLength // it will be returned without any modification. // @params numInput: string, number that should be decorated // numLength: number, the length numInput should be filled up to // function decorateNumber(numInput, numLength) { if (numInput.toString().length < numLength) { var strTemp = new String(); for (var i =0; i < numLength - String(numInput).length; i++) { strTemp += "0"; } return strTemp + numInput.toString(); } else { return numInput; } } // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // // @name loadURL(strURL, cbFunc, aCbArgs) // @method private method; Requests the resource at the given url // @params strURL: str, url to request // cbFunc: function ref; callback function thats gonna be passed through // aCbArgs: array; arguments that are gonna be passed through // function load(strURL, cbFunc, aCbArgs) { //GM_log("ENTER load"); if (new RegExp(/http\:\/\/[\w0-9-_\.@\/\+\?]+/).test(strURL)) { var objReqDetails = { method: "GET", url: strURL, headers: { "User-Agent": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.0.4)", "Accept": "application/atom+xml,application/xml,text/xml", "Referer": "http://www.wer-kennt-wen.de/events/calendar", "Cookie": document.cookie.split(/__utma=.*;\s(?=__utma=)/)[1], "Accept-Language": "de-de,en-us;q=0.7,en;q=0.3", "Accept-Encoding": "gzip,deflate", "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "Keep-Alive": "300", "Connection": "keep-alive" }, onerror: handleReqError, onreadystatechange: function(respDetails) { if (respDetails.readyState == 4) { interpretRespData(respDetails, strURL, cbFunc, aCbArgs); } } }; GM_xmlhttpRequest(objReqDetails); //GM_log("LEAVE grab"); return true; } else { alert("load: You gave me an invalid url"); //GM_log("LEAVE grab"); return false; } } // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // // @name interpretRespData(responseDetails, strURL, cbFunc, aCbArgs) // @method Joins the properties contained in responseDetails, and alerts the result // @params responseDetails: object, details about the request process // strURL: string; url of requested resource -> passed through // cbFunc: callback function; passed through // aCbArgs: array; arguments for callback function -> passed through // function interpretRespData(responseDetails, strURL, cbFunc, aCbArgs) { /* GM_log("ENTERED interpretRespData"); GM_log(" interpretRespData-> complete. The request is completed and all response data is available in other fields"); GM_log(" interpretRespData-> HTTP-Status: " + responseDetails.statusText); GM_log(" interpretRespData-> Response headers: " + responseDetails.responseHeaders); GM_log(" interpretRespData-> Status: " + responseDetails.status); */ var currIndex = changeDocElement(responseDetails.responseText); aCbArgs.push(currIndex) cbFunc(strURL, aCbArgs); //GM_log("LEAVING interpretRespData"); } // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- function handleReqError(responseDetails) { //GM_log("ENTERED handleReqError"); var strMsg = "handleReqError-> During the request/response an error occured.\n" + "handleReqError-> Status text: " + responseDetails.statusText; alert(strMsg); //GM_log("LEAVING handleReqError"); } // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // // @name changeDocElement(strInnerHTML) // @method private method; replaces the current body with the one in strInnerHTML // @params strInnerHTML: string; contains the new html code // function changeDocElement(strInnerHTML) { var currIndex = aTempNodes.push(document.createElement("div")) - 1; aTempNodes[currIndex].innerHTML = /(\x3Cbody[\s\u0000-\uFFFF]*?\x3E)([\s\u0000-\uFFFF]*)(\x3C\/body\x3E)/.exec(strInnerHTML)[2]; return currIndex; } // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- this.save = function() { // checking for bday counter inside the greasemonkey space if (getData("BDAY-COUNTER") == null) { // bday coutner doesnt exist, so we create him saveData("BDAY-COUNTER", 0); } var numGrabbedElements = parseInt(getElement("BDAY-COUNTER")); if (numGrabbedElements != NaN) { alert(" grabbedElements.length = " + getElement().getLength()); alert(" numGrabbedElements = " + numGrabbedElements); //dumpElements(); for (var i=1; i <= numGrabbedElements; i++) { alert("i: " + i + " / " + decorateNumber(i, 5)); var strTmpKey = "BDAY" + decorateNumber(i, 5); alert(strTmpKey); var tmpValues = { key: strTmpKey, value: getElement(strTmpKey) }; GM_log(" Saving: " + tmpValues.key + " -> " + tmpValues.value); saveData(tmpValues.key, tmpValues.value); saveData("BDAY-COUNTER", parseInt(getData("BDAY-COUNTER")) + 1); } alert("Everything saved!"); } else { return false; } } // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // // @name saveData(key, value) // @method Saving a key-value pair into the gm persistent memory // @params key: string, property name // value: string, value // function saveData(key, value) { if (!!key) { GM_setValue(key, value); } } // //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // // @name getData(searchKey) // @method Looks for a key with searchKey as name and returns his value // @params searchKey: string, property name to search for // function getData(searchKey) { var value = GM_getValue(searchKey); if (value == undefined) { value = null; alert("No key \"" + searchKey + "\" found"); } return value; } function insertTable(refNode) { var aBuddies = new Array(); // formular container var myForm = document.createElement("form"); with (myForm) { setAttribute("action", ""); setAttribute("method", "post"); setAttribute("id", "myForm"); var n = document.createElement("div"); n.setAttribute("id", "col1"); n.setAttribute("class", "formColumn"); appendChild(n); n = document.createElement("div"); n.setAttribute("id", "col2"); n.setAttribute("class", "formColumn"); appendChild(n); n = document.createElement("div"); n.setAttribute("id", "col3"); n.setAttribute("class", "formColumn"); appendChild(n); n = document.createElement("div"); n.setAttribute("id", "col4"); n.setAttribute("class", "formColumn"); appendChild(n); n = document.createElement("br"); n.setAttribute("class", "clearer"); appendChild(n); } // css für myForm GM_addStyle("#myForm { display: block; width: 100%; float: left; }"); GM_addStyle(".formColumn { float: left; width: 24%; margin-top: 15px; margin-bottom: 35px; margin-left: 1%; }"); GM_addStyle(".formColumn label { display:block; background-color:#f4f4f4; margin: 4px 0; padding: 2px 0; border: none; }"); GM_addStyle(".formColumn label.accent { background-color: #e0e9f8; }"); GM_addStyle(".formColumn label input { margin-right: 7px }"); GM_addStyle(".clearer { clear: both; }") GM_addStyle("#col4 input { margin-top: 4px; margin-left: 15px; }"); // myForm in dom einfügen, damit getElementById greift if (document.getElementById("myForm") == null) { refNode.appendChild(myForm); } else { refNode.replaceChild(myForm, document.getElementById("myForm")); } // kontrollvariablen für contentbefüllung var counter = new Number(1); var nextStep = new Number(0); var linesPerCol = Math.ceil(parseInt(getElement("BDAY-COUNTER")) / 3); // contentspalten befüllen for (j = 1; j <= 3; j++) { // zwischenziele setzen (j < 3) ? nextStep += linesPerCol : nextStep = parseInt(getElement("BDAY-COUNTER")) - 1; with (document.getElementById("col" + j)) { var tmpLabel; while (counter <= nextStep) { tmpLabel = document.createElement("label"); if (counter % 2) { tmpLabel.setAttribute("class", "accent"); } var tmpCheckbox = document.createElement("input"); tmpCheckbox.setAttribute("type", "checkbox"); tmpCheckbox.setAttribute("name", "name_" + aBuddies[counter]); tmpCheckbox.setAttribute("value", "value_" + counter); tmpCheckbox.setAttribute("checked", "true"); var strRe = /([\u0000-\uFFFF]*)--X--([\u0000-\uFFFF]*)/.exec(getElement("BDAY" + decorateNumber(counter, 5))); var tmpTitle = document.createTextNode(strRe[1] + " (" + strRe[2] + ")"); //var tmpBr = document.createElement("br"); tmpLabel.appendChild(tmpCheckbox); tmpLabel.appendChild(tmpTitle); //tmpLabel.appendChild(tmpBr); appendChild(tmpLabel); counter++; } } } // button in vierte spalte einfügen var tmpButton = document.createElement("input"); with (tmpButton) { setAttribute("type", "submit"); setAttribute("name", "name_ABsenden"); setAttribute("value", "Ausgewählte Exportieren"); setAttribute("id", "myBtn"); //addEventListener("click", cbOnSubmit, false); } document.getElementById("col4").appendChild(tmpButton); } function removeNodes(aNodeList) { while (aNodeList.length) { var n = aNodeList.pop(); n.parentNode.removeChild(n); } } function cbChangeSel(e) { var selPatterns = { pattern: "//input[@type='checkbox']", all: function(refNode) { refNode.checked = true; }, none: function(refNode) { refNode.checked = false; }, invert: function(refNode) { if (refNode.checked) { refNode.checked = false; } else { refNode.checked = true; } } }; var result = document.evaluate(selPatterns.pattern, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); for (var i = 0; i < result.snapshotLength; i++) { selPatterns[e.target.id](result.snapshotItem(i)); } } function changeText(refNode) { with (refNode) { childNodes[1].childNodes[0].nodeValue = "Geburtstage als VCS-Termin exportieren"; with (childNodes[3]) { title = tagName; href = "javascript:void()"; id = "all"; addEventListener("click", cbChangeSel, false); childNodes[0].nodeValue = "Alle auswählen"; } with (childNodes[5]) { title = tagName; href = "javascript:void()"; id = "none"; addEventListener("click", cbChangeSel, false); childNodes[0].nodeValue = "Auswahl aufheben"; } with (childNodes[7]) { title = tagName; href = "javascript:void()"; id = "invert"; addEventListener("click", cbChangeSel, false); childNodes[0].nodeValue = "Auswahl umkehren"; } } } function cbClick(eventObj) { removeNodes(getRefNode("//div[@id='rahmen']/table/tbody/tr/td[@valign='top']/h2[1]/following-sibling::*")); var targetNode = getRefNode("//div[@id='rahmen']/table/tbody/tr/td[@valign='top']"); changeText(targetNode); insertTable(targetNode); } function getRefNode(strXPath) { var result = document.evaluate(strXPath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (result.snapshotLength == 1) { return result.snapshotItem(0); } else { var aTmp = []; for (var i = 0; i < result.snapshotLength; i++) { aTmp.push(result.snapshotItem(i)); } return aTmp; } } function startInjecting() { cbClick(); } } // END CLASS "CElementGrabber" function start() { var myGrabber = new CElementGrabber(); var aUrls = []; for (var k = 1; k <= 12; k++) { aUrls.push("http://www.wer-kennt-wen.de/events/calendar/year/2009/month/" + k); } myGrabber.grab("//td[starts-with(@class,'thisMonth')]/div[@class='clearfix']/ul[@class='events']/li[@class='birthday']/a", aUrls); } var res = document.evaluate("//div[@id='navigation']/ul/li[not(@class)][last()]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); var myRefNode = res.snapshotItem(0); var myClonedRefNode = myRefNode.cloneNode(true); with (myClonedRefNode.childNodes[0]) { title = tagName; addEventListener("click", start, false); href = "javascript:void()"; childNodes[0].nodeValue = "Grab BDays!"; } myRefNode.parentNode.appendChild(myClonedRefNode);