// Package Name: Generic Browsing Hotkey
// Version: 1.2
// License: BSD License
// Author: chency (chency _AT_ safego.net)
// Description: 針對 wretch/pchome/xuite/pixnet/yahoo/pbase/dcview 各線上相簿, 支援
//    1.統一前後頁熱鍵, 上頁(X,Left), 下頁(C,Right).
//    2.自動定位至相片.
//    3.點選相片跳下頁.
//    4.廣告頁面自動跳頁(pchome).
//    5.預取(prefetch)下頁相片(! yahoo).
//    6.累積模式(piling mode), 按 F 鍵啟動,
//        啟動時會觸發自動載入隨後所有相片於頁尾, 並在 title 顯示進度.
//        同時可續按 F 鍵, 將自動定位至各張新載入相片即時進行瀏覽.
// ChangeLog:
//    1.2 [2007-05-22]
//        1. 新增支援 PBase/DCView 線上相簿.
//        2. 新增累積模式(piling mode).
//        3. some bug fix.
//    1.1 [2007-05-06]
//        1. 新增支援 Pixnet/Yahoo 線上相簿.
//        2. 新增預取模式(prefetch).
//        3. some bug fix.
// 
// Download: http://chency.safego.net/ghotkey.user.js
// For any suggestion/question/problem, contact me by email.
//
// ==UserScript==
// @name          Generic Browsing Hotkey
// @description   Generic browsing hotkey(v1.2) for wretch/pchome/xuite/pixnet/yahoo/pbase/dcview photo site. For more info, please see the script source.
// @namespace     http://chency.safego.net/ghotkey
// @include       http://www.wretch.cc/album/show.php*
// @include       http://photo.pchome.com.tw/*
// @include       http://photo.xuite.net/*
// @include       http://www.pixnet.net/photo/*
// @include       http://tw.myblog.yahoo.com/*/photo*
// @include       http://www.pbase.com/*/image/*
// @include       http://dcview.com/my/showpic.asp*
// ==/UserScript==
//
/////////////////////////////////////////////////////////////////////////////////////////
function PhotoSite() {}
PhotoSite.prototype.initialGeneric = function( prevXpath, nextXpath ) {
  var prevUrl = '';
  var resItems = document.evaluate( prevXpath, document, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength > 0 ) prevUrl = resItems.snapshotItem(0);

  var nextUrl = '';
  var resItems = document.evaluate( nextXpath, document, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength > 0 ) nextUrl = resItems.snapshotItem(0);

  if( /#/.test( prevUrl ) ) prevUrl = /^(.+)#/.exec( prevUrl )[1];
  if( /#/.test( nextUrl ) ) nextUrl = /^(.+)#/.exec( nextUrl )[1];

  document.body.setAttribute( 'gh_prev_url', prevUrl );
  document.body.setAttribute( 'gh_next_url', nextUrl );
}
PhotoSite.prototype.isPreviousBrowsingKeycode = function( keycode ) {
  for( var i = 0; i < ghConfig.keyCodePrev.length; i++ ) {
    if( keycode == ghConfig.keyCodePrev[i] ) return true;
  }
  return false;
}
PhotoSite.prototype.isNextBrowsingKeycode = function( keycode ) {
  for( var i = 0; i < ghConfig.keyCodeNext.length; i++ ) {
    if( keycode == ghConfig.keyCodeNext[i] ) return true;
  }
  return false;
}
PhotoSite.prototype.isPilingKeycode = function( keycode ) {
  if( keycode == ghConfig.keyPiling ) return true;
  return false;
}
PhotoSite.prototype.isHotkeyEvent = function( event ) {
  if( event.shiftKey || event.altKey || event.ctrlKey ) return false;

  var keycode = event.keyCode;
  
  var isBrowsingKey = this.isPreviousBrowsingKeycode(keycode)
                   || this.isNextBrowsingKeycode(keycode)
                   || this.isPilingKeycode(keycode);
  if( isBrowsingKey ) {
    event.stopPropagation();
    event.preventDefault();
  }

  return isBrowsingKey;
}
PhotoSite.prototype.addHelperIframe = function( id, name, src, width, height, visibility ) {
  var ifrm = document.createElement( 'iframe' );
  ifrm.setAttribute( 'id', id );
  ifrm.setAttribute( 'name', name );
  if( src && (src.length > 0) ) ifrm.setAttribute( 'src', src );
	ifrm.style.width = width;
	ifrm.style.height = height;
  if( ! visibility ) ifrm.style.visibility = 'hidden';

  document.body.appendChild( ifrm );
}
PhotoSite.prototype.addPhotoTabScript = function() {
  var script = document.createElement("script");
  script.type = "text/javascript";
  script.innerHTML =
    " function addPhotoTab( photoId, title, imageUrl, photoUrl ) { " +
    "   var thumbDiv = document.createElement( 'div' ); " +
    "   thumbDiv.style.backgroundColor = 'white'; " +
    "   thumbDiv.style.padding = '20px'; " +
    "   thumbDiv.style.textAlign = 'center'; " +
    "   document.body.appendChild( thumbDiv ); " +

    "   var span = document.createElement( 'span' ); " +
    "   span.id = '_piling_' + photoId; " +
    "   span.innerHTML = title; " +
    "   span.style.color = 'black'; " +
    "   thumbDiv.appendChild(span); " +

    "   var br = document.createElement( 'br' ); " +
    "   thumbDiv.appendChild(br); " +

    "   var link = document.createElement( 'a' ); " +
    "   link.href = photoUrl; " +
    "   thumbDiv.appendChild(link); " +

    "   var image = document.createElement( 'img' ); " +
    "   image.src = imageUrl; " +
    "   image.alt = title; " +
    "   link.appendChild(image); " +
    " } ";

  document.body.appendChild( script );
}
PhotoSite.prototype.addPilingScript = function() {
  var script = document.createElement("script");
  script.type = "text/javascript";
  script.innerHTML =
    " function pilingPhoto() { " +
    "   var httpReq = new XMLHttpRequest(); " +
    "   httpReq.onreadystatechange = function() { pilingCallback( httpReq ); }; " +
    "   httpReq.open('GET', photoUrl, true); " +
    "   if( photoMime && (photoMime.length > 0) ) httpReq.overrideMimeType( photoMime ); " +
    "   httpReq.send(null); " +
    " } " +

    " function pilingCallback( httpReq ) { " +
    "   if( httpReq.readyState != 4 ) return; " +
    "   if( httpReq.status != 200 ) return; " +

    "   var responseText = httpReq.responseText; " +
    "   var imageInfo = processPage( responseText ); " +
    "   if( imageInfo ) addPhotoTab( imageInfo.photoId, imageInfo.title, imageInfo.imageUrl, imageInfo.photoUrl ); " +
    "   document.body.setAttribute( 'gh_piling_count_max', '' + photoId ); " +

    "   var title = document.body.getAttribute( 'gh_title_original' ); " +
    "   if( photoUrl.length > 0 ) { " +
    "     photoId = photoId + 1; " +
    "     document.title = '[' + photoId + '] ' + title ; " +
    "     pilingPhoto(); " +
    "   } else { " +
    "     document.title = '[' + photoId + ' done]' + title; " +
    "   } " +
    " } " +

    " var photoId = 1;" +
    " var photoUrl = window.location.href;" +
    " var photoMime;" +
    "" ;

  document.body.appendChild( script );
}
PhotoSite.prototype.addPilingHandler = function() {
  document.body.setAttribute( 'gh_is_piling', '0' );

  function pilling_key_handler ( e ) {
    var event = e || window.event;
    if( event.shiftKey || event.altKey || event.ctrlKey ) return false;
    var keycode = event.keyCode;
    if( keycode != ghConfig.keyPiling ) return;

    var isPiling = parseInt(document.body.getAttribute( 'gh_is_piling' ));
    if( ! isPiling ) {
      // piling initial
      document.body.setAttribute( 'gh_is_piling', '1' );
      document.body.setAttribute( 'gh_piling_count', '0' );
      document.body.setAttribute( 'gh_piling_count_max', '0' );
      document.body.setAttribute( 'gh_title_original', document.title );
      window.setTimeout( "pilingPhoto();", 100 );
    } else {
      var pilingCount = parseInt( document.body.getAttribute( 'gh_piling_count' ) );
      if( isNaN(pilingCount) ) return;
      var pilingCountMax = parseInt( document.body.getAttribute( 'gh_piling_count_max' ) );
      if( isNaN(pilingCountMax) ) return;

      pilingCount = pilingCount + 1;
      if( pilingCount > pilingCountMax ) return;

      document.body.setAttribute( 'gh_piling_count', '' + pilingCount );

      var url = /^([^#]+)#?/.exec( window.location.href )[1];
      window.location.href = url + '#_piling_' + pilingCount;
    }
  }
  document.addEventListener('keydown', pilling_key_handler, false);
}

PhotoSite.prototype.handleBrowsingHotkeyGeneric = function( e ) {
  var event = e || window.event;

  if( ! this.isHotkeyEvent( event ) ) return false;

  var keycode = event.keyCode;
  var nextUrl = '';
  if( this.isPreviousBrowsingKeycode(keycode) ) {
    nextUrl = document.body.getAttribute( 'gh_prev_url' );
  } else if( this.isNextBrowsingKeycode(keycode) ) {
    nextUrl = document.body.getAttribute( 'gh_next_url' );
  }
  if( nextUrl.length > 0 ) window.location.href = nextUrl;
  return true;
}
PhotoSite.prototype.changeAnchor = function( url, anchor ) {
  if( /#/.test( url ) ) {
    url = /^(.+)#/.exec( url )[1];
  }
  return url + "#" + anchor;
}
PhotoSite.prototype.letClickPhotoAsNext = function() {}
PhotoSite.prototype.skipAdPage = function() {}
PhotoSite.prototype.prefetchNextPhotoParentGeneric = function() {
  var prefetchUrl = document.body.getAttribute( 'gh_next_url' );
  if( prefetchUrl.length <= 0 ) return;

  prefetchUrl = prefetchUrl + ghConfig.magicAnchorId;

  this.addHelperIframe( 'prefetch_win', 'prefetch_win', prefetchUrl, '550px', '330px', false );
}
PhotoSite.prototype.prefetchNextPhotoChildGeneric = function( imageXpath ) {
  var script = document.createElement("script");
  script.type = "text/javascript";
  script.innerHTML = "var xpRes = document.evaluate( \"" + imageXpath + "\", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if( xpRes.snapshotLength > 0 ) window.location.href = xpRes.snapshotItem(0).getAttribute('src');";
  document.body.appendChild(script);
}


/////////////////////////////////////////////////////////////////////////////////////////
var WretchPhotoSite = new PhotoSite();
WretchPhotoSite.isMyUrl = function( url ) {
  return /^http:\/\/www\.wretch\.cc\/album\/show\.php/.test( url );
}
WretchPhotoSite.initial = function() {
  this.initialGeneric( "//a[@id='prev']", "//a[@id='next']" );

  function _customKeyup( event ) {
    var id = '';

    switch(event.keyCode) {
    case KEYCODE_Z: id ='first'; break;
    case KEYCODE_X: id ='prev'; break;
    case KEYCODE_C: id ='next'; break;
    case KEYCODE_V: id ='last'; break;
    case KEYCODE_B: id ='updir'; break;
    }

    if( id.length <= 0 ) return;
    window.location.href = document.getElementById( id ).href;
  }

  document.body.setAttribute('OnKeyUp', '');
  document.body.setAttribute('OnLoad', '');
  document.addEventListener('keyup', _customKeyup, false);
}
WretchPhotoSite.handleBrowsingHotkey = function(e) { WretchPhotoSite.handleBrowsingHotkeyGeneric(e); }
WretchPhotoSite.registerBrowsingHotkey = function() {
  document.addEventListener('keydown', WretchPhotoSite.handleBrowsingHotkey, false);
}
WretchPhotoSite.autoPhotoAnchor = function() {
  var anchorId = 'DisplayTitle';

  for( var i = 0; i < 20; i++ )
    document.body.appendChild( document.createElement( "br" ) );

  if( ! (new RegExp( "#" + anchorId + "$" ).test( window.location.href )) )
    window.location.href = this.changeAnchor( window.location.href, anchorId);
}
WretchPhotoSite.prefetchNextPhotoParent = function() {
  this.prefetchNextPhotoParentGeneric();
}
WretchPhotoSite.prefetchNextPhotoChild = function() {
  this.prefetchNextPhotoChildGeneric( "//img[@id='DisplayImage']" );
}
WretchPhotoSite.installPiling = function() {
  this.addPhotoTabScript();
  this.addPilingScript();

  var script = document.createElement("script");
  script.type = "text/javascript";
  script.innerHTML =
    " function processPage( responseText ) { " +
    "   var photoInfo = { 'photoId' : photoId, 'photoUrl' : photoUrl }; " +
    "   photoUrl = ''; " +
    // imageUrl
    "   var match = new RegExp( \"id='DisplayImage' src='([^']+)' \" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoInfo.imageUrl = match[1]; " +
    "   if( ! ( /^http:\\/\\/[^\\/]+\\.wretch\\.cc\\//.test( photoInfo.imageUrl )) ) return; " +
    // title
    "   photoInfo.title = photoId + '. '; " +
    "   var match = new RegExp( \"id=\\\"DisplayTitle\\\">\\\\s+([^<]*[^ ])\\\\s+</\" ).exec( responseText ); " +
    "   if( match ) photoInfo.title += match[1]; " +
    // photoUrl
    "   getNextPhotoUrl( responseText ); " +
    "   return photoInfo; " +
    " } " +

    " function getNextPhotoUrl( responseText ) { " +
    // photoNumber/photoNumberMax
    "   var match = new RegExp( \"<span id=\\\"CurrentPicNum\\\">([0-9]+)</span> +/ +([0-9]+) +\" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   var photoNumber = parseInt( match[1] ); " +
    "   var photoNumberMax = parseInt( match[2] ); " +
    "   if( photoNumber >= photoNumberMax ) return; " +
    // photoUrl
    "   var match = new RegExp( \"<a href=\\\"([^\\\"]+)\\\" id=\\\"next\\\" \" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoUrl = match[1]; " +
    " } " +
    "" ;
  document.body.appendChild(script);

  this.addPilingHandler();
}

/////////////////////////////////////////////////////////////////////////////////////////
var PchomePhotoSite = new PhotoSite();
PchomePhotoSite.isMyUrl = function( url ) {
  return /^http:\/\/photo\.pchome\.com\.tw\/[^\/]+\/[^\/]+$/.test( url );
}
PchomePhotoSite.initial = function() {
  this.initialGeneric( "//a[@accesskey='j']", "//a[@accesskey='k']" );
}
PchomePhotoSite.handleBrowsingHotkey = function(e) { PchomePhotoSite.handleBrowsingHotkeyGeneric(e); }
PchomePhotoSite.registerBrowsingHotkey = function() {
  document.addEventListener('keydown', PchomePhotoSite.handleBrowsingHotkey, false);
}
PchomePhotoSite.autoPhotoAnchor = function() {
  var anchorId = 'thisImage';

  if( ! (new RegExp( "#" + anchorId + "$" ).test( window.location.href )) )
    window.location.href = this.changeAnchor( window.location.href, anchorId );
}
PchomePhotoSite.letClickPhotoAsNext = function() {
  var resItems = document.evaluate( "//img[@class='ci' and @onerror='changeImg(this)']/ancestor::a", document, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength <= 0 ) return;

  var link = resItems.snapshotItem(0);
  link.setAttribute("onclick","");
  link.setAttribute("target","_self");
  var nextPhotoUrl = document.body.getAttribute( 'gh_next_url' );
  if( nextPhotoUrl.length <= 0 ) nextPhotoUrl = "javascript:alert('Last Photo');";
  link.setAttribute("href", nextPhotoUrl);
}
PchomePhotoSite.skipAdPage = function() {
  var resItems = document.evaluate( "//img[@src='/img/adtext.gif']", document, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength <= 0 ) return;

  window.location.reload();
}
PchomePhotoSite.prefetchNextPhotoParent = function() {
  this.prefetchNextPhotoParentGeneric();
}
PchomePhotoSite.prefetchNextPhotoChild = function() {
  this.prefetchNextPhotoChildGeneric( "//img[@onerror='changeImg(this)']" );
}
PchomePhotoSite.installPiling = function() {
  this.addPhotoTabScript();
  this.addPilingScript();

  var script = document.createElement("script");
  script.type = "text/javascript";
  script.innerHTML =
    " function processPage( responseText ) { " +
    "   var photoInfo = { 'photoId' : photoId, 'photoUrl' : photoUrl }; " +
    "   photoUrl = ''; " +
    // skip ad page
    "   var match = new RegExp( \"<img src=\\\"/img/adtext\\\\.gif\\\" width=\" ).exec( responseText ); " +
    "   if( match ) { " +
    "     getNextPhotoUrl( responseText ); " +
    "     if( photoUrl.length > 0 ) photoId -= 1; " +
    "     return; " +
    "   } " +
    // imageUrl
    "   var match = new RegExp( \"<img src=\\\"([^\\\"]+)\\\" [^>]+ onerror=\\\"changeImg\\\\(this\\\\)\\\">\" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoInfo.imageUrl = match[1]; " +
    "   if( ! ( /^\\//.test( photoInfo.imageUrl )) ) return; " +
    // title
    "   photoInfo.title = photoId + '. '; " +
    "   var match = new RegExp( \"<span class=\\\"t1630\\\"[^>]+>([^<]+)</span>\" ).exec( responseText ); " +
    "   if( match ) photoInfo.title += match[1]; " +
    // photoUrl
    "   getNextPhotoUrl( responseText ); " +
    "   return photoInfo; " +
    " } " +

    " function getNextPhotoUrl( responseText ) { " +
    // photoUrl
    "   var match = new RegExp( \"accesskey=\\\"k\\\" title=\\\"[^\\\"]+\\\" href=\\\"([^\\\"]+)\\\" class=\\\"pg03\\\"\" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoUrl = match[1]; " +
    " } " +

    " photoMime = 'text/html; charset=big5';" +
    "" ;
  document.body.appendChild(script);

  this.addPilingHandler();
}

/////////////////////////////////////////////////////////////////////////////////////////
var XuitePhotoSite = new PhotoSite();
XuitePhotoSite.isMyUrl = function( url ) {
  return /^http:\/\/photo\.xuite\.net\/[^\/]+\/[0-9]+\/[0-9]+\./.test( url );
}
XuitePhotoSite.initial = function() {
  this.initialGeneric(
    "//img[@alt='上一張(快速鍵 b)']/ancestor::a",
    "//img[@alt='下一張(快速鍵 n，或直接點照片也可以唷!!)']/ancestor::a" );

  // add additional checking process on original document.onkeyup handler
  var script = document.createElement("script");
  script.type = "text/javascript";
  script.innerHTML = "document.body.setAttribute( 'gh_is_mykey', '0' ); document.onkeyup = function (e) { if( parseInt( document.body.getAttribute( 'gh_is_mykey' ) ) <= 0 ) { Fast_Key(e); } document.body.setAttribute( 'gh_is_mykey', '0' ); }";
  document.body.appendChild(script);
}
XuitePhotoSite.handleBrowsingHotkey = function(e) {
  var isHandle = XuitePhotoSite.handleBrowsingHotkeyGeneric(e);
  if( isHandle )
    document.body.setAttribute( 'gh_is_mykey', '1' );
}
XuitePhotoSite.registerBrowsingHotkey = function() {
  document.addEventListener('keydown', XuitePhotoSite.handleBrowsingHotkey, false);
}
XuitePhotoSite.autoPhotoAnchor = function() {
  var anchorId = 'picture';

  if( ! (new RegExp( "#" + anchorId + "$" ).test( window.location.href )) )
    window.location.href = this.changeAnchor( window.location.href, anchorId );
}
XuitePhotoSite.letClickPhotoAsNext = function() {
  var resItems = document.evaluate( "//img[@onmouseout='javascript:Copy_Check(0);return false;']/ancestor::a", document, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength <= 0 ) return;

  var link = resItems.snapshotItem(0);
  var nextPhotoUrl = document.body.getAttribute( 'gh_next_url' );
  if( nextPhotoUrl.length <= 0 ) nextPhotoUrl = "javascript:alert('Last Photo');";
  link.setAttribute("href", nextPhotoUrl);
}
XuitePhotoSite.prefetchNextPhotoParent = function() {
  // find current photo image url
  var resItems = document.evaluate( "//img[@onmouseout='javascript:Copy_Check(0);return false;']", document, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength <= 0 ) return;

  var photoUrl = resItems.snapshotItem(0).getAttribute('src');
  var photoParts = /^(http:\/\/.+\/)([0-9]+)(\.jpg)$/.exec( photoUrl );
  if( photoParts == null ) return;

  // find next photo image url
  var resItems = document.evaluate( "//script[contains(text(),'MY_PHOTOS.insert')]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength <= 0 ) return;

  var photoScript = resItems.snapshotItem(0).innerHTML;
  var re = /MY_PHOTOS\.insert\( *([0-9]+),/g;
  var photoIds = [];
  while( matched = re.exec( photoScript ) )
    photoIds.push( matched[1] );

  while( photoIds.length > 0 ) {
    if( photoParts[2] == photoIds.shift() ) break;
  }
  var nextPhotoId = photoIds.shift();

  if( (! nextPhotoId) || (nextPhotoId.length <= 0) ) return;
  var nextPhotoUrl = photoParts[1] + nextPhotoId + photoParts[3];

  // prefetch next photo with iframe
  this.addHelperIframe( 'prefetch_win', 'prefetch_win', nextPhotoUrl, '550px', '330px', false );
}
XuitePhotoSite.prefetchNextPhotoChild = function() {}
XuitePhotoSite.installPiling = function() {
  // generate imageUrlFormat
  var resItems = document.evaluate( "//img[@onmouseout='javascript:Copy_Check(0);return false;']", document, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength <= 0 ) return;
  var photoUrl = resItems.snapshotItem(0).getAttribute('src');
  var photoParts = /^(http:\/\/.+\/)[0-9]+\.jpg$/.exec( photoUrl );
  if( photoParts == null ) return;
  var imagePrefix = photoParts[1];

  // generate photoNumberMax
  var maxText = document.evaluate( "//td[@class='portalBlue']/text()", document, null,
    XPathResult.STRING_TYPE, null).stringValue;
  if( ! maxText ) return;
  var photoNumberMax = parseInt( maxText.split( '/' )[1] );

  // generate photoUrlPrefix
  var match = /^(http.+\/)[0-9]+\.jpg/.exec( window.location.href );
  if( ! match ) return;
  var photoUrlPrefix = match[1];

  /////////////////////////////////
  this.addPhotoTabScript();
  this.addPilingScript();

  var script = document.createElement("script");
  script.type = "text/javascript";
  script.innerHTML =
    " function processPage( responseText ) { " +
    "   var photoInfo = { 'photoId' : photoId, 'photoUrl' : photoUrl }; " +
    "   photoUrl = ''; " +
    // imageUrl
    "   var match = new RegExp( \"complain_id='P\\/([0-9]+)';\" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   var imageNumber = match[1]; " +
    "   photoInfo.imageUrl = '" + imagePrefix + "' + imageNumber + '.jpg'; " +
    "   if( ! ( /^http:\\/\\/[^\\/]+\\.photo\\.xuite\\.net\\//.test( photoInfo.imageUrl )) ) return; " +
    // title
    "   photoInfo.title = photoId + '. '; " +
    "   var match = new RegExp( \"MY_PHOTOS.insert.\" + imageNumber + \",[^,]+,'([^']+)',\" ).exec( responseText ); " +
    "   if( match ) photoInfo.title += match[1]; " +
    // photoUrl
    "   getNextPhotoUrl( responseText, photoInfo.photoUrl ); " +
    "   return photoInfo; " +
    " } " +

    " function getNextPhotoUrl( responseText, photoUrlCurrent ) { " +
    // photoNumber
    "   var match = new RegExp( \"/([0-9]+)\\\\.jpg\" ).exec( photoUrlCurrent ); " +
    "   if( ! match ) return; " +
    "   var photoNumber = parseInt( match[1] ); " +
    "   if( photoNumber >= " + photoNumberMax + " ) return; " +
    // photoUrl
    "   var match = new RegExp( \"var picture_pos='([0-9]+)';\" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoUrl = '" + photoUrlPrefix + "' + (parseInt(match[1]) + 1) + '.jpg'; " +
    " } " +
    "" ;
  document.body.appendChild(script);

  this.addPilingHandler();
}

/////////////////////////////////////////////////////////////////////////////////////////
var YahooPhotoSite = new PhotoSite();
YahooPhotoSite.isMyUrl = function( url ) {
  return /^http:\/\/tw\.myblog\.yahoo\.com\/[^\/]+\/photo\?/.test( url );
}
YahooPhotoSite.initial = function() {
  this.initialGeneric( "//span[@class='prev']/a", "//span[@class='next']/a" );
}
YahooPhotoSite.handleBrowsingHotkey = function(e) { YahooPhotoSite.handleBrowsingHotkeyGeneric(e); }
YahooPhotoSite.registerBrowsingHotkey = function() {
  document.addEventListener('keydown', YahooPhotoSite.handleBrowsingHotkey, false);
}
YahooPhotoSite.autoPhotoAnchor = function() {
  var anchorId = 'phtcontent';

  for( var i = 0; i < 20; i++ )
    document.body.appendChild( document.createElement( "br" ) );

  var resItems = document.evaluate( "//div[@class='actionbar']", document, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength <= 0 ) return;
  resItems.snapshotItem(0).setAttribute('id',anchorId);

  if( ! (new RegExp( "#" + anchorId + "$" ).test( window.location.href )) )
    window.location.href = this.changeAnchor( window.location.href, anchorId );
}
YahooPhotoSite.letClickPhotoAsNext = function() {
  var resItems = document.evaluate( "//div[@class='phtcontent']/a", document, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength <= 0 ) return;

  var link = resItems.snapshotItem(0);
  link.setAttribute("target","_self");
  var nextPhotoUrl = document.body.getAttribute( 'gh_next_url' );
  if( nextPhotoUrl.length <= 0 ) nextPhotoUrl = "javascript:alert('Last Photo');";
  link.setAttribute("href", nextPhotoUrl);
}
YahooPhotoSite.prefetchNextPhotoParent = function() {}
YahooPhotoSite.prefetchNextPhotoChild = function() {}
YahooPhotoSite.installPiling = function() {
  this.addPhotoTabScript();
  this.addPilingScript();

  var script = document.createElement("script");
  script.type = "text/javascript";
  script.innerHTML =
    " function processPage( responseText ) { " +
    "   var photoInfo = { 'photoId' : photoId, 'photoUrl' : photoUrl }; " +
    "   photoUrl = ''; " +
    // imageUrl
    "   var match = new RegExp( \"<div class=\\\"phtcontent\\\"><a[^>]+><img src=\\\"([^\\\"]+)\\\"\" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoInfo.imageUrl = match[1]; " +
    "   if( ! ( /^http:\\/\\/[^\\/]+\\.yahoofs\\.com\\//.test( photoInfo.imageUrl )) ) return; " +
    // title
    "   photoInfo.title = photoId + '. '; " +
    "   var match = new RegExp( \"<div id=\\\"edit_title\\\">([^<]+)</div>\" ).exec( responseText ); " +
    "   if( match ) photoInfo.title += match[1]; " +
    // photoUrl
    "   getNextPhotoUrl( responseText ); " +
    "   return photoInfo; " +
    " } " +

    " function getNextPhotoUrl( responseText ) { " +
    "   var match = new RegExp( \"<span class=\\\"next\\\"><a href=\\\"([^\\\"]+)\\\" >\" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoUrl = match[1]; " +
    " } " +
    "" ;
  document.body.appendChild(script);

  this.addPilingHandler();
}

/////////////////////////////////////////////////////////////////////////////////////////
var PixnetPhotoSite = new PhotoSite();
PixnetPhotoSite.isMyUrl = function( url ) {
  return /^http:\/\/www\.pixnet\.net\/photo\//.test( url );
}
PixnetPhotoSite.initial = function() {
  this.initialGeneric( "//a[@class='pagePrev']", "//a[@class='pageNext']" );
}
PixnetPhotoSite.handleBrowsingHotkey = function(e) { PixnetPhotoSite.handleBrowsingHotkeyGeneric(e); }
PixnetPhotoSite.registerBrowsingHotkey = function() {
  document.addEventListener('keydown', PixnetPhotoSite.handleBrowsingHotkey, false);
}
PixnetPhotoSite.letClickPhotoAsNext = function() {
  var isDone;

  isDone = PixnetPhotoSite.letClickPhotoAsNextType1();
  if( isDone ) return;
  isDone = PixnetPhotoSite.letClickPhotoAsNextType2();
  if( isDone ) return;
}
PixnetPhotoSite.letClickPhotoAsNextType1 = function() {
  var resItems = document.evaluate( "//a[@alt='觀看原圖']", document, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength <= 0 ) return false;

  var link = resItems.snapshotItem(0);
  link.setAttribute("lhref", "");
  link.setAttribute("rel", "");
  var nextPhotoUrl = document.body.getAttribute( 'gh_next_url' );
  if( nextPhotoUrl.length <= 0 ) nextPhotoUrl = "javascript:alert('Last Photo');";
  link.setAttribute("href", nextPhotoUrl);

  return true;
}
PixnetPhotoSite.letClickPhotoAsNextType2 = function() {
  var resItems = document.evaluate( "//div[@id='imageFrame']/img", document, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength <= 0 ) return false;

  var image = resItems.snapshotItem(0);
  var link = document.createElement("a");
  var nextPhotoUrl = document.body.getAttribute( 'gh_next_url' );
  if( nextPhotoUrl.length <= 0 ) nextPhotoUrl = "javascript:alert('Last Photo');";
  link.setAttribute("href", nextPhotoUrl);

  image.parentNode.insertBefore(link, image.nextSibling);
  link.appendChild(image);

  return true;
}
PixnetPhotoSite.autoPhotoAnchor = function() {
  var anchorId = 'imageBox';

  for( var i = 0; i < 10; i++ )
    document.body.appendChild( document.createElement( "br" ) );

  if( ! (new RegExp( "#" + anchorId + "$" ).test( window.location.href )) )
    window.location.href = this.changeAnchor( window.location.href, anchorId );
}
PixnetPhotoSite.prefetchNextPhotoParent = function() {
  this.prefetchNextPhotoParentGeneric();
}
PixnetPhotoSite.prefetchNextPhotoChild = function() {
  this.prefetchNextPhotoChildGeneric( "//div[@id='imageFrame']/descendant::img" );
}
PixnetPhotoSite.installPiling = function() {
  this.addPhotoTabScript();
  this.addPilingScript();

  var script = document.createElement("script");
  script.type = "text/javascript";
  script.innerHTML =
    " function processPage( responseText ) { " +
    "   var photoInfo = { 'photoId' : photoId, 'photoUrl' : photoUrl }; " +
    "   photoUrl = ''; " +
    // imageUrl
    "   var match = new RegExp( \"圖片外框.+<img src=\\\"([^\\\"]+)\\\"\" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoInfo.imageUrl = match[1]; " +
    "   if( ! ( /^http:\\/\\/[^\\/]+\\.pixnet\\.net\\//.test( photoInfo.imageUrl )) ) return; " +
    // title
    "   photoInfo.title = photoId + '. '; " +
    "   var match = new RegExp( \"<div id=\\\"imageTitle\\\">\\\\s+<span>([^<]+)</span>\" ).exec( responseText ); " +
    "   if( match ) photoInfo.title += match[1]; " +
    // photoUrl
    "   getNextPhotoUrl( responseText ); " +
    "   return photoInfo; " +
    " } " +

    " function getNextPhotoUrl( responseText ) { " +
    // photoNumber/photoNumberMax
    "   var match = new RegExp( \"<div class=\\\"albumCount\\\">\\\\s+<span>[^0-9]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+</span>\" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   var photoNumber = parseInt( match[1] ); " +
    "   var photoNumberMax = parseInt( match[2] ); " +
    "   if( photoNumber >= photoNumberMax ) return; " +
    // photoUrl
    "   var match = new RegExp( \"<a href=\\\"([^\\\"]+)\\\" class=\\\"pageNext\\\" \" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoUrl = match[1]; " +
    " } " +
    "" ;
  document.body.appendChild(script);

  this.addPilingHandler();
}

/////////////////////////////////////////////////////////////////////////////////////////
var PbasePhotoSite = new PhotoSite();
PbasePhotoSite.isMyUrl = function( url ) {
  return /^http:\/\/www\.pbase\.com\/[^\/]+\/image\/[^\/]+$/.test( url );
}
PbasePhotoSite.initial = function() {
  this.initialGeneric( "//div[@id='slideshow']/descendant::a[text()='previous']", "//div[@id='slideshow']/descendant::a[text()='next']" );
}
PbasePhotoSite.handleBrowsingHotkey = function(e) { PbasePhotoSite.handleBrowsingHotkeyGeneric(e); }
PbasePhotoSite.registerBrowsingHotkey = function() {
  document.addEventListener('keydown', PbasePhotoSite.handleBrowsingHotkey, false);
}
PbasePhotoSite.autoPhotoAnchor = function() {
  var anchorId = 'thisImage';

  if( ! (new RegExp( "#" + anchorId + "$" ).test( window.location.href )) )
    window.location.href = this.changeAnchor( window.location.href, anchorId );
}
PbasePhotoSite.letClickPhotoAsNext = function() {
  var resItems = document.evaluate( "//img[@class='display']/ancestor::a", document, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if( resItems.snapshotLength <= 0 ) return;

  var link = resItems.snapshotItem(0);
  var nextPhotoUrl = document.body.getAttribute( 'gh_next_url' );
  if( nextPhotoUrl.length <= 0 ) nextPhotoUrl = "javascript:alert('Last Photo');";
  link.setAttribute("href", nextPhotoUrl);
}
PbasePhotoSite.skipAdPage = function() {}
PbasePhotoSite.prefetchNextPhotoParent = function() {
  this.prefetchNextPhotoParentGeneric();
}
PbasePhotoSite.prefetchNextPhotoChild = function() {
  this.prefetchNextPhotoChildGeneric( "//img[@class='display']" );
}
PbasePhotoSite.installPiling = function() {
  this.addPhotoTabScript();
  this.addPilingScript();

  var script = document.createElement("script");
  script.type = "text/javascript";
  script.innerHTML =
    " function processPage( responseText ) { " +
    "   var photoInfo = { 'photoId' : photoId, 'photoUrl' : photoUrl }; " +
    "   photoUrl = ''; " +
    // skip ad page
    "   var match = new RegExp( \"Must login as .+ to view this image\" ).exec( responseText ); " +
    "   if( match ) { " +
    "     getNextPhotoUrl( responseText ); " +
    "     if( photoUrl.length > 0 ) photoId -= 1; " +
    "     return; " +
    "   } " +
    // imageUrl
    "   var match = new RegExp( \"<IMG class=\\\"display\\\" src=\\\"([^\\\"]+)\\\" \" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoInfo.imageUrl = match[1]; " +
    // title
    "   photoInfo.title = photoId + '. '; " +
    // photoUrl
    "   getNextPhotoUrl( responseText ); " +
    "   return photoInfo; " +
    " } " +

    " function getNextPhotoUrl( responseText ) { " +
    // photoUrl
    "   var match = new RegExp( \"<a href=\\\"([^\\\"]+)\\\">next</a>\" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoUrl = match[1]; " +
    " } " +
    "" ;
  document.body.appendChild(script);

  this.addPilingHandler();
}

/////////////////////////////////////////////////////////////////////////////////////////
var DcviewPhotoSite = new PhotoSite();
DcviewPhotoSite.isMyUrl = function( url ) {
  return /^http:\/\/dcview\.com\/my\/showpic\.asp\?/.test( url );
}
DcviewPhotoSite.initial = function() {
  this.initialGeneric( "//a[starts-with(@href,'showpic.asp') and contains(@href,'&step=pre')]", "//a[starts-with(@href,'showpic.asp') and contains(@href,'&step=next')]" );
}
DcviewPhotoSite.handleBrowsingHotkey = function(e) { DcviewPhotoSite.handleBrowsingHotkeyGeneric(e); }
DcviewPhotoSite.registerBrowsingHotkey = function() {
  document.addEventListener('keydown', DcviewPhotoSite.handleBrowsingHotkey, false);
}
DcviewPhotoSite.autoPhotoAnchor = function() {
  var anchorId = 'dcviewimg1';

  for( var i = 0; i < 20; i++ )
    document.body.appendChild( document.createElement( "br" ) );

  if( ! (new RegExp( "#" + anchorId + "$" ).test( window.location.href )) )
    window.location.href = this.changeAnchor( window.location.href, anchorId);
}
DcviewPhotoSite.prefetchNextPhotoParent = function() {
  this.prefetchNextPhotoParentGeneric();
}
DcviewPhotoSite.prefetchNextPhotoChild = function() {
  this.prefetchNextPhotoChildGeneric( "//img[@id='dcviewimg1']" );
}
DcviewPhotoSite.installPiling = function() {
  var match = /showpic.asp\?nickname=([^&]+)&albumsn/.exec( window.location.href );
  if( ! match ) return;
  var nickname = match[1];

  /////////////////////////////////
  this.addPhotoTabScript();
  this.addPilingScript();

  var script = document.createElement("script");
  script.type = "text/javascript";
  script.innerHTML =
    " function processPage( responseText ) { " +
    "   var photoInfo = { 'photoId' : photoId, 'photoUrl' : photoUrl }; " +
    "   photoUrl = ''; " +
    // imageUrl
    "   var match = new RegExp( \"<img id=\\\"dcviewimg1\\\" src=\\\"([^\\\"]+)\\\" \" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoInfo.imageUrl = match[1]; " +
    "   if( ! ( /^http:\\/\\/[^\\/]+\\.dcview\\.com\\//.test( photoInfo.imageUrl )) ) return; " +
    // title
    "   photoInfo.title = photoId + '. '; " +
    "   var match = new RegExp( \"<font face=\\\"Verdana, Arial\\\"><b>([^<]+)</b>\" ).exec( responseText ); " +
    "   if( match ) photoInfo.title += match[1]; " +
    // photoUrl
    "   getNextPhotoUrl( responseText ); " +
    "   return photoInfo; " +
    " } " +

    " function getNextPhotoUrl( responseText ) { " +
    // photoNumber/photoNumberMax
    "   var match = new RegExp( \"class=eng[^>]+><[^>]+>.+ +# +([0-9]+) +/ +([0-9]+) +\" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   var photoNumber = parseInt( match[1] ); " +
    "   var photoNumberMax = parseInt( match[2] ); " +
    "   if( photoNumber >= photoNumberMax ) return; " +
    // photoUrl
    "   var match = new RegExp( \"<a href=\\\"(showpic.asp\\\\?nickname=)[^&]+(&[^\\\"]+&step=next)\\\">\" ).exec( responseText ); " +
    "   if( ! match ) return; " +
    "   photoUrl = match[1] + '" + nickname + "' + match[2]; " +
    " } " +

    " var photoMime = 'text/html; charset=big5';" +
    "" ;
  document.body.appendChild(script);

  this.addPilingHandler();
}


/////////////////////////////////////////////////////////////////////////////////////////
function PhotoSiteManager() {}
// static function
PhotoSiteManager.getSiteInstance = function( currentUrl ) {
  var site;

  if( (! site) && WretchPhotoSite.isMyUrl(currentUrl) ) site = WretchPhotoSite;
  if( (! site) && PchomePhotoSite.isMyUrl(currentUrl) ) site = PchomePhotoSite;
  if( (! site) && XuitePhotoSite.isMyUrl(currentUrl) ) site = XuitePhotoSite;
  if( (! site) && YahooPhotoSite.isMyUrl(currentUrl) ) site = YahooPhotoSite;
  if( (! site) && PixnetPhotoSite.isMyUrl(currentUrl) ) site = PixnetPhotoSite;
  if( (! site) && PbasePhotoSite.isMyUrl(currentUrl) ) site = PbasePhotoSite;
  if( (! site) && DcviewPhotoSite.isMyUrl(currentUrl) ) site = DcviewPhotoSite;

  return site;
}

/////////////////////////////////////////////////////////////////////////////////////////
var KEYCODE_LEFT = 37;
var KEYCODE_RIGHT = 39;
var KEYCODE_B = 66;
var KEYCODE_C = 67;
var KEYCODE_F = 70;
var KEYCODE_V = 86;
var KEYCODE_X = 88;
var KEYCODE_Z = 90;


var ghConfig = {
  isBrowsingHotkey : true,        // 使用統一瀏覽熱鍵
  isAutoPhotoAnchor : true,       // 自動定位頁面至圖片位置
  isClickPhotoAsNext : true,      // 點選圖片視為跳至下一張
  isSkipAdPage : true,            // 廣告頁面自動跳至下一張
  isPrefetchNextPhoto : true,     // 預取下一頁圖片
  isPiling : true,                // 累積模式
  //////////////////////////////////////////////////////////
  magicAnchorId : '#__magic',     // iframe magic anchor id
  keyCodePrev : [ KEYCODE_X, KEYCODE_LEFT ],  // 上一頁熱鍵
  keyCodeNext : [ KEYCODE_C, KEYCODE_RIGHT ], // 下一頁熱鍵
  keyPiling : KEYCODE_F           // 累積模式熱鍵
};


var site = PhotoSiteManager.getSiteInstance( window.location.href );
if( ! site ) return;

site.initial();
if( ghConfig.isSkipAdPage ) site.skipAdPage();

if( ghConfig.isPrefetchNextPhoto ) {
  if( window.location.hash != ghConfig.magicAnchorId ) {
    site.prefetchNextPhotoParent();
  } else {
    site.prefetchNextPhotoChild();
    return;
  }
}

if( ghConfig.isBrowsingHotkey ) site.registerBrowsingHotkey();
if( ghConfig.isClickPhotoAsNext ) site.letClickPhotoAsNext();
if( ghConfig.isAutoPhotoAnchor ) site.autoPhotoAnchor();
if( ghConfig.isPiling ) site.installPiling();

