a
    .b~,                     @   s   d Z ddlZddlmZ ddlmZmZmZ ddlm	Z	m
Z
 ddlmZmZ dZeedd	d
ZG dd deZG dd dZG dd deZG dd deZG dd deZG dd dZeedddZd eeedddZedkredZeej dS )!aR  
Created on Fri Jan 4 23:12:02 2019

@author: pourcelo

>>> from websites_test_framework.css_parser import parse
>>> css_content = parse("b {color: blue; font-weight: bold;} p>em {color: red}")
>>> css_content.selectors
['b', 'p>em']
>>> css_content.rules
[Rule(header='b', content=[Property(name='color', value='blue'),
                           Property(name='font-weight', value='bold')]),
 Rule(header='p>em', content=[Property(name='color', value='red')])]
>>> b = css_content.rules[0]
>>> b.properties
[Property(name='color', value='blue'),
 Property(name='font-weight', value='bold')]

    N)deque)ListUnionDict)Property
StringMode)is_url_relativeright_replacez4(?<!\\)('([^']|\\')*(?<!\\)')|("([^"]|\\")*(?<!\\)")selectorreturnc                 C   s6   |   } tdd| } tdd| } tdd| } | S )z'Remove unnecessary spaces in selectors.z\s+ z\s(?=([>~+])) z(?<=([>~+]))\s)stripresubr    r   V/home/manager/.local/lib/python3.9/site-packages/websites_test_framework/css_parser.py	normalize    s
    r   c                   @   s   e Zd ZdZdd ZdS )CssErrorzInvalid CSS.c                 C   s   | j s| jS | j d S Nr   )args__doc__selfr   r   r   __str__-   s    zCssError.__str__N)__name__
__module____qualname__r   r   r   r   r   r   r   *   s   r   c                   @   s   e Zd ZdS )GenericRuleN)r   r   r   r   r   r   r   r    1   s   r    c                   @   s   e Zd Zdd ZdS )CssRootc                 C   s
   g | _ d S Ncontentr   r   r   r   __init__6   s    zCssRoot.__init__N)r   r   r   r%   r   r   r   r   r!   5   s   r!   c                       sF   e Zd Z fddZdd fddZedd Zed	d
 Z  ZS )ParserStackc                    s   t    |   d S r"   )superr%   clearr   	__class__r   r   r%   ;   s    
zParserStack.__init__Nr   c                    s   t    | t  d S r"   )r'   r(   appendr!   r   r)   r   r   r(   ?   s    
zParserStack.clearc                 C   s   | d S )Nr   r   r   r   r   currentC   s    zParserStack.currentc                 C   s   t | dkS )N   )lenr   r   r   r   is_rootG   s    zParserStack.is_root)	r   r   r   r%   r(   propertyr.   r1   __classcell__r   r   r)   r   r&   :   s   
r&   c                   @   sF   e Zd Zdeeeeef  dddZe	dd Z
dd Zd	d
 ZdS )RuleNheaderr$   c                 C   s$   |d u rg }t || _t|| _d S r"   )r   r6   listr$   )r   r6   r$   r   r   r   r%   M   s    
zRule.__init__c                 C   s   dd | j D S )Nc                 S   s   g | ]}t |tr|qS r   )
isinstancer   ).0r$   r   r   r   
<listcomp>U       z#Rule.properties.<locals>.<listcomp>r#   r   r   r   r   
propertiesS   s    zRule.propertiesc                 C   s   d| j d| jdS )NzRule(header=z
, content=)r5   r   r   r   r   r   W   s    zRule.__str__c                 C   s   t | S r"   )strr   r   r   r   __repr__Z   s    zRule.__repr__)N)r   r   r   r>   r   r   r   r    r%   r2   r<   r   r?   r   r   r   r   r4   L   s
   
