<?xml version="1.0" encoding="UTF-8"?>

<!--
　JavaScript によるカスタム関数群 (Xalan 拡張)
 -->

<xsl:stylesheet version="1.0" 
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:js="http://www.remus.dti.ne.jp/~a-satomi/jsfunc"
	xmlns:xalan="http://xml.apache.org/xalan"
	exclude-result-prefixes="js xalan">

<xalan:component prefix="js"
	functions="r2a a2r chsuffix omitDirIndex getTime n2DTF deleteTab ct match replace calcSHA1">

<xalan:script lang="javascript">
<![CDATA[

function r2a (basePath, targetPath) {
	// 絶対パス basePath から見た相対パス targetPath の絶対パスを取得
	// js:r2a('/a/b/c/', '../d/') → "/a/b/d/"
	
	var b = basePath.split('/');
	var t = targetPath.split('/');

	if (targetPath.charAt(0) == '/')
		return targetPath;
	else if (targetPath.charAt(0) == '#')
		return basePath + targetPath;
	else if (t[0] == '.' || t[0] == '..')
		return r2a(b.slice(0, b.length - t[0].length).join('/') + '/', t.slice(1, t.length).join('/'));
	else
		return b.slice(0, b.length - 1).join('/') + '/' + targetPath;
}

function a2r (basePath, targetPath) {
	// 絶対パス basePath から見た絶対パス targetPath の相対パスを取得
	// js:a2r('/a/b/', '/a/c/') → "../c/"

	if (targetPath.charAt(0) != '/')
		return targetPath;
	else
		return a2r_(basePath, targetPath);
}

function a2r_(basePath, targetPath) {
	var b = basePath.split("/");
	var t = targetPath.split("/");

	if (!basePath)
		return targetPath;
	else if (!targetPath)
		return pathToGoUp(basePath);
	else if (b[0] == t[0])
		return a2r_(b.slice(1, b.length).join("/"), t.slice(1, t.length).join("/"));
	else
		return pathToGoUp(basePath) + targetPath;
}

function pathToGoUp (path) {
	var p = path.split("/"), ret = '';
	for (var i = 1; i < p.length; i++) ret += "../";
	return ret;
}

function chsuffix (path, suffix) {
	// path 末尾の拡張子を suffix へ置換
	if (path.charAt(path.length-1) == '/' || path.indexOf('/#') != -1)
		return path;
	else if (path.indexOf('#') != -1)
		return path.replace(/(.+)\.[^#]+(#.*)$/, '$1.' + suffix + '$2');
	else
		return path.replace(/(.+)\..+$/, '$1.' + suffix);
}

function omitDirIndex (_path) {
	// path がディレクトリのデフォルトインデックスファイルを示していたら path を / で終わらせる

	if (_path.charAt(0) == '#') return _path;
	
	var path = (_path.indexOf('/') != -1) ? _path : './' + _path;
	var default1 = 'index.xml';
	var default2 = 'index.html';
	path = path.replace(new RegExp('/' + default1), '/');
	path = path.replace(new RegExp('/' + default2), '/');
	if (path.substr(0, 2) == './' && path.charAt(2) != '#' && path.length > 2)
		return path.substring(2, path.length);
	else
		return path;
}

function getTime() {
	// 現在時刻を W3C DTF (YYYY-MM-DDThh:mm:ssTZD) 形式で取得
	var d = new Date();
	var year = 1900 + d.getYear();
	var month = 1 + d.getMonth();
	var date = d.getDate();
	var hour = d.getHours();
	var minute = d.getMinutes();
	if (month < 10) month = "0" + month;
	if (date < 10) date = "0" + date;
	if (hour < 10) hour = "0" + hour;
	if (minute < 10) minute = "0" + minute;
	return year + "-" + month + "-" + date  + "T" + hour  + ":" + minute  + ":" + "00+09:00";
}

function n2DTF(W3C_DTF) {
	// W3C DTF 形式 (YYYY-MM-DDThh:mm:ssTZD) → 自分好み日時表記 (YYYY/MM/DD - hh:mm) 変換
	return W3C_DTF.substring(0, 16).replace(/-/g, "/").replace(/T/, " - ");
}

function deleteTab(text) {
	// 主にリテラルの style/script のインデントを整える用途
	var ret = text;
	ret = ret.replace(/^\n*/, "\n");
	ret = ret.replace(/\s*$/, "\n    ");
	ret = ret.replace(/\t/g, "    ");
	return ret;
}

function ct(str, num) {
	// 文字列 str の文字数を length へ制限する（末尾切り落とし）
	var ret = '';
	var _num = (num - 1) * 2;
	var i  =  n = 0;
	while (n < _num && i < str.length) {
		ret += str.charAt(i);
		n = (str.charCodeAt(i) < 128) ? n+1 : n+2;
		i++;
	}
	if (n >= _num && str != ret) ret += '...';
	return ret;
}

function match(str, regex) {
	// 正規表現マッチテスト（返値 boolean)
	// js:match('対象文字列', /正規表現/)
	if (regex.match(/^\/(.+)\/(\w*)$/)) {
		var ptn  = new RegExp(RegExp.$1, RegExp.$2);
		return (str.match(ptn)) ? true : false;
	} else 
		return false;
}

function replace(str, regex, replace) {
	// 正規表現文字列置換
	// js:replace('対象文字列', /正規表現/, '置換文字列')
	if (regex.match(/^\/(.+)\/(\w*)$/)) {
		var ptn  = new RegExp(RegExp.$1, RegExp.$2);
		return str.replace(ptn, replace);
	} else 
		return str;
}



/*
 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
 * in FIPS PUB 180-1
 * Copyright (C) Paul Johnston 2000 - 2002.
 * See http://pajhome.org.uk/site/legal.html for details.
 */

/*
 * Convert a 32-bit number to a hex string with ms-byte first
 */
var hex_chr = "0123456789abcdef";
function hex(num)
{
  var str = "";
  for(var j = 7; j >= 0; j--)
    str += hex_chr.charAt((num >> (j * 4)) & 0x0F);
  return str;
}

/*
 * Convert a string to a sequence of 16-word blocks, stored as an array.
 * Append padding bits and the length, as described in the SHA1 standard.
 */
function str2blks_SHA1(str)
{
  var nblk = ((str.length + 8) >> 6) + 1;
  var blks = new Array(nblk * 16);
  for(var i = 0; i < nblk * 16; i++) blks[i] = 0;
  for(var i = 0; i < str.length; i++)
    blks[i >> 2] |= str.charCodeAt(i) << (24 - (i % 4) * 8);
  blks[i >> 2] |= 0x80 << (24 - (i % 4) * 8);
  blks[nblk * 16 - 1] = str.length * 8;
  return blks;
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left
 */
function rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

/*
 * Perform the appropriate triplet combination function for the current
 * iteration
 */
function ft(t, b, c, d)
{
  if(t < 20) return (b & c) | ((~b) & d);
  if(t < 40) return b ^ c ^ d;
  if(t < 60) return (b & c) | (b & d) | (c & d);
  return b ^ c ^ d;
}

/*
 * Determine the appropriate additive constant for the current iteration
 */
function kt(t)
{
  return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
         (t < 60) ? -1894007588 : -899497514;
}

/*
 * Take a string and return the hex representation of its SHA-1.
 */
function calcSHA1(str)
{
  var x = str2blks_SHA1(str);
  var w = new Array(80);

  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;
  var e = -1009589776;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;
    var olde = e;

    for(var j = 0; j < 80; j++)
    {
      if(j < 16) w[j] = x[i + j];
      else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
      var t = safe_add(safe_add(rol(a, 5), ft(j, b, c, d)), safe_add(safe_add(e, w[j]), kt(j)));
      e = d;
      d = c;
      c = rol(b, 30);
      b = a;
      a = t;
    }

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
    e = safe_add(e, olde);
  }
  return hex(a) + hex(b) + hex(c) + hex(d) + hex(e);
}

/* JS implementation of SHA-1 : end */

]]>
</xalan:script>
</xalan:component>

</xsl:stylesheet>

