<?php
/*
**	Copyright (C) 1995-2007 by the ITools Authors.
**	This file is part of ITools - the Internet Tools Library
**
**	ITools is free software; you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation; either version 3 of the License, or
**	(at your option) any later version.
**
**	ITools is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
**
**	itjs.class - functions to handle javascript inclusion
*/

class itjs
{
static $charset;

/**
 * Send HTTP headers (content-type) to transmit javascript code
 */
function send_headers($charset = null)
{
	if (!$charset)
		$charset = ini_get('default_charset') ?: 'iso-8859-1';

	self::$charset = $charset;

	header("Content-Type: application/x-javascript; charset=$charset");

	header('Expires: ' . gmdate('D, d M Y H:i:s', time()+10) . ' GMT');	# prevent broken data on IE reloads
}

/**
 * Serialize the result into a javascript script
 * @param $values Array with values to be serialized
 * @param $envelope Encapsulate the data when callback function is provided (iframe solution)
 * @return String with javascript code to be sent to client
 */
static function serialize($values, $envelope = false)
{
	if (($envelope || isset($values['eof'])) && ($callback = it::replace(array('[^\w.]' => ""), $_REQUEST['itjs_call'])))
	{
		$target = $_REQUEST['itjs_iframe'] ? "parent" : "window";
		list($itclass) = explode('.', $callback);
		$header = "$target.$itclass && $target.$callback && $target.$callback.dataReady(";
		$footer = "," . intval($_REQUEST['itjs_callid']) . ");";

		if ($_REQUEST['itjs_iframe'])	# iframe-based loading required by Opera 7
		{
			$header = '<script type="text/javascript">' . $header;
			$footer .= "</script>";
		}
	}

	$result = $header . itjs::encode($values) . $footer;

	return it_untaint($result);
}

/*
 * Encode the result into a javascript array to transfer and eval() in client
 */
static function encode($values)
{
	static $jskeyword = array("abstract" => 1, "boolean" => 1, "break" => 1, "byte" => 1, "case" => 1, "catch" => 1, "char" => 1, "class" => 1, "const" => 1, "continue" => 1, "debugger" => 1, "default" => 1, "delete" => 1, "do" => 1, "double" => 1, "each" => 1, "else" => 1, "enum" => 1, "export" => 1, "extends" => 1, "false" => 1, "final" => 1, "finally" => 1, "float" => 1, "for" => 1, "function" => 1, "goto" => 1, "if" => 1, "implements" => 1, "import" => 1, "in" => 1, "instanceof" => 1, "int" => 1, "interface" => 1, "long" => 1, "namespace" => 1, "native" => 1, "new" => 1, "null" => 1, "package" => 1, "private" => 1, "protected" => 1, "public" => 1, "return" => 1, "short" => 1, "static" => 1, "super" => 1, "switch" => 1, "synchronized" => 1, "this" => 1, "throw" => 1, "throws" => 1, "transient" => 1, "true" => 1, "try" => 1, "typeof" => 1, "var" => 1, "void" => 1, "volatile" => 1, "while" => 1, "with" => 1, "xml" => 1);

	$charset = self::$charset ?: ini_get('default_charset');
	$texts = ($values === array_values($values)) ? "[]0 " : "{}1\n";	# Numerical or associative array
	$result = $texts[0];

	foreach ($values as $key => $value)
	{
		$result .= $separator;

		if ($texts[2])
		{
			if ($jskeyword[$key] || !preg_match('/^[a-z_]\w*$/i', $key))
				$key = "'" . addslashes($key) . "'";

			$result .= "$key:";
		}

		if ($value === true)
			$result .= 'true';
		else if ($value === false)
			$result .= 'false';
		else if (!is_array($value))
		{
			$quote = (strval(intval($value)) === strval($value)) ? "" : '"';
			if (strtolower($charset) != "utf-8")
				$value = strtr($value, array("\x84" => '"', "\x93" => '"', "\x94" => '"'));
			$string = strtr($value, array("\0" => '\\0', '"' => '\\"', "</"=>"<\\/", "\n" => '\\n', "\r" => '\\r', "\t" => '\\t', "\\" => '\\\\'));
			$string = $GLOBALS['itjs_defaultconfig']['latin2unicode'] ? preg_replace_callback('/([\xa0-\xff])/', function($m) { return sprintf("\\u%04x", ord($m[1])); }, $string) : $string;
			$result .= $quote . $string . $quote;
		}
		else
			$result .= itjs::encode($value);

		$separator = "," . $texts[3];
	}

	$result .= $texts[1];

	return $result;
}

static function filenames($filelist)
{
	$result = array();
	$path = $GLOBALS['ULTRAHOME'] . "/itjs";
	$itjs = "/www/server/phpinclude/itools/itjs";
	$libsearch = strlen($GLOBALS['debug_lib']) > 1 ? "/www/lib-" . $GLOBALS['debug_lib'] . ".search.ch" : "/www/lib.search.ch";
	$special = array(
		"itools" => "$itjs/it.js,$itjs/http.js,$itjs/loader.js,$itjs/state.js,$itjs/timer.js",
		"boot.js" => "$itjs/boot.js",
		"state.html" => "$itjs/state.html",
		"error.gif" => "$itjs/error.gif",
		"search.css" => "$libsearch/doc/search.css",
		"searchlib.js" => "$libsearch/itjs/searchlib.js",
		"sl_state.js" => "$libsearch/itjs/sl_state.js",
		"sl_dropdown.js" => "$libsearch/itjs/sl_dropdown.js",
		"sl_collapse.js" => "$libsearch/itjs/sl_collapse.js",
		"sl_adid_debug.js" => "$libsearch/itjs/sl_adid_debug.js",
		"locateme.js" => "$libsearch/itjs/locateme.js",
		"prettyprint.css" => "$libsearch/doc/prettyprint.css",
		"PIE.htc" => "$libsearch/doc/PIE.htc",
	);

	foreach (array_filter(explode(",", it::replace(array('\?.*' => ""), basename($filelist)))) as $file)
	{
		$filenames = $special[$file] ? $special[$file] : "$path/$file";

		foreach (explode(",", $filenames) as $filename)
			if (!$seen[$filename]++)
				$result[] = $filename;
	}

	return $result;
}

/**
 * Return (php-interpreted by default) files that will be sent to client
 */
static function filecontents($filenames, $execphp = true)
{
	foreach ($filenames as $filename)
	{
		ob_start(); # Needs to capture inside loop to guarantee file order
		if (!(it::match('W3C_CSS_Validator', $_SERVER['HTTP_USER_AGENT']) && it::match('jquery-ui\.css', $filename)))
			$result .= it::replace(array('^1$' => ""), $execphp ? @include($filename) : @file_get_contents($filename), array('utf8' => false));
		$result .= ob_get_clean();
	}

	return $result;
}

/**
 * Strip comments and trim lines
 * @param $code String containing javascript code to be stripped
 * @return Naked code
 */
static function strip($code)
{
	if (!it::is_devel())
		$code = preg_replace(array(
			'|\s//.*$|m',
			'|\s+/\*.*?\*/|s', # MUST require at least one space before /* (jquery.js) but MUST NOT require space before */ (searchmap.js)
			'|^\s+|m'
		), array(), $code);
	if (EDC('print'))
		$code = it::replace(array('@media\s+print' => '@media screen, print'), $code);

	return $code;
}

/**
 * Compute checksum for list of files
 * @param $fnlist Either comma separated url or array of filenames to calculate checksum for
 * @return Checksum for given files
 */
static function checksum($fnlist)
{
	$filenames = array_merge(is_array($fnlist) ? $fnlist : itjs::filenames($fnlist), array("/www/lib.search.ch/var/jquery-ui/dist/minified/jquery.ui.core.min.js", "/www/lib.search.ch/var/jquery/dist/jquery.min.js"));
	$key = "itjs_" . md5(join("", it::map('"$v" . @filemtime("$v")', $filenames)));

	return it_cache::get($key) ?: it_cache::put($key, substr(md5(self::filecontents($filenames, false)), 0, 8));
}

}

?>