<?php
require ("json.php");
class Parser {
// Текст копирайта
public $copyright = "";
// Массив строк исходного файла
public $sourceLines;
// Количество строк исходника
public $length = 0;
// Текущая строка и ее индекс
public $currentLine = '', $index = -1;
// Имя последней "открытой" функции
public $FnLast;
// количество распарсенных функций
public $FnIndex = 1;
// "Скомпилированный" код
public $resultCode = '';
public static $outputDir = "../sources/";
public static $sourceDir = "../../source_code/";
public static $sourceFile = "konstruktor.compiled.js";
public static $tplFile = "template.js";
public static $copyFile = "copy.txt";
public function log($msg){
}
public static function getmicrotime(){
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
public function __construct() {
$this->startTime = self::getmicrotime();
$this->_jsonParser = new json();
$this->_bbParser = new bbCodeParser();
self::$copyFile = self::$sourceDir.self::$copyFile;
self::$tplFile = self::$sourceDir.self::$tplFile;
self::$sourceFile = self::$sourceDir.self::$sourceFile;
$this->copyright = $this->getCopyright();
}
private function getCopyright(){
$text = file_get_contents(self::$copyFile);
$date = date("d.m.Y H:i:s");
$text = preg_replace("/(\\$\\s*Date:\\s*).*(\\$)/", "$1{$date}$2", $text);
$this->copyright = $text;
}
public function updateCopyright(){
}
public function _end(){
$time = self::getmicrotime() - $this->startTime;
echo "\n/*parsed in {$time} sec*/";
}
public function parseBB($string) {
return $this->_bbParser->parse($string);
}
public function parseJSON($obj) {
if (is_string($obj)) {
// protect [module] by replacing it with [module,1]
if (sizeof(explode(',', $obj)) < 2) {
$obj = preg_replace("/\\]$/", ",1]", $obj);
}
// protect strings
$obj = preg_replace("/([a-zA-Z_\\-\\.]+)/", "'$1'", $obj);
$obj = $this->_jsonParser->decode($obj);
if (sizeof($obj) > 0) {
return $obj;
}
else {
return array ();
}
}
else {
return $this->_jsonParser->encode($obj);
}
}
public function nextLine() {
if (++$this->index < $this->length)
return ($this->currentLine = $this->sourceLines[$this->index]);
else {
return false;
}
}
public function reset(){
$this->index = -1;
$this-> length = 0;
$this->currentLine = '';
$this->sourceLines = array();
$this->resultCode = '';
}
public function setFile($fname){
$this->reset();
$source = file_get_contents($fname);
$this->sourceLines = explode("\n", $source);
$this->length = sizeof($this->sourceLines);
return $source;
}
public static function secure($i) {
return preg_replace("/[\'\"\\s\\?!%]/", "", $i);
}
}
class sourceParser extends Parser {
private $map = array ();
private function createIncludeList() {
$data = array ();
while (($line = $this->nextLine()) !== false) {
$line = trim($line);
if (strstr($line, "include")) {
$data[] = preg_replace("/(\\/\\/\\>\\s*\\@include)|\\s/", "", $line);
}
}
return $data;
}
private function loadFile($fname, $root = null) {
$file = ($root ? $root : self::$sourceDir) . $fname;
if (file_exists($file)) {
$lastupd = date("d.m.Y H:i:s", filemtime($file));
$fContent = trim(file_get_contents($file));
return "\n/*@last update {$lastupd}*/\n{$fContent}";
}
else
return "";
}
private function loadFolder($folder, $root = null) {
$dir = ($root ? $root : self::$sourceDir).$folder;
//echo "$dir<br>";
$list = scandir($dir);
$content = "";
foreach ($list as $fName) {
if (substr($fName,0,1) != '.') {
$content .= $this->loadFile($fName, $dir);
}
}
return $content;
}
private function parseTemplate() {
$source = $this->setFile(self::$tplFile);
$includeList = $this->createIncludeList();
foreach ($includeList as $inc) {
if (ereg("\\/$", $inc)) {
$content = $this->loadFolder($inc);
}
else if (ereg("\\.js$", $inc)) {
$content = $this->loadFile($inc);
}
if ($content) {
// protect RegExp.$ replacing
$content = preg_replace("/\\.\\$(\d)/", ".#@#$1", $content);
// replace @include with file content
$source = preg_replace("#\\/\\/\\>\\s*\\@include\\s*{$inc}#", $content, $source);
// restore RegExp.$
$source = preg_replace("/\\.#@#/", ".\\$", $source);
}
}
file_put_contents(self::$sourceFile, $source);
return $this;
}
private function parseFunctionData() {
$dataEnd = false;
$description = false;
$data = array ();
$dependCoreObject = "";
$testLine = trim($this->currentLine);
if (preg_match("/[\\}]?\\/\\/>\\s*(\\w+)\\s*(\\[.*\\])?(\\<)?$/", $testLine, $test)) {
echo "$testLine<br>";
$data['local'] = true;
if ($test[1] == 'e' && $this->lastOpenned) {
$data['name'] = $this->lastOpenned;
$data['fn'] = "}";
$this->lastOpenned = null;
//$data['local'] = true;
return $data;
} else if( $test[3] ){
return array(
'fn' => $this->nextLine(),
'name' => $test[1]
);
}
else {
$data['name'] = $test[1];
//echo $test[1]."\n";
}
if ($test[2]) {
$depend = $test[2];
$deps = $this->parseJSON($depend);
if (in_array("e", $deps)) {
$this->lastOpenned = $name = $data['name'].'End';
$depend = preg_replace("/([,]?)e,/", "$1{$name}$2", $depend);
$data['dep'] = $this->parseJSON($depend);
}
else if ($deps) {
$data['dep'] = $deps;
}
}
return $data;
}
else {
while (($line = $this->nextLine()) !== false && !$dataEnd) {
$line = trim($line);
if ($line == "*/") {
if (!$data['dep'] && $dependCoreObject) {
$data['dep'] = $this->parseJSON("[{$dependCoreObject}]");
}
$dataEnd = true;
}
else {
if (preg_match("/^\\*\\s?[@]?([\\w\\.]+)?\\s?([\\{\\[]([\\w\\.\\,]+)[\\}\\]])?\\s?(.*)$/", $line, $test)) {
$paramName = $test[1];
if ($paramName == 'param') {
$description = false;
if (!$data[$paramName]) {
$data[$paramName] = array ();
}
$data[$paramName][] = array (
'type'=>$test[3],
'val'=>$test[4]
);
}
else if ($paramName == 'dep') {
$description = false;
echo Debug::dump($test);
//$dep = $dependCoreObject.$test[2];
$depend = $this->parseJSON( $test[2] );
if($dependCoreObject){
$depend[] = $depend[0];
$depend[0] = $dependCoreObject;
$dependCoreObject = "";
}
//$this->parseJSON("[".preg_replace("/(^,)|([\\[\\]])/", "", $dep)."]");
$data[$paramName] = $depend;
if (in_array("e", $depend)) {
$this->lastOpenned = $data['name'].'End';
}
}
else if ($paramName == 'name') {
echo "$line<br>";
$description = false;
$data[$paramName] = $test[4];
if (preg_match("/(String|Array|Date|Object|RegExp)/", $test[4])) {
$a = explode('.', $test[4]);
$dependCoreObject = $a[0];
}
}
else if ($paramName == 'local') {
$description = false;
$data[$paramName] = true;
}
else if ($paramName == 'desc') {
$data[$paramName] = $test[4];
$description = true;
}
else if ($description) {
$data['desc'] .= "\n". substr($line, 2);
}
}
}
if ($dataEnd) {
return $data;
}
}
}
return false;
}
private function nextFunction() {
while (($line = $this->nextLine()) !== false) {
if (ereg("(\\/\\/\\s*[><].*)|(\\/\\*\\*)", trim($line))) {
$data = $this->parseFunctionData();
if (!$data['name']) {
$data['fn'] = null;
$data['error'] = true;
return $data;
}
if (!$data['fn']) {
$fnSource = array ();
while (($line = $this->nextLine()) !== false && !ereg("\\/\\/[><]\\s?.*[\n]?", $line)) {
$fnSource[] = $line;
}
$data['fn'] = implode("\n", $fnSource);
//"\n".preg_replace("/\\/\\/.*[\\n]?/", "", implode("\n", $tmp));
}
$data['num'] = $this->FnIndex++;
return $data;
}
}
return false;
}
private function createMap() {
$this->setFile( self::$sourceFile );
while ($data = $this->nextFunction()) {
if (!$data['error'] /*&& !$this->map[$data['name']]*/ ) {
//echo Debug::dump($data['name']) . "<br/>";
$this->map[$data['name']] = $data;
//echo $data['name']."<br>";
}
else {
$data['fn'] = null;
echo Debug::dump($data);
}
}
return $this;
}
private function writeMapFile() {
$ouputDir = self::$outputDir;
$mapfile = "{$ouputDir}map.xml";
$doc = new DomDocument('1.0');
$xmlRoot = $doc->createElement('konstruktor');
$xmlRoot = $doc->appendChild($xmlRoot);
foreach ($this->map as $FnName=>$item) {
$xmlFnData = $doc->createElement("function");
$xmlFnData = $xmlRoot->appendChild($xmlFnData);
$xmlFnData->setAttribute('name', $FnName);
foreach ($item as $key=>$value) {
if ($key == 'fn') {
file_put_contents("{$ouputDir}{$FnName}.txt", $value);
}
else {
if ($key == 'dep' && (strlen(($dep = implode(',', $value)))) > 1) {
$xmlDep = $doc->createElement($key);
$xmlDep = $xmlFnData->appendChild($xmlDep);
$v = $doc->createTextNode($dep);
$xmlDep->appendChild($v);
}
elseif ($key == 'desc') {
//$text = iconv("UTF-8", "CP1251", $value);
$xmlDesc = $doc->createElement($key);
$xmlFnData->appendChild($xmlDesc);
$v = $doc->createTextNode($value);
$xmlDesc->appendChild($v);
//echo "$text<br><br>";
}
elseif ($key == 'local' || $key == 'num') {
$xmlFnData->setAttribute($key, $value);
}
}
}
}
$xml_string = $doc->saveXML();
file_put_contents($mapfile, $xml_string);
return $this;
}
public function parse() {
$this
->parseTemplate()
->createMap()
->writeMapFile()
->_end();
}
}
class outputCompiler extends Parser {
private $map = array ();
private $modules = array();
private $loaded = false;
private $tested = array();
private function createMap() {
$doc = new DomDocument();
$xmlstr = file_get_contents(self::$outputDir."map.xml");
$doc->loadXML( iconv("CP1251", "UTF-8", $xmlstr) );
$data = $doc->getElementsByTagName("function");
foreach ($data as $fn) {
$this->map[$fn->getAttribute('name')] = array (
'local'=>$fn->getAttribute('local'),
'num'=>$fn->getAttribute('num'),
'dep'=>$fn->getElementsByTagName('dep')->item(0)->nodeValue
);
}
return $this;
}
private function loadModule($name) {
$name = preg_replace("/_/", ".", $name);
if (!($module = $this->map[$name])) {
//echo "$name\n";
return false;
}
if ( !$this->tested[$name] ) {
$this->modules[$module['num']] = array ($name, $module['local']);
$this->tested[$name] = 1;
//echo "$name\n";
if ($module['dep']) {
$list = explode(',', preg_replace("/[\\']/", "", $module['dep']));
foreach ($list as $newmodule) {
if (!$this->tested[$newmodule]) {
$this->loadModule($newmodule);
continue ;
}
}
return true;
}
}
else
return false;
}
private function loadModules() {
if ($_GET && isset($_GET['dev'])) {
$this->loaded = true;
return $this->fullSource();
}
else if ($_GET && $_GET['submit']) {
$this->files = array_map("Parser::secure", $_GET);
$this->outputfile = self::$outputDir."cache/".md5(serialize($this->files));
$this->files['start'] = 'on';
foreach ($this->files as $key=>$val) {
if ($val && !ereg("(on|1)", $val)) {
$this->loadModule($val);
}
else {
$this->loadModule($key);
}
}
ksort($this->modules);
return $this->joinFiles()->packCode();
}
else {
exit ('err;>');
}
return $this;
}
private function joinFiles() {
if ($this->loaded) {
return $this;
}
else {
foreach ($this->modules as $module) {
$f = $module[0];
$code = "\n" . trim(file_get_contents("../sources/{$f}.txt")) . "\n";
$this->resultCode .= $code;
}
}
return $this;
}
// Хероватая минификация:)
private function packCode() {
$before = strlen($this->resultCode);
$localRegs = array (
// Коменты
"/\\/\\/[^\n\r]*/"=>" ",
"/\\/\\*[^*]*\*+([^\/][^*]*\\*+)*\\//"=>" "
);
$packRegs = array (
// Пробелы, переводы строк
"/(\\b|\\x24)\\s+(\\b|\\x24)/"=>"$2 $3",
"/([+\\-])\\s+([+\\-])/"=>"$2 $3",
"/\\s+/"=>" ",
"/^(\\s|\n\r|\\b)$/"=>"",
// фикc var
"/}(\\s?\\w+[\\[\\]\\.]|var|\\(\\s*)/"=>"};$1",
"/}\\s*(var\\s*)/"=>"};$1",
// Оставшиеся ненужные пробелы
"/(\\s?([\\/\\(\\);,=+:\\?\\|\\&\\-\\{\\}><%*\\.\\!])\\s?)/"=>"$2",
// Фикс },}
"/\\},[\\s\n\r]*\\}/"=>"}}",
// Убираем ненужные ";" перед "}"
"/;\\}/"=>"}",
// Убираем ненужные ";" после "}"
"/\\};\\(/"=>"}(",
// Фикс }function
"/\\}(function|return|String|RegExp|Date|Object|Array|Number)/"=>"};$1",
// Фикс )function
"/\\)(function)/" => ");$1",
"/\\)(String|RegExp|Date|Object|Number|Array)\\.method/" => ");$1.method",
// Еще один фикс var
"/(\\}\\))\\s?(var)/" => "$1;$2"
);
$obfusRegs = array (
"/[Kk]onstruktor/"=>"K_",
"/[aA]ttr[s]?/"=>"A_",
"/(\\s?)dbK_([\\.]?)(\\w*)/"=>"$1D_$2$3",
"/ajaxK_/"=>"J",
"/domK_/"=>"_D",
"/EXPR/"=>"E",
"/[Cc]ache([\\.]?)/"=>"_C$1",
"/modificator/"=>"_M",
"/(modParam|parse|progress)/"=>"_p",
"/_pInt/"=>"parseInt",
"/undefined/"=>"_u",
"/([\'\"])_u([\'\"])/"=>"$1undefined$2",
"/(\\.)?([\\_]?)?(elements|expr)/"=>"$1$2e",
"/quick(N)?([ot]{2})?(R|F)?(et|ilter)?/"=>"_q$1$3",
"/[Ss]elector/"=>"_S",
"/query_S/"=>"querySelector",
"/[Tt]oken/"=>"_T",
"/node/"=>"nd",
"/(string|Styles)/"=>"_s",
"/'_s'/"=>"'string'",
"/sub_s/"=>"substring",
"/\\._s/"=>".string",
"/elem/"=>"_e",
"/(calculated|callback)/"=>"_c",
"/[Rr]oot/"=>"_r",
"/([sg])etValue/"=>"$1_v",
"/seekAndDestroy/"=>"_sd",
"/child(r)?(en)?/"=>"ch$1"
);
$oldCode = explode("\n", $this->resultCode);
$newCode = array ();
foreach ($oldCode as $line) {
if (!strstr($line, "__proto__") && !strstr($line, "cc_on")) {
foreach ($localRegs as $re=>$r) {
$line = preg_replace($re, $r, $line);
}
}
$newCode[] = trim($line);
}
$this->resultCode = implode("\n", $newCode);
foreach ($packRegs as $reg=>$replace) {
$this->resultCode = preg_replace($reg, $replace, $this->resultCode);
}
/*$index = 0;
foreach ($this->modules as $k) {
if ($k[1] && ($o = $k[0])) {
echo "$o\n";
++$index;
$this->resultCode = preg_replace("/([;,.])?{$o}([\\(\\;])/", "$1_{$index}$2", $this->resultCode);
}
}*/
foreach ($obfusRegs as $reg=>$replace) {
$this->resultCode = preg_replace($reg, $replace, $this->resultCode);
}
$after = strlen($this->resultCode);
$fsize = $after / 1024;
$ratio = $after / $before;
$copy = file_get_contents(self::$copyFile);
$copy = preg_replace("/(Filesize:)(\\s*)(\\d+)\\s*kB/","$1 {$fsize} kB",$copy);
$copy = preg_replace("/(ratio:)\\s*\\d+/","\$1 {$ratio}",$copy);
//$copy = preg_replace("/(Parsed in:)\\s*\\d+/","\$1 {$ratio}",$copy);
$r = $copy . $this->resultCode;
file_put_contents($this->outputfile, $r);
//header("Content-Type: text/javascript");
//header("Content-Disposition: attachment; filename=\"konstruktor.js\";");
//header("Content-Transfer-Encoding: binary");
//header("Content-Length: ".strlen($r));
readfile($this->outputfile);
//echo "\n/*compress ratio: " . $after / $ . "*/";
return $this;
}
public function parse() {
$this
->createMap()
->loadModules()
//->download()
->_end();
}
}
class docBuilder extends Parser {
}
class bbCodeParser {
var $tags = array ('b'=>'strong', 'i'=>'em', 'u'=>'span style="text-decoration:underline"', 'quote'=>'blockquote', 's'=>'span style="text-decoration: line-through"', 'list'=>'ul', '\*'=>'li');
var $mapped = array ('url'=> array ('a', 'href', true), 'img'=> array ('img', 'src', false));
var $tags_with_att = array ('color'=> array ('font', 'color'), 'size'=> array ('font', 'size'), 'url'=> array ('a', 'href'));
var $convert_newlines = true;
var $parse_smilies = true;
var $auto_links = true;
var $_code = '';
function bbCodeParser($new = true, $parse = true, $links = true) {
$this->convert_newlines = $new;
$this->parse_smilies = $parse;
$this->auto_links = $links;
}
function parse($code) {
$this->_code = $code;
$this->_strip_html();
$this->_parse_tags();
$this->_parse_mapped();
$this->_parse_tags_with_att();
$this->_parse_links();
$this->_convert_nl();
return $this->_code;
}
function _strip_html() {
$this->_code = strip_tags($this->_code);
}
function _convert_nl() {
if ($this->convert_newlines) {
$this->_code = nl2br($this->_code);
}
}
function _parse_tags() {
foreach ($this->tags as $old=>$new) {
$ex = explode(' ', $new);
$this->_code = preg_replace('/\['.$old.'\](.+?)\[\/'.$old.'\]/is', '<'.$new.'>$1</'.$ex[0].'>', $this->_code);
}
}
function _parse_mapped() {
foreach ($this->mapped as $tag=>$data) {
$reg = '/\['.$tag.'\](.+?)\[\/'.$tag.'\]/is';
if ($data[2]) {
$this->_code = preg_replace($reg, '<'.$data[0].' '.$data[1].'="$1">$1</'.$data[0].'>', $this->_code);
}
else {
$this->_code = preg_replace($reg, '<'.$data[0].' '.$data[1].'="$1">', $this->_code);
}
}
}
function _parse_tags_with_att() {
foreach ($this->tags_with_att as $tag=>$data) {
$this->_code = preg_replace('/\['.$tag.'=(.+?)\](.+?)\[\/'.$tag.'\]/is', '<'.$data[0].' '.$data[1].'="$1">$2</'.$data[0].'>', $this->_code);
}
}
function _parse_links() {
if ($this->auto_links) {
$this->_code = preg_replace('/([^"])(http:\/\/|ftp:\/\/)([^\s,]*)/i', '$1<a href="$2$3">$2$3</a>', $this->_code);
$this->_code = preg_replace('/([^"])([A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4})/i', '$1<a href="mailto:$2">$2</a>', $this->_code);
}
}
function addTag($old, $new) {
$this->tags[$old] = $new;
}
function addMapped($bb, $html, $att, $end = true) {
$this->mapped[$bb] = array ($html, $att, $end);
}
function addTagWithAttribute($bb, $html, $att) {
$this->tags_with_att[$bb] = array ($html, $att);
}
}
require "debugger.php";
?>