Show
Ignore:
Timestamp:
08/02/08 17:43:44 (4 months ago)
Author:
cho45
Message:
  • 展開を手動で継続できるように
  • TODO: 展開できない場合でも expand がつくのを修正
Files:
1 modified

Legend:

Unmodified
Added
Removed
  • lang/javascript/userscripts/hatena.haiku.expandrepliestree.user.js

    r16974 r16976  
    66// ==/UserScript== 
    77 
    8 (function (unsafeWindow) { with (D()) { 
     8location.href = "javascript:"+encodeURIComponent(uneval(function () { 
     9 
     10(function (Global) { with (D()) { 
    911        const WAIT  = 1; 
    10         const LEVEL = 9; 
     12        const LEVEL = 1; 
    1113        const ICON  = [ 
    1214                "data:image/png;base64,", 
     
    1820        ].join(""); 
    1921 
    20  
    21         GM_addStyle([ 
     22        var loading = false; 
     23 
     24 
     25        addStyle([ 
    2226                "div.info .expand a {", 
    2327                        "background: transparent url('",ICON,"') no-repeat scroll 0 65%;", 
     
    2529                        "margin: 0 0 0 3px", 
    2630                "}", 
    27         ].join("")); 
     31        ]); 
    2832 
    2933        function applyJavaScript (entry) { 
     
    3943                }); 
    4044 
    41                 unsafeWindow.Hatena.Haiku.Pager.dispatchEvent('loadedEntries', entry.parentNode.wrappedJSObject || entry.parentNode); 
     45                Global.Hatena.Haiku.Pager.dispatchEvent('loadedEntries', entry.parentNode.wrappedJSObject || entry.parentNode); 
    4246                return entry; 
    4347        } 
     
    4549        function expandReplies (entry, level) { 
    4650                if (typeof level == "undefined") level = LEVEL; 
    47                 if (level < 1) return next(); 
    4851                log("level:" + level); 
    49  
    50                 var a        = $X("./div[@class='list-body']/div/a[img[@src='/images/icon-replylink.gif']]", entry)[0]; 
    51                 var raw_href = $X("string(@href)", a); 
    52                 var es       = $X("//div[@class='entry' and .//span[@class='timestamp']/a[@href = '"+raw_href+"']]"); 
    53  
    54                 if (a)      a.style.opacity = "0.6"; 
    55  
    56                 $X(".//span[@class='expand']", entry).forEach(function (e) { 
    57                         e.innerHTML = "Loading..."; 
    58                 }); 
     52                if (entry._expanded) return next(); 
     53 
     54                if (level < 1) { 
     55                        // 深すぎるので、さらに展開するためのリンクをつけたうえで終了する。 
     56                        return next(function (i) { 
     57                                log("level too deep. Please click the link to expand more."); 
     58                                addExpandLink(entry, function () { 
     59                                        log("expand more -> child"); 
     60                                        return expandReplies(entry, LEVEL); 
     61                                }); 
     62                        }); 
     63                } 
     64 
     65                entry._expanded = true; 
     66 
    5967                return parallel([ 
    60                         expandChildReplies(entry, level - 1), 
    61                         !a ? next() : es.length ? appendAndLoadNext(es[0]) : xhttp.get(a.href).next(function (data) { 
     68                        expandChildReplies(entry, level), 
     69                        expandParent(entry, level) 
     70                ]); 
     71 
     72                function expandParent (entry, level) { 
     73                        log(["expandParent", entry, level]); 
     74 
     75                        var a        = $X("./div[@class='list-body']/div/a[img[@src='/images/icon-replylink.gif']]", entry)[0]; 
     76                        var raw_href = $X("string(@href)", a); 
     77                        var es       = $X("//div[@class='entry' and ./div/div/span[@class='timestamp']/a[@href = '"+raw_href+"']]"); 
     78 
     79                        if (a) a.style.opacity = "0.6"; 
     80 
     81                        return !a ? next() : es.length ? appendAndLoadNext(es[0]) : http.get(a.href).next(function (data) { 
    6282                                var t = h(data.responseText.replace(/<\/?(?:script|i?frame)[^>]*>/g, "")); 
    6383                                return parallel($X(".//div[@class='entries']/div[@class='entry']", t).map(appendAndLoadNext)); 
    64                         }), 
    65                 ]). 
    66                 next(function () { 
    67                         $X(".//span[@class='expand']", entry).forEach(function (e) { 
    68                                 e.parentNode.removeChild(e); 
    6984                        }); 
    70                 }); 
    71  
    72                 function appendAndLoadNext (e) { 
    73                         if (a) a.style.opacity = "0.2"; 
    74                         e = applyJavaScript(e); 
    75                         var body = $X("./div[@class='list-body']", e)[0]; 
    76                         entry.parentNode.insertBefore(e, entry); 
    77                         body.appendChild(entry); 
    78                         return wait(WAIT).next(function () { 
    79                                 return call(expandReplies, e, level - 1); 
    80                         }); 
     85 
     86                        function appendAndLoadNext (e) { 
     87                                if (e._expanded) return next(); 
     88 
     89                                if (a) a.style.opacity = "0.2"; 
     90                                e = applyJavaScript(e); 
     91                                var body = $X("./div[@class='list-body']", e)[0]; 
     92                                entry.parentNode.insertBefore(e, entry); 
     93                                body.appendChild(entry); 
     94                                return wait(WAIT).next(function () { 
     95                                        return call(expandReplies, e, level - 1); 
     96                                }); 
     97                        } 
    8198                } 
    8299 
    83100                function expandChildReplies (entry, level) { 
    84                         log("level:" + level); 
    85                         if (level < 1) return next(); 
     101                        log(["expandChildReplies", entry, level]); 
    86102 
    87103                        var body = $X("./div[@class='list-body']", entry)[0]; 
     
    98114                                        var es = $X("//div[@class='entry' and .//span[@class='timestamp']/a[@href = '"+raw_href+"']]"); 
    99115                                        if (es.length) return appendAndLoadNext(es[0]); 
    100                                         return xhttp.get(a.href).next(function (data) { 
     116                                        return http.get(a.href).next(function (data) { 
    101117                                                // remove unnecessary tags 
    102118                                                var t = h(data.responseText.replace(/<\/?(?:script|i?frame)[^>]*>/g, "")); 
     
    117133 
    118134                                                return wait(WAIT).next(function () { 
    119                                                         return call(expandChildReplies, e, level - 1); 
     135                                                        return call(expandReplies, e, level - 1); 
    120136                                                }); 
    121137                                        } 
     
    125141        } 
    126142 
     143        function addExpandLink (e, handler) { 
     144                if (e._gm_expandreplies_applied) return; 
     145                e._gm_expandreplies_applied = true; 
     146 
     147                var info = $X("./div/div[@class='info']", e)[0]; 
     148                var link = dom("<span class='expand'><a href='javascript:void(156)'>Expand</a></span>", info).root; 
     149                link.addEventListener("click", function () { 
     150                        if (window.getSelection().toString()) return; 
     151                        if (e._loading) return; 
     152                        e._loading = true; 
     153                        log("Loading..."); 
     154                        link.innerHTML = "loading"; 
     155                        handler().next(function () { 
     156                                link.parentNode.removeChild(link); 
     157                        }).error(function (e) { alert(e) }); 
     158                }, false); 
     159        } 
     160 
    127161        // observe pagerize 
    128162        var num = 0; 
    129163        next(function () { 
     164                if (loading) return wait(WAIT).next(arguments.callee); 
     165 
    130166                var n = $X("count(//div[@class='entries']/div[@class='entry'])"); 
    131167                if (num < n) { 
     
    133169                        loop(entries.length, function (n) { 
    134170                                var e = entries[n].wrappedJSObject || entries[n]; 
    135                                 if (e._gm_expandreplies_applied) return; 
    136                                 e._gm_expandreplies_applied = true; 
    137                                 var handler = function () { 
    138                                         if (window.getSelection().toString()) return; 
    139                                         if (e._loading) return; 
    140                                         e._loading = true; 
    141                                         log("Loading..."); 
    142                                         expandReplies(e).next(function () { 
     171                                addExpandLink(e, function () { 
     172                                        return expandReplies(e). 
     173                                        next(function () { 
    143174                                                if (log.element) { 
    144175                                                        log.element.parentNode.removeChild(log.element); 
    145176                                                        log.element = null; 
    146177                                                } 
    147                                         }).error(function (e) { 
     178                                        }). 
     179                                        error(function (e) { 
    148180                                                alert("Error"+e); 
    149181                                        }); 
    150                                 }; 
    151                                 // $X("div/div[@class='body']", e)[0].addEventListener("click", handler, false); 
    152  
    153                                 var info = $X("./div/div[@class='info']", e)[0]; 
    154                                 info.appendChild(h("<span class='expand'><a href='javascript:void(156)'>Expand</a></span>").firstChild); 
    155                                 info.addEventListener("click", handler, false); 
     182                                }); 
    156183                        }); 
    157184 
     
    174201} 
    175202 
     203function addStyle (a) { 
     204        dom("<style type='text/css'>#{css}</style>", document.body, { css : a.join("") }); 
     205} 
     206 
     207 
     208function dom (str, cur, txt) { 
     209        var t, ret = {}, stack = [cur = cur || document.createElement("div")]; 
     210        while (str.length) { 
     211                if (str.indexOf("<") == 0) { 
     212                        if (t = str.match(/^\s*<(\/?[^\s>\/]+)([^>]+?)?(\/)?>/)) { 
     213                                var tag = t[1], attrs = t[2], isempty = !!t[3]; 
     214                                if (tag.indexOf("/") == -1) { 
     215                                        child = document.createElement(tag); 
     216                                        if (attrs) attrs.replace(/([a-z]+)=(?:'([^']+)'|"([^"]+)")/gi, 
     217                                                function (m, name, v1, v2) { 
     218                                                        var v = text(v1 || v2); 
     219                                                        if (name == "class") ret[v] = child; 
     220                                                        child.setAttribute(name, v); 
     221                                                } 
     222                                        ); 
     223                                        cur.appendChild(ret.root ? child : (ret.root = child)); 
     224                                        if (!isempty) { 
     225                                                stack.push(cur); 
     226                                                cur = child; 
     227                                        } 
     228                                } else cur = stack.pop(); 
     229                        } else throw("Parse Error: " + str); 
     230                } else { 
     231                        if (t = str.match(/^([^<]+)/)) cur.appendChild(document.createTextNode(text(t[0]))); 
     232                } 
     233                str = str.substring(t[0].length); 
     234        } 
     235        return ret; 
     236 
     237        function text (str) { 
     238                return str 
     239                        .replace(/&(#(x)?)?([^;]+);/g, function (_, isNumRef, isHex, ref) { 
     240                                return isNumRef ? String.fromCharCode(parseInt(ref, isHex ? 16 : 10)): 
     241                                                  {"lt":"<","gt":"<","amp":"&"}[ref]; 
     242                        }) 
     243                        .replace(/#\{([^}]+)\}/, function (_, name) { 
     244                                return txt[name]; 
     245                        }); 
     246        } 
     247} 
     248 
     249 
    176250function log (m) { 
    177         unsafeWindow.console.log(uneval(m)); 
     251        Global.console.log(m); 
    178252 
    179253        if (!arguments.callee.element) { 
     
    263337 
    264338// Usage:: with (D()) { your code } 
    265 // JSDefeered 0.2.0 (c) Copyright (c) 2007 cho45 ( www.lowreal.net ) 
     339// JSDeferred 0.2.1 (c) Copyright (c) 2007 cho45 ( www.lowreal.net ) 
    266340// See http://coderepos.org/share/wiki/JSDeferred 
    267341function D () { 
     
    314388Deferred.parallel = function (dl) { 
    315389        var ret = new Deferred(), values = {}, num = 0; 
    316         for (var i in dl) { 
    317                 if (!dl.hasOwnProperty(i)) continue; 
     390        for (var i in dl) if (dl.hasOwnProperty(i)) { 
    318391                (function (d, i) { 
    319392                        d.next(function (v) { 
     
    333406        } 
    334407        if (!num) Deferred.next(function () { ret.call() }); 
     408        ret.canceller = function () { 
     409                for (var i in dl) if (dl.hasOwnProperty(i)) { 
     410                        dl[i].cancel(); 
     411                } 
     412        }; 
    335413        return ret; 
    336414}; 
     
    341419                clearTimeout(id); 
    342420                d.call((new Date).getTime() - t.getTime()); 
    343         }, n * 1000) 
    344         d.canceller   = function () { try { clearTimeout(id) } catch (e) {} }; 
     421        }, n * 1000); 
     422        d.canceller = function () { try { clearTimeout(id) } catch (e) {} }; 
    345423        return d; 
    346424}; 
     
    350428        var id = setTimeout(function () { clearTimeout(id); d.call() }, 0); 
    351429        if (fun) d.callback.ok = fun; 
    352         d.canceller   = function () { try { clearTimeout(id) } catch (e) {} }; 
     430        d.canceller = function () { try { clearTimeout(id) } catch (e) {} }; 
    353431        return d; 
    354432}; 
     
    365443        var o = { 
    366444                begin : n.begin || 0, 
    367                 end   : n.end   || (n - 1), 
     445                end   : (typeof n.end == "number") ? n.end : n - 1, 
    368446                step  : n.step  || 1, 
    369447                last  : false, 
     
    392470                        } 
    393471                } 
    394                 return Deferred.call(_loop, o.begin); 
     472                return (o.begin <= o.end) ? Deferred.call(_loop, o.begin) : null; 
    395473        }); 
    396474}; 
     
    436514                d.fail(res); 
    437515        }; 
    438         GM_xmlhttpRequest(opts); 
     516        setTimeout(function () { 
     517                GM_xmlhttpRequest(opts); 
     518        }, 0); 
    439519        return d; 
    440520} 
    441 xhttp.get  = function (url)       { return xhttp({method:"get", url:url}) } 
    442 xhttp.post = function (url, data) { return xhttp({method:"post", url:url, data:data, headers:{"Content-Type":"application/x-www-form-urlencoded"}}) } 
     521xhttp.get  = function (url)       { return xhttp({method:"get",  url:url}) }; 
     522xhttp.post = function (url, data) { return xhttp({method:"post", url:url, data:data, headers:{"Content-Type":"application/x-www-form-urlencoded"}}) }; 
    443523 
    444524 
     
    447527        var req = new XMLHttpRequest(); 
    448528        req.open(opts.method, opts.url, true); 
     529        if (opts.headers) { 
     530                for (var k in opts.headers) if (opts.headers.hasOwnProperty(k)) { 
     531                        req.setRequestHeader(k, opts.headers[k]); 
     532                } 
     533        } 
    449534        req.onreadystatechange = function () { 
    450535                if (req.readyState == 4) d.call(req); 
    451536        }; 
    452537        req.send(opts.data || null); 
     538        d.xhr = req; 
    453539        return d; 
    454540} 
    455 http.get  = function (url)       { return http({method:"get", url:url}) } 
    456 http.post = function (url, data) { return http({method:"post", url:url, data:data}) } 
     541http.get   = function (url)       { return http({method:"get",  url:url}) }; 
     542http.post  = function (url, data) { return http({method:"post", url:url, data:data, headers:{"Content-Type":"application/x-www-form-urlencoded"}}) }; 
     543http.jsonp = function (url, params) { 
     544        if (!params) params = {}; 
     545 
     546        var Global = (function () { return this })(); 
     547        var d = Deferred(); 
     548        var cbname = params["callback"]; 
     549        if (!cbname) do { 
     550                cbname = "callback" + String(Math.random()).slice(2); 
     551        } while (typeof(Global[cbname]) != "undefined"); 
     552 
     553        params["callback"] = cbname; 
     554 
     555        url += (url.indexOf("?") == -1) ? "?" : "&"; 
     556 
     557        for (var name in params) if (params.hasOwnProperty(name)) { 
     558                url = url + encodeURIComponent(name) + "=" + encodeURIComponent(params[name]) + "&"; 
     559        } 
     560 
     561        var script = document.createElement('script'); 
     562        script.type    = "text/javascript"; 
     563        script.charset = "utf-8"; 
     564        script.src     = url; 
     565        document.body.appendChild(script); 
     566 
     567        Global[cbname] = function callback (data) { 
     568                delete Global[cbname]; 
     569                document.body.removeChild(script); 
     570                d.call(data); 
     571        }; 
     572        return d; 
     573}; 
    457574 
    458575Deferred.Deferred = Deferred; 
     
    462579}// End of JSDeferred 
    463580 
    464 })(this.unsafeWindow || this); 
    465  
    466  
     581})(this); 
     582 
     583}))+"()";