Advertisement
Guest User

heise.de forum: all comments on one page 21.03.2012

a guest
Sep 20th, 2013
358
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name           Heise.de Forum: all comments on one page
  3. // @description    All comments on one page, iReply, quick-vote, user-scores.
  4. // @include        http://*heise.de/*/foren/*
  5. // @include        http://*heise.de/foren/*
  6. // @version        21.03.2012
  7. // ==/UserScript==
  8.  
  9. /*global console, unsafeWindow, window, prompt, escape */
  10. /*global GM_log, opera */
  11.  
  12. if(typeof unsafeWindow === 'undefined') {
  13.    unsafeWindow = window;
  14. }
  15.  
  16. (function(HACOOP, $, undefined) {
  17.  
  18. /********************************************
  19.  * USER SETTINGS
  20.  ********************************************/
  21. HACOOP.Settings = {
  22.    
  23.     RemoteStorageKeyPrefix: "HACOOP",
  24.    
  25.     // User score is scaled by this value and then fitted into 0-10
  26.     PosterScoreScaleFactor: 0.3,
  27.    
  28.     OverviewPagePostCount:  16,
  29.     MaxJoinedPosts:         16 * 10, // in overview, add x posts * y pages
  30.     MaxJoinedPostsInThread: 80,
  31.     BranchBorderStyle: '1px dashed',
  32.    
  33.     RemarkTagPrefix: "!-!",
  34.    
  35.     // Reply stuff
  36.     EnableIReply:    1,
  37.     EnableAutoQuote: 1,
  38.     EnableQuickVote: 1,
  39.    
  40.     // Colors used for different branch levels
  41.     GetBranchColor: function(level) {
  42.         switch(level % 8) {
  43.             case 0:  return "#999999";
  44.             case 1:  return "#445599";
  45.             case 2:  return "#995544";
  46.             case 3:  return "#449955";
  47.             case 4:  return "#994455";
  48.             case 5:  return "#554499";
  49.             case 6:  return "#CC77CC";
  50.             case 7:  return "#554499";
  51.         }
  52.     },
  53.    
  54.     // Colors used for coloring quote levels
  55.     GetQuoteColor: function(level) {
  56.         switch(level) {
  57.             case 2:  return "#668811";
  58.             case 3:  return "#445599";
  59.             case 4:  return "#995544";
  60.             case 5:  return "#449955";
  61.             case 6:  return "#994455";
  62.             case 7:  return "#554499";
  63.             case 8:  return "#CC77CC";
  64.             case 9:  return "#554499";
  65.             // ...
  66.             default: return "";
  67.         }
  68.     }
  69. };
  70.  
  71. HACOOP.Conventions = {
  72.     IsNoTextSubject: function(html) {
  73.         var kT = html.match(/(kwT)|(k\.w\.T\.)|\((kT|k\.T\.|k T|nT|n\.T\.|kein Text|nix)\)/i) !== null;
  74.         var re = html.match(/\bRe: /i) !== null;
  75.         return kT && !re;
  76.     },
  77.     GetQuoteLevel: function(html) {
  78.         var patternLength = 10;
  79.         var m = html.match(/^((?:> )+)/);
  80.         if(m === null) {
  81.             return 0;
  82.         }
  83.         return m[0].length / patternLength;
  84.     }
  85. };
  86.  
  87. /********************************************
  88.  * THE CODE
  89.  ********************************************/
  90. HACOOP.Browser = {
  91.     Log: function(msg) {
  92.         if(typeof console !== 'undefined') {
  93.             if(typeof console.log !== 'undefined') {
  94.                 console.log(msg);
  95.             }
  96.         }
  97.         else if(typeof GM_log !== 'undefined') {
  98.             GM_log(msg);
  99.         }
  100.         else if(typeof opera !== 'undefined') {
  101.             opera.postError(msg);
  102.         }
  103.         else {
  104.             // TODO
  105.             // alert(msg);
  106.         }
  107.     },
  108.     AjaxRequest: function(url, callback, args) {
  109.         var xmlHttp = new XMLHttpRequest();
  110.        
  111.         url = HACOOP.Page.EnsureAbsoluteUrl(url);
  112.        
  113.         xmlHttp.open('GET', url, true);
  114.         xmlHttp.onreadystatechange = function () {
  115.             if(xmlHttp.readyState === 4 &&
  116.                xmlHttp.status     === 200) {
  117.                 callback(url, xmlHttp.responseText, args);
  118.            }
  119.         };
  120.        
  121.         xmlHttp.send(null);
  122.     }
  123. };
  124.  
  125. function encodeHTML(text) {
  126.     return $('<div/>').text(text).html();
  127. }
  128.  
  129. HACOOP.Helpers = {
  130.     Trim: function(str) {
  131.         var a = str.replace(/^\s+/, '');
  132.         return a.replace(/\s+$/, '');
  133.     },
  134.     GetFirstMatching: function(jquerySelectors, context){
  135.         for(var i = 0; i < jquerySelectors.length; i++) {
  136.             var selector = jquerySelectors[i];
  137.             var res = $(selector, context);
  138.             if(res.length > 0) {
  139.                 return res;
  140.             }
  141.         }
  142.         return null;
  143.     },
  144.     IsRemarkTag: function(tag) {
  145.         return tag.match("^"+HACOOP.Settings.RemarkTagPrefix);
  146.     }
  147. };
  148.  
  149. HACOOP.Images = {
  150.     BranchOpt_Collapse: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAn5JREFUeNrEV01oE0EU/mY3ie1BUfHiQSmiJwVFDyIVCv5URFREpTftSejVQ/AHj6IEEXoq9FQFhUAxiEIg1YOFHDwIChEFxV+EIpYehNZsdnd8b3Y32V1isxvi5JEvmze7+963732zmRFSSqxmQoh2w5nR25iFgdNw8bhyBedozG53Yaf46oLV0MbMwzfx6MYDSNt6L/nIPo93FT8lAS/5Q8iFxadydn5IHdn/F4lO8UWKFnDy4vA2nL04MoVnryea1xzZM4V7LyZQ/YTS8+s4T0NO0hYkJZA9VsDXA0PYfGZfHi8/FOiE+kDdTV/7d+RRelVA9SNKc9daJHpBIHviDhYObsfGozvHUPteRMMJznEC73eWir9ryxjm3hYx/w7l8lWcYmH2gsD6k3ex5LgUjbBiAfcvzaDyZrxJYHT3DC5Mj2MwR9PDoF4RnlzGOrr9d6f4GSQwCraXDhsIuUO3UBbChuT6C68F7HMFKnkcJ7dOWCQYSWInIbBM+EL44ZPgunnhAxGI5lN+JiwRGoSVXhGwfITM9ZKLkO8ZJ/+JFJZBSuOcjluPEGBfoDvLdHOTywUJtcCNF+h/E3CkHa2AtPUScBFtgfJ1EVAaIJELs6VB5evWgPQzSvRBA66sI/zIytcuQjPm90WE6IMIPdFZEQKOfg1Ykb8a5euchqz6YKHU9LVWQNhRDQjtIoy1QLcG7NgssHW/isNvQvRHA95/AS/3WIzsQzsBw1+J6SbAK14z18CagWA96PmmoYeAbCyjOjldG3bd1qBh1MDjwT4llaZS7o7XErYSNsXI84vgF+Eb7wXS7I7TEuCkg4QBRCaievI//lLcTkPgrwADAJGtdBYZQxi9AAAAAElFTkSuQmCC',
  151.     BranchOpt_Expand:   'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAkxJREFUeNrkVz1IHEEU/uZ2NaeFGEhloensY2chORuJgthY2KWxFFFEsBGs/CEYxNLWzkYEzWFx6hZ26dKkCKiFECwUDYm3Pze+Nze3t3fRc1e9ucKBx/LN3/vm/cy+EVJK1GpCiCi0B1ew7QNDBV7GQsMpEhvY3ZvFCPX40QWP7W8jWWth5WujM/B8rZ1YNNkCk1tfhnic5CbJhkkJpIMC0Jruxrfv41o98KlnA9zP4/UmIKQ+uNBScoO2tEi4X2ICCNVYoQeeoPaZBIJCUFQqItgUgeKh85CagNRYmLRAQbqUe2UXKGySgA+3wgUKmyQgZSUBadICrDMghcIqB7/CRmOg5IIobkgQRrHRe6DKAoFxCzTaBT6ZXFo6AzQ2mgV84lKZEOLXFwOpRqZhI4NQaJMLUS4FAhi/CfNVLsibs4BUd79XLMvCf4EHWUcCzcOryAdU9Pgkrsu/X68iBhj71DWwhN823Q8Wyc403tCQ+xIEWm7/IJf5iP73XUUCF39/VLiA8ecxYtoMnJwCB4fI6RL9UQKpOC7fn8d0dhfOz18EOPjuEe7ncZ7H81VoxGhxCPwjOT9axlQuC+fkjMxGdkvpqpi/jLmfx3kez9fr4tT5sZ5mrO4tSWffLL5mBtHH7uC4YH8rs+/BcVaUcqKCS5WdMZ5makItiTQm8Y7kA5E4WnAgN88h+cuY+/W4lWR/kfBxGloiM4f1dDt6b69wfLCIieqTx32cJiUQJdFB0kZyrX3+n/J6ESilL6dZE4mnA85/yMW12p0AAwDSETpC4pZz6QAAAABJRU5ErkJggg==',
  152.     End: ''
  153. };
  154.  
  155. HACOOP.Page = {
  156.     BaseUrl: 'http://' + document.location.host,
  157.     EnsureAbsoluteUrl: function(url) {
  158.         if(!url) { return url; }
  159.         if(url.match(/^\//)) {
  160.             url = this.BaseUrl + url;
  161.         }
  162.         return url;
  163.     },
  164.     Styles: {
  165.       Add: function(css) {
  166.         var style = document.createElement("style");
  167.         style.type = "text/css";
  168.         style.innerHTML = css;
  169.    
  170.         document.body.appendChild(style);
  171.       }  
  172.     },
  173.     Scripts: {
  174.         AddByCode: function(code) {
  175.             var script = document.createElement("script");
  176.             script.type = "text/javascript";
  177.             script.innerHTML = code;
  178.        
  179.             document.body.appendChild(script);
  180.         },
  181.         AddByURL: function(url) {
  182.             var script = document.createElement("script");
  183.             script.type = "text/javascript";
  184.             script.src = url;
  185.        
  186.             document.body.appendChild(script);
  187.         }
  188.     }
  189. };
  190.  
  191. HACOOP.DB = {
  192.     Remote: {
  193.         MasterPrefix: 'HACOOP',
  194.         GetRemoteStorage: function() {
  195.             if(window.remoteStorage) {
  196.                 return window.remoteStorage;
  197.             }
  198.             if((typeof unsafeWindow !== 'undefined') &&
  199.                unsafeWindow.remoteStorage) {
  200.                 return unsafeWindow.remoteStorage;
  201.             }
  202.             HACOOP.Browser.Log('Remote storage not loaded. Can\'t get value.');
  203.             return null;
  204.         },
  205.         Get: function(prefix, key, callback) {
  206.             var rs = this.GetRemoteStorage();
  207.            
  208.             var cb = function(string, key) {
  209.                 var obj;
  210.                 try {
  211.                     obj = JSON.parse(string);
  212.                 }
  213.                 catch(ex) {
  214.                     HACOOP.Browser.Log('Couldn\'t parse remotely stored value: ' + string);
  215.                     obj = ex;
  216.                 }
  217.                 callback(obj);
  218.             };
  219.             rs.getItem(this.MasterPrefix + '-' + prefix + '-' + key, cb);
  220.         },
  221.         // max. 64kB per key
  222.         // TODO: store in multiple strings
  223.         Set: function(prefix, key, value) {
  224.             var rs = this.GetRemoteStorage();
  225.            
  226.             var string = JSON.stringify(value);
  227.             rs.setItem(this.MasterPrefix + '-' + prefix + '-' + key, string);
  228.         }
  229.     },
  230.     Local: {
  231.         Get: function(key, defaultValue) {
  232.             if(!window.localStorage) {
  233.                 HACOOP.Browser.Log('Local storage not available. Can\'t get value.');
  234.                 return;
  235.             }
  236.             var string = window.localStorage.getItem(key);
  237.             if(string === null) {
  238.                 return defaultValue;
  239.             }
  240.            
  241.             try {
  242.                 return JSON.parse(string);
  243.             }
  244.             catch(ex) {
  245.                 HACOOP.Browser.Log('Couldn\'t parse locally stored value:' + string);
  246.                 return null;
  247.             }
  248.         },
  249.         Set: function(key, value) {
  250.             if(!window.localStorage) {
  251.                 HACOOP.Browser.Log('Local storage not available. Can\'t save.');
  252.                 return;
  253.             }
  254.             var string = JSON.stringify(value);
  255.             window.localStorage.setItem(key, string);
  256.         }
  257.     },
  258.     Settings: {
  259.         Prefix: 'Settings',
  260.         DB: null,
  261.         Get: function() {
  262.             if(this.DB) {
  263.                 return this.DB;
  264.             }
  265.             this.DB = HACOOP.DB.Local.Get(this.Prefix, {});
  266.             HACOOP.Browser.Log("Settings from local DB:", this.DB);
  267.             return this.DB;
  268.         },
  269.         Set: function(settings) {
  270.             this.DB = settings;
  271.             HACOOP.Browser.Log("Settings to local DB:", this.DB);
  272.             HACOOP.DB.Local.Set(this.Prefix, this.DB);
  273.         }
  274.     },
  275.     UserScores: {
  276.         Prefix: 'score_',
  277.         GetPosterScore: function(author) {
  278.             author = HACOOP.Helpers.Trim(author);
  279.             return HACOOP.DB.Local.Get(this.Prefix + author);
  280.         },
  281.         ScorePoster: function(author, score) {
  282.             author = HACOOP.Helpers.Trim(author);
  283.             var oldScore = this.GetPosterScore(author);
  284.             HACOOP.DB.Local.Set(this.Prefix + author, oldScore + score);
  285.         }
  286.     },
  287.     UserTags: {
  288.         Prefix: "UserTags",
  289.         DB: null,
  290.         Load: function() {
  291.             if(this.DB) {
  292.                 return;
  293.             }
  294.             this.DB = HACOOP.DB.Local.Get(this.Prefix, {});
  295.         },
  296.         Save: function() {
  297.             HACOOP.DB.Local.Set(this.Prefix, this.DB);
  298.         },
  299.         Clear: function() {
  300.             this.DB = {};
  301.             this.Save();
  302.         },
  303.         _knownTagsCache: null,
  304.         GetKnownTags: function() {
  305.             if(this._knownTagsCache) {
  306.                 return this._knownTagsCache;
  307.             }
  308.             var tags = {};
  309.             for(var userName in this.DB) {
  310.                 if(this.DB.hasOwnProperty(userName)) {
  311.                     var entry = this.DB[userName];
  312.                     for(var tag in entry) {
  313.                         if(entry.hasOwnProperty(tag)) {
  314.                             if(HACOOP.Helpers.IsRemarkTag(tag)) {
  315.                                 continue;
  316.                             }
  317.                            
  318.                             if(!tags[tag]) {
  319.                                 tags[tag] = 0;
  320.                             }
  321.                             tags[tag]++;
  322.                         }
  323.                     }
  324.                 }
  325.             }
  326.             this._knownTagsCache = tags;
  327.             return tags;
  328.         },
  329.         SanizeUserName: function(userName) {
  330.             userName = HACOOP.Helpers.Trim(userName);
  331.             return userName;
  332.         },
  333.         GetUserEntry: function(userName) {
  334.             userName = this.SanizeUserName(userName);
  335.             this.Load();
  336.            
  337.             var entry = this.DB[userName];
  338.             if(!entry) {
  339.                 this.DB[userName] = {};
  340.                 entry = this.DB[userName];
  341.             }
  342.             return entry;
  343.         },
  344.         Get: function(userName, tag) {
  345.             var userEntry = this.GetUserEntry(userName);
  346.             return userEntry[tag];
  347.         },
  348.         Set: function(userName, tag, val) {
  349.             var userEntry = this.GetUserEntry(userName);
  350.             userEntry[tag] = val;
  351.            
  352.             this._knownTagsCache = null;
  353.             this.Save();
  354.         },
  355.         Inc: function(userName, tag, offVal) {
  356.             var oldVal = this.Get(userName, tag);
  357.             HACOOP.Browser.Log("Inc(): UserName=", userName, ", Tag=", tag, ", OffVal=", offVal);
  358.             HACOOP.Browser.Log("Inc(): oldVal=", oldVal);
  359.             if(!oldVal) {
  360.                 oldVal = 0;
  361.             }
  362.             var newVal = oldVal + offVal;
  363.             this.Set(userName, tag, newVal);
  364.             return newVal;
  365.         }
  366.     },
  367.     LoadFromRemote: function(prefix, callback) {
  368.         this.Remote.Get(prefix, 'Settings', function(o) {
  369.             if(o) {
  370.                 HACOOP.DB.Settings.Set(o);
  371.             }
  372.             HACOOP.Browser.Log("Retrieved Settings:", o);
  373.             callback(o);
  374.         });
  375.         this.Remote.Get(prefix, 'UserTags', function(o) {
  376.             if(o) {
  377.                 HACOOP.DB.UserTags.DB = o;
  378.                 HACOOP.DB.UserTags.Save();
  379.             }
  380.             HACOOP.Browser.Log("Retrieved UserTags:", o);
  381.             callback(o);
  382.         });
  383.     },
  384.     SaveToRemote: function(prefix) {
  385.         this.Remote.Set(prefix, 'Settings', this.Settings.DB);
  386.         this.Remote.Set(prefix, 'UserTags', this.UserTags.DB);
  387.     }
  388. };
  389.  
  390. HACOOP.Heise = {
  391.     PageTypes: {}, // defined below
  392.     Urls: {
  393.         SearchUrl: HACOOP.Page.BaseUrl + '/foren/suche?q=',
  394.         GetSearchURLForUser: function(userName) {
  395.             return this.SearchUrl + encodeURIComponent(userName);
  396.         },
  397.         GetScoreBarCodeImageCode: function(absScore) {
  398.             // TODO: think about making it logarithmical
  399.             var score = absScore * HACOOP.Settings.PosterScoreScaleFactor;
  400.             score += 5;
  401.             if(score >= 4 && score <= 6) {
  402.                 return null;
  403.             }
  404.        
  405.             score = Math.min(score, 10);
  406.             score = Math.max(score, 0);
  407.             score += 1;
  408.             score = Math.round(score);
  409.            
  410.             return "<img src=\"/icons/forum/wertung_" + score + ".gif\" title=\"User-Score: " + absScore + "\"/>";
  411.         },
  412.         GetPosterScoreBarCode: function(userName) {
  413.             var absScore = HACOOP.DB.UserScores.GetPosterScore(userName);
  414.             if (absScore === undefined) {
  415.                 return null;
  416.             }
  417.             return this.GetScoreBarCodeImageCode(absScore);
  418.         }
  419.     },
  420.     RegExps: {
  421.         PostingBody: /((<div class="vote_posting">[\s\S]*?)?(?=(<div class="posting_date">))\3[\s\S]*?(?=(<div class="tovote_links">))\4[\s\S]*?<\/div>)/,
  422.         TitleLinkURL: /<a[^>]*? href="([\s\S]*?)"[^>]*? title=(["'])[\s\S]*?\2[^>]*?>/i,
  423.         TitleLinkURL2: /<a[^>]*? title=(["'])[\s\S]*?\2[^>]*? href="([\s\S]*?)"[^>]*?>/i
  424.     },
  425.     GetTitleLinkURL: function(html) {
  426.         var res = html.match(this.RegExps.TitleLinkURL);
  427.         if(res) {
  428.             return res[1];
  429.         }
  430.         res = html.match(this.RegExps.TitleLinkURL2);
  431.         if(res) {
  432.             return res[2];
  433.         }
  434.        
  435.         return null;
  436.     },
  437.     EnsureShowThreadLinks: function() {
  438.         $('a').each(function(index, element) {
  439.             if(element.href.match(/\/read\//)) {
  440.                 element.href = element.href.replace(/\/read\/(?!showthread-1)/, '/read/showthread-1/');
  441.             }
  442.         });
  443.     },
  444.     GetPageType: function(url, content) {
  445.         for(var propName in this.PageTypes) { if(true) {
  446.             var pageType = this.PageTypes[propName];
  447.            
  448.             if(url.match(pageType.RegExp)) {
  449.                 return pageType;
  450.             }
  451.             if(pageType.ContentMatch(content)) {
  452.                 return pageType;
  453.             }
  454.         }}
  455.         return null;
  456.     }
  457. };
  458.  
  459. // http://www.heise.de/developer/artikel/foren/forum-223899/write/
  460. // http://www.heise.de/foren/forum-7320/write/
  461. HACOOP.Heise.PageTypes.NewThread = (function() {
  462.     function onPageLoad() {
  463.        
  464.     }
  465.    
  466.     return {
  467.         Name: 'New Thread',
  468.         ContentMatch: function(html) { return false; },
  469.         RegExp: /\/foren\/forum-\d+\/write\//i,
  470.         OnPageLoad: onPageLoad
  471.     };
  472. })();
  473.  
  474. HACOOP.Heise.PageTypes.EditPage = (function() {
  475.     function onPageLoad() {
  476.        
  477.     }
  478.    
  479.     return {
  480.         Name: 'Edit Page',
  481.         ContentMatch: function(html) { return false; },
  482.         RegExp: /^a$/i, // TODO
  483.         OnPageLoad: onPageLoad
  484.     };
  485. })();
  486.  
  487.  
  488. // http://www.heise.de/open/foren/forum-91253/msg-16549489/write/
  489. // http://www.heise.de/ix/foren/forum-222910/msg-21507591/write/
  490. // http://www.heise.de/developer/artikel/foren/forum-223899/msg-21561448/write/
  491. // http://www.heise.de/foren/forum-7265/msg-21538411/write/
  492. HACOOP.Heise.PageTypes.ReplyPage = (function() {
  493.    
  494.     function autoQuote() {
  495.         if(document.getElementsByName("message")[0].value !== "") {
  496.             return;
  497.         }
  498.        
  499.         // select the right button the ultra hacky way
  500.         document.getElementsByName("quote")[0].click();
  501.     }
  502.    
  503.     function cleanUpReplyPage()
  504.     {
  505.         var form = HACOOP.Helpers.GetFirstMatching(['div.forum_content', '#mitte_forum']);
  506.         var html = form[0].innerHTML;
  507.        
  508.         // messy but working
  509.         html = html.replace(/(Unsere Foren|Dieses Forum)[\s\S]*<textarea/i, "<textarea");
  510.         html = html.replace(/<i>\([^)]+\)<\/i>/ig, "");
  511.    
  512.         document.body.innerHTML = html;
  513.     }
  514.                
  515.     function onPageLoad() {
  516.        
  517.         // if it's inlined...
  518.         if(window !== top) {
  519.             if(HACOOP.Settings.EnableAutoQuote) {
  520.                 autoQuote();
  521.             }
  522.             cleanUpReplyPage();
  523.             return;
  524.         }
  525.     }
  526.    
  527.     return {
  528.         Name: 'Reply Page',
  529.         ContentMatch: function(html) {
  530.             return html.match(/<span class="red">Um die Übersichtlichkeit[^<]+kürzen.</);
  531.         },
  532.         RegExp: /\/foren\/forum-\d+\/msg-\d+\/write\//i,
  533.         OnPageLoad: onPageLoad
  534.     };
  535. })();
  536.  
  537.  
  538. // http://www.heise.de/open/foren/S-heise-open-mit-neuem-Layout-und-neuer-Struktur/forum-91253/msg-16549489/read/showthread-1/
  539. // http://www.heise.de/newsticker/foren/S-Warum-ueberhaupt-Warnungen/forum-224299/msg-21576375/read/showthread-1/
  540. // http://www.heise.de/ix/foren/S-1-2-OT-einfache-Intranet-Suche/forum-222910/msg-21507591/read/showthread-1/
  541. // http://www.heise.de/tp/foren/S-Viel-zu-kompliziert-gedacht-DAK-schiesst-sich-mit-Studie-selbst-ins-Knie/forum-224387/msg-21583811/read/
  542. // http://www.heise.de/tp/foren/S-Re-Viel-zu-kompliziert-gedacht-DAK-schiesst-sich-mit-Studie-selbst-ins-Knie/forum-224387/msg-21584200/read/
  543. // http://www.heise.de/developer/artikel/foren/S-Da-regen-sich-alle-ueber-Vorratsdatenspeicherung-auf/forum-223899/msg-21561448/read/showthread-1/
  544. // http://www.heise.de/foto/foren/S-Re-Was-tun/forum-134009/msg-21576590/read/showthread-1/
  545. HACOOP.Heise.PageTypes.ThreadPost = (function() {
  546.    
  547.     function quickVoteFunctionBuilder(voteLink, url, author) {
  548.         return function() {
  549.             HACOOP.Browser.Log("voted for author: " + author);
  550.    
  551.             // mark as voted
  552.             voteLink.style.backgroundColor = "yellow";
  553.    
  554.             // extract score
  555.             var matches = url.match(/postvote-(-?\d)/);
  556.             if(!matches) { return; }
  557.    
  558.             var score = parseInt(matches[1], 10);
  559.             HACOOP.Browser.Log("score: " + score);
  560.    
  561.             // score the author
  562.             HACOOP.DB.UserScores.ScorePoster(author, score);
  563.         };
  564.     }
  565.    
  566.    
  567.     function countLevel(el) {
  568.         var lvl = 1;
  569.    
  570.         // limit loop, just to be sure
  571.         for(var i = 0; i < 10000; i++) {
  572.             var par = el.parentNode;
  573.             el = par;
  574.    
  575.             if(par === null) { break; }
  576.             if(par.className === "thread_tree") { break; }
  577.             if(par.className === "nextlevel" ||
  578.                par.className === "nextlevel_line") {
  579.                 lvl++;
  580.             }
  581.         }
  582.    
  583.         return lvl;
  584.     }
  585.    
  586.     function colorizeQuotes(div) {
  587.         $('p span.quote', div).each(function(i, element) {
  588.             var quoteLevel = HACOOP.Conventions.GetQuoteLevel(element.innerHTML);
  589.             if(quoteLevel > 0) {
  590.                 var color = HACOOP.Settings.GetQuoteColor(quoteLevel);
  591.                 if(color !== "") {
  592.                     element.innerHTML = "<span style=\"color: " + color + "\">" + element.innerHTML + "</span>";
  593.                 }
  594.             }
  595.         });
  596.     }
  597.    
  598.     function buildDropdownMenu(specialEntries, entryKeyVals) {
  599.        
  600.         // make some ordinary container
  601.         var container = document.createElement('div');
  602.         container.setAttribute('class', 'dropDown');
  603.        
  604.         // fill it with some crazy HTML
  605.         var code = "<ul>";
  606.         for(var skey in entryKeyVals) {
  607.             var sval = entryKeyVals[skey];
  608.            
  609.             code += '<li key="'+escape(sval)+'" val="'+escape(skey)+'">' +
  610.                     encodeHTML(skey) +
  611.                     '</li>';
  612.         }
  613.         code += "</ul><ul>";
  614.         for(var key in specialEntries) {
  615.             var val = specialEntries[key];
  616.            
  617.             code += '<li class="special" key="'+escape(val)+'" val="'+escape(key)+'">' +
  618.                     encodeHTML(key) +
  619.                     '</li>';
  620.         }
  621.         code += "</ul>";
  622.         container.innerHTML = code;
  623.        
  624.         document.body.appendChild(container);
  625.         return $(container);
  626.     }
  627.    
  628.     var newTagText = 'new...';
  629.     var newRemarkTagText = 'remark...';
  630.    
  631.     function handleAddTagButtonClick() {
  632.         var button = $(this);
  633.         var knownTags = HACOOP.DB.UserTags.GetKnownTags();
  634.        
  635.         var specialVals = {};
  636.         specialVals[newTagText] = -1;
  637.         specialVals[newRemarkTagText] = -1;
  638.        
  639.         var ddm = buildDropdownMenu(specialVals, knownTags);
  640.        
  641.         ddm.slideUp(0);
  642.        
  643.         function handleAddTagEntryClick() {
  644.             var entry = $(this);
  645.             var tag = entry.attr('val');
  646.             if(tag === newTagText) {
  647.                 tag = prompt("Name für neues Tag?");
  648.                 if(!tag) {
  649.                     return;
  650.                 }
  651.             }
  652.             else if(tag === newRemarkTagText) {
  653.                 tag = prompt("Kommentar?");
  654.                 if(!tag) {
  655.                     return;
  656.                 }
  657.                 tag = HACOOP.Settings.RemarkTagPrefix + tag;
  658.             }
  659.  
  660.             var tagContainer = button.closest('div.userTagsContainer');
  661.             var userName = tagContainer.attr('HACOOP-userName');
  662.            
  663.             HACOOP.DB.UserTags.Inc(userName, tag, 1);
  664.             fillUserTagContainer(tagContainer[0]);
  665.         }
  666.        
  667.         $(ddm).find('li').click(handleAddTagEntryClick);
  668.        
  669.         ddm.css('top', button.offset().top + button.height());
  670.         ddm.css('left', button.offset().left);
  671.         ddm.slideDown(300);
  672.        
  673.         return false;
  674.     }
  675.     function handleTagScoreChange(userTagDiv, val) {
  676.         var tagScoreSpan = $(userTagDiv).find('span.tagScore');
  677.         var userTagsDiv = $(userTagDiv).closest('div.userTagsContainer');
  678.         if(!userTagsDiv.length) {
  679.             alert("Can't find score userName!");
  680.         }
  681.         if(!userTagDiv.length) {
  682.             alert("Can't find score tag!");
  683.         }
  684.         var userName = userTagsDiv.attr('HACOOP-userName');
  685.         var userTag  = userTagDiv.attr('HACOOP-tag');
  686.         HACOOP.Browser.Log("UserName: ", userName, ", Tag: ", userTag);
  687.        
  688.         var newVal = HACOOP.DB.UserTags.Inc(userName, userTag, val);
  689.         HACOOP.Browser.Log("New value: ", newVal);
  690.         $(tagScoreSpan)[0].innerHTML = newVal;
  691.     }
  692.     function handleTagScoreLeft() {
  693.         handleTagScoreChange($(this), 1);
  694.         return false;
  695.     }
  696.     function handleTagScoreRight() {
  697.         handleTagScoreChange($(this), -1);
  698.         return false;
  699.     }
  700.    
  701.     function fillUserTagContainer(containerDivObj) {
  702.        
  703.         var userName = containerDivObj.getAttribute('HACOOP-userName');
  704.        
  705.         containerDivObj.innerHTML = '<span class="addUserTagButton">+</span>';
  706.         var userTagsEntry = HACOOP.DB.UserTags.GetUserEntry(userName);
  707.         for(var tag in userTagsEntry) {
  708.             var tagScore = userTagsEntry[tag];
  709.             if(tagScore === 0) {
  710.                 continue;
  711.             }
  712.            
  713.             var tagDiv = document.createElement('div');
  714.             tagDiv.setAttribute('HACOOP-tag', tag);
  715.            
  716.             if(HACOOP.Helpers.IsRemarkTag(tag)) {
  717.                 tagDiv.setAttribute('class', 'userTag remarkTag');
  718.                 tag = tag.substring(HACOOP.Settings.RemarkTagPrefix.length);
  719.             }
  720.             else {
  721.                 tagDiv.setAttribute('class', 'userTag');
  722.             }
  723.  
  724.             tagDiv.innerHTML = '<span class="' + ((tagScore > 0) ? 'positive' : 'negative')+ '">'+encodeHTML(tag)+'</span><span class="tagScore">'+tagScore+'</span>';
  725.             containerDivObj.appendChild(tagDiv);
  726.         }
  727.        
  728.         $('span.addUserTagButton', containerDivObj).click(handleAddTagButtonClick);
  729.         $('div.userTag', containerDivObj).click("contextmenu", handleTagScoreLeft);
  730.         $('div.userTag', containerDivObj).bind("contextmenu",  handleTagScoreRight);
  731.     }
  732.  
  733.     function processMessageDiv(div, messageUrl, pageInfo) {
  734.    
  735.         // link username to search
  736.         var userIs = HACOOP.Helpers.GetFirstMatching(['div.user_info i a', 'div.user_info i'], div);
  737.         var userI = null;
  738.         if(userIs.length) {
  739.             userI = userIs[0];
  740.         }
  741.  
  742.         var userName;
  743.         if(userI !== null) {
  744.             userName = userI.innerHTML.replace(/^(.*?),&nbsp;.*$/, "$1");
  745.             userI.innerHTML = "<a href=\"" + HACOOP.Heise.Urls.GetSearchURLForUser(userName) + "\">" + userI.innerHTML + "</a>";
  746.    
  747.             var scoreImgCode = HACOOP.Heise.Urls.GetPosterScoreBarCode(userName);
  748.             if(scoreImgCode !== null) {
  749.               userI.innerHTML += "&nbsp;&nbsp;" + scoreImgCode;
  750.             }
  751.            
  752.             var userTagsDivObj = document.createElement('div');
  753.             userTagsDivObj.setAttribute('class', 'userTagsContainer');
  754.             userTagsDivObj.setAttribute('HACOOP-userName', userName);
  755.            
  756.             $(userTagsDivObj).insertAfter($('div.user_info', div));
  757.  
  758.             fillUserTagContainer(userTagsDivObj);
  759.  
  760.             // HACOOP.Browser.Log("Known Tags:");
  761.             // HACOOP.Browser.Log(HACOOP.DB.UserTags.GetKnownTags());
  762.             // HACOOP.DB.UserTags.Set(userName, 'toast', 123);
  763.             // HACOOP.DB.UserTags.Set(userName, 'bread', 123);
  764.             // HACOOP.DB.UserTags.Clear();
  765.         }
  766.         else {
  767.             userName = "?";
  768.             HACOOP.Browser.Log("Could not find username div!");
  769.         }
  770.        
  771.         colorizeQuotes(div);
  772.    
  773.         // set the reply & edit links
  774.         if(pageInfo.ReplyLink) {
  775.             var replyLinkInlineA = "<a style=\"color: #6673DD\" onclick=\"iReply('" + div.getAttribute("replyFrameID") +
  776.                 "', '" + pageInfo.ReplyLink + "')\">iReply</a>";
  777.             var replyLinkA = "<a href=\"" + pageInfo.ReplyLink + "\">Beantworten</a>";
  778.    
  779.             if(userI) {
  780.                 userI.innerHTML += " --- " + replyLinkA + (HACOOP.Settings.EnableIReply ? " / " + replyLinkInlineA : "");
  781.                 if(pageInfo.EditLink) {
  782.                     var editLinkA = "<a href=\"" + pageInfo.EditLink + "\">Editieren</a>";
  783.                     userI.innerHTML += " / " + editLinkA;
  784.                 }
  785.             }
  786.         }
  787.    
  788.         // link posting title to posting
  789.         var postingSubject = $('h3.posting_subject', div);
  790.         if(postingSubject.length)
  791.         {
  792.             if(HACOOP.Conventions.IsNoTextSubject(postingSubject[0].innerHTML))
  793.             {
  794.                 var postingText = $('p.posting_text', div)[0];
  795.                 postingText.style.color = "#CCCCCC";
  796.                 postingText.style.margin = "0.3em";
  797.                 // postingSubject.style.color = "#AA00AA"; // colorize title is better
  798.             }
  799.             postingSubject[0].innerHTML = "<a href=\"" + messageUrl + "\">" + postingSubject[0].innerHTML + "</a>";
  800.         }
  801.         else {
  802.             HACOOP.Browser.Log("Posting subject not found!");
  803.         }
  804.        
  805.         // var voteLinksDiv = xpath(".//div[@class='tovote_links'][1]", div);
  806.         // if(voteLinksDiv !== null)
  807.         //  voteLinksDiv.style.float = "right";
  808.         // TODO: float
  809.            
  810.         // relink voting buttons
  811.         if(HACOOP.Settings.EnableQuickVote) {
  812.             $('div.tovote_links a', div).each(function(i, voteLink) {
  813.                 var url = voteLink.href;
  814.    
  815.                 voteLink.removeAttribute("href");
  816.                 voteLink.addEventListener("click",
  817.                     quickVoteFunctionBuilder(voteLink, url, userName),
  818.                     true);
  819.    
  820.                 voteLink.setAttribute("onclick", "sendVote(this, '" + url + "');");
  821.             });
  822.         }
  823.     }
  824.    
  825.     function appendReplyFrame(div, id, divPost)
  826.     {
  827.         if(div === null) {
  828.             HACOOP.Browser.Log("div to attach reply frame to is null");
  829.             return;
  830.         }
  831.    
  832.         var iReplyFrame = document.createElement('iframe');
  833.         iReplyFrame.id = "reply" + id;
  834.         iReplyFrame.style.display = 'none';
  835.         iReplyFrame.style.width   = '100%';
  836.         iReplyFrame.style.height  = '38em';
  837.         divPost.setAttribute("replyFrameID", iReplyFrame.id);
  838.    
  839.         var divReply = document.createElement('div');
  840.         divReply.style.marginLeft = divPost.style.marginLeft;
  841.         divReply.appendChild(iReplyFrame);
  842.    
  843.         div.appendChild(divReply);
  844.     }
  845.    
  846.     // parse stuff directly out of the HTML
  847.     function parsePostingPageHTML(html) {
  848.        
  849.         // pre-check the posting for performance issues
  850.         // else match can lock up
  851.         if(html.indexOf('<div class="posting_date">') === -1 ||
  852.            html.indexOf('<div class="tovote_links">') === -1) {
  853.             HACOOP.Browser.Log("No known posting:");
  854.             HACOOP.Browser.Log(html);
  855.             return null;
  856.         }
  857.            
  858.         var mtchs = html.match(HACOOP.Heise.RegExps.PostingBody);
  859.         if(mtchs === null) {
  860.             HACOOP.Browser.Log("Could not parse posting.");
  861.             return null;
  862.         }
  863.         var postHtml = mtchs[1];
  864.         if(!postHtml) {
  865.             HACOOP.Browser.Log("Could not parse posting (2).");
  866.             return null;
  867.         }
  868.        
  869.         var replyLinks = html.match(/<a href="([^"]*)"\s*>Beantworten<\/a>/);
  870.         var replyLink  = (replyLinks !== null) ? replyLinks[1] : "";
  871.         var editLinks  = html.match(/<a href="([^"]*)"[^>]*>\s*Editieren<\/a>/);
  872.         var editLink   = (editLinks !== null) ? editLinks[1] : "";
  873.        
  874.         replyLink = HACOOP.Page.EnsureAbsoluteUrl(replyLink);
  875.         editLink  = HACOOP.Page.EnsureAbsoluteUrl(editLink);
  876.        
  877.         return {
  878.             PostHTML: postHtml,
  879.             ReplyLink: replyLink,
  880.             EditLink: editLink
  881.         };
  882.     }
  883.    
  884.     function callbackThread(url, html, args)
  885.     {
  886.         HACOOP.Browser.Log('Processing post from ' + url);
  887.        
  888.         var pageInfo;
  889.         try
  890.         {
  891.             pageInfo = parsePostingPageHTML(html);
  892.             if(!pageInfo)
  893.             {
  894.                 HACOOP.Browser.Log('Error processing post HTML: No page info.');
  895.                 return;
  896.             }
  897.         }
  898.         catch(e)
  899.         {
  900.             HACOOP.Browser.Log('Error processing post HTML: ' + e);
  901.             return;
  902.         }
  903.    
  904.         try
  905.         {
  906.             // insert the relevant part into the DOM
  907.             args.Div.innerHTML = pageInfo.PostHTML;
  908.    
  909.             // manipulate it further
  910.             processMessageDiv(args.Div, url, pageInfo);
  911.         }
  912.         catch(ex)
  913.         {
  914.             HACOOP.Browser.Log('Error processing post DOM: ' + ex);
  915.         }
  916.     }
  917.    
  918.    
  919.     function collapseBranch() {
  920.         var button = $(this);
  921.         var branch;
  922.        
  923.         if(button.attr('class') === 'treeCollapseButton') {
  924.             var hid = button.next().attr('HACOOP-id');
  925.             branch = $('div.HACOOP-posting[HACOOP-id="'+hid+'"]').closest('div.HACOOP-branch');
  926.         }
  927.         else {
  928.             branch = button.closest('div.HACOOP-branch');
  929.         }
  930.        
  931.         if(branch.attr('collapsed')) {
  932.             branch.height('');
  933.             branch.attr('collapsed', '');
  934.             button.attr('src', HACOOP.Images.BranchOpt_Collapse);
  935.         }
  936.         else {
  937.             branch.css('overflow', 'hidden');
  938.             branch.height('4em');
  939.             branch.attr('collapsed', '1');
  940.             button.attr('src', HACOOP.Images.BranchOpt_Expand);
  941.         }
  942.     }
  943.    
  944.     function floatThreadTree() {
  945.        
  946.         function getCentermostPosting() {
  947.             function getYDistanceFromCenter(obj) {
  948.                 if($(obj).parents('div.HACOOP-branch[collapsed="1"]').length) {
  949.                     return 10000000;
  950.                 }
  951.                
  952.                 var winMidY = $(window).scrollTop() + $(window).height() / 2;
  953.                 var objMidY = $(obj).position().top +    $(obj).height() / 2;
  954.                 return Math.abs(winMidY - objMidY);
  955.             }
  956.             function comparator(a, b) {
  957.               return getYDistanceFromCenter(a) - getYDistanceFromCenter(b);
  958.             }
  959.             return $(".HACOOP-posting").sort(comparator)[0];
  960.         }
  961.        
  962.         // move the content to the left
  963.         var mainContainer = $('#container');
  964.         mainContainer.css('margin', '0');
  965.        
  966.         // add a container for the thread tree
  967.         var treeContainerObj = document.createElement('div');
  968.         treeContainerObj.id = "treeContainer";
  969.         document.body.appendChild(treeContainerObj);
  970.        
  971.        
  972.         var contentContainer = $('#container_content');
  973.        
  974.         var treeContainer = $('#treeContainer');
  975.         treeContainer.css('background-color', 'white');
  976.         treeContainer.css('position', 'absolute');
  977.         treeContainer.css('left', contentContainer.width());
  978.  
  979.         // put the thread tree in a div and move it right
  980.         var threadTree = $('ul.thread_tree');
  981.         threadTree.appendTo(treeContainer);
  982.        
  983.         // click on a topic = scroll to it
  984.         threadTree.find('div.thread_title').click(function(){
  985.             var id = $(this).closest('div').attr('hacoop-id');
  986.             var postingDiv = $('.HACOOP-posting[hacoop-id="'+id+'"]');
  987.             if(postingDiv) {
  988.                 $(window).scrollTop(postingDiv.position().top);
  989.                 return false;
  990.             }
  991.             return true;
  992.         });
  993.         // doubleclick on a topic = open it regularly
  994.         threadTree.find('a').dblclick(function(){
  995.             window.location.href = $(this).attr('href');
  996.             return false;
  997.         });
  998.        
  999.         function updateFloatTreePosition() {
  1000.             var centerPost = getCentermostPosting();
  1001.             var id = centerPost.getAttribute('HACOOP-id');
  1002.  
  1003.             var nonActiveTreePosts = treeContainer.find('div.thread_title[HACOOP-id!="'+id+'"]');
  1004.             nonActiveTreePosts.each(function(i) {
  1005.                $(this).css("background", "none");
  1006.             });
  1007.             var activeTreePost = treeContainer.find('div.thread_title[HACOOP-id="'+id+'"]');
  1008.             if(activeTreePost.length === 1) {
  1009.                 activeTreePost.css("background-color", "#FFFFBB");
  1010.             }
  1011.            
  1012.             var topOffset;
  1013.             if(treeContainer.outerHeight() > $(window).height()) {
  1014.                 topOffset = $(window).height() / 2 - activeTreePost.position().top;
  1015.             }
  1016.             else {
  1017.                 topOffset = ($(window).height() - treeContainer.outerHeight()) / 2;
  1018.             }
  1019.             treeContainer.css('top', $(window).scrollTop() + topOffset);
  1020.         }
  1021.        
  1022.         $(window).scroll(updateFloatTreePosition);
  1023.         $(window).resize(updateFloatTreePosition);
  1024.        
  1025.         // show collapse buttons
  1026.         var baseLevel = countLevel($('div.thread_title[HACOOP-id="0"]', threadTree)[0]);
  1027.        
  1028.         $('div.thread_title', threadTree).each(function() {
  1029.             var line = $(this);
  1030.             //if(!line.attr('HACOOP-id') || countLevel(line[0]) != 1 + baseLevel) {
  1031.             //    return;
  1032.             //}
  1033.            
  1034.             var collapseImgObj = document.createElement('img');
  1035.             collapseImgObj.setAttribute('class', 'treeCollapseButton');
  1036.             collapseImgObj.setAttribute('src', HACOOP.Images.BranchOpt_Collapse);
  1037.             document.body.appendChild(collapseImgObj);
  1038.            
  1039.             line.before(collapseImgObj);
  1040.             $(collapseImgObj).click(collapseBranch);
  1041.         });
  1042.        
  1043.         updateFloatTreePosition();
  1044.     }
  1045.    
  1046.     function doit() {
  1047.        
  1048.         var divStack = [];
  1049.         divStack.peek = function() { return this[this.length - 1]; };
  1050.        
  1051.         var threadsList = $('ul.thread_tree');
  1052.         if(!threadsList.length) {
  1053.             HACOOP.Browser.Log("Thread tree not found - activate it!");
  1054.             return;
  1055.         }
  1056.    
  1057.         // create a container for all the replies
  1058.         var repliesDiv = document.createElement('div');
  1059.         repliesDiv.id = "repliesContainer";
  1060.         threadsList[0].parentNode.insertBefore(repliesDiv, threadsList[0]);
  1061.         divStack.push(repliesDiv);
  1062.        
  1063.         var threadMsgs = $('div.thread_title', threadsList);
  1064.         var maxJoinCnt  = Math.min(threadMsgs.length, HACOOP.Settings.MaxJoinedPostsInThread);
  1065.         var afterActive = 0;
  1066.         var cntJoined   = 0;
  1067.         var rootAbsoluteDepth = 0;
  1068.        
  1069.         for(var i = 0; i < threadMsgs.length && cntJoined <= maxJoinCnt; i++) {
  1070.             var msgDiv   = threadMsgs[i];
  1071.             var isActive = 0;
  1072.    
  1073.             // search the currently selected beitrag
  1074.             if(msgDiv.innerHTML.match("beitrag_aktiv") ||
  1075.                msgDiv.innerHTML.match("active_post")) {
  1076.                 afterActive = 1;
  1077.                 isActive = 1;
  1078.                 rootAbsoluteDepth = countLevel(msgDiv);
  1079.                 msgDiv.setAttribute("HACOOP-id", 0);
  1080.                 continue;
  1081.             }
  1082.             else if(afterActive === 0) { continue; }
  1083.            
  1084.             // count the number of next_levels upwards
  1085.             var curRelativeDepth = countLevel(msgDiv) - rootAbsoluteDepth;
  1086.    
  1087.             // only show current subnode
  1088.             if(curRelativeDepth <= 0) { break; }
  1089.    
  1090.             // find the URL
  1091.             var url = HACOOP.Heise.GetTitleLinkURL(msgDiv.innerHTML);
  1092.             if(!url || url.match(/^\s*$/)) {
  1093.                 HACOOP.Browser.Log("Error parsing: " + msgDiv.innerHTML);
  1094.                 continue;
  1095.             }
  1096.             HACOOP.Browser.Log("URL: " + url);
  1097.    
  1098.             // create div for the branch
  1099.             var divBranch = document.createElement('div');
  1100.             if(!isActive) {
  1101.                 divBranch.style.marginLeft = "20px";
  1102.             }
  1103.            
  1104.             $(divBranch).addClass('HACOOP-branch');
  1105.            
  1106.             divBranch.style.borderLeft = HACOOP.Settings.BranchBorderStyle + " " + HACOOP.Settings.GetBranchColor(curRelativeDepth);
  1107.    
  1108.             divBranch.innerHTML = '<div class="HACOOP-branchOptions">' +
  1109.                 '<img class="collapseBranchButton" src="'+HACOOP.Images.BranchOpt_Collapse+'" title="collapse/expand branch" />'+
  1110.                 '</div>';
  1111.                
  1112.             $('.collapseBranchButton', divBranch).click(collapseBranch);
  1113.    
  1114.             // create div for the post
  1115.             var divPost = document.createElement('div');
  1116.            
  1117.             $(divPost).addClass('HACOOP-posting');
  1118.             divPost.style.border = "1px dashed #DDDDDD";
  1119.             divPost.style.marginLeft = "8px"; // some space from the border
  1120.            
  1121.             divPost.setAttribute("HACOOP-id", i);
  1122.             msgDiv.setAttribute("HACOOP-id", i);
  1123.    
  1124.             // decend down to current level
  1125.             while(curRelativeDepth < divStack.length) { divStack.pop(); }
  1126.    
  1127.             // add it
  1128.             divStack.peek().appendChild(divBranch);
  1129.             divBranch.appendChild(divPost);
  1130.    
  1131.             // create the ireply frame
  1132.             appendReplyFrame(divBranch, cntJoined, divPost);
  1133.    
  1134.             // remember current branch
  1135.             divStack.push(divBranch);
  1136.    
  1137.             // grep it
  1138.             HACOOP.Browser.AjaxRequest(url, callbackThread, { Nr: i, Div: divPost});
  1139.    
  1140.             cntJoined++;
  1141.         }
  1142.     }
  1143.    
  1144.     function joinThreadPosts()
  1145.     {
  1146.         var rootPostDiv = HACOOP.Helpers.GetFirstMatching([
  1147.             'div.forum_content',
  1148.             '#mitte_forum',
  1149.             '#mitte',
  1150.             'td.f-content'
  1151.         ]);
  1152.         if(rootPostDiv === null) {
  1153.             HACOOP.Browser.Log("Root div not found!");
  1154.             return;
  1155.         }
  1156.        
  1157.         var rootPostText = rootPostDiv.children('p.posting_text');
  1158.         appendReplyFrame(rootPostText[0], -1, rootPostDiv[0]);
  1159.    
  1160.         rootPostText.attr("HACOOP-id", 0);
  1161.         $(rootPostText).addClass('HACOOP-posting');
  1162.    
  1163.         var pageInfo = parsePostingPageHTML(rootPostDiv[0].innerHTML);
  1164.         processMessageDiv(rootPostDiv[0], document.location.href, pageInfo);
  1165.    
  1166.         doit();
  1167.        
  1168.         if(HACOOP.DB.Settings.Get().chkFloatThreadTree) {
  1169.             floatThreadTree();
  1170.         }
  1171.     }
  1172.    
  1173.     function addHelperScripts() {
  1174.        
  1175.         // iReply
  1176.         HACOOP.Page.Scripts.AddByCode(
  1177.         'function iReply(frameId, replyUrl) {' +
  1178.             'var frm = document.getElementById(frameId);' +
  1179.             'frm.src = replyUrl;' +
  1180.             'frm.style.display = ""' +
  1181.         '}');
  1182.        
  1183.         // QuickVote
  1184.         HACOOP.Page.Scripts.AddByCode(
  1185.         'function sendVote(target, voteUrl) {' +
  1186.             'var xmlHttp = new XMLHttpRequest();' +
  1187.             'xmlHttp.open(\'GET\', voteUrl, true);' +
  1188.             'xmlHttp.send(null);' +
  1189.             'target.style.backgroundColor = "yellow";' +
  1190.         '}');
  1191.     }
  1192.    
  1193.     function onPageLoad() {
  1194.         addHelperScripts();
  1195.         joinThreadPosts();
  1196.     }
  1197.    
  1198.     return {
  1199.         Name: 'Thread Post Page',
  1200.         ContentMatch: function(html) { return false; },
  1201.         RegExp: /\/foren\/S-.*\/forum-\d+\/msg-\d+\/read\//i,
  1202.         OnPageLoad: onPageLoad
  1203.     };
  1204. })();
  1205.  
  1206.  
  1207.  
  1208. // http://www.heise.de/newsticker/foren/S-Gespraeche-ueber-freiwillige-Two-Strikes-Regelung-gescheitert/forum-224299/list/
  1209. // http://www.heise.de/mobil/artikel/foren/
  1210. // http://www.heise.de/ix/foren/
  1211. // http://www.heise.de/ix/foren/S-Steter-Strom/forum-222619/list/
  1212. // http://www.heise.de/ix/foren/S-Ausgangskontrolle/forum-222616/list/
  1213. // http://www.heise.de/tp/foren/
  1214. // http://www.heise.de/foto/foren/llist/
  1215. // http://www.heise.de/foto/foren/llist/hs-16/
  1216. // http://www.heise.de/foto/foren/S-Kaufberatung/forum-134019/list/hs-48/
  1217. // http://www.heise.de/foto/foren/
  1218. // http://www.heise.de/developer/foren/S-Ajax/forum-146167/list/
  1219. // http://www.heise.de/tp/foren/S-Studie-zur-Wirkung-des-Nichtraucherschutzesgesetzes-in-Deutschland-ist-fragwuerdig/forum-224387/list/
  1220. // http://www.heise.de/developer/artikel/foren/
  1221. // http://www.heise.de/developer/artikel/foren/hs-16/
  1222. // http://www.heise.de/developer/artikel/foren/S-Ein-erster-Entwicklerblick-auf-die-Windows-8-Consumer-Preview/forum-223899/list/c-all/hs-0/
  1223. HACOOP.Heise.PageTypes.BoardOverview = (function() {
  1224.    
  1225.     function addPosterScores(root) {
  1226.         $('div.thread_user', root).each(function(index, element) {
  1227.             var imgCode = HACOOP.Heise.Urls.GetPosterScoreBarCode(
  1228.                 HACOOP.Helpers.Trim(element.innerHTML));
  1229.             if(imgCode !== null) {
  1230.                 element.innerHTML += "&nbsp;&nbsp;"+imgCode;
  1231.             }
  1232.         });
  1233.     }
  1234.  
  1235.     function callbackOverviewPage(url, txt, startli)
  1236.     {
  1237.         var matches = txt.match(/<ul class=\"(thread_tree|fora_list)\">([\s\S]*)<\/ul>[\s\S]*?<ul class="forum_navi">/i);
  1238.         if(!matches) {
  1239.             startli.innerHTML = "<b><i>Fehler beim Laden</i></b>";
  1240.             return;
  1241.         }
  1242.    
  1243.         var lis = matches[2];
  1244.         lis = lis.replace(/\/read(?!\/showthread-1)/g, "/read/showthread-1");
  1245.         startli.innerHTML = "<ul style=\"padding-left: 0px; list-style-type: none\">" + lis + "</ul>";
  1246.    
  1247.         addPosterScores(startli);
  1248.     }
  1249.    
  1250.     function extractPostStart(url)
  1251.     {
  1252.         var matches = url.match(/\/hs-(\d+)/);
  1253.         if(!matches) {
  1254.             return -1;
  1255.         }
  1256.         return parseInt(matches[1], 10);
  1257.     }
  1258.     function insertPostStart(url, nr)
  1259.     {
  1260.         var eall = "";
  1261.         if(document.location.href.match(/\/e-all/)) {
  1262.             eall = "/e-all";
  1263.         }
  1264.         return url.replace(/(\/(list|foren)\/hs)-\d+/, "$1-" + nr + eall);
  1265.     }
  1266.     function getFirstLastPageNr() {
  1267.        
  1268.         // find the first and last of the page URLs
  1269.         var nextPageURL = "", lastPageURL  = "";
  1270.        
  1271.         var pageLinks = $('ul.forum_navi li');
  1272.         for(var i = 0; i < pageLinks.length; i++) {
  1273.             var pageLink = pageLinks[i];
  1274.    
  1275.             if(pageLink.innerHTML.match(/>Neuere</)) { break; }
  1276.             // plain number, no link => current page, start counting
  1277.             if(pageLink.innerHTML.match(/^\d+$/))    { nextPageURL = ""; }
  1278.    
  1279.             // find the URL
  1280.             var url = HACOOP.Heise.GetTitleLinkURL(pageLink.innerHTML);
  1281.             if(!url) { continue; }
  1282.    
  1283.             nextPageURL = nextPageURL || url;
  1284.             lastPageURL = url;
  1285.         }
  1286.         if(!nextPageURL || !lastPageURL) {
  1287.             HACOOP.Browser.Log("found no page URLs");
  1288.             return null;
  1289.         }
  1290.    
  1291.         // extract the post numbers
  1292.         var nextPostNr = extractPostStart(nextPageURL);
  1293.         var lastPostNr = extractPostStart(lastPageURL);
  1294.         if(nextPostNr < 0 || lastPostNr < 0) {
  1295.             HACOOP.Browser.Log("found no post numbers");
  1296.             return null;
  1297.         }
  1298.        
  1299.         return {
  1300.             NextPostNr: nextPostNr,
  1301.             LastPostNr: lastPostNr,
  1302.             NextURL: nextPageURL
  1303.         };
  1304.     }
  1305.    
  1306.     function onPageLoad() {
  1307.        
  1308.         // add poster scores
  1309.         addPosterScores(document);
  1310.        
  1311.         // get page bounds
  1312.         var pageBounds = getFirstLastPageNr();
  1313.         if(pageBounds === null) {
  1314.             return;
  1315.         }
  1316.        
  1317.         // limit pages to users setting
  1318.         var limited = false;
  1319.         if(pageBounds.LastPostNr - pageBounds.NextPostNr > HACOOP.Settings.MaxJoinedPosts) {
  1320.             pageBounds.LastPostNr = pageBounds.NextPostNr + HACOOP.Settings.MaxJoinedPosts;
  1321.             limited = true;
  1322.         }
  1323.    
  1324.         // add list items and load the overview pages into them
  1325.         var threadTrees = HACOOP.Helpers.GetFirstMatching(['ul.thread_tree', 'ul.fora_list']);
  1326.         if(!threadTrees) {
  1327.             HACOOP.Browser.Log("no forum_navi or thread_tree");
  1328.             return;
  1329.         }
  1330.         var threadTree = threadTrees[0];
  1331.    
  1332.         for(var j = pageBounds.NextPostNr;
  1333.             j <= pageBounds.LastPostNr;
  1334.             j += HACOOP.Settings.OverviewPagePostCount)
  1335.         {
  1336.             var srcUrl = HACOOP.Page.EnsureAbsoluteUrl(insertPostStart(pageBounds.NextURL, j));
  1337.    
  1338.             // append a placeholder
  1339.             var li = document.createElement('li');
  1340.             li.innerHTML = "<b>Beitr&auml;ge ab Nr. " + j + "</b>";
  1341.             threadTree.appendChild(li);
  1342.    
  1343.             li = document.createElement('li');
  1344.             li.innerHTML = "<i>Lade...</i>";
  1345.             threadTree.appendChild(li);
  1346.    
  1347.             // request the content
  1348.             HACOOP.Browser.AjaxRequest(srcUrl, callbackOverviewPage, li);
  1349.         }
  1350.    
  1351.         // add links to navigate
  1352.         if(pageBounds.NextPostNr > HACOOP.Settings.OverviewPagePostCount) {
  1353.             var li_bck = document.createElement('li');
  1354.             li_bck.innerHTML = "<a href=\"" + HACOOP.Page.EnsureAbsoluteUrl(insertPostStart(pageBounds.NextURL, pageBounds.NextPostNr - HACOOP.Settings.MaxJoinedPosts)) + "\"><b>Zurück...</b></a>";
  1355.             threadTree.insertBefore(li_bck, threadTree.childNodes[0]);
  1356.         }
  1357.         if(limited) {
  1358.             var li_fwd = document.createElement('li');
  1359.             li_fwd.innerHTML = "<a href=\"" + HACOOP.Page.EnsureAbsoluteUrl(insertPostStart(pageBounds.NextURL, pageBounds.LastPostNr + HACOOP.Settings.OverviewPagePostCount)) + "\"><b>Weiter...</b></a>";
  1360.             threadTree.appendChild(li_fwd);
  1361.         }
  1362.     }
  1363.    
  1364.     return {
  1365.         Name: 'Board Overview',
  1366.         ContentMatch: function(html) { return false; },
  1367.         RegExp: /\/foren\//i,
  1368.         OnPageLoad: onPageLoad
  1369.     };
  1370. })();
  1371.  
  1372.  
  1373. HACOOP.Page.Styles.Add(
  1374.     "img.treeCollapseButton { cursor: pointer; float:left; height: 1em; } " +
  1375.     "div.HACOOP-branchOptions { float: left; }" +
  1376.     "div.HACOOP-branchOptions img { cursor: pointer; width: 1.2em !important; }" +
  1377.     "span.addUserTagButton { cursor: pointer; border: 1px solid black; background-color: #EEDDAA; }" +
  1378.     "div.userTagsContainer { display: inline; border: 1px solid #DDFFFF; background-color: #EFFFFF; }" +
  1379.     "div.userTag { display: inline; margin-left: 1.0em; font-size: 80%; }" +
  1380.     "div.userTag span.positive { color: green; }" +
  1381.     "div.userTag span.negative { color: red; }" +
  1382.     "div.remarkTag span { color: gray !important; }" +
  1383.     "span.tagScore { margin-left: 0.5em; font-weight: bold;  }" +
  1384.     "div.dropDown { position: absolute; background-color: white; border: 2px solid #FFEEAA; padding: 0 1em 0 1em; }" +
  1385.     "div.dropDown ul { font-size: 80%; padding: 0em; } " +
  1386.     "div.dropDown ul li { cursor: pointer; } " +
  1387.     "div.dropDown ul li.special { cursor: pointer; font-weight: bold; } "
  1388.     );
  1389.  
  1390. // menu
  1391. HACOOP.Page.Styles.Add("@media print { #HACOOPmenuButton { display: none; } }");
  1392. HACOOP.Page.Styles.Add(
  1393.     "#HACOOPmenuButton { background-color: #eeeeee; font-size: 70%; position: fixed; top: 50px; right: -0.5em; border-bottom: 0; padding: 6px 0 6px 0; }" +
  1394.     "#HACOOPoptionDialog { position: fixed; top: 80px; right: 0; z-index: 1001; }" +
  1395.     ".ghhpane { position: absolute; color: #333333; background-color: #fcfcfc; border: 1px solid #cccccc; -moz-border-radius: 4px; padding: 0.25em 1.5em; font-size: 13px; display: none} " +
  1396.     "#tabStripDiv {margin:0 -1.5em;padding:0 3px 0 8px;border-bottom:1px solid #ccc;} #tabStrip { padding-bottom:0;} " +
  1397.     "#tabStrip button{color:#555;background-color:#f5f5f5;margin:0 2px 0 0;border:1px solid #ccc;padding:1px 2px;height:22px;-moz-border-radius:2px;} " +
  1398.     "#tabStrip .currentTab { background-color: #fcfcfc; border-bottom-color: #fcfcfc; }" +
  1399.     ".HACOOPmenuTab { margin-top: 1em; height: 19em; overflow-y: scroll; border: 1px solid #333; display: none; } " +
  1400.     "#tabContainer div.currentTab { display: block !important; } "
  1401.     );
  1402.  
  1403.  
  1404. HACOOP.Page.Styles.Add("div.ghhider{color:#888;} div.ghhider:hover{background-color:#eee;} " +
  1405.   "button.ghhider{color:#555;background-color:#fcfcfc;font-size:0.85em;margin:auto 2px;border:1px solid #ccc;-moz-border-radius:4px;padding:2px 3px;} " +
  1406.   "button.ghhider:hover{color:#000;background:#ff8;} .ghh1time{background:#eee;} .inlinediv{display:inline;} " +
  1407.   ".ghhbtn{color:#555;background-color:#f0f0f0;margin:auto 2px;border:1px solid #ccc;-moz-border-radius:4px;padding:2px 3px;} " +
  1408.   ".ghhbtn:hover{color:#000;background:#ff8;} " +
  1409.   ".ghhhost{display:block;padding:0 0.25em;cursor:pointer;} #ghhutil{text-align:center;margin:0.5em 0 1em 0;border:1px solid #ccc;-moz-border-radius:4px;padding:3px 0;} " +
  1410.   ".ghhinfo{font-size:12px;line-height:9px;position:absolute;top:0;right:0;z-index:1001;border:4px solid transparent;-moz-border-radius-bottomleft:8px;-moz-border-radius-topleft:8px;margin-top:1px;padding-left:1px} " +
  1411.   ".ghhdel{text-decoration:line-through;color:#333;} .ghhpb{text-decoration:none;color:#f00;} " +
  1412.   ".ghhblk{text-decoration:none;color:#333;} .ghhd{position:relative;line-height:1.2em;cursor:pointer;} " +
  1413.   ".ghhindent{position:absolute;left:350px;top:-3px;} #btnedit p{margin:2px 4px 4px 4px;} #ghhblockform input[type='radio'], #HACOOPoptionDialog input[type='radio']{vertical-align:bottom;margin-top:5px;margin-bottom:1px} " +
  1414.   ".ghhtbl{border:1px solid black;border-collapse:collapse} .ghhtbl td, .ghhtbl th{border:1px solid black;padding:2px 4px;} ");
  1415.  
  1416. function readSettingsFromForm() {
  1417.     var settings = {};
  1418.    
  1419.     settings['HACOOPokvPassword'] = $('#HACOOPokvPassword').val();
  1420.     settings['chkSaveRegularly'] = $('#chkSaveRegularly').attr('checked');
  1421.    
  1422.     settings['chkShowUserTags'] = $('#chkShowUserTags').attr('checked');
  1423.     settings['chkFloatThreadTree'] = $('#chkFloatThreadTree').attr('checked');
  1424.    
  1425.     HACOOP.Browser.Log("Settings from form:", settings);
  1426.     return settings;
  1427. }
  1428. function showSettingsInForm(settings) {
  1429.    
  1430.     HACOOP.Browser.Log("Settings to form:", settings);
  1431.     if(!settings) {
  1432.         return;
  1433.     }
  1434.    
  1435.     $('#HACOOPokvPassword').val(settings['HACOOPokvPassword']);
  1436.     $('#chkSaveRegularly').attr('checked', settings['chkSaveRegularly']);
  1437.    
  1438.     $('#chkShowUserTags').attr('checked', settings['chkShowUserTags']);
  1439.     $('#chkFloatThreadTree').attr('checked', settings['chkFloatThreadTree']);
  1440. }
  1441.  
  1442. function showManageForm(e) {
  1443.  
  1444.     var optionDialogs = $("#HACOOPoptionDialog");
  1445.     if (!optionDialogs.length) {
  1446.         addOptionDialog();
  1447.     }
  1448.     showSettingsInForm(HACOOP.DB.Settings.Get());
  1449.    
  1450.     $('#HACOOPoptionDialog').css('display', 'block');
  1451. }
  1452.  
  1453.    
  1454. function addOptionDialog() {
  1455.  
  1456.     function tbtChangeTab() {
  1457.         var tbt = $(this);
  1458.         var tabid = tbt.attr('data-tabid');
  1459.        
  1460.         var currentTab = $('#tabContainer div.currentTab');
  1461.         currentTab.removeClass("currentTab");
  1462.         var currentTabBtn = $('#tabStrip button.currentTab');
  1463.         currentTabBtn.removeClass("currentTab");
  1464.        
  1465.         var newTab = $('#'+tabid);
  1466.         newTab.addClass("currentTab");
  1467.         tbt.addClass("currentTab");
  1468.     }
  1469.     function getSettings() {
  1470.         var settings = readSettingsFromForm();
  1471.         HACOOP.DB.Settings.Set(settings);
  1472.         return settings;
  1473.     }
  1474.     function btLoadClicked() {
  1475.         var settings = getSettings();
  1476.         HACOOP.DB.LoadFromRemote(settings.HACOOPokvPassword, function(o) {
  1477.             if(!o) {
  1478.                 alert('Error retrieving! :(');
  1479.             }
  1480.             else {
  1481.                 showSettingsInForm(HACOOP.DB.Settings.Get());
  1482.             }
  1483.         });
  1484.     }
  1485.     function btSaveClicked() {
  1486.         var settings = getSettings();
  1487.         HACOOP.DB.SaveToRemote(settings.HACOOPokvPassword);
  1488.     }
  1489.     function btCloseClicked() {
  1490.         getSettings();
  1491.         $('#HACOOPoptionDialog').css('display', 'none');
  1492.     }
  1493.  
  1494.     var mfd = document.createElement("div");
  1495.  
  1496.     mfd.id = "HACOOPoptionDialog";
  1497.     mfd.className = "ghhpane";
  1498.     mfd.setAttribute("style", "display: none;");
  1499.  
  1500.     mfd.innerHTML =
  1501. '    <form onsubmit="return false;">' +
  1502. '      <div id="tabStripDiv">' +
  1503. '        <p id="tabStrip" style="margin:6px 0 -1px 0">' +
  1504. '            <b>HACOOP</b>' +
  1505. '            <button title="Saving stuff online." data-tabid="tabSync" class="tabButton currentTab" id="tbtRemote">Sync</button>' +
  1506. '            <button title="Settings" data-tabid="tabOptions" class="tabButton" id="tbtOptions">Options</button>' +
  1507. '        </p>' +
  1508. '      </div>' +
  1509. '' +
  1510. '      <div style="width: 30em" id="tabContainer">' +
  1511. '        <div class="HACOOPmenuTab currentTab" id="tabSync">' +
  1512. '' +
  1513. '            <p style="padding:0.25em; margin:0.25em">' +
  1514. '                Preferences and data are stored in the browsers <i>localStorage</i>.' +
  1515. '                This storage location can be cleared, just as other local stores (like cookies).' +
  1516. '            </p>' +
  1517. '            <p style="padding: 0.25em; margin: 0.25em">' +
  1518. '                You can load or save your HACOOP-specific data from/to <b>openkeyval.com</b>.' +
  1519. '                To do this, choose your private &quot;password&quot; and press <i>Load/Save</i>.' +
  1520. '            </p>' +
  1521. '' +
  1522. '            <p style="border-top:1px solid #000; padding:0.45em; margin:0.85em">' +
  1523. '                Prefix (Password): <input id="HACOOPokvPassword" type="text" /><br>' +
  1524. '                <input type="checkbox" id="chkSaveRegularly" name="chk1click">save regularly<br>' +
  1525. '' +
  1526. '                <button id="btLoad">Load</button>' +
  1527. '                <button id="btSave">Save</button>' +
  1528. '            </p>' +
  1529. '        </div>' +
  1530. '' +
  1531. '        <div class="HACOOPmenuTab" id="tabOptions">' +
  1532. '            <p style="padding: 0.25em; margin: 0.25em">' +
  1533. '               <input type="checkbox" id="chkShowUserTags">Show user tags.<br>' +
  1534. '               <input type="checkbox" id="chkFloatThreadTree">Float thread tree.<br>' +
  1535. '           </p>' +
  1536. '        </div>' +
  1537. '      </div>' +
  1538. '' +
  1539. '      <p style="text-align: right;">' +
  1540. '          <button id="btClose">OK</button>' +
  1541. '      </p>' +
  1542. '    </form>'
  1543. ;
  1544.  
  1545.   document.body.appendChild(mfd);
  1546.  
  1547.  
  1548.   $('#btLoad').click(btLoadClicked);
  1549.   $('#btSave').click(btSaveClicked);
  1550.   $('#btClose').click(btCloseClicked);
  1551.  
  1552.   $('button.tabButton').click(tbtChangeTab);
  1553. }
  1554.  
  1555. function addMenuButton()
  1556. {
  1557.     var mbtn = document.createElement("button");
  1558.      
  1559.     mbtn.id = "HACOOPmenuButton";
  1560.     mbtn.setAttribute("title", "Manage Heise ACOOP Settings");
  1561.     mbtn.addEventListener("click", showManageForm, true);
  1562.     mbtn.appendChild(document.createTextNode("ACOOP"));
  1563.      
  1564.     document.body.appendChild(mbtn);
  1565. }
  1566.  
  1567. HACOOP.Menu = (function() {
  1568.    
  1569.     function initMenu() {
  1570.         addMenuButton();
  1571.     }
  1572.    
  1573.     return {
  1574.         Init: initMenu
  1575.     };
  1576. })();
  1577.  
  1578. HACOOP.Main = function() {
  1579.    
  1580.     HACOOP.Browser.Log('HACOOP Startup!');
  1581.     function backgroundClick() {
  1582.         $('div.dropDown').remove();
  1583.     }
  1584.    
  1585.     this.Page.Scripts.AddByURL('http://cdn.openkeyval.org/statics/openkeyval.packed.js');
  1586.    
  1587.     this.Heise.CurrentPageType = this.Heise.GetPageType(
  1588.         document.location.href, document.body.innerHTML);
  1589.     HACOOP.Browser.Log("Detected page type: " + (typeof this.Heise.CurrentPageType));
  1590.    
  1591.     this.Heise.EnsureShowThreadLinks();
  1592.     this.Menu.Init();
  1593.    
  1594.     $(document).click(backgroundClick);
  1595.    
  1596.     if(this.Heise.CurrentPageType === null) {
  1597.         alert("Unknown page type");
  1598.     }
  1599.     else {
  1600.         HACOOP.Browser.Log("Calling page's OnPageLoad");
  1601.         this.Heise.CurrentPageType.OnPageLoad();
  1602.     }
  1603. };
  1604.  
  1605. // let's do it!
  1606. HACOOP.Main();
  1607.  
  1608. // Heise comes with some antique jQuery we can use
  1609. }( window.HACOOP = window.HACOOP || {}, unsafeWindow.jQuery) );
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement