<?php
// +----------------------------------------------------------------------
// | Wendasns [ 开源问答社区系统 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2020~2021 http://wendasns.com All rights reserved.
// +----------------------------------------------------------------------
namespace wendasns;
use \DomDocument;
use \DomXPath;

class JQuery
{
	private $html = '';
	private $document;
	private $node;
	public $length = 0;
	private $rule = '';
	private $cache = '';
	
    public function __construct($html=null)
    {
        
        if(!is_null($html)){
        	$this->document($html);
        }
    }
    
    public function document($html)
    {
		$ar = explode("\n",$html);
		$kg = false;
		foreach($ar as $k=>$v){
			if(preg_match('/<script.*?type="text\/html"/',$v)){
				$kg = true;
			}
			preg_match("/(.*?<\/script>)(.*)/",$v,$row);
			if(isset($row[1]) && $kg){
				$this->cache .= $row[1]."\n";
				if(empty($row[2])){
					unset($ar[$k]);
				}else{
					$ar[$k] = $row[2];
				}
				$kg = false;
			}
			if($kg){
				$this->cache .= $v."\n";
				unset($ar[$k]);
			}
		}
		$html = implode("\n",$ar);
    	$this->html = $html;
        $this->document = new DomDocument();
        @$this->document->loadHtml($html);
        return $this;
    }
    
    public function name($name)
    {
    	$prefix = substr($name,0,1);
    	if($prefix=='#'){
    		$this->node = $this->document->getElementById(substr($name,1));
    	}elseif($prefix=='.'){
    		$finder = new DomXPath($this->document);
    		$this->node = $finder->query("//*[contains(@class, '".substr($name,1)."')]");
    	}else{
    		$this->node = $this->document->getElementsByTagName($name);
    	}
    	$this->length = isset($this->node->length)?$this->node->length:0;
    	
    	return $this;
    }

    public function children($name=null)
    {
    	$this->node = $this->node->getElementsByTagName($name);
    	return $this;
    }

    public function eq($i)
    {
    	if(isset($this->node->length)){
    		$this->node = $this->node->item($i);
    	}
    	return $this;
    }
   
    //返回或设置节点属性
    public function attr($attribute,$val=null)
    {
    	if(is_null($val)){
    		return $this->node->getAttribute($attribute);
    	}else{
    		$this->node->setAttribute($attribute,$val);
    		$this->html = $this->document->saveHTML($this->document->documentElement);
    	}
    }
   
    //向被选元素添加一个或多个类
    public function addClass($class)
    {
    	$original_class = $this->node->getAttribute('class');
    	$this->node->setAttribute('class',$original_class.' '.$class);
    	$this->html = $this->document->saveHTML($this->document->documentElement);
    	return $this;
    }
   
    //向被选元素删除一个或多个类
    public function removeClass($class=null)
    {
    	$original_class = $this->node->getAttribute('class');
    	if(is_null($class)){
    		$new_class = '';
    	}else{
    		$dar = explode(' ',$class);
    		$oar = explode(' ',$original_class);
    		foreach($oar as $k=>$v){
    			if(in_array($v,$dar)){
	    			unset($oar[$k]);
	    		}
    		}
    		
    		$new_class = implode(' ',$oar);
    	}
    	$this->node->setAttribute('class',$new_class);
    	$this->html = $this->document->saveHTML($this->document->documentElement);
    	return $this;
    }

    //获取元素样式属性或向被选元素添加一个或多个样式属性
    public function css($item,$value=null)
    {
    	$original_style = $this->node->getAttribute('style');
    	if(is_array($item)){
    		$style = http_build_query($item,'',';');
    		$new_style = str_replace('=', ':', $style).';'.$original_style;
    	}else{
	    	if(is_null($value)){
	    		$style = str_replace([':',';'], ['=','&'], $original_style);
	    		parse_str($style, $parameter);
	    		return isset($parameter[$item])?$parameter[$item]:'';
	    	}
    		$new_style = $item.':'.$value.';'.$original_style;
    	}
    	$this->node->setAttribute('style',$new_style);
    	$this->html = $this->document->saveHTML($this->document->documentElement);
    	return $this;
    }
   
    //显示元素
    public function show()
    {
    	$original_style = $this->node->getAttribute('style');
    	
    	if(strpos($original_style,'display') !== false){
    		$new_style = preg_replace('/display:.*?(;|$)/','',$original_style);
    	}else{
    		$new_style = $original_style;
    	}
    	$this->node->setAttribute('style',$new_style);
    	$this->html = $this->document->saveHTML($this->document->documentElement);
    	return $this;
    }
   
    //隐藏元素
    public function hide()
    {
    	$original_style = $this->node->getAttribute('style');
    	
    	if(strpos($original_style,'display') !== false){
    		$new_style = preg_replace('/display:.*?(;|$)/','display:none;',$original_style);
    	}else{
    		$new_style = 'display:none;'.$original_style;
    	}
    	$this->node->setAttribute('style',$new_style);
    	$this->html = $this->document->saveHTML($this->document->documentElement);
    	return $this;
    }
   
    //设置或获取元素纯文本内容
    public function text($content=NULL)
    {
    	if(is_null($this->node)){
    		return '';
    	}
    	if(is_null($content)){
    		return $this->node->textContent;
    	}
    	$html = $this->getHtml('html');
    	$this->html = str_replace($html,htmlspecialchars($content),$this->html);
    }
   
    //设置或获取元素html内容
    public function html($content=NULL)
    {
    	if(is_null($this->node)){
    		return '';
    	}
    	$html = $this->getHtml('html');
    	if(is_null($content)){//获取html代码
    		return $html;
    	}
    	
    	$this->html = str_replace($html,$content,$this->html);
    }
    
