mirror of
https://github.com/pierre42100/comunic
synced 2025-06-21 09:35:19 +00:00
First commit
This commit is contained in:
1
3rdparty/luminous/src/.htaccess
vendored
Executable file
1
3rdparty/luminous/src/.htaccess
vendored
Executable file
@ -0,0 +1 @@
|
||||
Deny from all
|
97
3rdparty/luminous/src/cache/cache.class.php
vendored
Executable file
97
3rdparty/luminous/src/cache/cache.class.php
vendored
Executable file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/// @cond ALL
|
||||
|
||||
/**
|
||||
* Cache superclass provides a skeleton for implementations using the filesystem
|
||||
* or SQL, or anything else.
|
||||
*/
|
||||
abstract class LuminousCache {
|
||||
|
||||
protected $gz = true;
|
||||
protected $id = null;
|
||||
protected $timeout = 0;
|
||||
protected $cache_hit = false;
|
||||
private $use_cache = true;
|
||||
private $creation_check = false;
|
||||
|
||||
private $errors = array();
|
||||
|
||||
public function __construct($id) {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function set_purge_time($seconds) {
|
||||
$this->timeout = $seconds;
|
||||
}
|
||||
|
||||
private function _compress($data) {
|
||||
return $this->gz? gzcompress($data) : $data;
|
||||
}
|
||||
private function _decompress($data) {
|
||||
return $this->gz? gzuncompress($data) : $data;
|
||||
}
|
||||
|
||||
protected function log_error($msg) {
|
||||
$this->errors[] = $msg;
|
||||
}
|
||||
|
||||
public function has_errors() { return !empty($this->errrors); }
|
||||
public function errors() { return $this->errors; }
|
||||
|
||||
protected abstract function _create();
|
||||
protected abstract function _read();
|
||||
protected abstract function _write($data);
|
||||
protected abstract function _update();
|
||||
|
||||
protected abstract function _purge();
|
||||
|
||||
private function purge() {
|
||||
assert($this->creation_check);
|
||||
if ($this->use_cache)
|
||||
$this->_purge();
|
||||
}
|
||||
|
||||
private function create() {
|
||||
if ($this->creation_check) return;
|
||||
$this->creation_check = true;
|
||||
if (!$this->_create()) {
|
||||
$this->use_cache = false;
|
||||
} else {
|
||||
$this->purge();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Reads from the cache
|
||||
* @returns the cached string or @c null
|
||||
*/
|
||||
public function read() {
|
||||
$this->create();
|
||||
if (!$this->use_cache) return null;
|
||||
|
||||
$contents = $this->_read();
|
||||
if ($contents !== false) {
|
||||
$this->cache_hit = true;
|
||||
$contents = $this->_decompress($contents);
|
||||
$this->_update();
|
||||
return $contents;
|
||||
} else return null;
|
||||
}
|
||||
/**
|
||||
* @brief Writes into the cache
|
||||
* @param $data the data to write
|
||||
*/
|
||||
public function write($data) {
|
||||
$this->create();
|
||||
$this->purge();
|
||||
if (!$this->cache_hit && $this->use_cache)
|
||||
$this->_write($this->_compress($data));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
// ends 'ALL'
|
150
3rdparty/luminous/src/cache/fscache.class.php
vendored
Executable file
150
3rdparty/luminous/src/cache/fscache.class.php
vendored
Executable file
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
/// @cond ALL
|
||||
|
||||
/**
|
||||
* File system cache driver
|
||||
* @brief The FS cache driver handles storing the cache on the filesystem
|
||||
*
|
||||
* The general structure of the cache is as follows:
|
||||
* @c /cache_dir/prefix/cache_id
|
||||
*
|
||||
* @c cache_dir is the root cache directory (normally luminous/cache),
|
||||
* @c prefix is used to separate things out into multiple locations - we take
|
||||
* the first two characters of the @c cache_id just to reduce the number of
|
||||
* files in any one directory (and maybe on some filesystems this allows
|
||||
* slightly faster lookups?).
|
||||
* @c cache_id is the unique identifier of the input string (with its first
|
||||
* two character missing, for @c prefix).
|
||||
*
|
||||
* This driver implements necessary functions for reading/writing the cache
|
||||
* and performing maintenance.
|
||||
*
|
||||
*/
|
||||
class LuminousFileSystemCache extends LuminousCache {
|
||||
|
||||
/// root cache directory
|
||||
private $dir = null;
|
||||
|
||||
/// full path to the cached file (for convenience)
|
||||
private $path = null;
|
||||
|
||||
/// subdir within the cache - we factor out the first two
|
||||
/// characters of the filename, this reduces the number of files in
|
||||
/// any one folder.
|
||||
private $subdir = null;
|
||||
|
||||
/// the base filename of the cached file
|
||||
private $filename = null;
|
||||
|
||||
public function __construct($id) {
|
||||
$this->dir = luminous::root() . '/cache/';
|
||||
$this->subdir = substr($id, 0, 2);
|
||||
$this->filename = substr($id, 2);
|
||||
|
||||
$this->path = rtrim($this->dir, '/') . '/' .
|
||||
$this->subdir . '/' . $this->filename;
|
||||
|
||||
parent::__construct($id);
|
||||
}
|
||||
|
||||
protected function _log_error($file, $msg) {
|
||||
$this->log_error( str_replace('%s', "\"$file\"", $msg) . "\n"
|
||||
. "File exists: " . var_export(file_exists($file), true) . "\n"
|
||||
. "Readable?: " . var_export(is_readable($file), true) . "\n"
|
||||
. "Writable?: " . var_export(is_writable($file), true) . "\n"
|
||||
. (!file_exists($this->dir)?
|
||||
"Your cache dir (\"{$this->dir}\") does not exist! \n" : '' )
|
||||
. (file_exists($this->dir) && !is_writable($this->dir)?
|
||||
"Your cache dir (\"{$this->dir}\") is not writable! \n" : '' )
|
||||
);
|
||||
}
|
||||
|
||||
protected function _create() {
|
||||
$target = $this->dir . '/' . $this->subdir;
|
||||
if (!@mkdir($target, 0777, true) && !is_dir($target)) {
|
||||
$this->_log_error($target, "%s does not exist, and cannot create.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function _update() {
|
||||
if (!(@touch($this->path))) {
|
||||
$this->_log_error($this->path, "Failed to update (touch) %s");
|
||||
}
|
||||
}
|
||||
|
||||
protected function _read() {
|
||||
$contents = false;
|
||||
if (file_exists($this->path)) {
|
||||
$contents = @file_get_contents($this->path);
|
||||
if ($contents === false)
|
||||
$this->_log_error($this->path, 'Failed to read %s"');
|
||||
}
|
||||
return $contents;
|
||||
}
|
||||
|
||||
protected function _write($data) {
|
||||
if (@file_put_contents($this->path, $data, LOCK_EX) === false) {
|
||||
$this->_log_error($this->path, "Error writing to %s");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Purges the contents of a directory recursively
|
||||
*/
|
||||
private function _purge_recurse($dir) {
|
||||
$base = $dir . '/';
|
||||
$time = time();
|
||||
if (substr($dir, 0, strlen($this->dir)) !== $this->dir) {
|
||||
// uh oh, we somehow tried to escape from the cache directory
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
foreach(scandir($dir) as $f) {
|
||||
$fn = $base . $f;
|
||||
|
||||
if ($f[0] === '.') continue;
|
||||
|
||||
if (is_dir($fn)) {
|
||||
$this->_purge_recurse($fn);
|
||||
}
|
||||
else {
|
||||
$update = filemtime($fn);
|
||||
if ($time - $update > $this->timeout) {
|
||||
unlink($fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function _purge() {
|
||||
if ($this->timeout <= 0) return;
|
||||
$purge_file = $this->dir . '/.purgedata';
|
||||
if (!file_exists($purge_file)) @touch($purge_file);
|
||||
$last = 0;
|
||||
$fh = @fopen($purge_file, 'r+');
|
||||
if (!$fh) {
|
||||
$this->_log_error($purge_file,
|
||||
"Error encountered opening %s for writing");
|
||||
return;
|
||||
}
|
||||
$time = time();
|
||||
if (flock($fh, LOCK_EX)) {
|
||||
if (filesize($purge_file))
|
||||
$last = (int)fread($fh, filesize($purge_file));
|
||||
else $last = 0;
|
||||
if ($time - $last > 60*60*24) {
|
||||
rewind($fh);
|
||||
ftruncate($fh, 0);
|
||||
rewind($fh);
|
||||
fwrite($fh, $time);
|
||||
$this->_purge_recurse($this->dir);
|
||||
}
|
||||
flock($fh, LOCK_UN);
|
||||
fclose($fh);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
20
3rdparty/luminous/src/cache/sql/cache.mysql
vendored
Executable file
20
3rdparty/luminous/src/cache/sql/cache.mysql
vendored
Executable file
@ -0,0 +1,20 @@
|
||||
CREATE TABLE IF NOT EXISTS `luminous_cache`
|
||||
(
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
-- this is the cache ID, it's the hex representation of an MD5sum.
|
||||
cache_id CHAR(32) NOT NULL UNIQUE,
|
||||
-- cached output
|
||||
output MEDIUMTEXT NOT NULL,
|
||||
-- date of insertion
|
||||
insert_date INT NOT NULL,
|
||||
-- date of most recent hit
|
||||
hit_date INT NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
-- we create an index on cache ID because it's what's most interesting, but in reality, we don't
|
||||
-- usually need to worry about many bytes. 6 chars gives us 3 bytes,
|
||||
-- or roughly 16 million combinations
|
||||
INDEX(cache_id(6)),
|
||||
INDEX(hit_date)
|
||||
) ENGINE = MYISAM;
|
||||
|
||||
-- we explictly set MyISAM because at least on my Xampp installation, innodb is painfully slow
|
149
3rdparty/luminous/src/cache/sqlcache.class.php
vendored
Executable file
149
3rdparty/luminous/src/cache/sqlcache.class.php
vendored
Executable file
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/// @cond ALL
|
||||
class LuminousSQLSafetyException extends Exception {}
|
||||
|
||||
/*
|
||||
* A note regarding escaping:
|
||||
* Escaping is hard because we don't want to rely on an RBDMS specific escaping
|
||||
* function.
|
||||
* Therefore:
|
||||
* All the data and queries are specifically designed such that escaping is
|
||||
* unnecessary. String types are either b64 or b16, which means no inconvenient
|
||||
* characters, and integer types are, well, integers.
|
||||
*/
|
||||
class LuminousSQLCache extends LuminousCache {
|
||||
static $table_name = 'luminous_cache';
|
||||
static $queries = array(
|
||||
|
||||
// FIXME: INSERT IGNORE is MySQL specific.
|
||||
// we do need an ignore on duplicate because there's a race condition
|
||||
// between reading from the cache and then writing into it if the
|
||||
// read failed
|
||||
'insert' => 'INSERT IGNORE INTO `%s` (cache_id, output, insert_date, hit_date)
|
||||
VALUES("%s", "%s", %d, %d);',
|
||||
'update' => 'UPDATE `%s` SET hit_date=%d WHERE cache_id="%s";',
|
||||
'select' => 'SELECT output FROM `%s` WHERE cache_id="%s";',
|
||||
'purge' => 'DELETE FROM `%s` WHERE hit_date <= %d AND cache_id != "last_purge";',
|
||||
'get_purge_time' => 'SELECT hit_date FROM `%s` WHERE cache_id="last_purge" LIMIT 1;',
|
||||
'set_purge_time' => 'UPDATE `%s` SET hit_date = %d WHERE cache_id="last_purge";',
|
||||
'set_purge_time_initial' => 'INSERT IGNORE INTO `%s` (cache_id, hit_date)
|
||||
VALUES ("last_purge", %d);'
|
||||
);
|
||||
|
||||
private $sql_function = null;
|
||||
|
||||
public function set_sql_function($func) {
|
||||
$this->sql_function = $func;
|
||||
}
|
||||
|
||||
private static function _safety_check($string) {
|
||||
// we should only be handling very restricted data in queries.
|
||||
// http://en.wikipedia.org/wiki/Base64#Variants_summary_table
|
||||
if (is_int($string)
|
||||
|| (is_string($string)
|
||||
&& preg_match('@^[a-zA-Z0-9\-\+=_/\.:!]*$@i', $string)))
|
||||
return $string;
|
||||
else {
|
||||
throw new LuminousSQLSafetyException();
|
||||
}
|
||||
}
|
||||
|
||||
private function _query($sql) {
|
||||
|
||||
return call_user_func($this->sql_function, $sql);
|
||||
}
|
||||
|
||||
protected function _create() {
|
||||
try {
|
||||
if (!is_callable($this->sql_function))
|
||||
throw new Exception('LuminousSQLCache does not have a callable SQL function');
|
||||
$r = $this->_query(file_get_contents(dirname(__FILE__) . '/sql/cache.mysql'));
|
||||
if ($r === false)
|
||||
throw new Exception('Creation of cache table failed (query returned false)');
|
||||
} catch(Exception $e) {
|
||||
$this->log_error($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function _update() {
|
||||
$this->_query(
|
||||
sprintf(self::$queries['update'],
|
||||
self::_safety_check(self::$table_name),
|
||||
time(),
|
||||
self::_safety_check($this->id)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected function _read() {
|
||||
$ret = false;
|
||||
try {
|
||||
$ret = $this->_query(
|
||||
sprintf(self::$queries['select'],
|
||||
self::_safety_check(self::$table_name),
|
||||
self::_safety_check($this->id)
|
||||
)
|
||||
);
|
||||
if (!empty($ret) && isset($ret[0]) && isset($ret[0]['output'])) {
|
||||
return base64_decode($ret[0]['output']);
|
||||
}
|
||||
} catch (LuminousSQLSafetyException $e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function _write($data) {
|
||||
$data = base64_encode($data);
|
||||
$time = time();
|
||||
// try {
|
||||
$this->_query(sprintf(self::$queries['insert'],
|
||||
self::_safety_check(self::$table_name),
|
||||
self::_safety_check($this->id),
|
||||
self::_safety_check($data),
|
||||
self::_safety_check($time),
|
||||
self::_safety_check($time)
|
||||
));
|
||||
// } catch(LuminousSQLSafetyException $e) {}
|
||||
}
|
||||
|
||||
protected function _purge() {
|
||||
if ($this->timeout <= 0) return;
|
||||
$purge_time_ = $this->_query(
|
||||
sprintf(self::$queries['get_purge_time'],
|
||||
self::_safety_check(self::$table_name),
|
||||
self::_safety_check(time())
|
||||
)
|
||||
);
|
||||
$purge_time = 0;
|
||||
if ($purge_time_ !== false
|
||||
&& !empty($purge_time_) && isset($purge_time_[0]['hit_date'])) {
|
||||
$purge_time = $purge_time_[0]['hit_date'];
|
||||
} else {
|
||||
// we need to insert the record
|
||||
$this->_query(
|
||||
sprintf(self::$queries['set_purge_time_initial'],
|
||||
self::_safety_check(self::$table_name),
|
||||
self::_safety_check(time())
|
||||
)
|
||||
);
|
||||
}
|
||||
if ($purge_time < time() - 60*60*24) {
|
||||
// XXX: does this need to be in a try block?
|
||||
try {
|
||||
$this->_query(
|
||||
sprintf(self::$queries['purge'],
|
||||
self::_safety_check(self::$table_name),
|
||||
self::_safety_check(time() - $this->timeout)));
|
||||
} catch(LuminousSQLSafetyException $e) {}
|
||||
$this->_query(
|
||||
sprintf(self::$queries['set_purge_time'],
|
||||
self::_safety_check(self::$table_name),
|
||||
self::_safety_check(time())
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @endcond
|
222
3rdparty/luminous/src/cli.php
vendored
Executable file
222
3rdparty/luminous/src/cli.php
vendored
Executable file
@ -0,0 +1,222 @@
|
||||
<?php
|
||||
///@cond ALL
|
||||
/* command line interface */
|
||||
|
||||
class LuminousCLI {
|
||||
private $options = array(
|
||||
'input-file' => null,
|
||||
'output-file' => null,
|
||||
'lang' => null,
|
||||
'format' => 'html-full',
|
||||
'height' => 0,
|
||||
'theme' => 'geonyx',
|
||||
'code' => null,
|
||||
'line-numbers' => true,
|
||||
);
|
||||
private $cmd_option_map = array(
|
||||
'-i' => 'input-file',
|
||||
'-o' => 'output-file',
|
||||
'-l' => 'lang',
|
||||
'-f' => 'format',
|
||||
'-h' => 'height',
|
||||
'-t' => 'theme',
|
||||
);
|
||||
|
||||
static function print_help() {
|
||||
echo <<<EOF
|
||||
Usage:
|
||||
php luminous.php [OPTIONS] [SOURCE_CODE]
|
||||
|
||||
SOURCE_CODE may be omitted if you specify -i. Use '-' to read code from stdin.
|
||||
|
||||
Options:
|
||||
-f <format> Output format. This can be:
|
||||
'html' - An HTML snippet (div). CSS is not included
|
||||
on the page, the style-sheets must be included by
|
||||
hand.
|
||||
'html-full' - A full HTML page. CSS is embedded.
|
||||
'latex' - A LaTeX document.
|
||||
Default: 'html-full'
|
||||
-h <height> Constrains the height of the widget with 'html'
|
||||
formatter. Has no effect on other formatters.
|
||||
Default: 0
|
||||
-i <filename> Input file. If this is omitted, SOURCE_CODE is used.
|
||||
-l <language> Language code. If this is omitted, the language is
|
||||
guessed.
|
||||
-o <filename> Output file to write. If this is omitted, stdout is
|
||||
used.
|
||||
-t <theme> Theme to use. See --list-themes for valid themes
|
||||
--no-numbers Disables line numbering
|
||||
|
||||
--list-codes Lists valid language codes and exits
|
||||
--list-themes Lists valid themes and exits
|
||||
|
||||
--help Display this text and exit
|
||||
--version Display version number and exit
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
|
||||
function error($string) {
|
||||
echo "Error: $string
|
||||
see --help for help
|
||||
";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
function set_lookahead($option, $i) {
|
||||
global $argv;
|
||||
if (isset($argv[$i+1]))
|
||||
$this->options[$this->cmd_option_map[$option]] = $argv[$i+1];
|
||||
else self::error('Missing option for ' . $option);
|
||||
}
|
||||
|
||||
|
||||
function list_codes() {
|
||||
foreach(luminous::scanners() as $name=>$codes) {
|
||||
echo sprintf("%s: %s\n", $name, join(', ', $codes));
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
function list_themes() {
|
||||
echo preg_replace('/\.css$/m', '', join("\n", luminous::themes()) . "\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
function parse_args() {
|
||||
global $argv, $argc;
|
||||
for($i=1; $i<$argc; $i++) {
|
||||
$a = $argv[$i];
|
||||
|
||||
if (isset($this->cmd_option_map[$a])) {
|
||||
$this->set_lookahead($a, $i++);
|
||||
}
|
||||
elseif ($a === '--list-codes') {
|
||||
self::list_codes();
|
||||
}
|
||||
elseif ($a === '--list-themes') {
|
||||
self::list_themes();
|
||||
}
|
||||
elseif ($a === '--help') {
|
||||
self::print_help();
|
||||
exit(0);
|
||||
}
|
||||
elseif($a === '--version') {
|
||||
echo LUMINOUS_VERSION;
|
||||
echo "\n";
|
||||
exit(0);
|
||||
}
|
||||
elseif ($a === '--no-numbers') {
|
||||
$this->options['line-numbers'] = false;
|
||||
}
|
||||
else {
|
||||
if ($this->options['code'] !== null) {
|
||||
self::error('Unknown option: ' . $a);
|
||||
} else {
|
||||
$this->options['code'] = $a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function highlight() {
|
||||
$this->parse_args();
|
||||
|
||||
// figure out the code
|
||||
|
||||
// error cases are:
|
||||
// no input file or source code,
|
||||
if ($this->options['code'] === null
|
||||
&& $this->options['input-file'] === null) {
|
||||
$this->error('No input file or source code specified');
|
||||
}
|
||||
// or both input file and source code
|
||||
elseif ($this->options['code'] !== null
|
||||
&& $this->options['input-file'] !== null) {
|
||||
$this->error('Input file (-i) and source code specified. You probably '
|
||||
. 'didn\'t mean this');
|
||||
}
|
||||
|
||||
// is there an input file? use that.
|
||||
if ($this->options['input-file'] !== null) {
|
||||
$c = @file_get_contents($this->options['input-file']);
|
||||
if ($c === false) {
|
||||
$this->error('Could not read from ' . $this->options['input-file']);
|
||||
}
|
||||
else {
|
||||
$this->options['code'] = $c;
|
||||
}
|
||||
}
|
||||
// else we're expecting code to have been given on the command line,
|
||||
// but it might be '-' which means read stdin
|
||||
elseif ($this->options['code'] === '-') {
|
||||
$code = '';
|
||||
while (($line = fgets(STDIN)) !== false)
|
||||
$code .= $line;
|
||||
$this->options['code'] = $code;
|
||||
}
|
||||
|
||||
// set the formatter
|
||||
luminous::set('format', $this->options['format']);
|
||||
// lame check that the formatter is okay
|
||||
try { luminous::formatter(); }
|
||||
catch(Exception $e) {
|
||||
$this->error('Unknown formatter ' . $this->options['format']);
|
||||
}
|
||||
|
||||
// set the theme
|
||||
$valid_themes = luminous::themes();
|
||||
$theme = $this->options['theme'];
|
||||
if (!preg_match('/\.css$/', $theme)) $theme .= '.css';
|
||||
if (!luminous::theme_exists($theme)) {
|
||||
$this->error('No such theme: ' . $theme);
|
||||
} else {
|
||||
luminous::set('theme', $theme);
|
||||
}
|
||||
|
||||
|
||||
// set the language
|
||||
if ($this->options['lang'] === null) {
|
||||
// guessing
|
||||
$this->options['lang'] = luminous::guess_language($this->options['code']);
|
||||
}
|
||||
|
||||
// user provided language
|
||||
$scanners = luminous::scanners();
|
||||
$valid_scanner = false;
|
||||
foreach($scanners as $lang=>$codes) {
|
||||
if (in_array($this->options['lang'], $codes)) {
|
||||
$valid_scanner = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$valid_scanner) $this->error('No such language: '
|
||||
. $this->options['lang']);
|
||||
|
||||
|
||||
// other options
|
||||
luminous::set('max-height', $this->options['height']);
|
||||
luminous::set('line-numbers', $this->options['line-numbers']);
|
||||
|
||||
$h = luminous::highlight($this->options['lang'], $this->options['code']);
|
||||
if ($this->options['output-file'] !== null) {
|
||||
$r = @file_put_contents($this->options['output-file'], $h, LOCK_EX);
|
||||
if ($r === false)
|
||||
$this->error('Could not write to ' . $this->options['output-file']);
|
||||
} else {
|
||||
echo $h;
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
$luminous_cli = new LuminousCLI();
|
||||
$luminous_cli->highlight();
|
||||
}
|
||||
main();
|
||||
|
||||
///@endcond
|
296
3rdparty/luminous/src/core/filters.class.php
vendored
Executable file
296
3rdparty/luminous/src/core/filters.class.php
vendored
Executable file
@ -0,0 +1,296 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @cond CORE
|
||||
* @brief A collection of useful common filters.
|
||||
*
|
||||
* Filters are either stream filters or individual filters.
|
||||
* Stream filters operate on the entire token stream, and return the new
|
||||
* token stream. Individual filters operate on individual tokens (bound by type),
|
||||
* and return the new token. Any publicly available member here is one of those,
|
||||
* therefore the return and param documents are omitted.
|
||||
*
|
||||
*/
|
||||
// Poor man's namespace.
|
||||
class LuminousFilters {
|
||||
|
||||
/**
|
||||
* @brief Gets the expected number of arguments to a doc-comment command/tag
|
||||
* @param $command the name of the command
|
||||
* @returns The expected number of arguments for a command, this is either
|
||||
* 0 or 1 at the moment
|
||||
* @internal
|
||||
*/
|
||||
private static function doxygen_arg_length($command) {
|
||||
switch(strtolower($command)) {
|
||||
case "addtogroup":
|
||||
case "category":
|
||||
case "class":
|
||||
case "cond":
|
||||
case "def":
|
||||
case "defgroup":
|
||||
case "dir":
|
||||
case "elseif":
|
||||
case "enum":
|
||||
case "exception":
|
||||
case "example":
|
||||
case "extends":
|
||||
case "if":
|
||||
case "ifnot":
|
||||
case "file":
|
||||
case "headerfile":
|
||||
case "implements":
|
||||
case "ingroup":
|
||||
case "interface":
|
||||
case "memberof":
|
||||
case "namespace":
|
||||
case "package":
|
||||
case "page":
|
||||
case "par":
|
||||
case "param":
|
||||
case "relates":
|
||||
case "relatesalso":
|
||||
case "retval":
|
||||
case 'see':
|
||||
case 'since':
|
||||
case "tparam":
|
||||
case "throw":
|
||||
case "throws":
|
||||
case "weakgroup":
|
||||
case "xrefitem":
|
||||
return 1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief callback to doxygenize
|
||||
* Highlights Doxygen-esque doc-comment syntax.
|
||||
* This is a callback to doxygenize().
|
||||
* @return the highlighted string
|
||||
* @internal
|
||||
*/
|
||||
private static function doxygenize_cb($matches) {
|
||||
$lead = $matches[1];
|
||||
$tag_char = $matches[2];
|
||||
$tag = $matches[3];
|
||||
|
||||
$line = "";
|
||||
if (isset($matches[4]))
|
||||
$line = $matches[4];
|
||||
|
||||
$len = -1;
|
||||
// JSDoc-like
|
||||
$l_ = ltrim($line);
|
||||
if (isset($l_[0]) && $l_[0] === '{') {
|
||||
$line = preg_replace('/({[^}]*})/', "<DOCPROPERTY>$1</DOCPROPERTY>", $line);
|
||||
return "$lead<DOCTAG>$tag_char$tag</DOCTAG>$line";
|
||||
}
|
||||
else
|
||||
$len = self::doxygen_arg_length($tag);
|
||||
|
||||
if($len === 0)
|
||||
return "$lead<DOCTAG>$tag_char$tag</DOCTAG>$line";
|
||||
else {
|
||||
$l = explode(' ', $line);
|
||||
$start = "$lead<DOCTAG>$tag_char$tag</DOCTAG><DOCPROPERTY>";
|
||||
|
||||
$j = 0;
|
||||
$c = count($l);
|
||||
for($i=0; $j<$len && $i<$c; $i++)
|
||||
{
|
||||
$s = $l[$i];
|
||||
$start .= $s . ' ';
|
||||
unset($l[$i]);
|
||||
if (trim($s) !== '')
|
||||
$j++;
|
||||
}
|
||||
$start = preg_replace('/ $/', '', $start);
|
||||
$start .= "</DOCPROPERTY>";
|
||||
$l = array_values($l);
|
||||
if (!empty($l)) $start .= ' ';
|
||||
$start .= implode(' ', $l);
|
||||
return $start;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Highlights doc-comment tags inside a comment block.
|
||||
*
|
||||
* @see generic_doc_comment
|
||||
* @internal
|
||||
*/
|
||||
static function doxygenize($token) {
|
||||
$token = LuminousUtils::escape_token($token);
|
||||
$token[1] = preg_replace_callback("/(^(?>[\/\*#\s\{]*))([\@\\\])([\\w]*)(\}|[ \t]+.*?)?$/m",
|
||||
array('LuminousFilters', 'doxygenize_cb'), $token[1]);
|
||||
return $token;
|
||||
|
||||
}
|
||||
/**
|
||||
* @brief Generic filter to highlight JavaDoc, PHPDoc, Doxygen, JSdoc, and similar doc comment syntax.
|
||||
*
|
||||
* A cursory check will be performed to try to validate that the token
|
||||
* really is a doc-comment, it does this by checking for common formats.
|
||||
*
|
||||
* If the check is successful, the token will be switched to type
|
||||
* 'DOCCOMMENT' and its doc-tags will be highlighted
|
||||
*
|
||||
* This is a wrapper around doxygenize(). If the checks are not necessary,
|
||||
* or incorrect for your situation, you may instead choose to use
|
||||
* doxygenize() directly.
|
||||
*/
|
||||
static function generic_doc_comment($token) {
|
||||
// checks if a comment is in the form:
|
||||
// xyyz where x may = y but y != z.
|
||||
// This matches, say, /** comment but does not match /********/
|
||||
// same with /// comment but not ///////////////
|
||||
$s = $token[1];
|
||||
if (isset($s[3])
|
||||
&& ($s[2] === $s[1] || $s[2] === '!')
|
||||
&& !ctype_space($s[0])
|
||||
&& !ctype_space($s[1])
|
||||
&& $s[3] !== $s[2]
|
||||
)
|
||||
{
|
||||
$token[0] = 'DOCCOMMENT';
|
||||
$token = self::doxygenize($token);
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Highlights comment notes
|
||||
* Highlights keywords in comments, i.e. "NOTE", "XXX", "FIXME", "TODO",
|
||||
* "HACK", "BUG"
|
||||
*/
|
||||
static function comment_note($token) {
|
||||
$token = LuminousUtils::escape_token($token);
|
||||
$token[1] = preg_replace('/\\b(?:NOTE|XXX|FIXME|TODO|HACK|BUG):?/',
|
||||
'<COMMENT_NOTE>$0</COMMENT_NOTE>', $token[1]);
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Highlights generic escape sequences in strings
|
||||
* Highlights escape sequences in strings. There is no checking on which
|
||||
* sequences are legal, this is simply a generic function which checks for
|
||||
* \\u... unicode, \\d... octal, \\x... hex and finally just any character
|
||||
* following a backslash.
|
||||
*/
|
||||
static function string($token) {
|
||||
if (strpos($token[1], '\\') === false) return $token;
|
||||
|
||||
$token = LuminousUtils::escape_token($token);
|
||||
$token[1] = preg_replace('/
|
||||
\\\\
|
||||
(?:
|
||||
(?:u[a-f0-9]{4,8}) # unicode
|
||||
|\d{1,3} # octal
|
||||
|x[a-fA-F0-9]{2} # hex
|
||||
|. # whatever
|
||||
)
|
||||
/xi', '<ESC>$0</ESC>', $token[1]);
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tries to highlight PCRE style regular expression syntax
|
||||
*/
|
||||
static function pcre($token, $delimited=true) {
|
||||
$token = self::string($token);
|
||||
$token = LuminousUtils::escape_token($token);
|
||||
$str = &$token[1];
|
||||
$flags = array();
|
||||
if ($delimited) {
|
||||
$str = preg_replace('/^[^[:alnum:]<>\s]/', '<DELIMITER>$0</DELIMITER>', $str);
|
||||
if (preg_match("/[[:alpha:]]+$/", $str, $matches)){
|
||||
$m = $matches[0];
|
||||
$flags = str_split($m);
|
||||
$str = preg_replace("/((?<!\A)[^[:alnum:]\s<>])([[:alpha:]]+)$/",
|
||||
"<DELIMITER>$1</DELIMITER><KEYWORD>$2</KEYWORD>", $str);
|
||||
} else
|
||||
$str = preg_replace('/[^[:alnum:]<>]$/', '<DELIMITER>$0</DELIMITER>', $str);
|
||||
|
||||
}
|
||||
|
||||
$str = preg_replace("/((?<!\\\)[\*\+\.|])|((?<![\(\\\])\?)/",
|
||||
"<REGEX_OPERATOR>$0</REGEX_OPERATOR>", $str);
|
||||
$str = preg_replace("/(?<=\()\?(?:(?:[a-zA-Z:!|=])|(?:(?:<)[=!]))/",
|
||||
"<REGEX_SUBPATTERN>$0</REGEX_SUBPATTERN>", $str);
|
||||
$str = preg_replace("/(?<!\\\)[\(\)]/",
|
||||
"<REGEX_SUBPATTERN_MARKER>$0</REGEX_SUBPATTERN_MARKER>", $str);
|
||||
$str = preg_replace("/(?<!\\\)[\[\]]/",
|
||||
"<REGEX_CLASS_MARKER>$0</REGEX_CLASS_MARKER>", $str);
|
||||
$str = preg_replace("/(?<!\\\)
|
||||
\{
|
||||
(
|
||||
((?>\d+)(,(?>\d+)?)?)
|
||||
|
|
||||
(,(?>\d+))
|
||||
)
|
||||
\}/x", "<REGEX_REPEAT_MARKER>$0</REGEX_REPEAT_MARKER>", $str);
|
||||
|
||||
// extended regex: # signifies a comment
|
||||
if (in_array('x', $flags))
|
||||
$str = preg_replace('/(?<!\\\)#.*$/m', '<COMMENT>$0</COMMENT>',
|
||||
$str);
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Translates any token type of an uppercase/numeric IDENT to 'CONSTANT'
|
||||
*/
|
||||
static function upper_to_constant($token) {
|
||||
// check for this because it may have been mapped to a function or something
|
||||
if ($token[0] === 'IDENT' && preg_match('/^[A-Z_][A-Z_0-9]{3,}$/', $token[1]))
|
||||
$token[0] = 'CONSTANT';
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Translates anything of type 'IDENT' to the null type
|
||||
*/
|
||||
static function clean_ident($token) {
|
||||
if ($token[0] === 'IDENT') $token[0] = null;
|
||||
return $token;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Attempts to apply OO syntax highlighting
|
||||
*
|
||||
* Tries to apply generic OO syntax highlighting. Any identifer immediately
|
||||
* preceding a '.', '::' or '->' token is mapped to an 'OO'.
|
||||
* Any identifer token immediatel following any of those tokens is mapped to
|
||||
* an 'OBJ'.
|
||||
* This is a stream filter.
|
||||
*/
|
||||
static function oo_stream_filter($tokens) {
|
||||
$c = count($tokens);
|
||||
for($i=0; $i<$c; $i++) {
|
||||
if ($tokens[$i][0] !== 'IDENT') continue;
|
||||
if ($i > 0) {
|
||||
$s = $tokens[$i-1][1];
|
||||
if ($s === '.' || $s === '->' || $s === '::') {
|
||||
$tokens[$i][0] = 'OO';
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($i < $c-1) {
|
||||
$s = $tokens[$i+1][1];
|
||||
if ($s === '.' || $s === '->' || $s === '::') {
|
||||
$tokens[$i][0] = 'OBJ';
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
// end CORE
|
1854
3rdparty/luminous/src/core/scanner.class.php
vendored
Executable file
1854
3rdparty/luminous/src/core/scanner.class.php
vendored
Executable file
File diff suppressed because it is too large
Load Diff
84
3rdparty/luminous/src/core/strsearch.class.php
vendored
Executable file
84
3rdparty/luminous/src/core/strsearch.class.php
vendored
Executable file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @cond CORE
|
||||
* @brief A basic preg_match wrapper which caches its results
|
||||
*
|
||||
* @note This class is used by Scanner and should not need to be used by
|
||||
* anything else.
|
||||
*
|
||||
* A class encapsulating the process of searching for a substring efficiently
|
||||
* it handles caching of results.
|
||||
*
|
||||
* @warning One instance should be used only incrementally along a string.
|
||||
* i.e. do not call it with index = 5 then index = 1.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
*/
|
||||
class LuminousStringSearch
|
||||
{
|
||||
/// A copy of the string to operate on.
|
||||
private $string;
|
||||
|
||||
/**
|
||||
* The cache is stored as a map of pattern => result,
|
||||
* the result is an array of:
|
||||
* (0=>index, 1=>match_groups), OR, it is false if there are no more results
|
||||
* left in the string.
|
||||
*/
|
||||
private $cache = array();
|
||||
|
||||
public function __construct($str) {
|
||||
$this->string = $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a search for the given pattern past the given index.
|
||||
* @param $search the pattern to search for
|
||||
* @param $index the minimum string index (offset) of a result
|
||||
* @param $matches a reference to the return location of the match groups
|
||||
* @return the index or false if no match is found.
|
||||
*/
|
||||
public function
|
||||
match($search, $index, &$matches) {
|
||||
$r = false; // return value
|
||||
|
||||
if (isset($this->cache[$search])) {
|
||||
$a = $this->cache[$search];
|
||||
if ($a === false) return false; // no more results
|
||||
|
||||
$r = $a[0];
|
||||
$matches = $a[1];
|
||||
assert($matches !== null);
|
||||
|
||||
if ($r >= $index) // cache is good!
|
||||
return $r;
|
||||
}
|
||||
// cache not set, or out of date, we have to perform the match
|
||||
if (!($ret = preg_match($search, $this->string, $matches_,
|
||||
PREG_OFFSET_CAPTURE, $index))) {
|
||||
if ($ret === false && LUMINOUS_DEBUG) {
|
||||
throw new Exception('preg_match returned false for pattern: "'
|
||||
. $search . '", with code: ' . LuminousUtils::pcre_error_decode(
|
||||
preg_last_error()) . " with string length " . strlen($this->string)
|
||||
. " and offset " . $index
|
||||
);
|
||||
}
|
||||
$this->cache[$search] = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
$r = $matches_[0][1];
|
||||
// strip the offsets from the match_groups
|
||||
foreach($matches_ as $i=>&$v)
|
||||
$v = $v[0];
|
||||
|
||||
$this->cache[$search] = array($r, $matches_);
|
||||
|
||||
$matches = $matches_;
|
||||
return $r;
|
||||
}
|
||||
}
|
||||
|
||||
/// @endcond
|
49
3rdparty/luminous/src/core/tokenpresets.class.php
vendored
Executable file
49
3rdparty/luminous/src/core/tokenpresets.class.php
vendored
Executable file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @cond CORE
|
||||
* @brief A set of pre-defined patterns to match various common tokens
|
||||
*/
|
||||
abstract class LuminousTokenPresets {
|
||||
|
||||
/// multi-line double quoted string using backslash escapes
|
||||
static $DOUBLE_STR = '/" (?> [^"\\\\]+ | \\\\.)* (?:"|$)/xs';
|
||||
|
||||
/// single line double quoted string using backslash escapes
|
||||
static $DOUBLE_STR_SL = "/\"(?> [^\\\\\"\n]+ | \\\\.)*(?:\$|\")/xms";
|
||||
|
||||
/// multi-line single quote string using backslash escapes
|
||||
static $SINGLE_STR = "/' (?> [^'\\\\]+ | \\\\.)* (?:'|\$)/xs";
|
||||
|
||||
/// single line single quoted string using backslash escapes
|
||||
static $SINGLE_STR_SL = "/'(?> [^\\\\'\n]+ | \\\\.)*(?:\$|')/xms";
|
||||
|
||||
/// Single quoted c-style character
|
||||
static $CHAR = "(?: \\\\(?: x[A-F0-9]{1,2}| . ) | . ) (?: '|\$)/ixm";
|
||||
|
||||
/// hexadecimal literal
|
||||
static $NUM_HEX = '/0[Xx][a-fA-F0-9]+/';
|
||||
|
||||
/// Real number, i.e. an integer or a float, optionally with an exponent
|
||||
static $NUM_REAL = '/
|
||||
(?: \d+ (?: \.\d+ )? | \.?\d+) # int, fraction or significand
|
||||
(?:e[+-]?\d+)? # exponent
|
||||
/ix';
|
||||
|
||||
/// Single line c++ style comment
|
||||
static $C_COMMENT_SL = '% // .* %x';
|
||||
|
||||
/// Multiline C style comment
|
||||
static $C_COMMENT_ML = '% / \* (?> [^\\*]+ | \\*(?!/) )* (?: \\*/ | $) %sx';
|
||||
|
||||
/// Perl/Python/Ruby style hash-comment (single line)
|
||||
static $PERL_COMMENT = '/#.*/';
|
||||
|
||||
/// SQL style single quoted string using '' to escape
|
||||
static $SQL_SINGLE_STR = "/ ' (?> [^']+ | '' )* (?: '|\$)/x";
|
||||
|
||||
/// SQL style single quoted string using '' or \' to escape
|
||||
static $SQL_SINGLE_STR_BSLASH = "/ ' (?> [^'\\\\]+ | '' | \\\\. )* (?: '|\$)/x";
|
||||
|
||||
}
|
||||
/// @endcond
|
118
3rdparty/luminous/src/core/utils.class.php
vendored
Executable file
118
3rdparty/luminous/src/core/utils.class.php
vendored
Executable file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @cond CORE
|
||||
* @brief A set of utility functions for scanners
|
||||
*/
|
||||
class LuminousUtils {
|
||||
|
||||
/**
|
||||
* @brief Tries to balance a delimiter
|
||||
*
|
||||
* Tries to 'balance' a single character delimiter, i.e:
|
||||
* '(' is mapped to ')'
|
||||
* '{' is mapped to '}',
|
||||
* '[' is mapped to ']',
|
||||
* '<' is mapped to '>'
|
||||
* Any other character is mapped to itself.
|
||||
*
|
||||
* @param $delimiter the left/opening delimiter to try to balance
|
||||
* @return The corresponding close delimiter character, or the input
|
||||
* character.
|
||||
*/
|
||||
static function balance_delimiter($delimiter) {
|
||||
switch($delimiter) {
|
||||
case '(' : return ')';
|
||||
case '{' : return '}';
|
||||
case '[' : return ']';
|
||||
case '<' : return '>';
|
||||
default: return $delimiter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Escapes a string suitable for use in XML
|
||||
*
|
||||
* Escapes a string according to the Luminous internal escaping format
|
||||
* (this is currently htmlspecialchars with ENT_NOQUOTES.)
|
||||
* @param $string the string to escape
|
||||
* @return the escaped string
|
||||
*/
|
||||
static function escape_string($string) {
|
||||
return htmlspecialchars($string, ENT_NOQUOTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Escapes a token so its string is suitable for use in XML
|
||||
*
|
||||
* Escapes a token. If the token is already escaped, nothing changes.
|
||||
* If the token is not escaped, the escaped flag is set (index 2) and the
|
||||
* token text (index 1) is escaped according to the internal escaping format
|
||||
* @param $token the token to escape
|
||||
* @return the escaped token
|
||||
*/
|
||||
static function escape_token($token) {
|
||||
$esc = &$token[2];
|
||||
if (!$esc) {
|
||||
$str = &$token[1];
|
||||
$str = htmlspecialchars($str, ENT_NOQUOTES);
|
||||
$esc = true;
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wraps a block of text in an XML tag
|
||||
*
|
||||
* Tags a block of text. The block is assumed to have been escaped correctly
|
||||
* with LuminousUtils::escape_string.
|
||||
* @param $type the type to tag the string as, this is the token name
|
||||
* @param $block the block of text
|
||||
* @param $split_multiline if this is set to true, the tags are closed at
|
||||
* the end of each line and re-opened again on the next line. This is
|
||||
* useful for output formats like HTML, where spanning multiple lines
|
||||
* could break the markup
|
||||
* @return The tagged block of text. This resembles an XML fragment.
|
||||
*/
|
||||
static function tag_block($type, $block, $split_multiline=true) {
|
||||
if ($type === null) return $block;
|
||||
$open = '<' . $type . '>';
|
||||
$close = '</' . $type . '>';
|
||||
if ($split_multiline)
|
||||
return $open . str_replace("\n", $close . "\n" . $open, $block) .
|
||||
$close;
|
||||
else
|
||||
return $open . $block . $close;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decodes PCRE error codes to human readable strings
|
||||
*
|
||||
* Decodes a PCRE error code, which was returned by preg_last_error(), to
|
||||
* something readable
|
||||
* @param $errcode the error code
|
||||
* @return the error description, as string. This is currently the same
|
||||
* as the constant name, so the constant PREG_NO_ERROR is mapped to the
|
||||
* string 'PREG_NO_ERROR'
|
||||
*/
|
||||
static function pcre_error_decode($errcode) {
|
||||
switch ($errcode) {
|
||||
case PREG_NO_ERROR:
|
||||
return 'PREG_NO_ERROR';
|
||||
case PREG_INTERNAL_ERROR:
|
||||
return 'PREG_INTERNAL_ERROR';
|
||||
case PREG_BACKTRACK_LIMIT_ERROR:
|
||||
return 'PREG_BACKTRACK_LIMIT_ERROR';
|
||||
case PREG_RECURSION_LIMIT_ERROR:
|
||||
return 'PREG_RECURSION_LIMIT_ERROR';
|
||||
case PREG_BAD_UTF8_ERROR:
|
||||
return 'PREG_BAD_UTF8_ERROR';
|
||||
case PREG_BAD_UTF8_OFFSET_ERROR:
|
||||
return 'PREG_BAD_UTF8_OFFSET_ERROR';
|
||||
default:
|
||||
return "Unknown error code `$errcode'";
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
// ends CORE
|
3
3rdparty/luminous/src/debug.php
vendored
Executable file
3
3rdparty/luminous/src/debug.php
vendored
Executable file
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
define('LUMINOUS_DEBUG', false);
|
20
3rdparty/luminous/src/doxygen.php
vendored
Executable file
20
3rdparty/luminous/src/doxygen.php
vendored
Executable file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
// A few things to make Doxygen a little nicer. No code here.
|
||||
|
||||
/**
|
||||
* @mainpage
|
||||
*
|
||||
* This is the API documentation for Luminous, a PHP syntax highlighter.
|
||||
* This is mostly for development purposes, and not a lot of use to users.
|
||||
*
|
||||
* However, users may be interested in the \link luminous user's API \endlink
|
||||
* and the LuminousOptions class
|
||||
*
|
||||
*
|
||||
* Luminous site: http://luminous.asgaard.co.uk
|
||||
*
|
||||
* User's documentation: http://luminous.asgaard.co.uk/index.php/docs/show/index
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
173
3rdparty/luminous/src/formatters/formatter.class.php
vendored
Executable file
173
3rdparty/luminous/src/formatters/formatter.class.php
vendored
Executable file
@ -0,0 +1,173 @@
|
||||
<?php
|
||||
/// @cond ALL
|
||||
|
||||
|
||||
/**
|
||||
* \file luminous_formatter.class.php
|
||||
* \brief Formatting logic -- converts Luminous output into displayable formats
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Abstract class to convert Luminous output into a universal format.
|
||||
*
|
||||
* Abstract base class to implement an output formatter. A formatter
|
||||
* will convert Luminous's tags into some kind of output (e.g. HTML), by
|
||||
* overriding the method Format().
|
||||
*/
|
||||
abstract class LuminousFormatter {
|
||||
/// Number of chars to wrap at
|
||||
public $wrap_length = 120;
|
||||
/// Don't use this yet.
|
||||
public $language_specific_tags = false;
|
||||
/**
|
||||
* Tab width, in spaces. If this is -1 or 0, tabs will not be converted. This
|
||||
* is not recommended as browsers may render tabs as different widths which
|
||||
* will break the wrapping.
|
||||
*/
|
||||
public $tab_width = 2;
|
||||
|
||||
/// Whether or not to add line numbering
|
||||
public $line_numbers = true;
|
||||
/// Number of first line
|
||||
public $start_line = 1;
|
||||
|
||||
/// An array of lines to be highlighted initially, if the formatter supports
|
||||
/// it
|
||||
public $highlight_lines = array();
|
||||
|
||||
/// sets whether or not to link URIs.
|
||||
public $link = true;
|
||||
|
||||
/**
|
||||
* Height of the resulting output. This may or may not make any sense
|
||||
* depending on the output format.
|
||||
*
|
||||
* Use 0 or -1 for no limit.
|
||||
*/
|
||||
public $height = 0;
|
||||
|
||||
|
||||
/**
|
||||
* The language of the source code being highlighted. Formatters may choose
|
||||
* to do something with this.
|
||||
*/
|
||||
public $language = null;
|
||||
|
||||
|
||||
/**
|
||||
* The main method for interacting with formatter objects.
|
||||
* @param src the input string, which is of the form output by an instance of
|
||||
* Luminous.
|
||||
* @return The input string reformatted to some other specification.
|
||||
*/
|
||||
public abstract function format($src);
|
||||
|
||||
/**
|
||||
* If relevant, the formatter should implement this and use LuminousCSSParser
|
||||
* to port the theme.
|
||||
* @param $theme A CSS string representing the theme
|
||||
*/
|
||||
public function set_theme($theme)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Handles line wrapping.
|
||||
* @param line the line which needs to be broken. This is a reference, which
|
||||
* will be operated upon. After calling, $line will have appropriate line
|
||||
* breaks to wrap to the given width, and will contain at least one line break
|
||||
* at the end.
|
||||
* @param wrap_length the width to wrap to.
|
||||
*
|
||||
* @return the number of lines it was broken up into (1 obviously means no
|
||||
* wrapping occurred.).
|
||||
*
|
||||
* @todo wrap to indent? or not? hm.
|
||||
*
|
||||
*/
|
||||
protected static function wrap_line(&$line, $wrap_length) {
|
||||
// The vast majority of lines will not need wrapping so it pays to
|
||||
// check this first.
|
||||
if ($wrap_length <= 0 || !isset($line[$wrap_length])
|
||||
|| strlen(strip_tags($line)) < $wrap_length) {
|
||||
$line .= "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
$line_split = preg_split('/((?:<.*?>)|(?:&.*?;)|[ \t]+)/',
|
||||
$line, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
|
||||
$strlen = 0;
|
||||
$line_cpy = "";
|
||||
$num_lines = 1;
|
||||
|
||||
$num_open = 0;
|
||||
foreach($line_split as $l) {
|
||||
|
||||
$l0 = $l[0];
|
||||
if ($l0 === '<') {
|
||||
$line_cpy .= $l;
|
||||
continue;
|
||||
}
|
||||
|
||||
$s = strlen($l);
|
||||
|
||||
if($l0 === '&') {
|
||||
// html entity codes only count as 1 char.
|
||||
if(++$strlen > $wrap_length) {
|
||||
$strlen = 1;
|
||||
$line_cpy .= "\n";
|
||||
$num_lines++;
|
||||
}
|
||||
$line_cpy .= $l;
|
||||
|
||||
continue;
|
||||
}
|
||||
if ($s+$strlen <= $wrap_length) {
|
||||
$line_cpy .= $l;
|
||||
$strlen += $s;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($s <= $wrap_length) {
|
||||
$line_cpy .= "\n" . $l;
|
||||
$num_lines++;
|
||||
$strlen = $s;
|
||||
continue;
|
||||
}
|
||||
// at this point, the line needs wrapping.
|
||||
|
||||
// bump us up to the next line
|
||||
$diff = $wrap_length-$strlen;
|
||||
|
||||
$line_cpy .= substr($l, 0, $diff) . "\n";
|
||||
$l_ = substr($l, $diff);
|
||||
// now start copying.
|
||||
$strlen = 0;
|
||||
// this would probably be marginally faster if it did its own arithmetic
|
||||
// instead of calling strlen
|
||||
|
||||
while (strlen($l_) > 0) {
|
||||
$strl = strlen($l_);
|
||||
$num_lines++;
|
||||
|
||||
if ($strl > $wrap_length) {
|
||||
$line_cpy .= substr($l_, 0, $wrap_length) . "\n";
|
||||
$l_ = substr($l_, $wrap_length);
|
||||
} else {
|
||||
$line_cpy .= $l_;
|
||||
$strlen = $strl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$line = $line_cpy . "\n";
|
||||
|
||||
return $num_lines;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @endcond
|
344
3rdparty/luminous/src/formatters/htmlformatter.class.php
vendored
Executable file
344
3rdparty/luminous/src/formatters/htmlformatter.class.php
vendored
Executable file
@ -0,0 +1,344 @@
|
||||
<?php
|
||||
/// @cond ALL
|
||||
|
||||
/**
|
||||
* Collection of templates and templating utilities
|
||||
*/
|
||||
class LuminousHTMLTemplates {
|
||||
|
||||
// NOTE Don't worry about whitespace in the templates - it gets stripped from the innerHTML,
|
||||
// so the <pre>s aren't affected. Make it readable :)
|
||||
|
||||
/// Normal container
|
||||
const container_template = '
|
||||
<div
|
||||
class="luminous"
|
||||
data-language="{language}"
|
||||
style="{height_css}"
|
||||
>
|
||||
{subelement}
|
||||
</div>';
|
||||
|
||||
/// Inline code container
|
||||
const inline_template = '
|
||||
<div
|
||||
class="luminous inline"
|
||||
data-language="{language}"
|
||||
>
|
||||
{subelement}
|
||||
</div>';
|
||||
|
||||
/// line number-less
|
||||
const numberless_template = '
|
||||
<pre
|
||||
class="code"
|
||||
>
|
||||
{code}
|
||||
</pre>';
|
||||
|
||||
/// line numbered
|
||||
// NOTE: there's a good reason we use tables here and that's because
|
||||
// nothing else works reliably.
|
||||
const numbered_template = '
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<pre class="line-numbers">
|
||||
{line_numbers}
|
||||
</pre>
|
||||
</td>
|
||||
|
||||
<td class="code-container">
|
||||
<pre class="code numbered"
|
||||
data-startline="{start_line}"
|
||||
data-highlightlines="{highlight_lines}"
|
||||
>
|
||||
{code}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>';
|
||||
|
||||
|
||||
|
||||
|
||||
private static function _strip_template_whitespace_cb($matches) {
|
||||
return ($matches[0][0] === '<')? $matches[0] : '';
|
||||
}
|
||||
private static function _strip_template_whitespace($string) {
|
||||
return preg_replace_callback('/\s+|<[^>]++>/',
|
||||
array('self', '_strip_template_whitespace_cb'),
|
||||
$string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a string with a given set of values
|
||||
* The format syntax uses {xyz} as a placeholder, which will be
|
||||
* substituted from the 'xyz' key from $variables
|
||||
*
|
||||
* @param $template The template string
|
||||
* @param $variables An associative (keyed) array of values to be substituted
|
||||
* @param $strip_whitespace_from_template If @c TRUE, the template's whitespace is removed.
|
||||
* This allows templates to be written to be easeier to read, without having to worry about
|
||||
* the pre element inherting any unintended whitespace
|
||||
*/
|
||||
public static function format($template, $variables, $strip_whitespace_from_template = true) {
|
||||
|
||||
if ($strip_whitespace_from_template) {
|
||||
$template = self::_strip_template_whitespace($template);
|
||||
}
|
||||
|
||||
foreach($variables as $search => $replace) {
|
||||
$template = str_replace("{" . $search . "}", $replace, $template);
|
||||
}
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
|
||||
class LuminousFormatterHTML extends LuminousFormatter {
|
||||
|
||||
// overridden by inline formatter
|
||||
protected $inline = false;
|
||||
public $height = 0;
|
||||
/**
|
||||
* strict HTML standards: the target attribute won't be used in links
|
||||
* \since 0.5.7
|
||||
*/
|
||||
public $strict_standards = false;
|
||||
|
||||
private function height_css() {
|
||||
$height = trim('' . $this->height);
|
||||
$css = '';
|
||||
if (!empty($height) && (int)$height > 0) {
|
||||
// look for units, use px is there are none
|
||||
if (!preg_match('/\D$/', $height)) $height .= 'px';
|
||||
$css = "max-height: {$height};";
|
||||
}
|
||||
else
|
||||
$css = '';
|
||||
return $css;
|
||||
}
|
||||
|
||||
private static function template_cb($matches) {
|
||||
|
||||
}
|
||||
|
||||
// strips out unnecessary whitespace from a template
|
||||
private static function template($t, $vars=array()) {
|
||||
$t = preg_replace_callback('/\s+|<[^>]++>/',
|
||||
array('self', 'template_cb'),
|
||||
$t);
|
||||
array_unshift($vars, $t);
|
||||
$code = call_user_func_array('sprintf', $vars);
|
||||
return $code;
|
||||
}
|
||||
|
||||
private function lines_numberless($src) {
|
||||
$lines = array();
|
||||
$lines_original = explode("\n", $src);
|
||||
foreach($lines_original as $line) {
|
||||
$l = $line;
|
||||
$num = $this->wrap_line($l, $this->wrap_length);
|
||||
// strip the newline if we're going to join it. Seems the easiest way to
|
||||
// fix http://code.google.com/p/luminous/issues/detail?id=10
|
||||
$l = substr($l, 0, -1);
|
||||
$lines[] = $l;
|
||||
}
|
||||
$lines = implode("\n", $lines);
|
||||
return $lines;
|
||||
}
|
||||
|
||||
private function format_numberless($src) {
|
||||
return LuminousHTMLTemplates::format(
|
||||
LuminousHTMLTemplates::numberless_template,
|
||||
array(
|
||||
'height_css' => $this->height_css(),
|
||||
'code' => $this->lines_numberless($src)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function format($src) {
|
||||
|
||||
$line_numbers = false;
|
||||
|
||||
if ($this->link) $src = $this->linkify($src);
|
||||
|
||||
$code_block = null;
|
||||
if ($this->line_numbers) {
|
||||
$code_block = $this->format_numbered($src);
|
||||
}
|
||||
else {
|
||||
$code_block = $this->format_numberless($src);
|
||||
}
|
||||
|
||||
// convert </ABC> to </span>
|
||||
$code_block = preg_replace('/(?<=<\/)[A-Z_0-9]+(?=>)/S', 'span',
|
||||
$code_block);
|
||||
// convert <ABC> to <span class=ABC>
|
||||
$cb = create_function('$matches',
|
||||
'$m1 = strtolower($matches[1]);
|
||||
return "<span class=\'" . $m1 . "\'>";
|
||||
');
|
||||
$code_block = preg_replace_callback('/<([A-Z_0-9]+)>/', $cb, $code_block);
|
||||
|
||||
$format_data = array(
|
||||
'language' => ($this->language === null)? '' : htmlentities($this->language),
|
||||
'subelement' => $code_block,
|
||||
'height_css' => $this->height_css()
|
||||
);
|
||||
return LuminousHTMLTemplates::format(
|
||||
$this->inline? LuminousHTMLTemplates::inline_template :
|
||||
LuminousHTMLTemplates::container_template,
|
||||
$format_data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects and links URLs - callback
|
||||
*/
|
||||
protected function linkify_cb($matches) {
|
||||
$uri = (isset($matches[1]) && strlen(trim($matches[1])))? $matches[0]
|
||||
: "http://" . $matches[0];
|
||||
|
||||
// we dont want to link if it would cause malformed HTML
|
||||
$open_tags = array();
|
||||
$close_tags = array();
|
||||
preg_match_all("/<(?!\/)([^\s>]*).*?>/", $matches[0], $open_tags,
|
||||
PREG_SET_ORDER);
|
||||
preg_match_all("/<\/([^\s>]*).*?>/", $matches[0], $close_tags,
|
||||
PREG_SET_ORDER);
|
||||
|
||||
if (count($open_tags) != count($close_tags))
|
||||
return $matches[0];
|
||||
if (isset($open_tags[0])
|
||||
&& trim($open_tags[0][1]) !== trim($close_tags[0][1])
|
||||
)
|
||||
return $matches[0];
|
||||
|
||||
$uri = strip_tags($uri);
|
||||
|
||||
$target = ($this->strict_standards)? '' : ' target="_blank"';
|
||||
return "<a href='{$uri}' class='link'{$target}>{$matches[0]}</a>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects and links URLs
|
||||
*/
|
||||
protected function linkify($src) {
|
||||
if (stripos($src, "http") === false && stripos($src, "www") === false)
|
||||
return $src;
|
||||
|
||||
$chars = "0-9a-zA-Z\$\-_\.+!\*,%";
|
||||
$src_ = $src;
|
||||
// everyone stand back, I know regular expressions
|
||||
$src = preg_replace_callback(
|
||||
"@(?<![\w])
|
||||
(?:(https?://(?:www[0-9]*\.)?) | (?:www\d*\.) )
|
||||
|
||||
# domain and tld
|
||||
(?:[$chars]+)+\.[$chars]{2,}
|
||||
# we don't include tags at the EOL because these are likely to be
|
||||
# line-enclosing tags.
|
||||
(?:[/$chars/?=\#;]+|&|<[^>]+>(?!$))*
|
||||
@xm",
|
||||
array($this, 'linkify_cb'), $src);
|
||||
// this can hit a backtracking limit, in which case it nulls our string
|
||||
// FIXME: see if we can make the above regex more resiliant wrt
|
||||
// backtracking
|
||||
if (preg_last_error() !== PREG_NO_ERROR) {
|
||||
$src = $src_;
|
||||
}
|
||||
return $src;
|
||||
}
|
||||
|
||||
|
||||
private function format_numbered($src) {
|
||||
|
||||
$lines = '<span>' .
|
||||
str_replace("\n", "\n</span><span>", $src, $num_replacements) .
|
||||
"\n</span>";
|
||||
$num_lines = $num_replacements + 1;
|
||||
|
||||
$line_numbers = '<span>' . implode('</span><span>',
|
||||
range($this->start_line, $this->start_line + $num_lines - 1, 1)
|
||||
) . '</span>';
|
||||
|
||||
|
||||
$format_data = array(
|
||||
'line_number_digits' => strlen( (string)($this->start_line) + $num_lines ), // max number of digits in the line - this is used by the CSS
|
||||
'start_line' => $this->start_line,
|
||||
'height_css' => $this->height_css(),
|
||||
'highlight_lines' => implode(',', $this->highlight_lines),
|
||||
'code' => $lines,
|
||||
'line_numbers' => $line_numbers
|
||||
);
|
||||
|
||||
return LuminousHTMLTemplates::format(
|
||||
LuminousHTMLTemplates::numbered_template,
|
||||
$format_data
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LuminousFormatterHTMLInline extends LuminousFormatterHTML {
|
||||
|
||||
public function format($src) {
|
||||
$this->line_numbers = false;
|
||||
$this->height = 0;
|
||||
$this->inline = true;
|
||||
return parent::format($src);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class LuminousFormatterHTMLFullPage extends LuminousFormatterHTML {
|
||||
protected $theme_css = null;
|
||||
protected $css = null;
|
||||
public function set_theme($css) {
|
||||
$this->theme_css = $css;
|
||||
}
|
||||
protected function get_layout() {
|
||||
// this path info shouldn't really be here
|
||||
$path = luminous::root() . '/style/luminous.css';
|
||||
$this->css = file_get_contents($path);
|
||||
}
|
||||
public function format($src) {
|
||||
$this->height = 0;
|
||||
$this->get_layout();
|
||||
$fmted = parent::format($src);
|
||||
return <<<EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title></title>
|
||||
<style type='text/css'>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
/* luminous.css */
|
||||
{$this->css}
|
||||
/* End luminous.css */
|
||||
/* Theme CSS */
|
||||
{$this->theme_css}
|
||||
/* End theme CSS */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Begin luminous code //-->
|
||||
$fmted
|
||||
<!-- End Luminous code //-->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
EOF;
|
||||
}
|
||||
}
|
||||
/// @endcond
|
14
3rdparty/luminous/src/formatters/identityformatter.class.php
vendored
Executable file
14
3rdparty/luminous/src/formatters/identityformatter.class.php
vendored
Executable file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/// @cond ALL
|
||||
|
||||
/**
|
||||
* Identity formatter. Returns what it's given. Implemented for consistency.
|
||||
*/
|
||||
class LuminousIdentityFormatter extends LuminousFormatter {
|
||||
public function format($str) {
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
||||
/// @endcond
|
200
3rdparty/luminous/src/formatters/latexformatter.class.php
vendored
Executable file
200
3rdparty/luminous/src/formatters/latexformatter.class.php
vendored
Executable file
@ -0,0 +1,200 @@
|
||||
<?php
|
||||
/// @cond ALL
|
||||
require_once(dirname(__FILE__) . '/../utils/cssparser.class.php');
|
||||
|
||||
/**
|
||||
* LaTeX output formatter for Luminous.
|
||||
*
|
||||
* \since 0.5.4
|
||||
*/
|
||||
class LuminousFormatterLatex extends LuminousFormatter {
|
||||
|
||||
private $css = null;
|
||||
function __construct() { }
|
||||
|
||||
function set_theme($theme) {
|
||||
$this->css = new LuminousCSSParser();
|
||||
$this->css->convert($theme);
|
||||
}
|
||||
/// Converts a hexadecimal string in the form #ABCDEF to an RGB array
|
||||
/// where each element is normalised to the range 0-1
|
||||
static function hex2rgb($hex) {
|
||||
$x = hexdec(substr($hex, 1));
|
||||
$b = $x % 256;
|
||||
$g = ($x >> 8) % 256;
|
||||
$r = ($x >> 16) % 256;
|
||||
|
||||
$b /= 255.0;
|
||||
$g /= 255.0;
|
||||
$r /= 255.0;
|
||||
|
||||
$b = round($b, 2);
|
||||
$g = round($g, 2);
|
||||
$r = round($r, 2);
|
||||
|
||||
$rgb = array($r, $g, $b);
|
||||
return $rgb;
|
||||
}
|
||||
protected function linkify($src) {
|
||||
return $src;
|
||||
}
|
||||
/// Defines all the styling commands, these are obtained from the css parser
|
||||
function define_style_commands() {
|
||||
if ($this->css === null)
|
||||
throw new Exception('LaTeX formatter has not been set a theme');
|
||||
$cmds = array();
|
||||
foreach($this->css->rules() as $name=>$properties) {
|
||||
if (!preg_match('/^\w+$/', $name))
|
||||
continue;
|
||||
$cmd = "{#1}" ;
|
||||
if ($this->css->value($name, 'bold', false) === true)
|
||||
$cmd = "{\\textbf$cmd}";
|
||||
if ($this->css->value($name, 'italic', false) === true)
|
||||
$cmd = "{\\emph$cmd}";
|
||||
if (($col = $this->css->value($name, 'color', null)) !== null) {
|
||||
if (preg_match('/^#[a-f0-9]{6}$/i', $col)) {
|
||||
$rgb = self::hex2rgb($col);
|
||||
$col_str = "{$rgb[0]}, {$rgb[1]}, $rgb[2]";
|
||||
$cmd = "{\\textcolor[rgb]{{$col_str}}$cmd}";
|
||||
}
|
||||
}
|
||||
$name = str_replace('_', '', $name);
|
||||
$name = strtoupper($name);
|
||||
$cmds[] = "\\newcommand{\\lms{$name}}[1]$cmd";
|
||||
}
|
||||
|
||||
if ($this->line_numbers &&
|
||||
($col = $this->css->value('code', 'color', null)) !== null) {
|
||||
if (preg_match('/^#[a-f0-9]{6}$/i', $col)) {
|
||||
$rgb = self::hex2rgb($col);
|
||||
$col_str = "{$rgb[0]}, {$rgb[1]}, $rgb[2]";
|
||||
$cmd = "\\renewcommand{\\theFancyVerbLine}{%
|
||||
\\textcolor[rgb]{{$col_str}}{\arabic{FancyVerbLine}}}";
|
||||
$cmds[] = $cmd;
|
||||
}
|
||||
}
|
||||
|
||||
return implode("\n", $cmds);
|
||||
}
|
||||
|
||||
function get_background_colour() {
|
||||
if (($col = $this->css->value('code', 'bgcolor', null)) !== null) {
|
||||
if (preg_match('/^#[a-f0-9]{6}$/i', $col))
|
||||
$rgb = self::hex2rgb($col);
|
||||
$col_str = "{$rgb[0]}, {$rgb[1]}, $rgb[2]";
|
||||
return "\\pagecolor[rgb]{{$col_str}}";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function format($str) {
|
||||
$out = '';
|
||||
|
||||
$verbcmd = "\\begin{Verbatim}[commandchars=\\\\\\{\}";
|
||||
if ($this->line_numbers)
|
||||
$verbcmd .= ",numbers=left,firstnumber=1,stepnumber=1";
|
||||
$verbcmd .= ']';
|
||||
// define the preamble
|
||||
$out .= <<<EOF
|
||||
\documentclass{article}
|
||||
\usepackage{fullpage}
|
||||
\usepackage{color}
|
||||
\usepackage{fancyvrb}
|
||||
\begin{document}
|
||||
{$this->define_style_commands()}
|
||||
{$this->get_background_colour()}
|
||||
|
||||
$verbcmd
|
||||
|
||||
EOF;
|
||||
|
||||
$s = '';
|
||||
$str = preg_replace('%<([^/>]+)>\s*</\\1>%', '', $str);
|
||||
$str = str_replace("\t", ' ', $str);
|
||||
|
||||
$lines = explode("\n", $str);
|
||||
|
||||
if ($this->wrap_length > 0) {
|
||||
$str = '';
|
||||
foreach($lines as $i=>$l) {
|
||||
$this->wrap_line($l, $this->wrap_length);
|
||||
$str .= $l;
|
||||
}
|
||||
}
|
||||
|
||||
$str_ = preg_split('/(<[^>]+>)/', $str, -1,
|
||||
PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
$f1 = create_function('$matches', '
|
||||
return "\\\lms" . str_replace("_", "", $matches[1]) . "{"; ');
|
||||
$f2 = create_function('$matches', '
|
||||
if ($matches[0][0] == "\\\")
|
||||
return "{\\\textbackslash}";
|
||||
return "\\\" . $matches[0];');
|
||||
|
||||
foreach($str_ as $s_) {
|
||||
if ($s_[0] === '<') {
|
||||
$s_ = preg_replace('%</[^>]+>%', '}', $s_);
|
||||
$s_ = preg_replace_callback('%<([^>]+)>%', $f1
|
||||
,$s_);
|
||||
} else {
|
||||
$s_ = str_replace('>', '>', $s_);
|
||||
$s_ = str_replace('<', '<', $s_);
|
||||
$s_ = str_replace('&', '&', $s_);
|
||||
$s_ = preg_replace_callback('/[#{}_$\\\&]|&(?=amp;)/', $f2, $s_);
|
||||
}
|
||||
|
||||
$s .= $s_;
|
||||
}
|
||||
|
||||
unset($str_);
|
||||
|
||||
$s = "\\lmsCODE{" . $s . '}';
|
||||
|
||||
|
||||
/* XXX:
|
||||
* hack alert: leaving newline literals (\n) inside arguments seems to
|
||||
* leave them being totally ignored. This is a problem for wrapping.
|
||||
*
|
||||
* the current solution is to close all open lms commands before the
|
||||
* newline then reopen them afterwards.
|
||||
*/
|
||||
|
||||
$stack = array();
|
||||
$pieces = preg_split('/(\\\lms[^\{]+\{|(?<!\\\)(\\\\\\\\)*[\{\}])/', $s,
|
||||
-1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
// NOTE: p being a reference is probably going to necessitate a lot of
|
||||
// copying to pass through all these preg_* and str* calls.
|
||||
// consider rewriting.
|
||||
foreach($pieces as $k=>&$p) {
|
||||
if (preg_match('/^\\\lms/', $p))
|
||||
$stack[] = "" . $p;
|
||||
elseif(preg_match('/^(\\\\\\\\)*\}/', $p)) {
|
||||
array_pop($stack);
|
||||
}
|
||||
elseif(preg_match('/^(\\\\\\\\)*{/', $p))
|
||||
$stack [] = $p;
|
||||
elseif(strpos($p, "\n") !== false) {
|
||||
$before = "";
|
||||
$after = "";
|
||||
foreach($stack as $st_) {
|
||||
$before .= $st_;
|
||||
$after .= '}';
|
||||
}
|
||||
$p = str_replace("\n", "$after\n$before" , $p);
|
||||
}
|
||||
}
|
||||
|
||||
$s = implode('', $pieces);
|
||||
|
||||
$out .= $s;
|
||||
$out .= <<<EOF
|
||||
\end{Verbatim}
|
||||
\end{document}
|
||||
EOF;
|
||||
return $out;
|
||||
}
|
||||
|
||||
}
|
||||
/// @endcond
|
140
3rdparty/luminous/src/load_scanners.php
vendored
Executable file
140
3rdparty/luminous/src/load_scanners.php
vendored
Executable file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This is a horrible routine to register all the default
|
||||
* scanners. The code is distracting at best so it's been factored into this one
|
||||
* file.
|
||||
*
|
||||
* We include it into the main program with a require statement, which
|
||||
* due to the literal way PHP includes work, when done within a function gives
|
||||
* us access to that function's scope.
|
||||
* We are in the scope of a method inside the Luminous_ object, so we refer to
|
||||
* $this as being the $luminous_ singleton object.
|
||||
*/
|
||||
|
||||
|
||||
$language_dir = luminous::root() . '/languages/';
|
||||
|
||||
|
||||
// this is a dummy file which includes ECMAScript dependencies in a
|
||||
// non-circular way.
|
||||
$this->scanners->AddScanner('ecma-includes', null, null,
|
||||
"$language_dir/include/ecma.php");
|
||||
|
||||
$this->scanners->AddScanner(array('ada', 'adb', 'ads'),
|
||||
'LuminousAdaScanner', 'Ada', "$language_dir/ada.php");
|
||||
|
||||
$this->scanners->AddScanner(array('as', 'actionscript'),
|
||||
'LuminousActionScriptScanner', 'ActionScript', "$language_dir/as.php",
|
||||
'ecma');
|
||||
|
||||
$this->scanners->AddScanner(array('bnf'),
|
||||
'LuminousBNFScanner', 'Backus Naur Form', "$language_dir/bnf.php");
|
||||
|
||||
$this->scanners->AddScanner(array('bash', 'sh'),
|
||||
'LuminousBashScanner', 'Bash', "$language_dir/bash.php");
|
||||
|
||||
$this->scanners->AddScanner(array('c', 'cpp', 'h', 'hpp', 'cxx', 'hxx'),
|
||||
'LuminousCppScanner', 'C/C++', "$language_dir/cpp.php");
|
||||
|
||||
$this->scanners->AddScanner(array('cs', 'csharp', 'c#'),
|
||||
'LuminousCSharpScanner', 'C#', "$language_dir/csharp.php");
|
||||
|
||||
$this->scanners->AddScanner('css',
|
||||
'LuminousCSSScanner', 'CSS', "$language_dir/css.php");
|
||||
|
||||
$this->scanners->AddScanner(array('diff', 'patch'),
|
||||
'LuminousDiffScanner', 'Diff', "$language_dir/diff.php");
|
||||
|
||||
$this->scanners->AddScanner(array('prettydiff', 'prettypatch',
|
||||
'diffpretty', 'patchpretty'),
|
||||
'LuminousPrettyDiffScanner', 'Diff-Pretty', "$language_dir/diff.php");
|
||||
|
||||
$this->scanners->AddScanner(array('html', 'htm'),
|
||||
'LuminousHTMLScanner', 'HTML', "$language_dir/html.php",
|
||||
array('js', 'css'));
|
||||
|
||||
$this->scanners->AddScanner(array('ecma', 'ecmascript'),
|
||||
'LuminousECMAScriptScanner', 'ECMAScript',
|
||||
"$language_dir/ecmascript.php", 'ecma-includes');
|
||||
|
||||
$this->scanners->AddScanner(array('erlang', 'erl', 'hrl'),
|
||||
'LuminousErlangScanner', 'Erlang', "$language_dir/erlang.php");
|
||||
|
||||
$this->scanners->AddScanner('go', 'LuminousGoScanner', 'Go',
|
||||
"$language_dir/go.php");
|
||||
|
||||
|
||||
$this->scanners->AddScanner(array('groovy'),
|
||||
'LuminousGroovyScanner', 'Groovy',
|
||||
"$language_dir/groovy.php");
|
||||
|
||||
$this->scanners->AddScanner(array('haskell', 'hs'),
|
||||
'LuminousHaskellScanner', 'Haskell', "$language_dir/haskell.php");
|
||||
|
||||
$this->scanners->AddScanner('java',
|
||||
'LuminousJavaScanner', 'Java', "$language_dir/java.php");
|
||||
|
||||
$this->scanners->AddScanner(array('js', 'javascript'),
|
||||
'LuminousJavaScriptScanner', 'JavaScript', "$language_dir/javascript.php",
|
||||
array('ecma'));
|
||||
|
||||
$this->scanners->AddScanner('json',
|
||||
'LuminousJSONScanner', 'JSON', "$language_dir/json.php");
|
||||
|
||||
$this->scanners->AddScanner(array('latex', 'tex'),
|
||||
'LuminousLatexScanner', 'LaTeX', "$language_dir/latex.php");
|
||||
|
||||
$this->scanners->AddScanner(array('lolcode', 'lolc', 'lol'),
|
||||
'LuminousLOLCODEScanner', 'LOLCODE', "$language_dir/lolcode.php");
|
||||
|
||||
$this->scanners->AddScanner(array('m', 'matlab'),
|
||||
'LuminousMATLABScanner', 'MATLAB', "$language_dir/matlab.php");
|
||||
|
||||
$this->scanners->AddScanner(array('perl', 'pl', 'pm'),
|
||||
'LuminousPerlScanner', 'Perl', "$language_dir/perl.php");
|
||||
|
||||
$this->scanners->AddScanner(array('rails','rhtml', 'ror'),
|
||||
'LuminousRailsScanner', 'Ruby on Rails',
|
||||
"$language_dir/rails.php", array('ruby', 'html'));
|
||||
|
||||
$this->scanners->AddScanner(array('ruby','rb'),
|
||||
'LuminousRubyScanner', 'Ruby', "$language_dir/ruby.php");
|
||||
|
||||
$this->scanners->AddScanner(array('plain', 'text', 'txt'),
|
||||
'LuminousIdentityScanner', 'Plain', "$language_dir/identity.php");
|
||||
|
||||
// PHP Snippet does not require an initial <?php tag to begin highlighting
|
||||
$this->scanners->AddScanner('php_snippet', 'LuminousPHPSnippetScanner',
|
||||
'PHP Snippet', "$language_dir/php.php", array('html'));
|
||||
|
||||
$this->scanners->AddScanner('php',
|
||||
'LuminousPHPScanner', 'PHP', "$language_dir/php.php",
|
||||
array('html'));
|
||||
|
||||
$this->scanners->AddScanner(array('python', 'py'),
|
||||
'LuminousPythonScanner', 'Python', "$language_dir/python.php");
|
||||
$this->scanners->AddScanner(array('django', 'djt'),
|
||||
'LuminousDjangoScanner', 'Django', "$language_dir/python.php",
|
||||
array('html')
|
||||
);
|
||||
$this->scanners->AddScanner(array('scala', 'scl'),
|
||||
'LuminousScalaScanner', 'Scala', "$language_dir/scala.php", 'xml');
|
||||
|
||||
$this->scanners->AddScanner('scss',
|
||||
'LuminousSCSSScanner', 'SCSS', "$language_dir/scss.php");
|
||||
|
||||
$this->scanners->AddScanner(array('sql', 'mysql'),
|
||||
'LuminousSQLScanner', 'SQL', "$language_dir/sql.php");
|
||||
|
||||
$this->scanners->AddScanner(array('vim', 'vimscript'),
|
||||
'LuminousVimScriptScanner', 'Vim Script', "$language_dir/vim.php");
|
||||
|
||||
$this->scanners->AddScanner(array('vb', 'bas'),
|
||||
'LuminousVBScanner', 'Visual Basic', "$language_dir/vb.php",
|
||||
'xml');
|
||||
|
||||
$this->scanners->AddScanner('xml', 'LuminousXMLScanner',
|
||||
'XML', "$language_dir/xml.php", 'html');
|
||||
|
||||
$this->scanners->SetDefaultScanner('plain');
|
626
3rdparty/luminous/src/luminous.php
vendored
Executable file
626
3rdparty/luminous/src/luminous.php
vendored
Executable file
@ -0,0 +1,626 @@
|
||||
<?php
|
||||
|
||||
|
||||
require_once(dirname(__FILE__) . '/debug.php');
|
||||
|
||||
require_once(dirname(__FILE__) . '/options.class.php');
|
||||
|
||||
require_once(dirname(__FILE__) . '/cache/cache.class.php');
|
||||
require_once(dirname(__FILE__) . '/cache/fscache.class.php');
|
||||
require_once(dirname(__FILE__) . '/cache/sqlcache.class.php');
|
||||
require_once(dirname(__FILE__) . '/scanners.class.php');
|
||||
require_once(dirname(__FILE__) . '/formatters/formatter.class.php');
|
||||
require_once(dirname(__FILE__) . '/core/scanner.class.php');
|
||||
|
||||
// updated automatically, use single quotes, keep single line
|
||||
define('LUMINOUS_VERSION', 'v0.7.0');
|
||||
|
||||
|
||||
/*
|
||||
* This file contains the public calling interface for Luminous. It's split
|
||||
* into two classes: one is basically the user-interface, the other is a
|
||||
* wrapper around it. The wrapper allows a single-line function call, and is
|
||||
* procedural. It's wrapped in an abstract class for a namespace.
|
||||
* The real class is instantiated into a singleton which is manipulated by
|
||||
* the abstract class methods.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @cond ALL
|
||||
* This is kind of a pseudo-UI class. It's a singleton which will be
|
||||
* manipulated by a few procedural functions, for ease of use.
|
||||
* It's technically supposed to be private to this class, but this is a sort
|
||||
* of 'agreed' privateness, and we expose an instance of it globally.
|
||||
*
|
||||
* In fact, it is used by (at least) the diff scanner, which uses its
|
||||
* scanner table.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
*/
|
||||
class _Luminous {
|
||||
|
||||
/// Settings array
|
||||
/// @see LuminousOptions
|
||||
public $settings;
|
||||
|
||||
public $scanners; ///< the scanner table
|
||||
|
||||
public $cache = null;
|
||||
|
||||
/**
|
||||
* The language is passed to the formatter which may choose to do something
|
||||
* interesting with it. If you use the scanners table this can be figured out
|
||||
* automatically, but if you pass in your own scanner, you will need to
|
||||
* give a language name if you want the formatter to consider it.
|
||||
*/
|
||||
public $language = null;
|
||||
|
||||
public function __construct() {
|
||||
$this->scanners = new LuminousScanners();
|
||||
$this->register_default_scanners();
|
||||
}
|
||||
|
||||
/// registers builtin scanners
|
||||
private function register_default_scanners() {
|
||||
$this->settings = new LuminousOptions();
|
||||
require dirname(__FILE__) . '/load_scanners.php';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an instance of the current formatter
|
||||
*/
|
||||
function get_formatter() {
|
||||
$fmt_path = dirname(__FILE__) . '/formatters/';
|
||||
|
||||
$fmt = $this->settings->format;
|
||||
$formatter = null;
|
||||
if (!is_string($fmt) && is_subclass_of($fmt, 'LuminousFormatter')) {
|
||||
$formatter = clone $fmt;
|
||||
} elseif ($fmt === 'html') {
|
||||
require_once($fmt_path . 'htmlformatter.class.php');
|
||||
$formatter = new LuminousFormatterHTML();
|
||||
} elseif ($fmt === 'html-inline') {
|
||||
require_once($fmt_path . 'htmlformatter.class.php');
|
||||
$formatter = new LuminousFormatterHTMLInline();
|
||||
} elseif ($fmt === 'html-full') {
|
||||
require_once($fmt_path . 'htmlformatter.class.php');
|
||||
$formatter = new LuminousFormatterHTMLFullPage();
|
||||
} elseif($fmt === 'latex') {
|
||||
require_once($fmt_path . 'latexformatter.class.php');
|
||||
$formatter = new LuminousFormatterLatex();
|
||||
} elseif($fmt === null || $fmt === 'none') {
|
||||
require_once($fmt_path . 'identityformatter.class.php');
|
||||
$formatter = new LuminousIdentityFormatter();
|
||||
}
|
||||
|
||||
if ($formatter === null) {
|
||||
throw new Exception('Unknown formatter: ' . $this->settings->format);
|
||||
return null;
|
||||
}
|
||||
$this->set_formatter_options($formatter);
|
||||
return $formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a formatter instance according to our current options/settings
|
||||
*/
|
||||
private function set_formatter_options(&$formatter) {
|
||||
$formatter->wrap_length = $this->settings->wrap_width;
|
||||
$formatter->line_numbers = $this->settings->line_numbers;
|
||||
$formatter->start_line = $this->settings->start_line;
|
||||
$formatter->link = $this->settings->auto_link;
|
||||
$formatter->height = $this->settings->max_height;
|
||||
$formatter->strict_standards = $this->settings->html_strict;
|
||||
$formatter->set_theme(luminous::theme($this->settings->theme));
|
||||
$formatter->highlight_lines = $this->settings->highlight_lines;
|
||||
$formatter->language = $this->language;
|
||||
}
|
||||
|
||||
/**
|
||||
* calculates a 'cache_id' for the input. This is dependent upon the
|
||||
* source code and the settings. This should be (near-as-feasible) unique
|
||||
* for any cobmination of source, language and settings
|
||||
*/
|
||||
private function cache_id($scanner, $source) {
|
||||
// to figure out the cache id, we mash a load of stuff together and
|
||||
// md5 it. This gives us a unique (assuming no collisions) handle to
|
||||
// a cache file, which depends on the input source, the relevant formatter
|
||||
// settings, the version, and scanner.
|
||||
$settings = array($this->settings->wrap_width,
|
||||
$this->settings->line_numbers,
|
||||
$this->settings->start_line,
|
||||
$this->settings->auto_link,
|
||||
$this->settings->max_height,
|
||||
$this->settings->format,
|
||||
$this->settings->theme,
|
||||
$this->settings->html_strict,
|
||||
LUMINOUS_VERSION,
|
||||
);
|
||||
|
||||
$id = md5($source);
|
||||
$id = md5($id . serialize($scanner));
|
||||
$id = md5($id . serialize($settings));
|
||||
return $id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The real highlighting function
|
||||
* @throw InvalidArgumentException if $scanner is not either a string or a
|
||||
* LuminousScanner instance, or if $source is not a string.
|
||||
*/
|
||||
function highlight($scanner, $source, $settings = null) {
|
||||
$old_settings = null;
|
||||
if ($settings !== null) {
|
||||
if (!is_array($settings)) {
|
||||
throw new Exception('Luminous internal error: Settings is not an array');
|
||||
}
|
||||
$old_settings = clone $this->settings;
|
||||
foreach($settings as $k=>$v) {
|
||||
$this->settings->set($k, $v);
|
||||
}
|
||||
}
|
||||
$should_reset_language = false;
|
||||
$this->cache = null;
|
||||
if (!is_string($source)) throw new InvalidArgumentException('Non-string '
|
||||
. 'supplied for $source');
|
||||
|
||||
if (!($scanner instanceof LuminousScanner)) {
|
||||
if (!is_string($scanner)) throw new InvalidArgumentException('Non-string
|
||||
or LuminousScanner instance supplied for $scanner');
|
||||
$code = $scanner;
|
||||
$scanner = $this->scanners->GetScanner($code);
|
||||
if ($scanner === null) throw new Exception("No known scanner for '$code' and no default set");
|
||||
$should_reset_language = true;
|
||||
$this->language = $this->scanners->GetDescription($code);
|
||||
}
|
||||
$cache_hit = true;
|
||||
$out = null;
|
||||
if ($this->settings->cache) {
|
||||
$cache_id = $this->cache_id($scanner, $source);
|
||||
if ($this->settings->sql_function !== null) {
|
||||
$this->cache = new LuminousSQLCache($cache_id);
|
||||
$this->cache->set_sql_function($this->settings->sql_function);
|
||||
} else {
|
||||
$this->cache = new LuminousFileSystemCache($cache_id);
|
||||
}
|
||||
$this->cache->set_purge_time($this->settings->cache_age);
|
||||
$out = $this->cache->read();
|
||||
}
|
||||
if ($out === null) {
|
||||
$cache_hit = false;
|
||||
$out_raw = $scanner->highlight($source);
|
||||
$formatter = $this->get_formatter();
|
||||
$out = $formatter->format($out_raw);
|
||||
}
|
||||
|
||||
if ($this->settings->cache && !$cache_hit) {
|
||||
$this->cache->write($out);
|
||||
}
|
||||
if ($should_reset_language) $this->language = null;
|
||||
if ($old_settings !== null) {
|
||||
$this->settings = $old_settings;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
// ends ALL
|
||||
|
||||
|
||||
// Here's our singleton.
|
||||
global $luminous_; // sometimes need this or the object seems to disappear
|
||||
$luminous_ = new _Luminous();
|
||||
|
||||
/// @cond USER
|
||||
|
||||
// here's our 'real' UI class, which uses the above singleton. This is all
|
||||
// static because these are actually procudural functions, we're using the
|
||||
// class as a namespace.
|
||||
/**
|
||||
* @brief Users' API
|
||||
*/
|
||||
abstract class luminous {
|
||||
|
||||
/**
|
||||
* @brief Highlights a string according to the current settings
|
||||
*
|
||||
* @param $scanner The scanner to use, this can either be a langauge code,
|
||||
* or it can be an instance of LuminousScanner.
|
||||
* @param $source The source string
|
||||
* @param $cache Whether or not to use the cache
|
||||
* @return the highlighted source code.
|
||||
*
|
||||
* To specify different output formats or other options, see set().
|
||||
*/
|
||||
static function highlight($scanner, $source, $cache_or_settings=null) {
|
||||
global $luminous_;
|
||||
try {
|
||||
$settings = null;
|
||||
if (is_bool($cache_or_settings)) {
|
||||
$settings = array('cache' => $cache_or_settings);
|
||||
} else if (is_array($cache_or_settings)) {
|
||||
$settings = $cache_or_settings;
|
||||
}
|
||||
$h = $luminous_->highlight($scanner, $source, $settings);
|
||||
if ($luminous_->settings->verbose) {
|
||||
$errs = self::cache_errors();
|
||||
if (!empty($errs)) {
|
||||
trigger_error("Le cache de Luminous a été desactivé en raison du manque de permission d'écriture. \n" .
|
||||
'See luminous::cache_errors() for details.');
|
||||
}
|
||||
}
|
||||
return $h;
|
||||
} catch (InvalidArgumentException $e) {
|
||||
// this is a user error, let it bubble
|
||||
//FIXME how do we let it bubble without throwing? the stack trace will
|
||||
// be wrong.
|
||||
throw $e;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// this is an internal error or a scanner error, or something
|
||||
// it might not technically be Luminous that caused it, but let's not
|
||||
// make it kill the whole page in production code
|
||||
if (LUMINOUS_DEBUG) throw $e;
|
||||
else {
|
||||
$return = $source;
|
||||
if (($t = self::setting('failure-tag')))
|
||||
$return = "<$t>$return</$t>";
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Highlights a file according to the current setings.
|
||||
*
|
||||
* @param $scanner The scanner to use, this can either be a langauge code,
|
||||
* or it can be an instance of LuminousScanner.
|
||||
* @param $file the source string
|
||||
* @param $cache Whether or not to use the cache
|
||||
* @return the highlighted source code.
|
||||
*
|
||||
* To specify different output formats or other options, see set().
|
||||
*/
|
||||
static function highlight_file($scanner, $file, $cache_or_settings=null) {
|
||||
return self::highlight($scanner, file_get_contents($file), $cache_or_settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a list of cache errors encountered during the most recent highlight
|
||||
*
|
||||
* @return An array of errors the cache encountered (which may be empty),
|
||||
* or @c FALSE if the cache is not enabled
|
||||
*/
|
||||
static function cache_errors() {
|
||||
global $luminous_;
|
||||
$c = $luminous_->cache;
|
||||
if ($c === null) return FALSE;
|
||||
return $c->errors();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers a scanner
|
||||
*
|
||||
* Registers a scanner with Luminous's scanner table. Utilising this
|
||||
* function means that Luminous will handle instantiation and inclusion of
|
||||
* the scanner's source file in a lazy-manner.
|
||||
*
|
||||
* @param $language_code A string or array of strings designating the
|
||||
* aliases by which this scanner may be accessed
|
||||
* @param $classname The class name of the scanner, as string. If you
|
||||
* leave this as 'null', it will be treated as a dummy file (you can use
|
||||
* this to handle a set of non-circular include rules, if you run into
|
||||
* problems).
|
||||
* @param $readable_language A human readable language name
|
||||
* @param $path The path to the source file containing your scanner
|
||||
* @param dependencies An array of other scanners which this scanner
|
||||
* depends on (as sub-scanners, or superclasses). Each item in the
|
||||
* array should be a $language_code for another scanner.
|
||||
*/
|
||||
static function register_scanner($language_code, $classname,
|
||||
$readable_language, $path, $dependencies=null) {
|
||||
global $luminous_;
|
||||
$luminous_->scanners->AddScanner($language_code, $classname,
|
||||
$readable_language, $path, $dependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the full filesystem path to Luminous
|
||||
* @return what Luminous thinks its location is on the filesystem
|
||||
* @internal
|
||||
*/
|
||||
static function root() {
|
||||
return realpath(dirname(__FILE__) . '/../');
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a list of installed themes
|
||||
*
|
||||
* @return the list of theme files present in style/.
|
||||
* Each theme will simply be a filename, and will end in .css, and will not
|
||||
* have any directory prefix.
|
||||
*/
|
||||
static function themes() {
|
||||
$themes_uri = self::root() . '/style/';
|
||||
$themes = array();
|
||||
foreach(glob($themes_uri . '/*.css') as $css) {
|
||||
$fn = trim(preg_replace("%.*/%", '', $css));
|
||||
switch($fn) {
|
||||
// these are special, exclude these
|
||||
case 'luminous.css':
|
||||
case 'luminous_print.css':
|
||||
case 'luminous.min.css':
|
||||
continue;
|
||||
default:
|
||||
$themes[] = $fn;
|
||||
}
|
||||
}
|
||||
return $themes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a theme exists
|
||||
* @param $theme the name of a theme, which should be suffixed with .css
|
||||
* @return @c TRUE if a theme exists in style/, else @c FALSE
|
||||
*/
|
||||
static function theme_exists($theme) {
|
||||
return in_array($theme, self::themes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads a CSS theme file
|
||||
* Gets the CSS-string content of a theme file.
|
||||
* Use this function for reading themes as it involves security
|
||||
* checks against reading arbitrary files
|
||||
*
|
||||
* @param $theme The name of the theme to retrieve, which may or may not
|
||||
* include the .css suffix.
|
||||
* @return the content of a theme; this is the actual CSS text.
|
||||
* @internal
|
||||
*/
|
||||
static function theme($theme) {
|
||||
if (!preg_match('/\.css$/i', $theme)) $theme .= '.css';
|
||||
if (self::theme_exists($theme))
|
||||
return file_get_contents(self::root() . "/style/" . $theme);
|
||||
else
|
||||
throw new Exception('No such theme file: ' . $theme);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets a setting's value
|
||||
* @param $option The name of the setting (corresponds to an attribute name
|
||||
* in LuminousOptions)
|
||||
* @return The value of the given setting
|
||||
* @throws Exception if the option is unrecognised
|
||||
*
|
||||
* Options are stored in LuminousOptions, which provides documentation of
|
||||
* each option.
|
||||
* @see LuminousOptions
|
||||
*/
|
||||
static function setting($option) {
|
||||
global $luminous_;
|
||||
$option = str_replace('-', '_', $option);
|
||||
return $luminous_->settings->$option;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the given option to the given value
|
||||
* @param $option The name of the setting (corresponds to an attribute name
|
||||
* in LuminousOptions)
|
||||
* @param $value The new value of the setting
|
||||
* @throws Exception if the option is unrecognised (and in various other
|
||||
* validation failures),
|
||||
* @throws InvalidArgumentException if the argument fails the type-validation
|
||||
* check
|
||||
*
|
||||
* @note This function can also accept multiple settings if $option is a
|
||||
* map of option_name=>value
|
||||
*
|
||||
* Options are stored in LuminousOptions, which provides documentation of
|
||||
* each option.
|
||||
*
|
||||
* @note as of 0.7 this is a thin wrapper around LuminousOptions::set()
|
||||
*
|
||||
* @see LuminousOptions::set
|
||||
*/
|
||||
static function set($option, $value=null) {
|
||||
global $luminous_;
|
||||
$luminous_->settings->set($option, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a list of registered scanners
|
||||
*
|
||||
* @return a list of scanners currently registered. The list is in the
|
||||
* format:
|
||||
*
|
||||
* language_name => codes,
|
||||
*
|
||||
* where language_name is a string, and codes is an array of strings.
|
||||
*
|
||||
* The array is sorted alphabetically by key.
|
||||
*/
|
||||
static function scanners() {
|
||||
global $luminous_;
|
||||
$scanners = $luminous_->scanners->ListScanners();
|
||||
ksort($scanners);
|
||||
return $scanners;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a formatter instance
|
||||
*
|
||||
* @return an instance of a LuminousFormatter according to the current
|
||||
* format setting
|
||||
*
|
||||
* This shouldn't be necessary for general usage, it is only implemented
|
||||
* for testing.
|
||||
* @internal
|
||||
*/
|
||||
static function formatter() {
|
||||
global $luminous_;
|
||||
return $luminous_->get_formatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Comparison function for guess_language()'s sorting
|
||||
*/
|
||||
static function __guess_language_cmp($a, $b) {
|
||||
$c = $a['p'] - $b['p'];
|
||||
if ($c === 0) return 0;
|
||||
elseif ($c < 0) return -1;
|
||||
else return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Attempts to guess the language of a piece of source code
|
||||
* @param $src The source code whose language is to be guessed
|
||||
* @param $confidence The desired confidence level: if this is 0.05 but the
|
||||
* best guess has a confidence of 0.04, then $default is returned. Note
|
||||
* that the confidence level returned by scanners is quite arbitrary, so
|
||||
* don't set this to '1' thinking that'll give you better results.
|
||||
* A realistic confidence is likely to be quite low, because a scanner will
|
||||
* only return 1 if it's able to pick out a shebang (#!) line or something
|
||||
* else definitive. If there exists no such identifier, a 'strong'
|
||||
* confidence which is right most of the time might be as low as 0.1.
|
||||
* Therefore it is recommended to keep this between 0.01 and 0.10.
|
||||
* @param $default The default name to return in the event that no scanner
|
||||
* thinks this source belongs to them (at the desired confidence).
|
||||
*
|
||||
* @return A valid code for the best scanner, or $default.
|
||||
*
|
||||
* This is a wrapper around luminous::guess_language_full
|
||||
*/
|
||||
static function guess_language($src, $confidence=0.05, $default = 'plain') {
|
||||
$guess = self::guess_language_full($src);
|
||||
if ($guess[0]['p'] >= $confidence)
|
||||
return $guess[0]['codes'][0];
|
||||
else
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Attempts to guess the language of a piece of source code
|
||||
* @param $src The source code whose language is to be guessed
|
||||
* @return An array - the array is ordered by probability, with the most
|
||||
* probable language coming first in the array.
|
||||
* Each array element is an array which represents a language (scanner),
|
||||
* and has the keys:
|
||||
* \li \c 'language' => Human-readable language description,
|
||||
* \li \c 'codes' => valid codes for the language (array),
|
||||
* \li \c 'p' => the probability (between 0.0 and 1.0 inclusive),
|
||||
*
|
||||
* note that \c 'language' and \c 'codes' are the key => value pair from
|
||||
* luminous::scanners()
|
||||
*
|
||||
* @warning Language guessing is inherently unreliable but should be right
|
||||
* about 80% of the time on common languages. Bear in mind that guessing is
|
||||
* going to be severely hampered in the case that one language is used to
|
||||
* generate code in another language.
|
||||
*
|
||||
* Usage for this function will be something like this:
|
||||
* @code
|
||||
* $guesses = luminous::guess_language($src);
|
||||
* $output = luminous::highlight($guesses[0]['codes'][0], $src);
|
||||
* @endcode
|
||||
*
|
||||
* @see luminous::guess_language
|
||||
*/
|
||||
static function guess_language_full($src) {
|
||||
global $luminous_;
|
||||
// first we're going to make an 'info' array for the source, which
|
||||
// precomputes some frequently useful things, like how many lines it
|
||||
// has, etc. It prevents scanners from redundantly figuring these things
|
||||
// out themselves
|
||||
$lines = preg_split("/\r\n|[\r\n]/", $src);
|
||||
$shebang = '';
|
||||
if (preg_match('/^#!.*/', $src, $m)) $shebang = $m[0];
|
||||
|
||||
$info = array(
|
||||
'lines' => $lines,
|
||||
'num_lines' => count($lines),
|
||||
'trimmed' => trim($src),
|
||||
'shebang' => $shebang
|
||||
);
|
||||
|
||||
$return = array();
|
||||
foreach(self::scanners() as $lang=>$codes) {
|
||||
$scanner_name = $luminous_->scanners->GetScanner($codes[0], false,
|
||||
false);
|
||||
assert($scanner_name !== null);
|
||||
$return[] = array(
|
||||
'language' => $lang,
|
||||
'codes' => $codes,
|
||||
'p' => call_user_func(array($scanner_name, 'guess_language'), $src,
|
||||
$info)
|
||||
);
|
||||
}
|
||||
uasort($return, array('luminous', '__guess_language_cmp'));
|
||||
$return = array_reverse($return);
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets the markup you need to include in your web page
|
||||
* @return a string representing everything that needs to be printed in
|
||||
* the \<head\> section of a website.
|
||||
*
|
||||
* This is influenced by the following settings:
|
||||
* relative-root,
|
||||
* include-javascript,
|
||||
* include-jquery
|
||||
* theme
|
||||
*/
|
||||
static function head_html() {
|
||||
global $luminous_;
|
||||
$theme = self::setting('theme');
|
||||
$relative_root = self::setting('relative-root');
|
||||
$js = self::setting('include-javascript');
|
||||
$jquery = self::setting('include-jquery');
|
||||
|
||||
if (!preg_match('/\.css$/i', $theme)) $theme .= '.css';
|
||||
if (!self::theme_exists($theme)) $theme = 'luminous_light.css';
|
||||
|
||||
if ($relative_root === null) {
|
||||
$relative_root = str_replace($_SERVER['DOCUMENT_ROOT'], '/',
|
||||
dirname(__FILE__));
|
||||
$relative_root = str_replace('\\', '/', $relative_root); // bah windows
|
||||
$relative_root = rtrim($relative_root, '/');
|
||||
// go up one level.
|
||||
$relative_root = preg_replace('%/[^/]*$%', '', $relative_root);
|
||||
}
|
||||
// if we ended up with any double slashes, let's zap them, and also
|
||||
// trim any trailing ones
|
||||
$relative_root = preg_replace('%(?<!:)//+%', '/', $relative_root);
|
||||
$relative_root = rtrim($relative_root, '/');
|
||||
$out = '';
|
||||
$link_template = "<link rel='stylesheet' type='text/css' href='$relative_root/style/%s' id='%s'>\n";
|
||||
$script_template = "<script type='text/javascript' src='$relative_root/client/%s'></script>\n";
|
||||
$out .= sprintf($link_template, 'luminous.css', 'luminous-style');
|
||||
$out .= sprintf($link_template, $theme, 'luminous-theme');
|
||||
if ($js) {
|
||||
if ($jquery)
|
||||
$out .= sprintf($script_template, 'jquery-1.6.4.min.js');
|
||||
$out .= sprintf($script_template, 'luminous.js');
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
// ends user
|
342
3rdparty/luminous/src/options.class.php
vendored
Executable file
342
3rdparty/luminous/src/options.class.php
vendored
Executable file
@ -0,0 +1,342 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @cond USER
|
||||
*
|
||||
* @brief Options class.
|
||||
*
|
||||
* @warning This object's structure isn't guaranteed to be stable so don't read
|
||||
* or write these directly. As a user, you should be using luminous::set()
|
||||
* and luminous::setting()
|
||||
*
|
||||
* We use a fair bit of PHP trickery in the implementation here. The keener
|
||||
* among you will notice that the options are all private: don't worry about
|
||||
* that. We override the __set() method to apply option specific validation.
|
||||
* Options can be written to as normal.
|
||||
*
|
||||
* The option variable names correspond with option strings that can be passed
|
||||
* through luminous::set(), however, for historical reasons, underscores can be
|
||||
* replaced with dashed in the call.
|
||||
*/
|
||||
class LuminousOptions {
|
||||
|
||||
/**
|
||||
* @brief Whether to use the built-in cache
|
||||
*/
|
||||
private $cache = true;
|
||||
|
||||
/**
|
||||
* @brief Maximum age of cache files in seconds
|
||||
*
|
||||
* Cache files which have not been read for this length of time will be
|
||||
* removed from the file system. The file's 'mtime' is used to calculate
|
||||
* when it was last used, and a cache hit triggers a 'touch'
|
||||
*
|
||||
* Set to -1 or 0 to disable cache purges
|
||||
*/
|
||||
private $cache_age = 7776000; // 90 days
|
||||
|
||||
/**
|
||||
* @brief Word wrapping
|
||||
*
|
||||
* If the formatter supports line wrapping, lines will be wrapped at
|
||||
* this number of characters (0 or -1 to disable)
|
||||
*/
|
||||
private $wrap_width = -1;
|
||||
|
||||
/**
|
||||
* @brief Line numbering
|
||||
*
|
||||
* If the formatter supports line numbering, this setting controls whether
|
||||
* or not lines should be numbered
|
||||
*/
|
||||
private $line_numbers = true;
|
||||
|
||||
/**
|
||||
* @brief Line number of first line
|
||||
*
|
||||
* If the formatter supports line numbering, this setting controls number
|
||||
* of the first line
|
||||
*/
|
||||
private $start_line = 1;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Highlighting of lines
|
||||
*
|
||||
* If the formatter supports highlighting lines, this setting allows
|
||||
* the caller to specify the set of line numbers to highlight
|
||||
*/
|
||||
private $highlight_lines = array();
|
||||
|
||||
/**
|
||||
* @brief Hyperlinking
|
||||
*
|
||||
* If the formatter supports hyper-linking, this setting controls whether
|
||||
* or not URLs will be automatically linked
|
||||
*/
|
||||
private $auto_link = true;
|
||||
|
||||
/**
|
||||
* @brief Widget height constraint
|
||||
*
|
||||
* If the formatter supports heigh constraint, this setting controls whether
|
||||
* or not to constrain the widget's height, and to what.
|
||||
*/
|
||||
private $max_height = -1;
|
||||
|
||||
/**
|
||||
* @brief Output format
|
||||
*
|
||||
* Chooses which output format to use. Current valid settings are:
|
||||
* \li 'html' - standard HTML element, contained in a \<div\> with class 'luminous',
|
||||
* CSS is not included and must be included on the page separately
|
||||
* (probably with luminous::head_html())
|
||||
* \li 'html-full' - A complete HTML document. CSS is included.
|
||||
* \li 'html-inline' - Very similar to 'html' but geared towards inline display.
|
||||
* Probably not very useful.
|
||||
* \li 'latex' - A full LaTeX document
|
||||
* \li 'none' or \c NULL - No formatter. Internal XML format is returned.
|
||||
* You probably don't want this.
|
||||
*/
|
||||
private $format = 'html';
|
||||
|
||||
/**
|
||||
* @brief Theme
|
||||
*
|
||||
* The default theme to use. This is observed by the HTML-full and LaTeX
|
||||
* formatters, it is also read by luminous::head_html().
|
||||
*
|
||||
* This should be a valid theme which exists in style/
|
||||
*/
|
||||
private $theme = 'luminous_light.css';
|
||||
|
||||
/**
|
||||
* @brief HTML strict standards mode
|
||||
*
|
||||
* The HTML4-strict doctype disallows a few things which are technically
|
||||
* useful. Set this to true if you don't want Luminous to break validation
|
||||
* on your HTML4-strict document. Luminous should be valid
|
||||
* HTML4 loose/transitional and HTML5 without needing to enable this.
|
||||
*/
|
||||
private $html_strict = false;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Location of Luminous relative to your document root
|
||||
*
|
||||
* If you use luminous::head_html(), it has to try to figure out the
|
||||
* path to the style/ directory so that it can return a correct URL to the
|
||||
* necessary stylesheets. Luminous may get this wrong in some situations,
|
||||
* specifically it is currently impossible to get this right if Luminous
|
||||
* exists on the filesystem outside of the document root, and you have used
|
||||
* a symbolic link to put it inside. For this reason, this setting allows you
|
||||
* to override the path.
|
||||
*
|
||||
* e.g. If you set this to '/extern/highlighter', the stylesheets will be
|
||||
* linked with
|
||||
* \<link rel='stylesheet' href='/extern/highlighter/style/luminous.css'\>
|
||||
*
|
||||
*/
|
||||
private $relative_root = null;
|
||||
|
||||
/**
|
||||
* @brief JavaScript extras
|
||||
*
|
||||
* controls whether luminous::head_html() outputs the javascript 'extras'.
|
||||
*/
|
||||
private $include_javascript = false;
|
||||
|
||||
/**
|
||||
* @brief jQuery
|
||||
*
|
||||
* Controls whether luminous::head_html() outputs jQuery, which is required
|
||||
* for the JavaScript extras. This has no effect if $include_javascript is
|
||||
* false.
|
||||
*/
|
||||
private $include_jquery = false;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Failure recovery
|
||||
*
|
||||
* If Luminous hits some kind of unrecoverable internal error, it should
|
||||
* return the input source code back to you. If you want, it can be
|
||||
* wrapped in an HTML tag. Hopefully you will never see this.
|
||||
*/
|
||||
private $failure_tag = 'pre';
|
||||
|
||||
/**
|
||||
* @brief Defines an SQL function which can execute queries on a database
|
||||
*
|
||||
* An SQL database can be used as a replacement for the file-system cache
|
||||
* database.
|
||||
* This function should act similarly to the mysql_query function:
|
||||
* it should take a single argument (the query string) and return:
|
||||
* @li boolean @c false if the query fails
|
||||
* @li boolean @c true if the query succeeds but has no return value
|
||||
* @li An array of associative arrays if the query returns rows (each
|
||||
* element is a row, and each row is an map keyed by field name)
|
||||
*/
|
||||
private $sql_function = null;
|
||||
|
||||
private $verbose = true;
|
||||
|
||||
|
||||
public function LuminousOptions($opts=null) {
|
||||
if (is_array($opts)) {
|
||||
$this->set($opts);
|
||||
}
|
||||
}
|
||||
|
||||
public function set($nameOrArray, $value=null) {
|
||||
$array = $nameOrArray;
|
||||
if (!is_array($array)) {
|
||||
$array = array($nameOrArray => $value);
|
||||
}
|
||||
foreach($array as $option => $value) {
|
||||
// for backwards compatibility we need to do this here
|
||||
$option = str_replace('-', '_', $option);
|
||||
$this->__set($option, $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static function check_type($value, $type, $nullable=false) {
|
||||
if ($nullable && $value === null) return true;
|
||||
$func = null;
|
||||
if ($type === 'string') $func = 'is_string';
|
||||
elseif($type === 'int') $func = 'is_int';
|
||||
elseif($type === 'numeric') $func = 'is_numeric';
|
||||
elseif($type === 'bool') $func = 'is_bool';
|
||||
elseif($type === 'func') $func = 'is_callable';
|
||||
elseif($type === 'array') $func = 'is_array';
|
||||
else {
|
||||
assert(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
$test = call_user_func($func, $value);
|
||||
if (!$test) {
|
||||
throw new InvalidArgumentException('Argument should be type ' . $type .
|
||||
($nullable? ' or null' : ''));
|
||||
}
|
||||
return $test;
|
||||
}
|
||||
|
||||
public function __get($name) {
|
||||
if (property_exists($this, $name))
|
||||
return $this->$name;
|
||||
else {
|
||||
throw new Exception('Unknown property: ' . $name);
|
||||
}
|
||||
}
|
||||
|
||||
public function __set($name, $value) {
|
||||
if ($name === 'auto_link')
|
||||
$this->set_bool($name, $value);
|
||||
else if ($name === 'cache') {
|
||||
$this->set_bool($name, $value);
|
||||
}
|
||||
elseif($name === 'cache_age') {
|
||||
if (self::check_type($value, 'int')) $this->$name = $value;
|
||||
}
|
||||
elseif($name === 'failure_tag') {
|
||||
if (self::check_type($value, 'string', true)) $this->$name = $value;
|
||||
}
|
||||
elseif($name === 'format')
|
||||
$this->set_format($value);
|
||||
elseif($name === 'html_strict') {
|
||||
if (self::check_type($value, 'bool')) $this->$name = $value;
|
||||
}
|
||||
elseif($name === 'include_javascript' || $name === 'include_jquery')
|
||||
$this->set_bool($name, $value);
|
||||
elseif($name === 'line_numbers')
|
||||
$this->set_bool($name, $value);
|
||||
elseif($name === 'start_line')
|
||||
$this->set_start_line($value);
|
||||
elseif($name === 'highlight_lines') {
|
||||
if (self::check_type($value, 'array'))
|
||||
$this->highlight_lines = $value;
|
||||
}
|
||||
elseif($name === 'max_height')
|
||||
$this->set_height($value);
|
||||
elseif($name === 'relative_root') {
|
||||
if (self::check_type($value, 'string', true)) $this->$name = $value;
|
||||
}
|
||||
elseif($name === 'theme')
|
||||
$this->set_theme($value);
|
||||
elseif($name === 'wrap_width') {
|
||||
if (self::check_type($value, 'int')) $this->$name = $value;
|
||||
}
|
||||
elseif($name === 'sql_function') {
|
||||
if (self::check_type($value, 'func', true)) $this->$name = $value;
|
||||
}
|
||||
elseif ($name === 'verbose') {
|
||||
$this->set_bool($name, $value);
|
||||
}
|
||||
else {
|
||||
throw new Exception('Unknown option: ' . $name);
|
||||
}
|
||||
}
|
||||
|
||||
private function set_bool($key, $value) {
|
||||
if (self::check_type($value, 'bool')) {
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
private function set_string($key, $value, $nullable=false) {
|
||||
if (self::check_type($value, 'string', $nullable)) {
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
private function set_start_line($value) {
|
||||
if (is_numeric($value) && $value > 0) {
|
||||
$this->start_line = $value;
|
||||
} else {
|
||||
throw new InvalidArgumentException('Start line must be a positive number');
|
||||
}
|
||||
}
|
||||
|
||||
private function set_format($value) {
|
||||
// formatter can either be an instance or an identifier (string)
|
||||
$is_obj = $value instanceof LuminousFormatter;
|
||||
if($is_obj || self::check_type($value, 'string', true)) {
|
||||
// validate the string is a known type
|
||||
if (!$is_obj && !in_array($value, array('html', 'html-full',
|
||||
'html-inline', 'latex', 'none', null), true)) {
|
||||
throw new Exception('Invalid formatter: ' . $value);
|
||||
}
|
||||
else {
|
||||
$this->format = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function set_theme($value) {
|
||||
if (self::check_type($value, 'string')) {
|
||||
if (!preg_match('/\.css$/', $value)) $value .= '.css';
|
||||
if (!luminous::theme_exists($value)) {
|
||||
throw new Exception('No such theme: '
|
||||
. luminous::root() . '/style/' . $value);
|
||||
}
|
||||
else $this->theme = $value;
|
||||
}
|
||||
}
|
||||
|
||||
private function set_height($value) {
|
||||
// height should be either a number or a numeric string with some units at
|
||||
// the end
|
||||
if (is_numeric($value)
|
||||
|| (is_string($value) && preg_match('/^\d+/', $value))
|
||||
) {
|
||||
$this->max_height = $value;
|
||||
}
|
||||
else {
|
||||
throw new InvalidArgumentException('Unrecognised format for height');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/// @endcond
|
221
3rdparty/luminous/src/scanners.class.php
vendored
Executable file
221
3rdparty/luminous/src/scanners.class.php
vendored
Executable file
@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
/// \cond ALL
|
||||
|
||||
/**
|
||||
* \file scanners.class.php
|
||||
* \brief Scanner lookup table definition.
|
||||
*/
|
||||
/**
|
||||
* \class LuminousScanners
|
||||
* \author Mark Watkinson
|
||||
* \brief A glorified lookup table for languages to scanners.
|
||||
* One of these is instantiated in the global scope at the bottom of this source.
|
||||
* The parser assumes it to exist and uses it to look up scanners.
|
||||
* Users seeking to override scanners or add new scanners should add their
|
||||
* scanner into '$luminous_scanners'.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
class LuminousScanners
|
||||
{
|
||||
|
||||
private $lookup_table = array(); /**<
|
||||
The language=>scanner lookup table. Scanner is an array with keys:
|
||||
scanner (the string of the scanner's class name),
|
||||
file (the path to the file in which its definition resides)
|
||||
dependencies (the language name for any scanners it this scanner
|
||||
either depends on or needs to instantiate itself)
|
||||
*/
|
||||
|
||||
private $default_scanner = null; /**<
|
||||
Language name of the default scanner to use if none is found
|
||||
for a particular language */
|
||||
|
||||
private $descriptions = array();
|
||||
|
||||
private $resolved_dependencies = array();
|
||||
|
||||
/**
|
||||
* Adds a scanner into the table, or overwrites an existing scanner.
|
||||
*
|
||||
* \param language_name may be either a string or an array of strings, if
|
||||
* multiple languages are to use the same scanner
|
||||
* \param $scanner the name of the LuminousScanner object as string, (not an
|
||||
* actual instance!). If the file is actually a dummy file (say for includes),
|
||||
* leave $scanner as \c null.
|
||||
* \param lang_description a human-readable description of the language.
|
||||
* \param file the path to the file in which the scanner is defined.
|
||||
* \param dependencies optional, a string or array of strings representing
|
||||
* the language names (given in another call to AddScanner, as
|
||||
* language_name), on which the instantiation of this scanner depends.
|
||||
* i.e. any super-classes, and any classes which this scanner instantiates
|
||||
* itself.
|
||||
*
|
||||
*/
|
||||
public function AddScanner($language_name, $scanner,
|
||||
$lang_description, $file=null, $dependencies=null)
|
||||
{
|
||||
|
||||
$dummy = $scanner === null;
|
||||
$d = array();
|
||||
if (is_array($dependencies))
|
||||
$d = $dependencies;
|
||||
elseif ($dependencies !== null)
|
||||
$d = array($dependencies);
|
||||
|
||||
$insert = array('scanner'=>$scanner,
|
||||
'file'=>$file,
|
||||
'dependencies'=>$d,
|
||||
'description'=>$lang_description
|
||||
);
|
||||
if (!is_array($language_name))
|
||||
$language_name = array($language_name);
|
||||
foreach($language_name as $l) {
|
||||
$this->lookup_table[$l] = $insert;
|
||||
if (!$dummy)
|
||||
$this->AddDescription($lang_description, $l);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function AddDescription($language_name, $language_code)
|
||||
{
|
||||
if (!isset($this->descriptions[$language_name]))
|
||||
$this->descriptions[$language_name] = array();
|
||||
$this->descriptions[$language_name][] = $language_code;
|
||||
}
|
||||
|
||||
|
||||
private function UnsetDescription($language_name)
|
||||
{
|
||||
foreach($this->descriptions as &$d)
|
||||
{
|
||||
foreach($d as $k=>$l)
|
||||
{
|
||||
if($l === $language_name)
|
||||
unset($d[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a scanner from the table
|
||||
*
|
||||
* \param language_name may be either a string or an array of strings, each of
|
||||
* which will be removed from the lookup table.
|
||||
*/
|
||||
public function RemoveScanner($language_name)
|
||||
{
|
||||
if (is_array($language_name))
|
||||
{
|
||||
foreach($language_name as $l)
|
||||
{
|
||||
unset($this->lookup_table[$l]);
|
||||
$this->UnsetDescription($l);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->UnsetDescription($language_name);
|
||||
unset($this->lookup_table[$language_name]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default scanner. This is used when none matches a lookup
|
||||
* \param scanner the LuminousScanner object
|
||||
*/
|
||||
public function SetDefaultScanner($scanner)
|
||||
{
|
||||
$this->default_scanner = $scanner;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method which retrives the desired scanner array, and
|
||||
* recursively settles the include dependencies while doing so.
|
||||
* \param language_name the name under which the gramar was originally indexed
|
||||
* \param default if true: if the scanner doesn't exist, return the default
|
||||
* scanner. If false, return false
|
||||
* \return the scanner-array stored for the given language name
|
||||
* \internal
|
||||
* \see LuminousScanners::GetScanner
|
||||
*/
|
||||
private function GetScannerArray($language_name, $default=true)
|
||||
{
|
||||
$g = null;
|
||||
if (array_key_exists($language_name, $this->lookup_table))
|
||||
$g = $this->lookup_table[$language_name];
|
||||
elseif($this->default_scanner !== null && $default === true)
|
||||
$g = $this->lookup_table[$this->default_scanner];
|
||||
|
||||
if ($g === null)
|
||||
return false;
|
||||
|
||||
// Break on circular dependencies.
|
||||
if (!isset($this->resolved_dependencies[$language_name]))
|
||||
{
|
||||
$this->resolved_dependencies[$language_name] = true;
|
||||
foreach($g['dependencies'] as $d)
|
||||
{
|
||||
$this->GetScannerArray($d, $default);
|
||||
}
|
||||
if ($g['file'] !== null)
|
||||
require_once($g['file']);
|
||||
}
|
||||
return $g;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a scanner for a language
|
||||
* \param language_name the name under which the gramar was originally indexed
|
||||
* \param default if true: if the scanner doesn't exist, return the default
|
||||
* scanner. If false, return false
|
||||
* \return The scanner, the default scanner, or null.
|
||||
*/
|
||||
function GetScanner($language_name, $default=true, $instance=true)
|
||||
{
|
||||
$resolved_dependencies = array();
|
||||
$g = $this->GetScannerArray($language_name, $default);
|
||||
$resolved_dependencies = array();
|
||||
|
||||
if ($g !== false) {
|
||||
return $instance? new $g['scanner'] : $g['scanner'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function GetDescription($language_name) {
|
||||
$resolved_dependencies = array();
|
||||
$g = $this->GetScannerArray($language_name, true);
|
||||
$resolved_dependencies = array();
|
||||
if ($g !== false) {
|
||||
return $g['description'];
|
||||
} else return null;
|
||||
}
|
||||
/**
|
||||
* Returns a list of known aliases for scanners.
|
||||
* \return a list, the list is such that each item is itself a list whose
|
||||
* elements are aliases of the same scanner. eg:
|
||||
* [
|
||||
* ['c', 'cpp'],
|
||||
* ['java'],
|
||||
* ['py', 'python']
|
||||
* ]
|
||||
* etc.
|
||||
*
|
||||
*/
|
||||
function ListScanners()
|
||||
{
|
||||
$l = $this->descriptions;
|
||||
return $l;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// \endcond
|
386
3rdparty/luminous/src/utils/cssparser.class.php
vendored
Executable file
386
3rdparty/luminous/src/utils/cssparser.class.php
vendored
Executable file
@ -0,0 +1,386 @@
|
||||
<?php
|
||||
|
||||
/// \cond ALL
|
||||
/*
|
||||
* This is a simple CSS parser, which we use to make CSS themes portable.
|
||||
* The basic idea is we're going to use the CSS scanner to tokenize the
|
||||
* input, then we're going to parse the tokens.
|
||||
* There is some amount of redundancy here with the scanner, but this way
|
||||
* means that we are 1) not dependent too much on the implementation of
|
||||
* the scanner, and 2) not having to write our own with full pattern matching.
|
||||
*
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__) . '/../luminous.php';
|
||||
|
||||
// source: http://www.w3schools.com/css/css_colornames.asp
|
||||
global $luminous_col2hex;
|
||||
$luminous_col2hex = array(
|
||||
'aliceblue' => '#f0f8ff',
|
||||
'antiquewhite' => '#faebd7',
|
||||
'aqua' => '#00ffff',
|
||||
'aquamarine' => '#7fffd4',
|
||||
'azure' => '#f0ffff',
|
||||
'beige' => '#f5f5dc',
|
||||
'bisque' => '#ffe4c4',
|
||||
'black' => '#000000',
|
||||
'blanchedalmond' => '#ffebcd',
|
||||
'blue' => '#0000ff',
|
||||
'blueviolet' => '#8a2be2',
|
||||
'brown' => '#a52a2a',
|
||||
'burlywood' => '#deb887',
|
||||
'cadetblue' => '#5f9ea0',
|
||||
'chartreuse' => '#7fff00',
|
||||
'chocolate' => '#d2691e',
|
||||
'coral' => '#ff7f50',
|
||||
'cornflowerblue' => '#6495ed',
|
||||
'cornsilk' => '#fff8dc',
|
||||
'crimson' => '#dc143c',
|
||||
'cyan' => '#00ffff',
|
||||
'darkblue' => '#00008b',
|
||||
'darkcyan' => '#008b8b',
|
||||
'darkgoldenrod' => '#b8860b',
|
||||
'darkgray' => '#a9a9a9',
|
||||
'darkgrey' => '#a9a9a9',
|
||||
'darkgreen' => '#006400',
|
||||
'darkkhaki' => '#bdb76b',
|
||||
'darkmagenta' => '#8b008b',
|
||||
'darkolivegreen' => '#556b2f',
|
||||
'darkorange' => '#ff8c00',
|
||||
'darkorchid' => '#9932cc',
|
||||
'darkred' => '#8b0000',
|
||||
'darksalmon' => '#e9967a',
|
||||
'darkseagreen' => '#8fbc8f',
|
||||
'darkslateblue' => '#483d8b',
|
||||
'darkslategray' => '#2f4f4f',
|
||||
'darkslategrey' => '#2f4f4f',
|
||||
'darkturquoise' => '#00ced1',
|
||||
'darkviolet' => '#9400d3',
|
||||
'deeppink' => '#ff1493',
|
||||
'deepskyblue' => '#00bfff',
|
||||
'dimgray' => '#696969',
|
||||
'dimgrey' => '#696969',
|
||||
'dodgerblue' => '#1e90ff',
|
||||
'firebrick' => '#b22222',
|
||||
'floralwhite' => '#fffaf0',
|
||||
'forestgreen' => '#228b22',
|
||||
'fuchsia' => '#ff00ff',
|
||||
'gainsboro' => '#dcdcdc',
|
||||
'ghostwhite' => '#f8f8ff',
|
||||
'gold' => '#ffd700',
|
||||
'goldenrod' => '#daa520',
|
||||
'gray' => '#808080',
|
||||
'grey' => '#808080',
|
||||
'green' => '#008000',
|
||||
'greenyellow' => '#adff2f',
|
||||
'honeydew' => '#f0fff0',
|
||||
'hotpink' => '#ff69b4',
|
||||
'indianred' => '#cd5c5c',
|
||||
'indigo' => '#4b0082',
|
||||
'ivory' => '#fffff0',
|
||||
'khaki' => '#f0e68c',
|
||||
'lavender' => '#e6e6fa',
|
||||
'lavenderblush' => '#fff0f5',
|
||||
'lawngreen' => '#7cfc00',
|
||||
'lemonchiffon' => '#fffacd',
|
||||
'lightblue' => '#add8e6',
|
||||
'lightcoral' => '#f08080',
|
||||
'lightcyan' => '#e0ffff',
|
||||
'lightgoldenrodyellow' => '#fafad2',
|
||||
'lightgray' => '#d3d3d3',
|
||||
'lightgrey' => '#d3d3d3',
|
||||
'lightgreen' => '#90ee90',
|
||||
'lightpink' => '#ffb6c1',
|
||||
'lightsalmon' => '#ffa07a',
|
||||
'lightseagreen' => '#20b2aa',
|
||||
'lightskyblue' => '#87cefa',
|
||||
'lightslategray' => '#778899',
|
||||
'lightslategrey' => '#778899',
|
||||
'lightsteelblue' => '#b0c4de',
|
||||
'lightyellow' => '#ffffe0',
|
||||
'lime' => '#00ff00',
|
||||
'limegreen' => '#32cd32',
|
||||
'linen' => '#faf0e6',
|
||||
'magenta' => '#ff00ff',
|
||||
'maroon' => '#800000',
|
||||
'mediumaquamarine' => '#66cdaa',
|
||||
'mediumblue' => '#0000cd',
|
||||
'mediumorchid' => '#ba55d3',
|
||||
'mediumpurple' => '#9370d8',
|
||||
'mediumseagreen' => '#3cb371',
|
||||
'mediumslateblue' => '#7b68ee',
|
||||
'mediumspringgreen' => '#00fa9a',
|
||||
'mediumturquoise' => '#48d1cc',
|
||||
'mediumvioletred' => '#c71585',
|
||||
'midnightblue' => '#191970',
|
||||
'mintcream' => '#f5fffa',
|
||||
'mistyrose' => '#ffe4e1',
|
||||
'moccasin' => '#ffe4b5',
|
||||
'navajowhite' => '#ffdead',
|
||||
'navy' => '#000080',
|
||||
'oldlace' => '#fdf5e6',
|
||||
'olive' => '#808000',
|
||||
'olivedrab' => '#6b8e23',
|
||||
'orange' => '#ffa500',
|
||||
'orangered' => '#ff4500',
|
||||
'orchid' => '#da70d6',
|
||||
'palegoldenrod' => '#eee8aa',
|
||||
'palegreen' => '#98fb98',
|
||||
'paleturquoise' => '#afeeee',
|
||||
'palevioletred' => '#d87093',
|
||||
'papayawhip' => '#ffefd5',
|
||||
'peachpuff' => '#ffdab9',
|
||||
'peru' => '#cd853f',
|
||||
'pink' => '#ffc0cb',
|
||||
'plum' => '#dda0dd',
|
||||
'powderblue' => '#b0e0e6',
|
||||
'purple' => '#800080',
|
||||
'red' => '#ff0000',
|
||||
'rosybrown' => '#bc8f8f',
|
||||
'royalblue' => '#4169e1',
|
||||
'saddlebrown' => '#8b4513',
|
||||
'salmon' => '#fa8072',
|
||||
'sandybrown' => '#f4a460',
|
||||
'seagreen' => '#2e8b57',
|
||||
'seashell' => '#fff5ee',
|
||||
'sienna' => '#a0522d',
|
||||
'silver' => '#c0c0c0',
|
||||
'skyblue' => '#87ceeb',
|
||||
'slateblue' => '#6a5acd',
|
||||
'slategray' => '#708090',
|
||||
'slategrey' => '#708090',
|
||||
'snow' => '#fffafa',
|
||||
'springgreen' => '#00ff7f',
|
||||
'steelblue' => '#4682b4',
|
||||
'tan' => '#d2b48c',
|
||||
'teal' => '#008080',
|
||||
'thistle' => '#d8bfd8',
|
||||
'tomato' => '#ff6347',
|
||||
'turquoise' => '#40e0d0',
|
||||
'violet' => '#ee82ee',
|
||||
'wheat' => '#f5deb3',
|
||||
'white' => '#ffffff',
|
||||
'whitesmoke' => '#f5f5f5',
|
||||
'yellow' => '#ffff00',
|
||||
'yellowgreen' => '#9acd32'
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Simple CSS parser to make theme files portable across output formats.
|
||||
*
|
||||
* This is CSS parser for making Luminous themes portable. This is not
|
||||
* a general CSS parser, but could be with a bit of work!
|
||||
*
|
||||
* Parses CSS strings into a map of rules and values. The resulting map is
|
||||
* a somewhat simplified version of CSS.
|
||||
* For simplificity we re-map the following properties:
|
||||
*
|
||||
* background-color => bgcolor
|
||||
* font-weight => bold? (bool)
|
||||
* font-style => italic? (bool)
|
||||
* text-decoration => underline? OR strikethrough? (bool)
|
||||
*
|
||||
* We also drop things like '!important'.
|
||||
*
|
||||
* Colours are stored as 6-digit hexadecimal strings with leading #. 3-digit
|
||||
* hex strings are expanded to their 6-digit equivalents. Named colour aliases
|
||||
* are replaced with their hex equivalents.
|
||||
*/
|
||||
class LuminousCSSParser {
|
||||
|
||||
private $data = array();
|
||||
|
||||
private static function format_property_value($prop, $value) {
|
||||
global $luminous_col2hex;
|
||||
// drop !important
|
||||
$value = preg_replace('/\s*!important$/', '', $value);
|
||||
// expand 3-digit hex
|
||||
if (preg_match('/^#([a-fA-F0-9]{3})$/', $value, $m))
|
||||
$value .= $m[1];
|
||||
// remove quotes
|
||||
$value = trim($value);
|
||||
if (preg_match('/^(["\'])(.*)\\1$/', $value, $m)) $value = $m[2];
|
||||
|
||||
// now get it into a simpler form:
|
||||
switch($prop) {
|
||||
case 'color':
|
||||
if (isset($luminous_col2hex[$value]))
|
||||
$value = $luminous_col2hex[$value];
|
||||
break;
|
||||
case 'background-color':
|
||||
$prop = 'bgcolor';
|
||||
if (isset($luminous_col2hex[$value]))
|
||||
$value = $luminous_col2hex[$value];
|
||||
break;
|
||||
case 'font-weight':
|
||||
$prop = 'bold';
|
||||
$value = in_array($value, array('bold', 'bolder', '700', '800', '900'));
|
||||
break;
|
||||
case 'font-style':
|
||||
$prop = 'italic';
|
||||
$value = in_array($value, array('italic', 'oblique'));
|
||||
case 'text-decoration':
|
||||
if ($value === 'line-through') {
|
||||
$prop = 'strikethrough';
|
||||
$value = true;
|
||||
}
|
||||
elseif($value === 'underline') {
|
||||
$prop = 'underline';
|
||||
$value = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return array($prop, $value);
|
||||
}
|
||||
|
||||
|
||||
private static function format_css_array($css) {
|
||||
$css_ = array();
|
||||
|
||||
// now cleanup the array, drop !important
|
||||
foreach($css as $selector=>$rules) {
|
||||
$rules_ = array();
|
||||
foreach($rules as $prop=>$value) {
|
||||
list($prop, $value) = self::format_property_value($prop, $value);
|
||||
$rules_[$prop] = $value;
|
||||
}
|
||||
// now split selector by comma
|
||||
$selectors = preg_split('/\s*,\s*/', $selector);
|
||||
|
||||
foreach($selectors as $s) {
|
||||
// drop .luminous from the selector
|
||||
$s = preg_replace('/^\.luminous\s*/', '', $s);
|
||||
// now we assume that if something is in the form .classname then
|
||||
// it's probably of interest, ie directly specifying a rule for a
|
||||
// highlighting calss.
|
||||
// and if it's not in that form them
|
||||
// it's probably something else (like a rule for hyperlinks or something)
|
||||
if (preg_match('/\.([\-\w]+)/', $s, $m)) $s = $m[1];
|
||||
else continue;
|
||||
if (!isset($css_[$s])) $css_[$s] = array();
|
||||
$css_[$s] = array_merge($css_[$s], $rules_);
|
||||
}
|
||||
}
|
||||
return $css_;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parsed rules. The rules are an array in the format:
|
||||
*
|
||||
* array(
|
||||
* $rule_name => array($property => $value)
|
||||
* )
|
||||
*
|
||||
* So, $rules['.comment']['color'] would return the color property of comment
|
||||
* classes.
|
||||
*/
|
||||
function rules() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the given property of the given rule name, or
|
||||
* returns $default.
|
||||
* @param $rule_name the CSS rule name, e.g. 'a', '.luminous', etc
|
||||
* @param $property the property to access, e.g. 'color'
|
||||
* @param $default the value to return in the case that the rule/proeprty
|
||||
* was not set. Default: null
|
||||
*/
|
||||
function value($rule_name, $property, $default=null) {
|
||||
if (isset($this->data[$rule_name][$property]))
|
||||
return $this->data[$rule_name][$property];
|
||||
else return $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a CSS string into a nested map of values.
|
||||
* See LuminousCSSParser::rules() for the structure.
|
||||
* @param $string the CSS string
|
||||
* @returns the rule map
|
||||
*/
|
||||
function convert($string) {
|
||||
$css = self::parse($string);
|
||||
$data = self::format_css_array($css);
|
||||
$this->data = $data;
|
||||
return $data;
|
||||
}
|
||||
|
||||
private static function parse($string) {
|
||||
global $luminous_col2hex;
|
||||
// singleton from usage API
|
||||
global $luminous_;
|
||||
$scanner = $luminous_->scanners->GetScanner('css');
|
||||
$scanner->string($string);
|
||||
$scanner->init();
|
||||
$scanner->main();
|
||||
$tokens = $scanner->token_array();
|
||||
|
||||
$block = false;
|
||||
$expect;
|
||||
$key;
|
||||
$value;
|
||||
$selector = '';
|
||||
|
||||
$css = array();
|
||||
// array of selectors => rules, where rules is an array itself of (property, value)
|
||||
// note this is going to get @font-face wrong, but we don't care about that.
|
||||
for($i=0; $i<count($tokens); $i++) {
|
||||
list($tok, $content, ) = $tokens[$i];
|
||||
if ($tok === 'COMMENT') continue;
|
||||
if (!$block) {
|
||||
$expect = 'key';
|
||||
// not in block, look for selectors.
|
||||
if ($content === '{') {
|
||||
$block = true;
|
||||
$key = '';
|
||||
$selector = trim($selector);
|
||||
|
||||
if (!isset($css[$selector]))
|
||||
$css[$selector] = array();
|
||||
}
|
||||
else {
|
||||
$selector .= $content;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ($content === '}') {
|
||||
$block = false;
|
||||
$value = null;
|
||||
$key = null;
|
||||
$selector = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
// expecting key, append to the key or finalise it
|
||||
if ($expect === 'key') {
|
||||
if ($content === ':') {
|
||||
$expect = 'value';
|
||||
$value = '';
|
||||
}
|
||||
else $key .= $content;
|
||||
} elseif($expect === 'value') {
|
||||
// expecting value, append to it or finalise and insert it (with the key)
|
||||
if ($content === ';') {
|
||||
// don't overwrite things - use the first definition
|
||||
// this is for stuff like rgba which might re-define something
|
||||
$key = trim($key);
|
||||
$value = trim($value);
|
||||
if (!isset($css[$selector][$key])) {
|
||||
$css[$selector][$key] = $value;
|
||||
}
|
||||
$expect = 'key';
|
||||
$key = '';
|
||||
$value = '';
|
||||
}
|
||||
else $value .= $content;
|
||||
}
|
||||
}
|
||||
return $css;
|
||||
}
|
||||
}
|
||||
/// \endcond
|
Reference in New Issue
Block a user