Changeset 2051

Show
Ignore:
Timestamp:
11/27/07 13:38:12 (6 years ago)
Author:
cho45
Message:

lang/javascript/userscripts/jautopagerize.user.js:

siteinfo にアクセスできないように (XSS 対策)

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • lang/javascript/userscripts/jautopagerize.user.js

    r2050 r2051  
    1212// Latest::  http://svn.coderepos.org/share/lang/javascript/userscripts/jautopagerize.user.js 
    1313// License:: CCPL ( http://creativecommons.org/licenses/by/3.0/ ) 
    14  
    15 // define export object; 
     14(function (AutoPagerize) { 
     15 
    1616AutoPagerize = {}; 
    1717AutoPagerize.VERSION = "jautopagerize $Rev$"; 
     
    4848}; 
    4949 
    50 window.AutoPagerize = AutoPagerize; 
    51  
    52 (function () { 
    53         if (window != window.parent) return; 
    54         BeCompatible(); 
    55  
    56         /* 
    57          * define utilities 
    58          */ 
    59  
    60         // Async is similar to Mochikit Deferred. 
    61         function Async () { this.init.apply(this, arguments) } 
    62         Async.prototype = { 
    63                 init : function () { 
    64                         this.chain = { ok: [], ng: [] }; 
    65                 }, 
    66  
    67                 ready : function (value) { 
    68                         return this.call("ok", value); 
    69                 }, 
    70  
    71                 error : function (value) { 
    72                         return this.call("ng", value); 
    73                 }, 
    74  
    75                 call : function (okng, value) { 
    76                         if (typeof value == "function") { 
    77                                 this.chain[okng].push(value); 
    78                         } else { try { 
    79                                 var c = this.chain[okng]; 
    80                                 for (var i = 0; i < c.length; i++) { 
    81                                         c[i](value); 
    82                                 } 
    83                         } catch (e) { this.call("ng", e) } } 
    84                         return this; 
    85                 } 
     50// define export object; 
     51window.AutoPagerize = { 
     52        VERSION   : AutoPagerize.VERSION, 
     53        addFilter : AutoPagerize.addFilter 
     54}; 
     55 
     56if (window != window.parent) return; 
     57BeCompatible(); 
     58 
     59/* 
     60 * define utilities 
     61 */ 
     62 
     63// Async is similar to Mochikit Deferred. 
     64function Async () { this.init.apply(this, arguments) } 
     65Async.prototype = { 
     66        init : function () { 
     67                this.chain = { ok: [], ng: [] }; 
     68        }, 
     69 
     70        ready : function (value) { 
     71                return this.call("ok", value); 
     72        }, 
     73 
     74        error : function (value) { 
     75                return this.call("ng", value); 
     76        }, 
     77 
     78        call : function (okng, value) { 
     79                if (typeof value == "function") { 
     80                        this.chain[okng].push(value); 
     81                } else { try { 
     82                        var c = this.chain[okng]; 
     83                        for (var i = 0; i < c.length; i++) { 
     84                                c[i](value); 
     85                        } 
     86                } catch (e) { this.call("ng", e) } } 
     87                return this; 
     88        } 
     89}; 
     90 
     91function AsyncList (asyncs) { 
     92        var ret = new Async(); 
     93        var values = []; 
     94        asyncs.forEach(function (a, i) { 
     95                a.ready(function (v) { 
     96                        asyncs.pop(); 
     97                        values[i] = v; 
     98                        if (asyncs.length == 0) { 
     99                                ret.ready(values); 
     100                        } 
     101                }); 
     102                a.error(function (v) { 
     103                        ret.error(v); 
     104                }); 
     105                values.push(null); 
     106        }); 
     107        return ret; 
     108} 
     109 
     110function Resource (uri, convertfun) { 
     111        var a = new Async(); 
     112        if (!convertfun) convertfun = function (i) { return i }; 
     113        if (uri.indexOf("http://") != 0) { 
     114                // resolve relative path; for Safari 
     115                var i = new Image(); 
     116                i.src = uri; 
     117                uri = i.src; 
     118                i.src = "#"; 
     119        } 
     120        log("Getting Resource:"+uri); 
     121        GM_xmlhttpRequest({ 
     122                method  : "GET", 
     123                url     : uri, 
     124                onload  : function (req) { try { 
     125                        var res = convertfun(req.responseText); 
     126                        a.ready(res); 
     127                } catch (e) { a.error(e) } }, 
     128                onerror : function (e) { 
     129                        a.error(e); 
     130                } 
     131        }); 
     132        return a; 
     133} 
     134 
     135function HTMLResource (uri) { 
     136        function createDocumentFromString (s) { 
     137                s = String(s); 
     138                s = s.replace(/<script[^>]+>([^\s]|\s)*?<\/script>/g, ""); 
     139                s = s.replace(/<\/?(i?frame|html|script|object)[^<>]+>/g, ""); 
     140                var d = createHTMLDocument(); 
     141                if (d) { 
     142                        while (d.documentElement.firstChild) d.documentElement.removeChild(d.documentElement.firstChild); 
     143                        var r = d.createRange(); 
     144                        r.selectNodeContents(d.documentElement); 
     145                        d.documentElement.appendChild(r.createContextualFragment(s)); 
     146                        return d; 
     147                } else { 
     148                        log("fallback: use div to parse HTML"); 
     149                        $X.forceRelative = true; 
     150                        d = document.createElement("div"); 
     151                        d.innerHTML = s; 
     152                        return d; 
     153                } 
     154        } 
     155        return Resource(uri, createDocumentFromString); 
     156} 
     157 
     158function CachedResource (uri, convertfun, expire) { 
     159        var a   = new Async(); 
     160        var key = uri; 
     161        var v   = eval(GM_getValue(key)) || ({}); 
     162        a.clear = function () { 
     163                GM_setValue(key, ""); 
     164                return this; 
    86165        }; 
    87  
    88         function AsyncList (asyncs) { 
    89                 var ret = new Async(); 
    90                 var values = []; 
    91                 asyncs.forEach(function (a, i) { 
    92                         a.ready(function (v) { 
    93                                 asyncs.pop(); 
    94                                 values[i] = v; 
    95                                 if (asyncs.length == 0) { 
    96                                         ret.ready(values); 
    97                                 } 
    98                         }); 
    99                         a.error(function (v) { 
    100                                 ret.error(v); 
    101                         }); 
    102                         values.push(null); 
    103                 }); 
    104                 return ret; 
    105         } 
    106  
    107         function Resource (uri, convertfun) { 
    108                 var a = new Async(); 
    109                 if (!convertfun) convertfun = function (i) { return i }; 
    110                 if (uri.indexOf("http://") != 0) { 
    111                         // resolve relative path; for Safari 
    112                         var i = new Image(); 
    113                         i.src = uri; 
    114                         uri = i.src; 
    115                         i.src = "#"; 
    116                 } 
    117                 log("Getting Resource:"+uri); 
     166        if (v.time && v.time > (new Date).getTime() - expire) { 
     167                log("Cache Hitted: " + key); 
     168                setTimeout(function () { a.ready(v.body); }, 10); 
     169        } else { 
     170                log("Cache expired; getting... " + key); 
    118171                GM_xmlhttpRequest({ 
    119172                        method  : "GET", 
     
    121174                        onload  : function (req) { try { 
    122175                                var res = convertfun(req.responseText); 
     176                                GM_setValue(key, {time:(new Date).getTime(), body:res}.toSource()); 
     177                                log("Cached: " + key); 
    123178                                a.ready(res); 
    124179                        } catch (e) { a.error(e) } }, 
    125180                        onerror : function (e) { 
    126                                 a.error(e); 
     181                                a.error("HTTPError:"+e); 
    127182                        } 
    128183                }); 
    129                 return a; 
    130         } 
    131  
    132         function HTMLResource (uri) { 
    133                 function createDocumentFromString (s) { 
    134                         s = String(s); 
    135                         s = s.replace(/<script[^>]+>([^\s]|\s)*?<\/script>/g, ""); 
    136                         s = s.replace(/<\/?(i?frame|html|script|object)[^<>]+>/g, ""); 
    137                         var d = createHTMLDocument(); 
    138                         if (d) { 
    139                                 while (d.documentElement.firstChild) d.documentElement.removeChild(d.documentElement.firstChild); 
    140                                 var r = d.createRange(); 
    141                                 r.selectNodeContents(d.documentElement); 
    142                                 d.documentElement.appendChild(r.createContextualFragment(s)); 
    143                                 return d; 
     184        } 
     185        return a; 
     186}; 
     187 
     188/* 
     189 * jAutoPagerize 
     190 */ 
     191 
     192AutoPagerize.onscroll = function (e) { 
     193        var height = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight); 
     194        var remain = height - window.innerHeight - window.scrollY; 
     195        //log([remain, height, window.innerHeight, window.scrollY]); 
     196        if (AutoPagerize.enable && (remain < AutoPagerize.Config.remain_height + window.innerHeight)) { 
     197                AutoPagerize.loadNext(); 
     198        } 
     199}; 
     200 
     201AutoPagerize.toggle = function () { 
     202        AutoPagerize.enable = !AutoPagerize.enable; 
     203        AutoPagerize.updateStatus(); 
     204        return AutoPagerize.enable; 
     205}; 
     206 
     207AutoPagerize.loadNext = function () { 
     208        if ( AutoPagerize._loading) return; 
     209        if (!AutoPagerize._nextURI) { 
     210                AutoPagerize._terminate = true; 
     211                AutoPagerize.updateStatus(); 
     212                return; 
     213        } 
     214 
     215        AutoPagerize._loading = true; 
     216        HTMLResource(AutoPagerize._nextURI).ready(function (r) { 
     217                AutoPagerize.filters.forEach(function (f) { f(r) }); 
     218                setTimeout(function () { try { 
     219                        // in Safari 3 & GreaseKit. ib is often undefined 
     220                        // and freeze as refering this... 
     221                        var ib  = AutoPagerize._insertBefore; 
     222                        var pib = ib.parentNode; 
     223                        pib.insertBefore(h("<hr /><p>AutoPagerized: <a href='%s'>%s</a></p>".replace(/%s/g, AutoPagerize._nextURI)), ib); 
     224                        $X(AutoPagerize._pageinfo.pageElement, r).forEach(function (i) { 
     225                                log(String(i)); 
     226                                i = document.importNode(i, true); 
     227                                pib.insertBefore(i, ib); 
     228                        }); 
     229 
     230                        AutoPagerize._nextURI = ($X(AutoPagerize._pageinfo.nextLink, r)[0] || {}).href; 
     231                        AutoPagerize._loading = false; 
     232                        AutoPagerize.updateStatus(); 
     233                } catch (e) { AutoPagerize.errorHandler(e) } }, 10); 
     234        }).error(AutoPagerize.errorHandler); 
     235        AutoPagerize.updateStatus(); 
     236}; 
     237 
     238AutoPagerize.updateStatus = function () { 
     239        var i = AutoPagerize.icon; 
     240        var s = i.style; 
     241        s.fontSize   = "0.7em"; 
     242        s.position   = "fixed"; 
     243        s.top        = "0"; 
     244        s.right      = "0"; 
     245        s.margin     = "0.3em"; 
     246        s.padding    = "0 1em"; 
     247        s.color      = "#fff"; 
     248        s.zIndex     = "9999"; 
     249        s.fontWeight = "bold"; 
     250        s.lineHeight = "1.33"; 
     251 
     252        if (AutoPagerize._error) { 
     253                i.innerHTML  = AutoPagerize._error; 
     254                s.background = AutoPagerize.Config.color["error"]; 
     255        } else 
     256        if (AutoPagerize._terminate) { 
     257                i.innerHTML  = "."; 
     258                s.background = AutoPagerize.Config.color["terminated"]; 
     259        } else 
     260        if (AutoPagerize._loading) { 
     261                i.innerHTML  = "loading..."; 
     262                s.background = AutoPagerize.Config.color["loading"]; 
     263        } else { 
     264                i.innerHTML  = "."; 
     265                s.background = AutoPagerize.Config.color[AutoPagerize.enable ? "on" : "off"]; 
     266        } 
     267}; 
     268 
     269 
     270AutoPagerize.init = function () { 
     271        // init/define all state variables 
     272        AutoPagerize.enable        = AutoPagerize.Config.autostart; 
     273        AutoPagerize.icon          = h("<div id='autopagerize_icon'/>").firstChild; 
     274        AutoPagerize._loading      = false; 
     275        AutoPagerize._terminate    = false; 
     276        AutoPagerize._error        = null; 
     277        AutoPagerize._pageinfo     = {}; 
     278        AutoPagerize._nextURI      = null; 
     279        AutoPagerize._insertBefore = null; 
     280 
     281        // get siteinfo 
     282        AsyncList(AutoPagerize.Config.site_info_urls.map(function (url) { 
     283                var cr = CachedResource(url, parseSiteInfo, AutoPagerize.Config.cache_expire); 
     284                if (location.href == url) cr.clear(); 
     285                return cr; 
     286 
     287                function parseSiteInfo (html) { 
     288                        var d = h(html); 
     289 
     290                        var siteinfo = []; 
     291                        $X(".//*[@class='autopagerize_data']", d).forEach(function (e) { 
     292                                // using replace as scan and folding key/value to i 
     293                                var i = {}; e.value.replace(/^\s*([^:\s]+):\s*(.*)$/gm, function (m, key, value) { 
     294                                        i[key] = value; 
     295                                }); 
     296                                siteinfo.push(i); 
     297                        }); 
     298                        return siteinfo; 
     299                } 
     300        })).ready(function (r) { 
     301                r = AutoPagerize.Config.site_info.concat(r.reduce(function (r, i) { return r.concat(i) })); 
     302                var info = r.filter(function (i) { 
     303                        if (!i.url)          return false; 
     304                        if (!i.nextLink)     return false; 
     305                        if (!i.pageElement)  return false; 
     306                        if (!i.insertBefore) return false; 
     307                        return location.href.match(i.url) && !!$X(i.insertBefore)[0]; 
     308                }); 
     309                if (!info.length) return; 
     310 
     311                AutoPagerize._pageinfo     = info[0]; 
     312                AutoPagerize._nextURI      = ($X(AutoPagerize._pageinfo.nextLink)[0] || {}).href; 
     313                AutoPagerize._insertBefore =  $X(AutoPagerize._pageinfo.insertBefore)[0]; 
     314                document.body.appendChild(AutoPagerize.icon); 
     315 
     316                window.addEventListener("scroll",   AutoPagerize.onscroll, false); 
     317                window.addEventListener("dblclick", AutoPagerize.toggle,   false); 
     318 
     319                AutoPagerize.updateStatus(); 
     320        }).error(AutoPagerize.errorHandler); 
     321}; 
     322 
     323AutoPagerize.errorHandler = function (e) { 
     324        AutoPagerize._error = String(e); 
     325        AutoPagerize.updateStatus(); 
     326        alert(e.toSource()); 
     327}; 
     328 
     329// Set error handler to all functions 
     330for (var i in AutoPagerize) { 
     331        if (AutoPagerize.hasOwnProperty(i) && 
     332                typeof AutoPagerize[i] == "function") { 
     333                AutoPagerize[i] = (function (f) { 
     334                        return function () { try { 
     335                                return f.apply(AutoPagerize, arguments); 
     336                        } catch (e) { AutoPagerize.errorHandler(e) } }; 
     337                })(AutoPagerize[i]); 
     338        } 
     339} 
     340 
     341 
     342var XPathGenerator = { 
     343        open : function () { 
     344                XPathGenerator._prev = []; 
     345                XPathGenerator._container = h("<textarea cols='40' rows='5'></textarea><br/>"); 
     346                XPathGenerator._inspect   = h("<button value>Inspect</button>").firstChild; 
     347                XPathGenerator._timer = setInterval(XPathGenerator.change, 1000); 
     348                XPathGenerator._container.appendChild(XPathGenerator._inspect); 
     349                with (XPathGenerator._container.style) { 
     350                        position = "fixed"; 
     351                        right    = "0"; 
     352                        top      = "2em"; 
     353                        opacity  = "0.9"; 
     354                } 
     355                document.body.appendChild(XPathGenerator._container); 
     356 
     357                XPathGenerator._inspect.addEventListener("click", function () { 
     358                        document.body.addEventListener("mouseover", XPathGenerator.mouseover, true); 
     359                        document.body.addEventListener("mousedown", XPathGenerator.mousedown, true); 
     360                        document.body.addEventListener("mouseout",  XPathGenerator.mouseout,  true); 
     361                }, false); 
     362        }, 
     363 
     364        close : function () { 
     365                XPathGenerator._container.parentNode.removeChild(XPathGenerator._container); 
     366                clearInterval(XPathGenerator._timer); 
     367        }, 
     368 
     369        mouseover : function (e) { 
     370                e.target.style.outline = "2px solid red"; 
     371                XPathGenerator._container.firstChild.value = XPathGenerator.getXPathByElement(e.target); 
     372        }, 
     373 
     374        mousedown : function (e) { 
     375                e.target.style.outline = ""; 
     376                document.body.removeEventListener("mouseover", XPathGenerator.mouseover, true); 
     377                document.body.removeEventListener("mousedown", XPathGenerator.mousedown, true); 
     378                document.body.removeEventListener("mouseout",  XPathGenerator.mouseout,  true); 
     379        }, 
     380 
     381        mouseout : function (e) { 
     382                e.target.style.outline = ""; 
     383        }, 
     384 
     385        change : function (e) { 
     386                var path = XPathGenerator._container.firstChild.value; 
     387                if (XPathGenerator._prev.value != path) { 
     388                        while (XPathGenerator._prev[0]) XPathGenerator._prev.pop().style.outline = ""; 
     389                        try { 
     390                                XPathGenerator._prev = $X(path).map(function (i) { 
     391                                        i.style.outline = "2px solid red"; 
     392                                        return i; 
     393                                }); 
     394                        } catch (e) {} 
     395                        XPathGenerator._prev.value = path; 
     396                } 
     397        }, 
     398 
     399        toggle : function () { 
     400                if (XPathGenerator._opened) { 
     401                        this.close(); 
     402                        XPathGenerator._opened = false; 
     403                } else { 
     404                        XPathGenerator._opened = true; 
     405                        this.open(); 
     406                } 
     407        }, 
     408 
     409        getXPathByElement : function (target) { 
     410                var pathElement  = ""; 
     411                var node = target; 
     412                if (node.nodeType == 9 /*DOCUMENT_NODE=9*/) { 
     413                        return "" 
     414                } else { 
     415                        var tagName = node.tagName.toLowerCase(); 
     416                        if (node.hasAttribute("id")) { 
     417                                // pathElement = tagName + '[@id="'+node.getAttribute("id")+'"]'; 
     418                                pathElement = 'id("'+node.getAttribute("id")+'")'; 
    144419                        } else { 
    145                                 log("fallback: use div to parse HTML"); 
    146                                 $X.forceRelative = true; 
    147                                 d = document.createElement("div"); 
    148                                 d.innerHTML = s; 
    149                                 return d; 
    150                         } 
    151                 } 
    152                 return Resource(uri, createDocumentFromString); 
    153         } 
    154  
    155         function CachedResource (uri, convertfun, expire) { 
    156                 var a   = new Async(); 
    157                 var key = uri; 
    158                 var v   = eval(GM_getValue(key)) || ({}); 
    159                 a.clear = function () { 
    160                         GM_setValue(key, ""); 
    161                         return this; 
    162                 }; 
    163                 if (v.time && v.time > (new Date).getTime() - expire) { 
    164                         log("Cache Hitted: " + key); 
    165                         setTimeout(function () { a.ready(v.body); }, 10); 
    166                 } else { 
    167                         log("Cache expired; getting... " + key); 
    168                         GM_xmlhttpRequest({ 
    169                                 method  : "GET", 
    170                                 url     : uri, 
    171                                 onload  : function (req) { try { 
    172                                         var res = convertfun(req.responseText); 
    173                                         GM_setValue(key, {time:(new Date).getTime(), body:res}.toSource()); 
    174                                         log("Cached: " + key); 
    175                                         a.ready(res); 
    176                                 } catch (e) { a.error(e) } }, 
    177                                 onerror : function (e) { 
    178                                         a.error("HTTPError:"+e); 
     420                                pathElement = arguments.callee(node.parentNode) + '/' + tagName; 
     421                                if (node.hasAttribute("class")){ 
     422                                        pathElement += '[@class="'+node.getAttribute("class")+'"]'; 
     423                                } else { 
     424                                        pathElement += '['+indexOf(node)+']'; 
    179425                                } 
    180                         }); 
    181                 } 
    182                 return a; 
    183         }; 
    184  
    185         /* 
    186          * jAutoPagerize 
    187          */ 
    188  
    189         AutoPagerize.onscroll = function (e) { 
    190                 var height = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight); 
    191                 var remain = height - window.innerHeight - window.scrollY; 
    192                 //log([remain, height, window.innerHeight, window.scrollY]); 
    193                 if (AutoPagerize.enable && (remain < AutoPagerize.Config.remain_height + window.innerHeight)) { 
    194                         AutoPagerize.loadNext(); 
    195                 } 
    196         }; 
    197  
    198         AutoPagerize.toggle = function () { 
    199                 AutoPagerize.enable = !AutoPagerize.enable; 
    200                 AutoPagerize.updateStatus(); 
    201                 return AutoPagerize.enable; 
    202         }; 
    203  
    204         AutoPagerize.loadNext = function () { 
    205                 if ( AutoPagerize._loading) return; 
    206                 if (!AutoPagerize._nextURI) { 
    207                         AutoPagerize._terminate = true; 
    208                         AutoPagerize.updateStatus(); 
    209                         return; 
    210                 } 
    211  
    212                 AutoPagerize._loading = true; 
    213                 HTMLResource(AutoPagerize._nextURI).ready(function (r) { 
    214                         AutoPagerize.filters.forEach(function (f) { f(r) }); 
    215                         setTimeout(function () { try { 
    216                                 // in Safari 3 & GreaseKit. ib is often undefined 
    217                                 // and freeze as refering this... 
    218                                 var ib  = AutoPagerize._insertBefore; 
    219                                 var pib = ib.parentNode; 
    220                                 pib.insertBefore(h("<hr /><p>AutoPagerized: <a href='%s'>%s</a></p>".replace(/%s/g, AutoPagerize._nextURI)), ib); 
    221                                 $X(AutoPagerize._pageinfo.pageElement, r).forEach(function (i) { 
    222                                         log(String(i)); 
    223                                         i = document.importNode(i, true); 
    224                                         pib.insertBefore(i, ib); 
    225                                 }); 
    226  
    227                                 AutoPagerize._nextURI = ($X(AutoPagerize._pageinfo.nextLink, r)[0] || {}).href; 
    228                                 AutoPagerize._loading = false; 
    229                                 AutoPagerize.updateStatus(); 
    230                         } catch (e) { AutoPagerize.errorHandler(e) } }, 10); 
    231                 }).error(AutoPagerize.errorHandler); 
    232                 AutoPagerize.updateStatus(); 
    233         }; 
    234  
    235         AutoPagerize.updateStatus = function () { 
    236                 var i = AutoPagerize.icon; 
    237                 var s = i.style; 
    238                 s.fontSize   = "0.7em"; 
    239                 s.position   = "fixed"; 
    240                 s.top        = "0"; 
    241                 s.right      = "0"; 
    242                 s.margin     = "0.3em"; 
    243                 s.padding    = "0 1em"; 
    244                 s.color      = "#fff"; 
    245                 s.zIndex     = "9999"; 
    246                 s.fontWeight = "bold"; 
    247                 s.lineHeight = "1.33"; 
    248  
    249                 if (AutoPagerize._error) { 
    250                         i.innerHTML  = AutoPagerize._error; 
    251                         s.background = AutoPagerize.Config.color["error"]; 
    252                 } else 
    253                 if (AutoPagerize._terminate) { 
    254                         i.innerHTML  = "."; 
    255                         s.background = AutoPagerize.Config.color["terminated"]; 
    256                 } else 
    257                 if (AutoPagerize._loading) { 
    258                         i.innerHTML  = "loading..."; 
    259                         s.background = AutoPagerize.Config.color["loading"]; 
    260                 } else { 
    261                         i.innerHTML  = "."; 
    262                         s.background = AutoPagerize.Config.color[AutoPagerize.enable ? "on" : "off"]; 
    263                 } 
    264         }; 
    265  
    266  
    267         AutoPagerize.init = function () { 
    268                 // init/define all state variables 
    269                 AutoPagerize.enable        = AutoPagerize.Config.autostart; 
    270                 AutoPagerize.icon          = h("<div id='autopagerize_icon'/>").firstChild; 
    271                 AutoPagerize._loading      = false; 
    272                 AutoPagerize._terminate    = false; 
    273                 AutoPagerize._error        = null; 
    274                 AutoPagerize._pageinfo     = {}; 
    275                 AutoPagerize._nextURI      = null; 
    276                 AutoPagerize._insertBefore = null; 
    277  
    278                 // get siteinfo 
    279                 AsyncList(AutoPagerize.Config.site_info_urls.map(function (url) { 
    280                         var cr = CachedResource(url, parseSiteInfo, AutoPagerize.Config.cache_expire); 
    281                         if (location.href == url) cr.clear(); 
    282                         return cr; 
    283  
    284                         function parseSiteInfo (html) { 
    285                                 var d = h(html); 
    286  
    287                                 var siteinfo = []; 
    288                                 $X(".//*[@class='autopagerize_data']", d).forEach(function (e) { 
    289                                         // using replace as scan and folding key/value to i 
    290                                         var i = {}; e.value.replace(/^\s*([^:\s]+):\s*(.*)$/gm, function (m, key, value) { 
    291                                                 i[key] = value; 
    292                                         }); 
    293                                         siteinfo.push(i); 
    294                                 }); 
    295                                 return siteinfo; 
    296                         } 
    297                 })).ready(function (r) { 
    298                         r = AutoPagerize.Config.site_info.concat(r.reduce(function (r, i) { return r.concat(i) })); 
    299                         var info = r.filter(function (i) { 
    300                                 if (!i.url)          return false; 
    301                                 if (!i.nextLink)     return false; 
    302                                 if (!i.pageElement)  return false; 
    303                                 if (!i.insertBefore) return false; 
    304                                 return location.href.match(i.url) && !!$X(i.insertBefore)[0]; 
    305                         }); 
    306                         if (!info.length) return; 
    307  
    308                         AutoPagerize._pageinfo     = info[0]; 
    309                         AutoPagerize._nextURI      = ($X(AutoPagerize._pageinfo.nextLink)[0] || {}).href; 
    310                         AutoPagerize._insertBefore =  $X(AutoPagerize._pageinfo.insertBefore)[0]; 
    311                         document.body.appendChild(AutoPagerize.icon); 
    312  
    313                         window.addEventListener("scroll",   AutoPagerize.onscroll, false); 
    314                         window.addEventListener("dblclick", AutoPagerize.toggle,   false); 
    315  
    316                         AutoPagerize.updateStatus(); 
    317                 }).error(AutoPagerize.errorHandler); 
    318         }; 
    319  
    320         AutoPagerize.errorHandler = function (e) { 
    321                 AutoPagerize._error = String(e); 
    322                 AutoPagerize.updateStatus(); 
    323                 alert(e.toSource()); 
    324         }; 
    325  
    326         // Set error handler to all functions 
    327         for (var i in AutoPagerize) { 
    328                 if (AutoPagerize.hasOwnProperty(i) && 
    329                     typeof AutoPagerize[i] == "function") { 
    330                         AutoPagerize[i] = (function (f) { 
    331                                 return function () { try { 
    332                                         return f.apply(AutoPagerize, arguments); 
    333                                 } catch (e) { AutoPagerize.errorHandler(e) } }; 
    334                         })(AutoPagerize[i]); 
    335                 } 
    336         } 
    337  
    338  
    339         var XPathGenerator = { 
    340                 open : function () { 
    341                         XPathGenerator._prev = []; 
    342                         XPathGenerator._container = h("<textarea cols='40' rows='5'></textarea><br/>"); 
    343                         XPathGenerator._inspect   = h("<button value>Inspect</button>").firstChild; 
    344                         XPathGenerator._timer = setInterval(XPathGenerator.change, 1000); 
    345                         XPathGenerator._container.appendChild(XPathGenerator._inspect); 
    346                         with (XPathGenerator._container.style) { 
    347                                 position = "fixed"; 
    348                                 right    = "0"; 
    349                                 top      = "2em"; 
    350                                 opacity  = "0.9"; 
    351                         } 
    352                         document.body.appendChild(XPathGenerator._container); 
    353  
    354                         XPathGenerator._inspect.addEventListener("click", function () { 
    355                                 document.body.addEventListener("mouseover", XPathGenerator.mouseover, true); 
    356                                 document.body.addEventListener("mousedown", XPathGenerator.mousedown, true); 
    357                                 document.body.addEventListener("mouseout",  XPathGenerator.mouseout,  true); 
    358                         }, false); 
    359                 }, 
    360  
    361                 close : function () { 
    362                         XPathGenerator._container.parentNode.removeChild(XPathGenerator._container); 
    363                         clearInterval(XPathGenerator._timer); 
    364                 }, 
    365  
    366                 mouseover : function (e) { 
    367                         e.target.style.outline = "2px solid red"; 
    368                         XPathGenerator._container.firstChild.value = XPathGenerator.getXPathByElement(e.target); 
    369                 }, 
    370  
    371                 mousedown : function (e) { 
    372                         e.target.style.outline = ""; 
    373                         document.body.removeEventListener("mouseover", XPathGenerator.mouseover, true); 
    374                         document.body.removeEventListener("mousedown", XPathGenerator.mousedown, true); 
    375                         document.body.removeEventListener("mouseout",  XPathGenerator.mouseout,  true); 
    376                 }, 
    377  
    378                 mouseout : function (e) { 
    379                         e.target.style.outline = ""; 
    380                 }, 
    381  
    382                 change : function (e) { 
    383                         var path = XPathGenerator._container.firstChild.value; 
    384                         if (XPathGenerator._prev.value != path) { 
    385                                 while (XPathGenerator._prev[0]) XPathGenerator._prev.pop().style.outline = ""; 
    386                                 try { 
    387                                         XPathGenerator._prev = $X(path).map(function (i) { 
    388                                                 i.style.outline = "2px solid red"; 
    389                                                 return i; 
    390                                         }); 
    391                                 } catch (e) {} 
    392                                 XPathGenerator._prev.value = path; 
    393                         } 
    394                 }, 
    395  
    396                 toggle : function () { 
    397                         if (XPathGenerator._opened) { 
    398                                 this.close(); 
    399                                 XPathGenerator._opened = false; 
     426                        } 
     427                } 
     428                return pathElement; 
     429 
     430                function indexOf (node) { 
     431                        for (var i = 0, r = 1, c = node.parentNode.childNodes, len = c.length; i < len; i++) { 
     432                                if (c[i].nodeName == node.nodeName && 
     433                                        c[i].nodeType == node.nodeType) { 
     434                                        if (c[i] == node) return r; 
     435                                        r++; 
     436                                } 
     437                        } 
     438                        return -1; 
     439                } 
     440        } 
     441}; 
     442 
     443// Run! 
     444AutoPagerize.init(); 
     445AutoPagerize.icon.addEventListener("click", function () { 
     446        XPathGenerator.toggle(); 
     447}, false); 
     448// for bookmarklet 
     449unsafeWindow.XPathGenerator = XPathGenerator; 
     450 
     451 
     452function BeCompatible () { 
     453        unsafeWindow = unsafeWindow || window; 
     454 
     455        // Memo:: 
     456        // Safari 3 has JS 1.6 functions but 1.8 and toSource. 
     457 
     458        if (!Array.prototype.reduce) { 
     459                Array.prototype.reduce = function(fun /*, initial*/) { 
     460                        var len = this.length; 
     461                        if (typeof fun != "function") throw new TypeError(); 
     462 
     463                        // no value to return if no initial value and an empty array 
     464                        if (len == 0 && arguments.length == 1) throw new TypeError(); 
     465 
     466                        var i = 0; 
     467                        if (arguments.length >= 2) { 
     468                                var rv = arguments[1]; 
    400469                        } else { 
    401                                 XPathGenerator._opened = true; 
    402                                 this.open(); 
    403                         } 
    404                 }, 
    405  
    406                 getXPathByElement : function (target) { 
    407                         var pathElement  = ""; 
    408                         var node = target; 
    409                         if (node.nodeType == 9 /*DOCUMENT_NODE=9*/) { 
    410                                 return "" 
    411                         } else { 
    412                                 var tagName = node.tagName.toLowerCase(); 
    413                                 if (node.hasAttribute("id")) { 
    414                                         // pathElement = tagName + '[@id="'+node.getAttribute("id")+'"]'; 
    415                                         pathElement = 'id("'+node.getAttribute("id")+'")'; 
    416                                 } else { 
    417                                         pathElement = arguments.callee(node.parentNode) + '/' + tagName; 
    418                                         if (node.hasAttribute("class")){ 
    419                                                 pathElement += '[@class="'+node.getAttribute("class")+'"]'; 
    420                                         } else { 
    421                                                 pathElement += '['+indexOf(node)+']'; 
     470                                do { 
     471                                        if (i in this) { 
     472                                                rv = this[i++]; 
     473                                                break; 
    422474                                        } 
     475 
     476                                        // if array contains no values, no initial value to return 
     477                                        if (++i >= len) throw new TypeError(); 
     478                                } while (true); 
     479                        } 
     480 
     481                        for (; i < len; i++) { 
     482                                if (i in this) rv = fun.call(null, rv, this[i], i, this); 
     483                        } 
     484 
     485                        return rv; 
     486                }; 
     487        } 
     488 
     489        if (!Object.prototype.toSource) { 
     490                Object.prototype.toSource = function () { 
     491                        var props = []; 
     492                        for (var key in this) { 
     493                                if (this.hasOwnProperty(key)) { 
     494                                        var v; 
     495                                        switch (typeof this[key]) { 
     496                                                case "undefined": v = "undefined"; break; 
     497                                                case "null"     : v = "null"; break; 
     498                                                default         : v = this[key].toSource(); 
     499                                        } 
     500                                        props.push(key.toSource() + ":" + v); 
    423501                                } 
    424502                        } 
    425                         return pathElement; 
    426  
    427                         function indexOf (node) { 
    428                                 for (var i = 0, r = 1, c = node.parentNode.childNodes, len = c.length; i < len; i++) { 
    429                                         if (c[i].nodeName == node.nodeName && 
    430                                                 c[i].nodeType == node.nodeType) { 
    431                                                 if (c[i] == node) return r; 
    432                                                 r++; 
    433                                         } 
    434                                 } 
    435                                 return -1; 
    436                         } 
    437                 } 
    438         }; 
    439  
    440         // Run! 
    441         AutoPagerize.init(); 
    442         AutoPagerize.icon.addEventListener("click", function () { 
    443                 XPathGenerator.toggle(); 
    444         }, false); 
    445         // for bookmarklet 
    446         unsafeWindow.XPathGenerator = XPathGenerator; 
    447  
    448  
    449         function BeCompatible () { 
    450                 unsafeWindow = unsafeWindow || window; 
    451  
    452                 // Memo:: 
    453                 // Safari 3 has JS 1.6 functions but 1.8 and toSource. 
    454  
    455                 if (!Array.prototype.reduce) { 
    456                         Array.prototype.reduce = function(fun /*, initial*/) { 
    457                                 var len = this.length; 
    458                                 if (typeof fun != "function") throw new TypeError(); 
    459  
    460                                 // no value to return if no initial value and an empty array 
    461                                 if (len == 0 && arguments.length == 1) throw new TypeError(); 
    462  
    463                                 var i = 0; 
    464                                 if (arguments.length >= 2) { 
    465                                         var rv = arguments[1]; 
    466                                 } else { 
    467                                         do { 
    468                                                 if (i in this) { 
    469                                                         rv = this[i++]; 
    470                                                         break; 
    471                                                 } 
    472  
    473                                                 // if array contains no values, no initial value to return 
    474                                                 if (++i >= len) throw new TypeError(); 
    475                                         } while (true); 
    476                                 } 
    477  
    478                                 for (; i < len; i++) { 
    479                                         if (i in this) rv = fun.call(null, rv, this[i], i, this); 
    480                                 } 
    481  
    482                                 return rv; 
    483                         }; 
    484                 } 
    485  
    486                 if (!Object.prototype.toSource) { 
    487                         Object.prototype.toSource = function () { 
    488                                 var props = []; 
    489                                 for (var key in this) { 
    490                                         if (this.hasOwnProperty(key)) { 
    491                                                 var v; 
    492                                                 switch (typeof this[key]) { 
    493                                                         case "undefined": v = "undefined"; break; 
    494                                                         case "null"     : v = "null"; break; 
    495                                                         default         : v = this[key].toSource(); 
    496                                                 } 
    497                                                 props.push(key.toSource() + ":" + v); 
    498                                         } 
    499                                 } 
    500                                 return "({" + props.join(",") + "})";; 
    501                         }; 
    502  
    503                         String.prototype.toSource = function () { 
    504                                 return '"' + this.replace(/"/g, '\\"') + '"'; 
    505                         }; 
    506  
    507                         Array.prototype.toSource = function () { 
    508                                 return "[" + this.map(function (i) { return i.toSource() }).join(",") + "]"; 
    509                         }; 
    510  
    511                         Date.prototype.toSource = function () { 
    512                                 return "(new Date(%d))".replace(/%d/, this.getTime()); 
    513                         }; 
    514  
    515                         Number.prototype.toSource = function () { 
    516                                 return String(this); 
    517                         }; 
    518  
    519                         RegExp.prototype.toSource = function () { 
    520                                 return String(this); 
    521                         }; 
    522  
    523                         Boolean.prototype.toSource = function () { 
    524                                 return String(this); 
    525                         }; 
    526                 } 
     503                        return "({" + props.join(",") + "})";; 
     504                }; 
     505 
     506                String.prototype.toSource = function () { 
     507                        return '"' + this.replace(/"/g, '\\"') + '"'; 
     508                }; 
     509 
     510                Array.prototype.toSource = function () { 
     511                        return "[" + this.map(function (i) { return i.toSource() }).join(",") + "]"; 
     512                }; 
     513 
     514                Date.prototype.toSource = function () { 
     515                        return "(new Date(%d))".replace(/%d/, this.getTime()); 
     516                }; 
     517 
     518                Number.prototype.toSource = function () { 
     519                        return String(this); 
     520                }; 
     521 
     522                RegExp.prototype.toSource = function () { 
     523                        return String(this); 
     524                }; 
     525 
     526                Boolean.prototype.toSource = function () { 
     527                        return String(this); 
     528                }; 
     529        } 
    527530 
    528531//              [ 
     
    534537// 
    535538//              throw ""; 
    536         } 
    537  
    538         /* 
    539          * template functions 
    540          */ 
    541         function createHTMLDocument (title) { 
    542                 // Firefox doesn't have createHTMLDocument 
    543                 if (!document.implementation.createHTMLDocument) { 
    544                         // Maybe this is the best way to create HTMLDocument, but not worked in any browser... 
    545                         // var html4dt = document.implementation.createDocumentType("HTML", "-//W3C//DTD HTML 4.01//EN", "http://www.w3.org/TR/html4/strict.dtd"); 
    546                         // var d =  document.implementation.createDocument("", "HTML", html4dt); 
    547                         // return d; 
    548  
    549                         // In Firefox 
    550                         // Try to create HTMLDocument from XSLT with <xsl:output method='html'/> 
    551                         if (typeof DOMParser != "undefined" && 
    552                             typeof XSLTProcessor != "undefined") { 
    553                                 return null; // avoid magical XSLTProcessor bug. so use div for parsing 
    554                                 var x = new XSLTProcessor(); 
    555                                 x.importStylesheet((new DOMParser).parseFromString([ 
    556                                         "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>", 
    557                                                 "<xsl:output method='html'/>", 
    558                                                 "<xsl:template match='/'>", 
    559                                                         "<html><head><title>", title, "</title></head><body/></html>", 
    560                                                 "</xsl:template>", 
    561                                         "</xsl:stylesheet>"].join(""), "application/xml")); 
    562                                 var ret = x.transformToDocument((new DOMParser).parseFromString("<nice-boat/>", "text/xml")); 
    563                                 return ret; 
    564                         } else { 
    565                                 return null; 
    566                         } 
     539} 
     540 
     541/* 
     542 * template functions 
     543 */ 
     544function createHTMLDocument (title) { 
     545        // Firefox doesn't have createHTMLDocument 
     546        if (!document.implementation.createHTMLDocument) { 
     547                // Maybe this is the best way to create HTMLDocument, but not worked in any browser... 
     548                // var html4dt = document.implementation.createDocumentType("HTML", "-//W3C//DTD HTML 4.01//EN", "http://www.w3.org/TR/html4/strict.dtd"); 
     549                // var d =  document.implementation.createDocument("", "HTML", html4dt); 
     550                // return d; 
     551 
     552                // In Firefox 
     553                // Try to create HTMLDocument from XSLT with <xsl:output method='html'/> 
     554                if (typeof DOMParser != "undefined" && 
     555                        typeof XSLTProcessor != "undefined") { 
     556                        return null; // avoid magical XSLTProcessor bug. so use div for parsing 
     557                        var x = new XSLTProcessor(); 
     558                        x.importStylesheet((new DOMParser).parseFromString([ 
     559                                "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>", 
     560                                        "<xsl:output method='html'/>", 
     561                                        "<xsl:template match='/'>", 
     562                                                "<html><head><title>", title, "</title></head><body/></html>", 
     563                                        "</xsl:template>", 
     564                                "</xsl:stylesheet>"].join(""), "application/xml")); 
     565                        var ret = x.transformToDocument((new DOMParser).parseFromString("<nice-boat/>", "text/xml")); 
     566                        return ret; 
    567567                } else { 
    568                         return document.implementation.createHTMLDocument(title); 
    569                 } 
    570         } 
    571  
    572  
    573         function log (m) { 
     568                        return null; 
     569                } 
     570        } else { 
     571                return document.implementation.createHTMLDocument(title); 
     572        } 
     573} 
     574 
     575 
     576function log (m) { 
    574577//              var c = unsafeWindow.console; 
    575578//              if (c) c.log.apply(c, arguments); 
    576                 var o = Array.prototype.concat.apply([], arguments); 
    577                 if (window.console) { 
    578                         window.console.log(o.join(", ")); 
    579                 } else 
    580                 if (GM_log) { 
    581                         GM_log(o); 
    582                 } else { 
    583                         location.href = "javascript:(function () { if (window.console) console.log.apply(console.log, "+o.toSource()+") })();"; 
    584                 } 
    585         } 
    586  
    587         function h (s) { 
    588                 var d = document.createElement("div"); 
    589                 d.innerHTML = s; 
    590                 return d; 
    591         } 
    592  
    593         // modified $X for relative path extension. 
    594         function $X (exp, context, type /* want type */) { 
    595                 if (arguments.callee.forceRelative || navigator.userAgent.match("Safari/523.12")) { 
    596                         exp = exp.replace(/id\((?:"([^"]+)"|'([^']+)')\)/g, function (_, v1, v2) { 
    597                                 return '//*[@id="' + (v1 || v2) + '"]'; 
    598                         }); 
    599                 } 
    600                 if (arguments.callee.forceRelative) { 
    601                         if (exp.indexOf("(//") == 0) 
    602                                 exp = "(.//" + exp.substring(3); 
    603                         else 
    604                                 exp = ((exp[0] == "/") ? "." : "./") + exp; 
    605                 } 
    606                 log("xpath:" + exp); 
    607  
    608                 if (typeof context == "function") { 
    609                         type    = context; 
    610                         context = null; 
    611                 } 
    612                 if (!context) context = document; 
    613                 var exp = (context.ownerDocument || context).createExpression(exp, function (prefix) { 
    614                         var o = document.createNSResolver(context)(prefix); 
    615                         if (o) return o; 
    616                         return (document.contentType == "application/xhtml+xml") ? "http://www.w3.org/1999/xhtml" : ""; 
     579        var o = Array.prototype.concat.apply([], arguments); 
     580        if (window.console) { 
     581                window.console.log(o.join(", ")); 
     582        } else 
     583        if (GM_log) { 
     584                GM_log(o); 
     585        } else { 
     586                location.href = "javascript:(function () { if (window.console) console.log.apply(console.log, "+o.toSource()+") })();"; 
     587        } 
     588} 
     589 
     590function h (s) { 
     591        var d = document.createElement("div"); 
     592        d.innerHTML = s; 
     593        return d; 
     594} 
     595 
     596// modified $X for relative path extension. 
     597function $X (exp, context, type /* want type */) { 
     598        if (arguments.callee.forceRelative || navigator.userAgent.match("Safari/523.12")) { 
     599                exp = exp.replace(/id\((?:"([^"]+)"|'([^']+)')\)/g, function (_, v1, v2) { 
     600                        return '//*[@id="' + (v1 || v2) + '"]'; 
    617601                }); 
    618  
    619                 switch (type) { 
    620                         case String: 
    621                                 return exp.evaluate( 
    622                                         context, 
    623                                         XPathResult.STRING_TYPE, 
    624                                         null 
    625                                 ).stringValue; 
    626                         case Number: 
    627                                 return exp.evaluate( 
    628                                         context, 
    629                                         XPathResult.NUMBER_TYPE, 
    630                                         null 
    631                                 ).numberValue; 
    632                         case Boolean: 
    633                                 return exp.evaluate( 
    634                                         context, 
    635                                         XPathResult.BOOLEAN_TYPE, 
    636                                         null 
    637                                 ).booleanValue; 
    638                         case Array: 
    639                                 var result = exp.evaluate( 
    640                                         context, 
    641                                         XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, 
    642                                         null 
    643                                 ); 
    644                                 var ret = []; 
    645                                 for (var i = 0, len = result.snapshotLength; i < len; i++) { 
    646                                         ret.push(result.snapshotItem(i)); 
     602        } 
     603        if (arguments.callee.forceRelative) { 
     604                if (exp.indexOf("(//") == 0) 
     605                        exp = "(.//" + exp.substring(3); 
     606                else 
     607                        exp = ((exp[0] == "/") ? "." : "./") + exp; 
     608        } 
     609        log("xpath:" + exp); 
     610 
     611        if (typeof context == "function") { 
     612                type    = context; 
     613                context = null; 
     614        } 
     615        if (!context) context = document; 
     616        var exp = (context.ownerDocument || context).createExpression(exp, function (prefix) { 
     617                var o = document.createNSResolver(context)(prefix); 
     618                if (o) return o; 
     619                return (document.contentType == "application/xhtml+xml") ? "http://www.w3.org/1999/xhtml" : ""; 
     620        }); 
     621 
     622        switch (type) { 
     623                case String: 
     624                        return exp.evaluate( 
     625                                context, 
     626                                XPathResult.STRING_TYPE, 
     627                                null 
     628                        ).stringValue; 
     629                case Number: 
     630                        return exp.evaluate( 
     631                                context, 
     632                                XPathResult.NUMBER_TYPE, 
     633                                null 
     634                        ).numberValue; 
     635                case Boolean: 
     636                        return exp.evaluate( 
     637                                context, 
     638                                XPathResult.BOOLEAN_TYPE, 
     639                                null 
     640                        ).booleanValue; 
     641                case Array: 
     642                        var result = exp.evaluate( 
     643                                context, 
     644                                XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, 
     645                                null 
     646                        ); 
     647                        var ret = []; 
     648                        for (var i = 0, len = result.snapshotLength; i < len; i++) { 
     649                                ret.push(result.snapshotItem(i)); 
     650                        } 
     651                        return ret; 
     652                case undefined: 
     653                        var result = exp.evaluate(context, XPathResult.ANY_TYPE, null); 
     654                        switch (result.resultType) { 
     655                                case XPathResult.STRING_TYPE : return result.stringValue; 
     656                                case XPathResult.NUMBER_TYPE : return result.numberValue; 
     657                                case XPathResult.BOOLEAN_TYPE: return result.booleanValue; 
     658                                case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: { 
     659                                        // not ensure the order. 
     660                                        var ret = []; 
     661                                        var i = null; 
     662                                        while (i = result.iterateNext()) { 
     663                                                ret.push(i); 
     664                                        } 
     665                                        return ret; 
    647666                                } 
    648                                 return ret; 
    649                         case undefined: 
    650                                 var result = exp.evaluate(context, XPathResult.ANY_TYPE, null); 
    651                                 switch (result.resultType) { 
    652                                         case XPathResult.STRING_TYPE : return result.stringValue; 
    653                                         case XPathResult.NUMBER_TYPE : return result.numberValue; 
    654                                         case XPathResult.BOOLEAN_TYPE: return result.booleanValue; 
    655                                         case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: { 
    656                                                 // not ensure the order. 
    657                                                 var ret = []; 
    658                                                 var i = null; 
    659                                                 while (i = result.iterateNext()) { 
    660                                                         ret.push(i); 
    661                                                 } 
    662                                                 return ret; 
    663                                         } 
    664                                 } 
    665                                 return null; 
    666                         default: 
    667                                 throw(TypeError("$X: specified type is not valid type.")); 
    668                 } 
    669         } 
     667                        } 
     668                        return null; 
     669                default: 
     670                        throw(TypeError("$X: specified type is not valid type.")); 
     671        } 
     672} 
    670673 
    671674//      unsafeWindow.$X = $X;