r4   c                   @   sF  e Zd ZdZd/eedddZeeddd	Zedd
dZ	eedddZ
d0edddZd1eeddddZd2edddZedd Zeee dddZedd Zeee ddd Zeee dd!d"Zeee dd#d$Zeed%d&d'Zeee d%d(d)Zd3ee dd+d,Zeeeef d%d-d.ZdS )4CssSimpleParserzCSS minimalist parser.r   Tstringverbosec                 C   s6   || _ g | _t | _d | _g | _|r2| j||d d S NrC   )raw_cached_charactersr&   _stack_modecommentsparse)r   rB   rC   r   r   r   r%   a   s    zCssSimpleParser.__init__r+   c                 C   s   t | jdko| jd dkS )Nr   r-   \)r0   rG   r   r   r   r   _escape_next_charj   s    z!CssSimpleParser._escape_next_charc                 C   s&   d | j dd}| j  |S )Nr   
r   )joinrG   r   replacer(   r   rB   r   r   r    _get_cached_characters_and_purgen   s    
z0CssSimpleParser._get_cached_characters_and_purge)rB   r   c                    s     fdd}t jd||t jdS )z*Remove comments in CSS code : /*comment*/.c                    s    j | d dS )Nr/   r   )rJ   r,   group)mr   r   r   extractv   s    z3CssSimpleParser._extracts_comments.<locals>.extractz/\*(.*?)\*/)flags)r   r   DOTALL)r   rB   rU   r   r   r   _extracts_commentss   s    z"CssSimpleParser._extracts_comments)rB   c                 C   s$   || _ | j  | j  d| _dS )z#Reset state: ready to parse again !N)rF   rG   r(   rH   rI   rQ   r   r   r   _reset|   s    

zCssSimpleParser._resetNrB   rC   r   c                 C   sb  |  | | |}| jj}| j}d}|D ]}| jdksD| jdkrd| jsZ|| jkrZd| _|| q*|dkr|   }|du r|rt	|nd}n2|
drd|vrd}||d  nt||}d}|dur|jj| q*|dkr|du r|  }q*|dkrL|  }|dur*t	| d| }d}nt	|}|jj| || q*|d	kr|durt||  }|jj| d}|jr|r|   td
dd |jjD }	t|	dksJ t|	dkr|rtd |jjD ]}
t|
 qtd t|  q*|| |dkr$d| _|dkr*d| _q*d| j}| r^|rZ| | tdS )zParse CSS code.N'";zurl(r=   :{}zNo block to close.c                 S   s   h | ]}t |qS r   )type)r9   eltr   r   r   	<setcomp>   r;   z(CssSimpleParser.parse.<locals>.<setcomp>   z 
*** Error in this CSS block ***z******
r   )rY   rX   rG   r,   rH   rI   rM   rR   r   r4   
startswithr   r.   r$   r1   _display_parser_stater   r0   printpoprO   )r   rB   rC   Zappend_to_cachestackZproperty_namecharcachenewtypesrb   	remainingr   r   r   rK      sx    













zCssSimpleParser.parse)rn   c                 C   sB   t d t d| j t d| j |dur6t dt| t d dS )z0Display parser inner state when an error occurs.z$*** Error in this CSS stylesheet ***zstack:zcontent:Nz	unparsed:z******)rg   rH   r$   repr)r   rn   r   r   r   rf      s    z%CssSimpleParser._display_parser_statec                 C   s   | j d jS r   )rH   r$   r   r   r   r   r$      s    zCssSimpleParser.contentc                    s    t t d fdd  | jS )z=Find recursively all the css rules and return them as a list.r+   c                    sF   g }| D ]8}t |tr2|| | |j qt |tsJ q|S r"   )r8   r4   r,   extendr$   r   )r$   resultselement
find_rulesr   r   rt      s    

