Show
Ignore:
Timestamp:
04/10/08 09:47:01 (5 years ago)
Author:
kdoya
Message:

websites/coderepos.org/trac/share/js/TracUtils.js: Added 'kdoya'.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • websites/coderepos.org/trac/share/js/TracUtils.js

    r9218 r9235  
    88 
    99$(function () { 
    10         TracUtils.installAll(); 
     10        TracUtils.installAll(); 
    1111}); 
    1212 
    1313var TracUtils = { 
    14         SVN_REPOS_BASE : 'http://svn.coderepos.org/share', 
    15         TRAC_BASE      : 'http://coderepos.org/share', 
    16         AUTHOR_ICONS   : { 
    17                 //'tomoyo'       : 'http://www.hatena.ne.jp/users/ma/mayuki/profile_s.gif' 
    18                 //'tomoyo'       : 'id:mayuki' 
    19                   '*default*'    : 'http://coderepos.org/share/htdocs/default-author-icon.png' 
    20                 , 'mayuki'       : 'id:mayuki' 
    21                 , 'yappo'        : 'id:yappo' 
    22                 , 'typester'     : 'http://www.gravatar.com/avatar.php?gravatar_id=fbc6511bcc0649366086c0445fb456d3&size=16'//'id:typester' 
    23                 , 'kentaro'      : 'id:antipop' 
    24                 , 'mizzy'        : 'id:gosukenator' 
    25                 , 'charsbar'     : 'id:charsbar' 
    26                 , 'secondlife'   : 'id:secondlife' 
    27                 , 'kazeburo'     : 'id:kazeburo' 
    28                 , 'otsune'       : 'id:otsune' 
    29                 , 'hirose31'     : 'id:hirose31' 
    30                 , 'nipotan'      : 'id:nipotan' 
    31                 , 'itkz'         : 'id:itkz' 
    32                 , 'xcezx'        : 'id:xcezx' 
    33                 , 'nyarla'       : 'id:nyarla-net' 
    34                 , 'tokuhirom'    : 'id:tokuhirom' 
    35                 , 'daisuke'      : 'id:lestrrat' 
    36                 , 'kana'         : 'id:ka-nacht' 
    37                 , 'cho45'        : 'id:cho45' 
    38                 , 'kan'          : 'id:kan' 
    39                 , 'zigorou'      : 'id:ZIGOROu' 
    40                 , 'masaki'       : 'id:ikasam_a' 
    41                 , 'holidays-l'   : 'id:holidays-l' 
    42                 , 'yoko'         : 'id:yksk' 
    43                 , 'mattn'        : 'id:mattn' 
    44                 , 'lopnor'       : 'id:lopnor' 
    45                 , 'gyuque'       : 'id:gyuque' 
    46                 , 'yugui'        : 'id:yugui' 
    47                 , 'akihito'      : 'id:t-akihito' 
    48                 , 'fujiwara'     : 'id:sfujiwara' 
    49                 , 'yusukebe'     : 'id:kamawada' 
    50                 , 'miyagawa'     : 'id:miyagawa' 
    51                 , 'topia'        : 'id:topia' 
    52                 , 'woremacx'     : 'id:woremacx' 
    53                 , 'amachang'     : 'id:amachang' 
    54                 , 'takesako'     : 'id:TAKESAKO' 
    55                 , 'eto'          : 'id:eto' 
    56                 , 'dankogai'     : 'id:dankogai' 
    57                 , 'jknaoya'      : 'id:jknaoya' 
    58                 , 'yasu'         : 'id:hirata_yasuyuki' 
    59                 , 'holygrail'    : 'id:HolyGrail' 
    60                 , 'elim'         : 'id:elim' 
    61                 , 'cheebow'      : 'id:cheebow' 
    62                 , 'ariela'       : 'http://www.gravatar.com/avatar.php?gravatar_id=dd5dfb5d738d5b5fb1c9a8b234945ea6&size=16'//'id:transrain' 
    63                 , 'tomyhero'     : 'id:tomyhero' 
    64                 , 'hetappi'      : 'id:hetappi' 
    65                 , 'coji'         : 'id:coji' 
    66                 , 'takemaru'     : 'id:teahut' 
    67                 , 'akasata'      : 'id:akasata' 
    68                 , 'okuryu'       : 'id:okuryu' 
    69                 , 'tasuku'       : 'id:tasukuchan' 
    70                 , 'snj14'        : 'id:snj14' 
    71                 , 'shachi'       : 'id:shachi_kk' 
    72                 , 'omoon'        : 'id:omoon' 
    73                 , 'aerith'       : 'id:aerith' 
    74                 , 'clouder'      : 'id:clouder' 
    75                 , 'takkyun'      : 'id:simpleboxes' 
    76                 , 'naoya_t'      : 'http://ntzone.info/misc/keroro.gif' 
    77                 , 'takefumi'     : 'id:takefumi' 
    78                 , 'walf443'      : 'id:walf443' 
    79                 , 'psychs'       : 'id:Psychs' 
    80                 , 'ewigkeit'     : 'id:Ewigkeit' 
    81                 , 'yoshiori'     : 'id:Yoshiori' 
    82                 , 'dev0000'      : 'http://www.developer0000.jp/16.jpg' 
    83                 , 'machu'        : 'id:kmachu' 
    84                 , 'norio_s'      : 'id:norio_s' 
    85                 , 'hsbt'         : 'id:h-sbt' 
    86                 , 'nori090'      : 'id:nori090' 
    87                 , 'lyokato'      : 'id:lyokato' 
    88                 , 'drgqst'       : 'id:drgqst' 
    89                 , 'mrkn'         : 'id:mrkn' 
    90                 , 'tfunato'      : 'id:tfunato' 
    91                 , 'tsuka'        : 'id:tsuka05' 
    92                 , 'niw'          : 'id:niw' 
    93                 , 'tomi-ru'      : 'id:tomi-ru' 
    94                 , 'yto'          : 'id:yto' 
    95                 , 'motemen'      : 'id:motemen' 
    96                 , 'shyouhei'     : 'http://mput.dip.jp/mput/images/icon.jpg' 
    97                 , 'm-takagi'     : 'http://www.gravatar.com/avatar.php?gravatar_id=c3f5a6e729b57f99cb9e7b3ca60152aa&size=16' 
    98                 , 'takano32'     : 'id:takano32' 
    99                 , 'kakutani'     : 'id:kakutani' 
    100                 , 'poppen'       : 'id:poppen' 
    101                 , 'kotas'        : 'id:kotas' 
    102                 , 'nekokak'      : 'id:nekokak' 
    103                 , 'tosik'        : 'id:tosik' 
    104                 , 'fbis'         : 'id:fbis' 
    105                 , 'rsk'          : 'id:rsky' 
    106                 , 'mallowlabs'   : 'id:mallowlabs' 
    107                 , 'nakamud'      : 'http://s3.amazonaws.com/twitter_production/profile_images/35752262/SP_mu-toko_mini.jpg' 
    108                 , 'chiba'        : 'id:nihen' 
    109                 , 'funaki'       : 'id:nob_funaki' 
    110                 , 'dzfl'         : 'id:dzfl' 
    111                 , 'shimooka'     : 'id:shimooka' 
    112 //              , 'noriaki'      : 'http://s3.amazonaws.com/twitter_production/profile_images/40454132/2f94929d_mini.jpg' 
    113                 , 'suztomo'      : 'id:suztomo' 
    114                 , 'klm'          : 'id:k1m' 
    115                 , 'akio0911'     : 'id:akio0911' 
    116                 , 'cactusman'    : 'id:cactusman' 
    117                 , 'vkgtaro'      : 'id:vkgtaro' 
    118                 , 'todesking'    : 'http://data.tumblr.com/avatar_1fe558be2f23_16.gif' 
    119                 , 'sho'          : 'id:sho' 
    120                 , 'a2c'          : 'id:a2c' 
    121                 , 'hidek'        : 'http://www.gravatar.com/avatar.php?gravatar_id=7d55041891ece2f35375108aa2d27255&size=16'//'id:hide-K'' 
    122                 , 'yhara'        : 'id:yhara' 
    123                 , 'PoohKid'      : 'id:PoohKid' 
    124                 , 'samurai20000' : 'id:samurai20000' 
    125                 , 'yasuyuki'     : 'id:YaSuYuKi' 
    126                 , 'mootoh'       : 'id:moira' 
    127                 , 'tokada'       : 'id:tokada' 
    128                 , 'llamerada'    : 'id:llamerada' 
    129                 , 'shn'          : 'id:shn' 
    130                 , 'nejimaki'     : 'id:firewood' 
    131                 , 'kan'          : 'id:mikihoshi' 
    132                 , 'hideden'      : 'id:hideden' 
    133                 , 'niku'         : 'http://niku.name/favicon.ico' 
    134                 , 'ohac'         : 'id:ohac' 
    135                 , 'hio'          : 'http://www.gravatar.com/avatar.php?gravatar_id=a72d2120c066d47ad3d29c17b2f4db72&size=16' 
    136                 , 'os0x'         : 'id:os0x' 
    137                 , 'akiyan'       : 'id:akiyan' 
    138                 , 'inudog'       : 'http://eringi.com/favicon.ico' 
    139                 , 'suzuki'       : 'id:zatsubun' 
    140                 , 'haruka'       : 'http://drumsoft.com/favicon.ico' 
    141                 , 'syttru'       : 'id:syttru' 
    142                 , 'iskwrsk'      : 'id:iskwrsk' 
    143                 , 'komatsuna'    : 'http://data.tumblr.com/avatar_82a9ef4053ec_30.gif' 
    144                 , 'tarchan'      : 'id:tarchan' 
    145                 , 'tyoro'        : 'id:tyoro1210' 
    146                 , 'Sore_0'       : 'id:Sore_0' 
    147                 , 'teramako'     : 'id:teramako' 
    148                 , 'kamipo'       : 'id:kamipo' 
    149                 , 'ihag'         : 'id:ihag' 
    150                 , 'valda'        : 'http://data.tumblr.com/avatar_a5fb0192ec7d_16.gif' 
    151                 , 'gan2'         : 'id:gan2' 
    152                 , 'sasezaki'     : 'id:sasezaki' 
    153         } 
    154  
    155         , installAll : function () { 
    156                 TracUtils.addJumpToReposLink(); 
    157                 TracUtils.clickableCodeArea(); 
    158                 TracUtils.addBodyClass(); 
    159                 TracUtils.addCommitterRecentCommits(); 
    160                 TracUtils.addRecentChangesForProject(); 
    161                 TracUtils.addUserscriptsUtils(); 
    162                 TracUtils.AuthorIcons.showAuthorIcons(); 
    163         } 
    164  
    165         , isWikiStart : function () { 
    166                 var pn = location.pathname 
    167                 return ( 
    168                         pn == "/share/wiki/WikiStart" || 
    169                         pn == "/share/wiki/" || 
    170                         pn == "/share/wiki" || 
    171                         pn == "/share/" || 
    172                         pn == "/share" 
    173                 ); 
    174         } 
    175  
    176         , getSourcePath : function (tracPath) { 
    177                 return String(tracPath).replace(TracUtils.TRAC_BASE + "/browser", TracUtils.SVN_REPOS_BASE).replace(/\?.*/, ''); 
    178         } 
    179  
    180         /** 
    181          * Browse Source のときに該当する svn web にすぐ翔べるリンクと 
    182          * ?rev= をはずすリンクを追加 
    183          */ 
    184         , addJumpToReposLink : function () { 
    185                 var aboutLink  = $("#metanav li.last a"); 
    186                 if (location.pathname.indexOf("/share/browser") == 0) { 
    187                         var sourcePath = TracUtils.getSourcePath(location); 
    188                         aboutLink.replaceWith( 
    189                                 $("<a title='Jump to Repository'>SVN Web</a>").attr("href", sourcePath) 
    190                         ); 
    191                         $("#ctxtnav li:first").after($("<li/>").append($("<a>SVN Web</a>").attr("href", sourcePath))); 
    192                         $("#ctxtnav li:first").after($("<li/>").append($("<a>Latest Revision</a>").attr("href", location.pathname))); 
    193                 } else { 
    194                         aboutLink.replaceWith( 
    195                                 $("<a title='Jump to Repository'>SVN Web</a>").attr("href", TracUtils.SVN_REPOS_BASE) 
    196                         ); 
    197                 } 
    198         } 
    199  
    200         /** 
    201          * Wiki に書かれた JS のコードを実行可能に 
    202          * 冒頭に //Executable と書くと実行できるようになる。 
    203          */ 
    204         , clickableCodeArea : function () { 
    205                 var start = "//Executable"; 
    206                 $("//*[@id='content']//div[@class='code']/pre[contains(.//span[@class='code-comment'],'"+start+"')]").each(function () { 
    207                         var e  = $(this); 
    208                         var js = e.text(); 
    209                         var a  = $("<a href='javascript:void(156)'>Click to execute.</a>").click(function () { 
    210                                 eval(js); 
    211                         }).css({ 
    212                                 fontSize   : "80%", 
    213                                 fontWeight : "bold", 
    214                                 border     : "1px solid #ccc", 
    215                                 display    : "block", 
    216                                 width      : "10em", 
    217                                 padding    : "0.2em", 
    218                                 margin     : "0 auto 0.2em 0", 
    219                                 textAlign  : "center" 
    220                         }).insertBefore(e.parent()); 
    221                 }); 
    222         } 
    223  
    224         /** 
    225          * CSS 補佐用 (IE6 で CSS セレクタの対応が貧弱なため) 
    226          */ 
    227         , addBodyClass : function () { 
    228                 if (location.pathname.indexOf("/share/wiki/Committers") == 0) { 
    229                         document.body.className = "committer"; 
    230                 } 
    231                 if (TracUtils.isWikiStart()) { 
    232                         $("//*[@id='Committers']/following-sibling::ul[1]").addClass("committer-list"); 
    233                         $("//*[@id='Projects']/following-sibling::ul[1]").addClass("project-list"); 
    234                 } 
    235         } 
    236  
    237         /** 
    238          * コミッタページにその人の最近のコミットリストを表示する。 
    239          * Trac の検索機能を使っているので、コミットログに名前をかきまくると 
    240          * 検索結果の1ページ目 == 10個しかみていないので 
    241          * その人の最近のコミットがなかったことになるいじめができる。 
    242          */ 
    243         , addCommitterRecentCommits : function () { 
    244                 if (!location.search && !/^\/share\/wiki\/Committers\/([^\/]{3,})/.test(location.pathname)) return; 
    245                 var author = RegExp.$1; 
    246                 $.get(TracUtils.TRAC_BASE+"/search?q="+author+"&noquickjump=1&changeset=on", {}, function (data) { 
    247                         $("<div class='recent-commits'><h2>Recent Commits</h2></div>").append( 
    248                                 $(data).xfind(".//dl") 
    249                                         .xfind("dd[span[@class='author'] != 'By "+author+"']") 
    250                                                 .xfind("preceding-sibling::dt[1] | preceding-sibling::dd[1]").remove().end() 
    251                                         .remove() 
    252                                         .end() 
    253                         ).appendTo($("#searchable, #content").get(0)); 
    254                 }); 
    255         } 
    256  
    257         /** 
    258          * プロジェクトページへ最近のコミットリストを表示する。 
    259          * {{{ 
    260          * svn co http://svn.coderepos.org/share/... 
    261          * }}} 
    262          * のように co と checkout, export の書いてあるページが対象。 
    263          */ 
    264         , addRecentChangesForProject : function () { 
    265                 // IE だとなぜかどうしようもなく遅いので処理しない 
    266                 if ($.browser.msie || !TracUtils.isWikiStart() && location.pathname.indexOf("/share/wiki/") != 0) return; 
    267                 var target_re = RegExp("svn\\s+(?:c(?:o|heckout)|export)\\s+"+TracUtils.SVN_REPOS_BASE+"(\\S+)"); 
    268                 $("#searchable pre").each(function () { 
    269                         var m = $(this).text().match(target_re); 
    270                         if (!m) return; 
    271                         var path = m[1].replace(/\/trunk\/?$/, ""); 
    272                         var url  = TracUtils.TRAC_BASE + "/log" + path; 
    273                         $.get(url, {}, function (data) { 
    274                                 // 高速化と、jQuery がだす getElementsByTagName がないという 
    275                                 // エラーを回避するため正規表現 
    276                                 data = data.match(/<table id="chglist"[\S\s]+<\/table>/)[0]; 
    277                                 $("<div><h2><a href='"+url+"'>Project Recent Changes</a></h2></div>") 
    278                                         .append( 
    279                                                 $(data) 
    280                                                         .find("tr:gt(10)").remove().end() 
    281                                                         .find("th.diff, td.diff, th.change, td.change, th.rev, td.rev").remove().end() 
    282                                         ) 
    283                                 .appendTo("#searchable"); 
    284                         }); 
    285                         return false; 
    286                 }); 
    287         } 
    288  
    289         /** 
    290          * Userscripts のユーティリティ 
    291          */ 
    292         , addUserscriptsUtils : function () { 
    293                 if (($.browser.mozilla || $.browser.safari)) { 
    294                         // 尻? 
    295                         var isUserJS = function(path) { 
    296                                 return /\.user\.js$/.test(path); 
    297                         }; 
    298                         // user.js のページがインストール画面でてうざいのをなんとかする。 
    299                         // あんまり広範囲にやりたくないので決め打ち 
    300                         if (location.pathname.substring(0, 42) == "/share/browser/lang/javascript/userscripts") { 
    301                                 $("a").each(function () { 
    302                                         if (isUserJS(this.href)) this.href += "?"; 
    303                                 }); 
    304                         } 
    305  
    306                         // user.js のページにインストールURLを追加する. 
    307                         if (isUserJS(location.pathname)) { 
    308                                 var uri = TracUtils.getSourcePath(location); 
    309                                 $('<a/>').attr({ 
    310                                         href  : uri, 
    311                                         title : uri.split('/').slice(-1) + ' をインストールする' 
    312                                 }) 
    313                                 .text('インストール - ' + uri) 
    314                                 .insertAfter($('h1:first')); 
    315                         } 
    316                 } 
    317         } 
    318  
    319         /** 
    320          * Trac のあらゆるところに設定されたアイコンを表示する。 
    321          * TracAuthorIcon.js を jQuery/XPath 依存にして簡略化したもの 
    322          */ 
    323         , AuthorIcons  : { 
    324                 showAuthorIcons : function () { 
    325                         var authors = []; 
    326                         switch ($("#content").attr("class")) { 
    327                                 case 'browser': 
    328                                         $('//*[@id="info"]//th').each(function () { 
    329                                                 authors.push([this, $(this).text().match(/checked in by ([^,]+)/)[1]]); 
    330                                         }); 
    331                                         $('//*[@id="dirlist"]//span[@class="author"]').each(function () { 
    332                                                 authors.push([this, $(this).text().match(/^([^:]+):/)[1]]); 
    333                                         }); 
    334                                         break; 
    335                                 case 'timeline': 
    336                                         $('//*[@id="content"]//dt/a').each(function () { 
    337                                                 authors.push([this, this.lastChild.nodeValue.match(/by\s+(.+)$/)[1]]); 
    338                                         }); 
    339                                         break; 
    340                                 case 'log': 
    341                                 case 'changeset': 
    342                                         authors = $('(//*[@id="overview"] | //*[@id="chglist"])//*[@class="author"]'); 
    343                                         break; 
    344                                 case 'wiki': 
    345                                         if (/^\/share\/wiki\/Committers\/([^\/]+)/.test(location.pathname)) { 
    346                                                 var author  = RegExp.$1; 
    347                                                 var iconurl = TracUtils.AuthorIcons.getIconByAuthor(author); 
    348                                                 if (!iconurl) break; 
    349                                                 var img = $("<img width='16' height='16' alt='' class='committer-icon'/>").attr("src", iconurl); 
    350                                                 var h1  = $("//*[@id='searchable']/h1[1]"); 
    351                                                 if (h1.length) { 
    352                                                         h1.append(img); 
    353                                                 } else { 
    354                                                         $("#content").prepend(img); 
    355                                                 } 
    356                                         } else 
    357                                         if (TracUtils.isWikiStart()) { 
    358                                                 // This is for committer list of CodeRepos. 
    359                                                 authors = $("//*[@id='Committers']/following-sibling::ul[1]//a[contains(@href, '/share/wiki/Committers/')]"); 
    360                                         } 
    361                                         break; 
    362                                 default: break; 
    363                         } 
    364  
    365                         for (var i = 0, n = authors.length; i < n; i++) { 
    366                                 TracUtils.AuthorIcons.insertIcon(authors[i]); 
    367                         } 
    368  
    369                         $("#metanav ul li.first").each(function () { 
    370                                 var uname = $(this).text().match(/logged in as ([^,]+)/); 
    371                                 if (uname) { 
    372                                         var a = $("<a/>").attr("href", TracUtils.AuthorIcons.getAuthorPage(uname[1])).append(uname[1]); 
    373                                         a.appendTo( 
    374                                                 $(this).empty().append("logged in as ") 
    375                                         ); 
    376                                         TracUtils.AuthorIcons.insertIcon(a); 
    377                                 } 
    378                         }); 
    379                 } 
    380  
    381                 , getIconByAuthor : function (author) { 
    382                         var iconUrl = TracUtils.AUTHOR_ICONS[author] || TracUtils.AUTHOR_ICONS["*default*"]; 
    383  
    384                         // Hatena ID 
    385                         iconUrl = iconUrl.replace(/^id:(([\w-]{2})[\w-]*)/, 'http://www.hatena.ne.jp/users/$2/$1/profile_s.gif'); 
    386  
    387                         return iconUrl; 
    388                 } 
    389  
    390                 , insertIcon : function (authorEOrArray) { 
    391                         var authorE, author; 
    392                         if (authorEOrArray instanceof Array) { 
    393                                 authorE = $(authorEOrArray[0]); 
    394                                 author  =   authorEOrArray[1]; 
    395                         } else { 
    396                                 authorE = $(authorEOrArray); 
    397                                 author  = authorE.text().replace(/^\s+|\??\s*$/g, ''); 
    398                         } 
    399  
    400                         var iconurl = TracUtils.AuthorIcons.getIconByAuthor(author); 
    401                         if (!iconurl) return; 
    402  
    403                         var img = $("<img width='16' height='16' alt='' class='committer-icon'/>") 
    404                                 .attr("src", iconurl) 
    405                                 .css("margin-right", "0.5em"); 
    406  
    407                         var a = $("<a style='background-image: none ! important; padding-left: 0pt; border: none;'/>") 
    408                                 .attr("href", TracUtils.AuthorIcons.getAuthorPage(author)) 
    409                                 .append(img); 
    410  
    411                         authorE.prepend(a); 
    412                 } 
    413  
    414                 , getAuthorPage : function (author) { 
    415                         return TracUtils.TRAC_BASE + "/wiki/Committers/" + author; 
    416                 } 
    417         } 
     14        SVN_REPOS_BASE : 'http://svn.coderepos.org/share', 
     15        TRAC_BASE      : 'http://coderepos.org/share', 
     16        AUTHOR_ICONS   : { 
     17                //'tomoyo'       : 'http://www.hatena.ne.jp/users/ma/mayuki/profile_s.gif' 
     18                //'tomoyo'       : 'id:mayuki' 
     19                  '*default*'    : 'http://coderepos.org/share/htdocs/default-author-icon.png' 
     20                , 'mayuki'       : 'id:mayuki' 
     21                , 'yappo'        : 'id:yappo' 
     22                , 'typester'     : 'http://www.gravatar.com/avatar.php?gravatar_id=fbc6511bcc0649366086c0445fb456d3&size=16'//'id:typester' 
     23                , 'kentaro'      : 'id:antipop' 
     24                , 'mizzy'        : 'id:gosukenator' 
     25                , 'charsbar'     : 'id:charsbar' 
     26                , 'secondlife'   : 'id:secondlife' 
     27                , 'kazeburo'     : 'id:kazeburo' 
     28                , 'otsune'       : 'id:otsune' 
     29                , 'hirose31'     : 'id:hirose31' 
     30                , 'nipotan'      : 'id:nipotan' 
     31                , 'itkz'         : 'id:itkz' 
     32                , 'xcezx'        : 'id:xcezx' 
     33                , 'nyarla'       : 'id:nyarla-net' 
     34                , 'tokuhirom'    : 'id:tokuhirom' 
     35                , 'daisuke'      : 'id:lestrrat' 
     36                , 'kana'         : 'id:ka-nacht' 
     37                , 'cho45'        : 'id:cho45' 
     38                , 'kan'          : 'id:kan' 
     39                , 'zigorou'      : 'id:ZIGOROu' 
     40                , 'masaki'       : 'id:ikasam_a' 
     41                , 'holidays-l'   : 'id:holidays-l' 
     42                , 'yoko'         : 'id:yksk' 
     43                , 'mattn'        : 'id:mattn' 
     44                , 'lopnor'       : 'id:lopnor' 
     45                , 'gyuque'       : 'id:gyuque' 
     46                , 'yugui'        : 'id:yugui' 
     47                , 'akihito'      : 'id:t-akihito' 
     48                , 'fujiwara'     : 'id:sfujiwara' 
     49                , 'yusukebe'     : 'id:kamawada' 
     50                , 'miyagawa'     : 'id:miyagawa' 
     51                , 'topia'        : 'id:topia' 
     52                , 'woremacx'     : 'id:woremacx' 
     53                , 'amachang'     : 'id:amachang' 
     54                , 'takesako'     : 'id:TAKESAKO' 
     55                , 'eto'          : 'id:eto' 
     56                , 'dankogai'     : 'id:dankogai' 
     57                , 'jknaoya'      : 'id:jknaoya' 
     58                , 'yasu'         : 'id:hirata_yasuyuki' 
     59                , 'holygrail'    : 'id:HolyGrail' 
     60                , 'elim'         : 'id:elim' 
     61                , 'cheebow'      : 'id:cheebow' 
     62                , 'ariela'       : 'http://www.gravatar.com/avatar.php?gravatar_id=dd5dfb5d738d5b5fb1c9a8b234945ea6&size=16'//'id:transrain' 
     63                , 'tomyhero'     : 'id:tomyhero' 
     64                , 'hetappi'      : 'id:hetappi' 
     65                , 'coji'         : 'id:coji' 
     66                , 'takemaru'     : 'id:teahut' 
     67                , 'akasata'      : 'id:akasata' 
     68                , 'okuryu'       : 'id:okuryu' 
     69                , 'tasuku'       : 'id:tasukuchan' 
     70                , 'snj14'        : 'id:snj14' 
     71                , 'shachi'       : 'id:shachi_kk' 
     72                , 'omoon'        : 'id:omoon' 
     73                , 'aerith'       : 'id:aerith' 
     74                , 'clouder'      : 'id:clouder' 
     75                , 'takkyun'      : 'id:simpleboxes' 
     76                , 'naoya_t'      : 'http://ntzone.info/misc/keroro.gif' 
     77                , 'takefumi'     : 'id:takefumi' 
     78                , 'walf443'      : 'id:walf443' 
     79                , 'psychs'       : 'id:Psychs' 
     80                , 'ewigkeit'     : 'id:Ewigkeit' 
     81                , 'yoshiori'     : 'id:Yoshiori' 
     82                , 'dev0000'      : 'http://www.developer0000.jp/16.jpg' 
     83                , 'machu'        : 'id:kmachu' 
     84                , 'norio_s'      : 'id:norio_s' 
     85                , 'hsbt'         : 'id:h-sbt' 
     86                , 'nori090'      : 'id:nori090' 
     87                , 'lyokato'      : 'id:lyokato' 
     88                , 'drgqst'       : 'id:drgqst' 
     89                , 'mrkn'         : 'id:mrkn' 
     90                , 'tfunato'      : 'id:tfunato' 
     91                , 'tsuka'        : 'id:tsuka05' 
     92                , 'niw'          : 'id:niw' 
     93                , 'tomi-ru'      : 'id:tomi-ru' 
     94                , 'yto'          : 'id:yto' 
     95                , 'motemen'      : 'id:motemen' 
     96                , 'shyouhei'     : 'http://mput.dip.jp/mput/images/icon.jpg' 
     97                , 'm-takagi'     : 'http://www.gravatar.com/avatar.php?gravatar_id=c3f5a6e729b57f99cb9e7b3ca60152aa&size=16' 
     98                , 'takano32'     : 'id:takano32' 
     99                , 'kakutani'     : 'id:kakutani' 
     100                , 'poppen'       : 'id:poppen' 
     101                , 'kotas'        : 'id:kotas' 
     102                , 'nekokak'      : 'id:nekokak' 
     103                , 'tosik'        : 'id:tosik' 
     104                , 'fbis'         : 'id:fbis' 
     105                , 'rsk'          : 'id:rsky' 
     106                , 'mallowlabs'   : 'id:mallowlabs' 
     107                , 'nakamud'      : 'http://s3.amazonaws.com/twitter_production/profile_images/35752262/SP_mu-toko_mini.jpg' 
     108                , 'chiba'        : 'id:nihen' 
     109                , 'funaki'       : 'id:nob_funaki' 
     110                , 'dzfl'         : 'id:dzfl' 
     111                , 'shimooka'     : 'id:shimooka' 
     112//              , 'noriaki'      : 'http://s3.amazonaws.com/twitter_production/profile_images/40454132/2f94929d_mini.jpg' 
     113                , 'suztomo'      : 'id:suztomo' 
     114                , 'klm'          : 'id:k1m' 
     115                , 'akio0911'     : 'id:akio0911' 
     116                , 'cactusman'    : 'id:cactusman' 
     117                , 'vkgtaro'      : 'id:vkgtaro' 
     118                , 'todesking'    : 'http://data.tumblr.com/avatar_1fe558be2f23_16.gif' 
     119                , 'sho'          : 'id:sho' 
     120                , 'a2c'          : 'id:a2c' 
     121                , 'hidek'        : 'http://www.gravatar.com/avatar.php?gravatar_id=7d55041891ece2f35375108aa2d27255&size=16'//'id:hide-K'' 
     122                , 'yhara'        : 'id:yhara' 
     123                , 'PoohKid'      : 'id:PoohKid' 
     124                , 'samurai20000' : 'id:samurai20000' 
     125                , 'yasuyuki'     : 'id:YaSuYuKi' 
     126                , 'mootoh'       : 'id:moira' 
     127                , 'tokada'       : 'id:tokada' 
     128                , 'llamerada'    : 'id:llamerada' 
     129                , 'shn'          : 'id:shn' 
     130                , 'nejimaki'     : 'id:firewood' 
     131                , 'kan'          : 'id:mikihoshi' 
     132                , 'hideden'      : 'id:hideden' 
     133                , 'niku'         : 'http://niku.name/favicon.ico' 
     134                , 'ohac'         : 'id:ohac' 
     135                , 'hio'          : 'http://www.gravatar.com/avatar.php?gravatar_id=a72d2120c066d47ad3d29c17b2f4db72&size=16' 
     136                , 'os0x'         : 'id:os0x' 
     137                , 'akiyan'       : 'id:akiyan' 
     138                , 'inudog'       : 'http://eringi.com/favicon.ico' 
     139                , 'suzuki'       : 'id:zatsubun' 
     140                , 'haruka'       : 'http://drumsoft.com/favicon.ico' 
     141                , 'syttru'       : 'id:syttru' 
     142                , 'iskwrsk'      : 'id:iskwrsk' 
     143                , 'komatsuna'    : 'http://data.tumblr.com/avatar_82a9ef4053ec_30.gif' 
     144                , 'tarchan'      : 'id:tarchan' 
     145                , 'tyoro'        : 'id:tyoro1210' 
     146                , 'Sore_0'       : 'id:Sore_0' 
     147                , 'teramako'     : 'id:teramako' 
     148                , 'kamipo'       : 'id:kamipo' 
     149                , 'ihag'         : 'id:ihag' 
     150                , 'valda'        : 'http://data.tumblr.com/avatar_a5fb0192ec7d_16.gif' 
     151                , 'gan2'         : 'id:gan2' 
     152                , 'sasezaki'     : 'id:sasezaki' 
     153                , 'kdoya'        : 'id:kdoya' 
     154        } 
     155 
     156        , installAll : function () { 
     157                TracUtils.addJumpToReposLink(); 
     158                TracUtils.clickableCodeArea(); 
     159                TracUtils.addBodyClass(); 
     160                TracUtils.addCommitterRecentCommits(); 
     161                TracUtils.addRecentChangesForProject(); 
     162                TracUtils.addUserscriptsUtils(); 
     163                TracUtils.AuthorIcons.showAuthorIcons(); 
     164        } 
     165 
     166        , isWikiStart : function () { 
     167                var pn = location.pathname 
     168                return ( 
     169                        pn == "/share/wiki/WikiStart" || 
     170                        pn == "/share/wiki/" || 
     171                        pn == "/share/wiki" || 
     172                        pn == "/share/" || 
     173                        pn == "/share" 
     174                ); 
     175        } 
     176 
     177        , getSourcePath : function (tracPath) { 
     178                return String(tracPath).replace(TracUtils.TRAC_BASE + "/browser", TracUtils.SVN_REPOS_BASE).replace(/\?.*/, ''); 
     179        } 
     180 
     181        /** 
     182         * Browse Source のときに該当する svn web にすぐ翔べるリンクと 
     183         * ?rev= をはずすリンクを追加 
     184         */ 
     185        , addJumpToReposLink : function () { 
     186                var aboutLink  = $("#metanav li.last a"); 
     187                if (location.pathname.indexOf("/share/browser") == 0) { 
     188                        var sourcePath = TracUtils.getSourcePath(location); 
     189                        aboutLink.replaceWith( 
     190                                $("<a title='Jump to Repository'>SVN Web</a>").attr("href", sourcePath) 
     191                        ); 
     192                        $("#ctxtnav li:first").after($("<li/>").append($("<a>SVN Web</a>").attr("href", sourcePath))); 
     193                        $("#ctxtnav li:first").after($("<li/>").append($("<a>Latest Revision</a>").attr("href", location.pathname))); 
     194                } else { 
     195                        aboutLink.replaceWith( 
     196                                $("<a title='Jump to Repository'>SVN Web</a>").attr("href", TracUtils.SVN_REPOS_BASE) 
     197                        ); 
     198                } 
     199        } 
     200 
     201        /** 
     202         * Wiki に書かれた JS のコードを実行可能に 
     203         * 冒頭に //Executable と書くと実行できるようになる。 
     204         */ 
     205        , clickableCodeArea : function () { 
     206                var start = "//Executable"; 
     207                $("//*[@id='content']//div[@class='code']/pre[contains(.//span[@class='code-comment'],'"+start+"')]").each(function () { 
     208                        var e  = $(this); 
     209                        var js = e.text(); 
     210                        var a  = $("<a href='javascript:void(156)'>Click to execute.</a>").click(function () { 
     211                                eval(js); 
     212                        }).css({ 
     213                                fontSize   : "80%", 
     214                                fontWeight : "bold", 
     215                                border     : "1px solid #ccc", 
     216                                display    : "block", 
     217                                width      : "10em", 
     218                                padding    : "0.2em", 
     219                                margin     : "0 auto 0.2em 0", 
     220                                textAlign  : "center" 
     221                        }).insertBefore(e.parent()); 
     222                }); 
     223        } 
     224 
     225        /** 
     226         * CSS 補佐用 (IE6 で CSS セレクタの対応が貧弱なため) 
     227         */ 
     228        , addBodyClass : function () { 
     229                if (location.pathname.indexOf("/share/wiki/Committers") == 0) { 
     230                        document.body.className = "committer"; 
     231                } 
     232                if (TracUtils.isWikiStart()) { 
     233                        $("//*[@id='Committers']/following-sibling::ul[1]").addClass("committer-list"); 
     234                        $("//*[@id='Projects']/following-sibling::ul[1]").addClass("project-list"); 
     235                } 
     236        } 
     237 
     238        /** 
     239         * コミッタページにその人の最近のコミットリストを表示する。 
     240         * Trac の検索機能を使っているので、コミットログに名前をかきまくると 
     241         * 検索結果の1ページ目 == 10個しかみていないので 
     242         * その人の最近のコミットがなかったことになるいじめができる。 
     243         */ 
     244        , addCommitterRecentCommits : function () { 
     245                if (!location.search && !/^\/share\/wiki\/Committers\/([^\/]{3,})/.test(location.pathname)) return; 
     246                var author = RegExp.$1; 
     247                $.get(TracUtils.TRAC_BASE+"/search?q="+author+"&noquickjump=1&changeset=on", {}, function (data) { 
     248                        $("<div class='recent-commits'><h2>Recent Commits</h2></div>").append( 
     249                                $(data).xfind(".//dl") 
     250                                        .xfind("dd[span[@class='author'] != 'By "+author+"']") 
     251                                                .xfind("preceding-sibling::dt[1] | preceding-sibling::dd[1]").remove().end() 
     252                                        .remove() 
     253                                        .end() 
     254                        ).appendTo($("#searchable, #content").get(0)); 
     255                }); 
     256        } 
     257 
     258        /** 
     259         * プロジェクトページへ最近のコミットリストを表示する。 
     260         * {{{ 
     261         * svn co http://svn.coderepos.org/share/... 
     262         * }}} 
     263         * のように co と checkout, export の書いてあるページが対象。 
     264         */ 
     265        , addRecentChangesForProject : function () { 
     266                // IE だとなぜかどうしようもなく遅いので処理しない 
     267                if ($.browser.msie || !TracUtils.isWikiStart() && location.pathname.indexOf("/share/wiki/") != 0) return; 
     268                var target_re = RegExp("svn\\s+(?:c(?:o|heckout)|export)\\s+"+TracUtils.SVN_REPOS_BASE+"(\\S+)"); 
     269                $("#searchable pre").each(function () { 
     270                        var m = $(this).text().match(target_re); 
     271                        if (!m) return; 
     272                        var path = m[1].replace(/\/trunk\/?$/, ""); 
     273                        var url  = TracUtils.TRAC_BASE + "/log" + path; 
     274                        $.get(url, {}, function (data) { 
     275                                // 高速化と、jQuery がだす getElementsByTagName がないという 
     276                                // エラーを回避するため正規表現 
     277                                data = data.match(/<table id="chglist"[\S\s]+<\/table>/)[0]; 
     278                                $("<div><h2><a href='"+url+"'>Project Recent Changes</a></h2></div>") 
     279                                        .append( 
     280                                                $(data) 
     281                                                        .find("tr:gt(10)").remove().end() 
     282                                                        .find("th.diff, td.diff, th.change, td.change, th.rev, td.rev").remove().end() 
     283                                        ) 
     284                                .appendTo("#searchable"); 
     285                        }); 
     286                        return false; 
     287                }); 
     288        } 
     289 
     290        /** 
     291         * Userscripts のユーティリティ 
     292         */ 
     293        , addUserscriptsUtils : function () { 
     294                if (($.browser.mozilla || $.browser.safari)) { 
     295                        // 尻? 
     296                        var isUserJS = function(path) { 
     297                                return /\.user\.js$/.test(path); 
     298                        }; 
     299                        // user.js のページがインストール画面でてうざいのをなんとかする。 
     300                        // あんまり広範囲にやりたくないので決め打ち 
     301                        if (location.pathname.substring(0, 42) == "/share/browser/lang/javascript/userscripts") { 
     302                                $("a").each(function () { 
     303                                        if (isUserJS(this.href)) this.href += "?"; 
     304                                }); 
     305                        } 
     306 
     307                        // user.js のページにインストールURLを追加する. 
     308                        if (isUserJS(location.pathname)) { 
     309                                var uri = TracUtils.getSourcePath(location); 
     310                                $('<a/>').attr({ 
     311                                        href  : uri, 
     312                                        title : uri.split('/').slice(-1) + ' をインストールする' 
     313                                }) 
     314                                .text('インストール - ' + uri) 
     315                                .insertAfter($('h1:first')); 
     316                        } 
     317                } 
     318        } 
     319 
     320        /** 
     321         * Trac のあらゆるところに設定されたアイコンを表示する。 
     322         * TracAuthorIcon.js を jQuery/XPath 依存にして簡略化したもの 
     323         */ 
     324        , AuthorIcons  : { 
     325                showAuthorIcons : function () { 
     326                        var authors = []; 
     327                        switch ($("#content").attr("class")) { 
     328                                case 'browser': 
     329                                        $('//*[@id="info"]//th').each(function () { 
     330                                                authors.push([this, $(this).text().match(/checked in by ([^,]+)/)[1]]); 
     331                                        }); 
     332                                        $('//*[@id="dirlist"]//span[@class="author"]').each(function () { 
     333                                                authors.push([this, $(this).text().match(/^([^:]+):/)[1]]); 
     334                                        }); 
     335                                        break; 
     336                                case 'timeline': 
     337                                        $('//*[@id="content"]//dt/a').each(function () { 
     338                                                authors.push([this, this.lastChild.nodeValue.match(/by\s+(.+)$/)[1]]); 
     339                                        }); 
     340                                        break; 
     341                                case 'log': 
     342                                case 'changeset': 
     343                                        authors = $('(//*[@id="overview"] | //*[@id="chglist"])//*[@class="author"]'); 
     344                                        break; 
     345                                case 'wiki': 
     346                                        if (/^\/share\/wiki\/Committers\/([^\/]+)/.test(location.pathname)) { 
     347                                                var author  = RegExp.$1; 
     348                                                var iconurl = TracUtils.AuthorIcons.getIconByAuthor(author); 
     349                                                if (!iconurl) break; 
     350                                                var img = $("<img width='16' height='16' alt='' class='committer-icon'/>").attr("src", iconurl); 
     351                                                var h1  = $("//*[@id='searchable']/h1[1]"); 
     352                                                if (h1.length) { 
     353                                                        h1.append(img); 
     354                                                } else { 
     355                                                        $("#content").prepend(img); 
     356                                                } 
     357                                        } else 
     358                                        if (TracUtils.isWikiStart()) { 
     359                                                // This is for committer list of CodeRepos. 
     360                                                authors = $("//*[@id='Committers']/following-sibling::ul[1]//a[contains(@href, '/share/wiki/Committers/')]"); 
     361                                        } 
     362                                        break; 
     363                                default: break; 
     364                        } 
     365 
     366                        for (var i = 0, n = authors.length; i < n; i++) { 
     367                                TracUtils.AuthorIcons.insertIcon(authors[i]); 
     368                        } 
     369 
     370                        $("#metanav ul li.first").each(function () { 
     371                                var uname = $(this).text().match(/logged in as ([^,]+)/); 
     372                                if (uname) { 
     373                                        var a = $("<a/>").attr("href", TracUtils.AuthorIcons.getAuthorPage(uname[1])).append(uname[1]); 
     374                                        a.appendTo( 
     375                                                $(this).empty().append("logged in as ") 
     376                                        ); 
     377                                        TracUtils.AuthorIcons.insertIcon(a); 
     378                                } 
     379                        }); 
     380                } 
     381 
     382                , getIconByAuthor : function (author) { 
     383                        var iconUrl = TracUtils.AUTHOR_ICONS[author] || TracUtils.AUTHOR_ICONS["*default*"]; 
     384 
     385                        // Hatena ID 
     386                        iconUrl = iconUrl.replace(/^id:(([\w-]{2})[\w-]*)/, 'http://www.hatena.ne.jp/users/$2/$1/profile_s.gif'); 
     387 
     388                        return iconUrl; 
     389                } 
     390 
     391                , insertIcon : function (authorEOrArray) { 
     392                        var authorE, author; 
     393                        if (authorEOrArray instanceof Array) { 
     394                                authorE = $(authorEOrArray[0]); 
     395                                author  =   authorEOrArray[1]; 
     396                        } else { 
     397                                authorE = $(authorEOrArray); 
     398                                author  = authorE.text().replace(/^\s+|\??\s*$/g, ''); 
     399                        } 
     400 
     401                        var iconurl = TracUtils.AuthorIcons.getIconByAuthor(author); 
     402                        if (!iconurl) return; 
     403 
     404                        var img = $("<img width='16' height='16' alt='' class='committer-icon'/>") 
     405                                .attr("src", iconurl) 
     406                                .css("margin-right", "0.5em"); 
     407 
     408                        var a = $("<a style='background-image: none ! important; padding-left: 0pt; border: none;'/>") 
     409                                .attr("href", TracUtils.AuthorIcons.getAuthorPage(author)) 
     410                                .append(img); 
     411 
     412                        authorE.prepend(a); 
     413                } 
     414 
     415                , getAuthorPage : function (author) { 
     416                        return TracUtils.TRAC_BASE + "/wiki/Committers/" + author; 
     417                } 
     418        } 
    418419}; 
    419420