    private function getHtml($type)
    {
    	$this->rule = '';
    	$this->setRule($this->node);
    	$attribute = [];
 
		foreach($this->node->attributes as $item){
			$nodeValue = str_replace(['.','*','+','?','(',')','[',']','{','}','|','^','$'],['\.','\*','\+','\?','\(','\)','\[','\]','\{','\}','\|','\^','\$'],$item->nodeValue);
			if (strpos($item->nodeValue, '"') !== false) {
				$attribute[] = sprintf("%s='%s'",$item->nodeName,$nodeValue);
			}else{
				$attribute[] = sprintf('%s="%s"',$item->nodeName,$nodeValue);
			}
			
		}
		$attribute = implode('.*?',$attribute);
		
		$rule = $attribute.'.*?>('.$this->rule.'.*?)</'.$this->node->tagName.'>';
		$rule = str_replace('/','\/',$rule);
		$html = str_replace("\n",'',$this->html);
		preg_match('/'.$rule.'/', $html, $matches);
    	return isset($matches[1])?$matches[1]:'';
    }
   
    private function setRule($node)
    {
    	if(!isset($node->childNodes)){
    		if($this->rule){
    			$this->rule .= '.*?</'.$node->tagName.'>';
    		}
    		return;
    	}
    	$nodes = $node->childNodes;
    	foreach($nodes as $childNode){
			if(isset($childNode->tagName)){
	    		if($childNode->hasChildNodes()){
	    			$this->setRule($childNode);
	    		}
	    		$this->rule .= '.*?</'.$childNode->tagName.'>';
			}
    	}
    }
   
    //返回父节点
    public function parent()
    {
    	if(isset($this->node->length)){
    		$this->node = $this->node->item(0)->parentNode;
    	}else{
    		$this->node = $this->node->parentNode;
    	}
    	return $this;
    }
   
    //在被选元素的结尾插入内容
    public function append($content)
    {
		$this->insert('append',$content);
    }
   
    //在被选元素的开头插入内容
    public function prepend($content)
    {
    	$this->insert('prepend',$content);
    }

    //在被选元素之后插入内容
    public function after($content)
    {
    	$this->insert('after',$content);
    }
   
    //在被选元素之前插入内容
    public function before($content)
    {
    	$this->insert('before',$content);
    }
   
    //删除节点
    public function remove()
    {
    	if(isset($this->node->length)){
    		foreach($this->node as $node){
    			$node->parentNode->removeChild($node);
    		}
    	}else{
    		$this->node->parentNode->removeChild($this->node);
    	}
    	$this->node = null;
    	$this->html = $this->document->saveHTML($this->document->documentElement);
    }
   
    //获取第一个子节点
    public function firstChild()
    {
    	//$this->node = $this->node->firstChild;
    	$ar = [];
    	foreach($this->node->childNodes as $node){
    		if(!isset($node->tagName)){
    			continue;
    		}
    		$ar[] = $node;
    	}
    	$this->node = $ar?reset($ar):null;
    	return $this;
    }

    //获取最后一个子节点
    public function lastChild()
    {
    	//$this->node = $this->node->lastChild;
    	$ar = [];
    	foreach($this->node->childNodes as $node){
    		if(!isset($node->tagName)){
    			continue;
    		}
    		$ar[] = $node;
    	}
    	$this->node = $ar?end($ar):null;
    	return $this;
    }
   
    //获取上一个兄弟节点
    public function prev()
    {
    	if(is_null($this->node->previousSibling)){
    		$this->node = null;
    		return $this;
    	}
    	$i = 0;
    	while($i<1){
    		if($this->node->previousSibling->nodeName!=='#text'){
    			$i = 1;
    		}
    		$this->node = $this->node->previousSibling;
    	}
    	return $this;
    }

    //获取下一个兄弟节点
    public function next()
    {
    	if(is_null($this->node->nextSibling)){
    		$this->node = null;
    		return $this;
    	}
    	$i = 0;
    	while($i<1){
    		if($this->node->nextSibling->nodeName!=='#text'){
    			$i = 1;
    		}
    		$this->node = $this->node->nextSibling;
    	}
    	return $this;
    }
    
    public function saveHTML()
    {
    	//var_dump($this->document->documentElement);
    	return "<!DOCTYPE html>\n".$this->html."\n".$this->cache;
    }
    
    private function insert($type,$content)
    {
    	if(is_null($this->node)){
    		return;
    	}
    	$add = function($type,$node,$content){
    		$item = $this->document->createTextNode('{wendasns}'.$content.'{/wendasns}');
	    	switch ($type){
	    		case 'append':
	    			$node->appendChild($item);
	    			break;
	    		case 'prepend':
	    			$node->insertBefore($item, $node->firstChild);
	    			break;
	    		case 'after':
	    			$node->parentNode->insertBefore($item, $node->nextSibling);
	    			break;
	    		default://before
	    			$node->parentNode->insertBefore($item, $node);
	    	}
    	};
    	
    	if(isset($this->node->length)){
    		foreach($this->node as $node){
	    		$add($type,$node,$content);
	    	}
    	}else{
	    	$add($type,$this->node,$content);
    	}
    	$html = $this->document->saveHTML($this->document->documentElement);
    	$this->html = preg_replace_callback('/\{wendasns\}(.*?)\{\/wendasns\}/',function($matches){
			return str_replace(['&lt;','&gt;'],['<','>'],$matches[1]);
		},$html);
    }
}