z)CssSimpleParser.rules.<locals>.find_rules)r   r4   r$   r   r   rs   r   rules   s    
zCssSimpleParser.rulesc                 C   s   dd | j D S )Nc                 S   s   g | ]}|j D ]}|qqS r   )r<   )r9   ruleZcss_propertyr   r   r   r:      r;   z.CssSimpleParser.properties.<locals>.<listcomp>ru   r   r   r   r   r<      s    zCssSimpleParser.propertiesc                 C   s   dd | j D S )ae  Find recursively all the css rules' headers and return them as a list.

        For each rule, the rule header is the part preceding the "{...}" block.
        If there is no "{...}" block, which may happen for some @rules, the whole rule
        is considered to be a header.

        While most headers are selectors, this is not true for @rules.
        c                 S   s   g | ]
}|j qS r   r6   r9   rv   r   r   r   r:      r;   z1CssSimpleParser.rules_headers.<locals>.<listcomp>rw   r   r   r   r   rules_headers   s    
zCssSimpleParser.rules_headersc                 C   s   dd | j D S )Nc                 S   s   g | ]}| d s|qS )@)re   r9   r6   r   r   r   r:      r;   z-CssSimpleParser.selectors.<locals>.<listcomp>rz   r   r   r   r   	selectors   s    zCssSimpleParser.selectorsc                 C   s   dd | j D S )Nc                 S   s"   g | ]}| d r| d qS )r{   r   )re   splitr|   r   r   r   r:     r;   z2CssSimpleParser.at_rules_names.<locals>.<listcomp>r}   r   r   r   r   at_rules_names  s    zCssSimpleParser.at_rules_namesr
   c                 C   s   t || jv S r"   )r   r~   r   r   r   r   r   has_selector  s    zCssSimpleParser.has_selectorc                    s   t    fdd| jD S )z3Return a list of all rules involving this selector.c                    s   g | ]}|j  kr|qS r   rx   ry   r   r   r   r:     r;   z,CssSimpleParser.find_all.<locals>.<listcomp>)r   ru   r   r   r   r   find_all  s    zCssSimpleParser.find_allFc                 C   sp   dd | j D }g }|D ]R}tt|}|d u r@td|d| dd  }t|s`|r|| q|S )Nc                 S   s   g | ]}|j d r|j qS )z@import )r6   re   ry   r   r   r   r:     r;   z0CssSimpleParser.list_imports.<locals>.<listcomp>zInvalid @import rule: .r/   r-   )	ru   r   searchRE_INNER_STRINGr   rS   r   r   r,   )r   absoluteheadersurlsr6   rT   urlr   r   r   list_imports  s    zCssSimpleParser.list_importsc                 C   s2   i }t |}| jD ]}|j|kr||j q|S )z4Return a dictionary of properties for this selector.)r   ru   r6   updater<   )r   r   drv   r   r   r   get_properties  s    

zCssSimpleParser.get_properties)r   T)r   )T)N)F)r   r   r   r   r>   boolr%   r2   rM   rR   rX   rY   rK   rf   r$   r   r4   ru   r<   rz   r~   r   r   r   r   r   r   r   r   r   r   r@   ^   s2   		M	

r@   )textr   c                 C   s   zt | dd W dS  ty   | d}| d}|| }|dkrR| |d 7 } n|dk rlt| ddt|} zt | dd W Y dS  ty   Y Y dS 0 Y n0 dS )	zInspect text and test if it looks like a bit of CSS code.

    This is used to distinguish real CSS comments from disabled CSS code.
    It uses only basic heuristic, so do not expect too much from it. ;)
    FrE   Tr_   r`   r   r   N)rK   r   countr	   abs)r   Zopening_bracketsZclosing_bracketsZunclosedr   r   r   looks_like_css_code(  s     

r   TrZ   c                 C   s   t | |dS rD   )r@   rA   r   r   r   rK   A  s    rK   __main__zPp + p { color: red; border: solid 2px }
 @media (print) {p + p {color: yellow;}})T)r   r   collectionsr   typingr   r   r   Z$websites_test_framework.custom_typesr   r   Zwebsites_test_framework.toolsr   r	   r   r>   r   RuntimeErrorr   r    r!   r&   r4   r@   r   r   rK   r   cssrg   r~   r   r   r   r   <module>   s&   
 K