<?php
    
if (isset($_SERVER["QUERY_STRING"])) {
         if(
$_SERVER["QUERY_STRING"] == "source") {
        
highlight_file("specificity.php");
        exit;
         }
    }
?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
 "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
    <title>CSS Specificity Calculator</title>
        <style type="text/css" media="screen">
        html, body {margin: 0; padding: 0; background: #abc; text-align: center;}
        #pagewrap {width: 870px; margin: 0 auto; padding: 10px; border-width: 0 6px; border-style: double; border-color: #008;  background: #fff; color: #000; font: 13px/1.5 tahoma, verdana, arial, sans-serif; text-align: left;}
        h1, h2, h3, h4 {font-family: "trebuchet ms", georgia, sans-serif; border-bottom: 1px solid #000;}
        h1 {font-size: 18px;}
        h2 {font-size: 16px;}
        h3 {font-size: 14px;}
        h4 {font-size: 13px; text-align: center; margin: 3px;}
        a {color: #00f; outline: none;}
        a:hover {background: #ffc; color: #a00;}
        table {width: 100%; border-collapse: collapse; font-size: 13px;}
        td, th {vertical-align: top; padding: 3px;}
        table#results {border: 1px solid #000;}
        table.rows td, table.rows th {border: 1px solid #000; border-width: 1px 0;}
        th.rse {width: 70%; text-align: left;} 
        th.rsc, th.rpr {width: 15%; text-align: center;}
        td.props, td.score {text-align: center; font-size: 13px;}
        td.score {font-weight: bold; letter-spacing: 1px;}
        td.propdata {border-left: 3px double #000; padding: 0 6px;}
        td.codeout {font-family: "Courier New", courier, monospace; font-size: 13px; padding: 3px 5px;}
        tr.row0 {background: #ffe;}
        tr.row1 {background: #ffc;}
        form {width: 90%; margin: 0 auto; padding:0; background: #fff;}
        form textarea {width: 98%; margin: 0 -3px 0 2px;}
        input.submit {float: right; margin: 5px 5px 5px 20px; display: inline;}
        form p {margin: 5px 0; text-align: right;}
            blockquote {border: 1px solid #ccc; background: #ffe; padding: 0 20px;}
        .footer {border-top: 3px double #ccc; background: #eee; width: 890px; margin: 0 -10px -10px -10px; }
        .footer p {width: 80%; margin: 0 auto; text-align: center;}
        .notice, h1 {width: 90%; margin: 0 auto; overflow: auto;}
        p.startover {text-align: right;}
        em {color: red;}
            .strike {text-decoration: line-through;}
        </style> 
    <script type="text/javascript">
        function show(data) {
            window.document.getElementById('code').innerHTML = data;
        }

        function floatdetails() {
            var posY = 0;
            var netscape = (navigator.appName.indexOf("Netscape") != -1);
            
            function move(id) {
                var divpos = document.getElementById ? document.getElementById(id): document.all ? document.all[id] : document.layers[id];
            
                divpos.sP = function(y) {
                    this.style.top = y + "px";
                };
                
                divpos.y = posY;
                return divpos;
            }
            
            window.floatdiv = function() {
                var pY = netscape ? pageYOffset : document.body.scrollTop;
                pos.y += (pY + posY - pos.y) / 8;
                pos.sP(pos.y);
                setTimeout("floatdiv()");
            }

            pos = move("code");
            floatdiv();
        }
</script>
</head>
<body>
<div id="pagewrap">
<h1>CSS specificity calculator</h1>
<?php
function cleanup(&$data$value) {
    
$data trim($data);
}

// Pseudo classes
$classes  = array(":link"":visited"":hover"":active"":focus"":target"":lang"":enabled"
                  
":disabled"":checked"":indeterminate"":root"":nth-child"":nth-last-child",
                  
":nth-of-type"":nth-last-of-type"":first-child"":last-child"":first-of-type"
                  
":last-of-type"":only-child"":only-of-type"":empty"":contains"":not");

// Pseudo elements
$elements = array(":before"":after"":first-line"":first-letter"":selection");

// Check if form has been submitted
if (isset($_POST["css"])) {
    
// Strip slashes
    
$_POST["css"] = get_magic_quotes_gpc() ? stripslashes($_POST["css"]) : $_POST["css"];

    
$data preg_replace("/\/\\*[\\s\\S]*?\\*\//"""$_POST["css"]);  // Strip comments
    
$data preg_replace("/\{(.*?)\}/s"", "$data);                  // Strip content and replace with delimiter
    
$data str_replace(" ,"","$data);                              // Strip spaces after selectors

    
$tmp explode(","$data);                                         // Get rid of selector groupings
    
array_walk($tmp'cleanup');                                        // Trim whitespace from end of selectors

    
?>
    <table id="results" cellspacing="0">
    <tr>
    <td width="70%">
    <table class="rows" cellspacing="0">
        <tr class="hdr">
            <th class="rse">Selector</th>
            <th class="rsc">Score<br><span>a,b,c,d</span></th>
            <th class="rpr">Properties</th>
        </tr>
    <?php
    $rowclass 
0;                                    // just a stripe function for table rows 
    
    // Loop through the selector array
    
foreach ($tmp as $var) {
      
// score should be in the annotation a, b, c, d
      
$a 0;   //  score a
      
$b 0;   //  score b
      
$c 0;   //  score c
      
$d 0;   //  score d
                
        
if (!empty($var)) {
            
$str strtolower($var);

        
// start to clean up the array ready to count the HTML or XML elements (all words) that are left
        // remove arguments from selectors before counting as these might include classes, attributes etc
        // e.g. :not{.post) 0r :not([DISABLED]) nth-child(even)

        // remove the negation pseudo class but keep the selectors inside it
        
$str preg_replace("/(:not)\(([^)]+)\)/"," $2 ",$str); 

        
// now we can wipe the rest of the brackets and their contents
            
$str preg_replace("/(\([^)]*\))*/"""$str);  

            
$b += substr_count($str'#');            // Count the IDs and add to b
            
$c += substr_count($str'.');            // Count the classes and add to c
            
$c += substr_count($str'[');            // Count the attributes and add to c

            
foreach ($classes as $test) {                   
                
$c += substr_count($str$test);      // Count the pseudo-classes and add to c
            
}

            foreach (
$elements as $test) {            // Count the pseudo-elements and add to d
                
$d += substr_count($str$test);
            }

        
// Remove the pseudo selectors, ids or classes along with any following text to avoid
        // anything which might match HTML tags or form a word for the count at the end, 
            
$cleaned preg_replace("/([:#.][-_A-Za-z0-9]+)*/"""$str);  

            
$cleaned preg_replace("/\[.*\]/"" "$cleaned);     // Remove the attribute selectors
            
$cleaned preg_replace("/[+>*]/"" "$cleaned);      // Remove the adjacent sibling, child and universal selectors

        //only want a word count left so remove any other characters from any words that are left
            
$cleaned preg_replace("/[.-_:0-9]/"""$cleaned);   // Remove any characters that might be in an xml element name

        // add word count to elements d
        
$d += str_word_count($cleaned);

            
// Now to find the actually properties
            
$code substr($_POST["css"], strpos($_POST["css"], $var)); // Find the position of the current selector
            
$first strpos($code"{") + 1;                            // Get the position of the first {
            
$last strpos($code"}");                                 // Get the position of the first }

            
$code substr($code$first$last $first);              // Get the content between the { and }
            
$code preg_replace("/\/\\*[\\s\\S]*?\\*\//"""$code);  // Strip any comments from the properties

            // Now make the data safe for javascript
            
$code trim($code);                                        // Trim the properties
            
$code str_replace("\\""\\\\"$code);                   // Escape any slashes
            
$code str_replace("'""\\'"$code);                     // Escape any apostrophes
            
$code str_replace("\r\n""<br>"$code);                 // Convert new lines to HTML break (Can't use nl2br as it doesn't remove the newlines)
            
$code str_replace("\n""<br>"$code);
            
$code str_replace("\r""<br>"$code);

        
$score $a.','.$b.','.$c.','.$d;
            
$defined substr_count($code":");                        // Count the number of properties the selector defines
                        
            
?>
            <tr class="row<?=$rowclass?>">
                <td onmouseover="show('<?=$code;?>');" onmouseout="show('');" class="codeout" style="cursor: pointer;"><?=$var;?></td>
                <td class="score"><?=$score;?></td>
                <td class="props"><?=$defined;?></td>
            </tr>
            <?php
        $rowclass 
$rowclass;
        }
    }
    
?>
    </table>
    </td>
    <td class="propdata">
       <h4>Property data</h4>
       <div id="code" style="position: relative;"></div>
       <script type="text/javascript">floatdetails();</script>
    </td>
    </tr>
    </table>
    <div class="notice">
    <h2>Note: for styles applied using the inline HTML &lt;style&gt; attribute add 1 to the <em>'a'</em> part of the score.</h2>
    <p>the score is read, left to right, 'a' beats 'b' beats 'c' beats 'd', then the individual scores come into play.</p>
    <p><em>Remember:</em> that although XML elements are now counted too, if they have periods '.' in the element name the calculator will not give a reliable count for the class component (c) part of the score.</p>

    <p class="startover"><a href="specificity.php">Start over</a></p>
    <?php
} else {
    
?>
    <div class="notice">
    <p>Paste your CSS into the field below, the specificity of your selectors will be calculated for you.</p>
    <p>If you don't know what CSS specificity is, you probably don't need this tool, but for an explanation see:</p>
        <ul>
        <li><a href="http://www.molly.com/2005/10/06/css2-and-css21-specificity-clarified/">Molly - CSS2 and CSS2.1 Specificity Clarified</a></li>
        <li><a href="http://iamacamera.org/default.aspx?id=95">CSS Specificity for Poker Players</a></li>
        <li><a href="http://meyerweb.com/eric/css/link-specificity.html">Eric Meyer on why it's not base10</a></li>
        <li><a href="http://www.stuffandnonsense.co.uk/archives/css_specificity_wars.html">Andy Clarke - CSS: Specificity Wars : Sith Power!</a></li>
        <li><a href="http://www.smashingmagazine.com/2007/07/27/css-specificity-things-you-should-know/">Smashing Magazine - CSS Specificity: Things You Should Know</a></li>
        </ul>
    <h2>Current limitations</h2>
    <ul>
     <li>Your CSS <b>must</b> <a href="http://jigsaw.w3.org/css-validator/" title="Ensure the file is valid CSS">be valid</a> in order for the calculator to work</li>
     <li class="strike">Currently only HTML elements are counted in the score, custom tags such as XML are not</li>
     <li>XML elements are now counted too <strong><em>BUT</em></strong> if they have periods '.' in the element name the calculator will not give a reliable count for the class component (c) part of the score.</li>
    </ul>
    <form method="post" action="specificity.php">
        <textarea name="css" rows="15"></textarea>
        <br /><input type="submit" class="submit" value="Calculate" />
    </form>
    <?
}
?>
</div>
<div class="changelog">
<h3>Changes/Updates:</h3> 
<ul>
  <li>Update: disabled count of :not() while counting the arguments instead.
      <p>Ref: <a href="http://www.w3.org/TR/css3-selectors/#specificity">CSS3 Selector Specificity Calculation</a></p>
      <p><i>Selectors inside the negation pseudo-class are counted like any other,<br>but the negation itself does not count as a pseudo-class</i></p>
</li>
  <li>Update: amended to allow for CSS3 selector arguments.</li>
  <li>Update: now includes XML element count (unlesss there are full stops in the element name)</li>
  <li>Amended to reflect that specificity calculations should not be in base10.</li>
</ul>
</div>
<div class="w3cref">
<blockquote>
<h3><a href="http://www.w3.org/TR/CSS21/cascade.html#specificity">6.4.3 Calculating a selector's specificity</a></h3>

<p>A selector's specificity is calculated as follows:</p>

<ul>
<li>count 1 if the declaration is from is a &lt;style&gt; attribute rather than a rule with a selector, 0 otherwise (= a) (In HTML, values of an element's "style" attribute are style sheet rules. These rules have no selectors, so a=1, b=0, c=0, and d=0.)</li>
<li>count the number of ID attributes in the selector (= b)</li>
<li>count the number of other attributes and pseudo-classes in the selector (= c)</li>
<li>count the number of element names and pseudo-elements in the selector (= d)</li>
</ul>

<p>The specificity is based only on the form of the selector. In particular, a selector of the form "[id=p33]" is counted as an attribute selector (a=0, b=0, c=1, d=0), even if the id attribute is defined as an "ID" in the source document's DTD.</p>
<p>Concatenating the four numbers a-b-c-d (in a number system with a large base) gives the specificity.</p> 


<div class="examples">
<p>Some examples:</p>
<pre>
 *             {}  /* a=0 b=0 c=0 d=0 -&gt; specificity = 0,0,0,0 */
 li            {}  /* a=0 b=0 c=0 d=1 -&gt; specificity = 0,0,0,1 */
 li:first-line {}  /* a=0 b=0 c=0 d=2 -&gt; specificity = 0,0,0,2 */
 ul li         {}  /* a=0 b=0 c=0 d=2 -&gt; specificity = 0,0,0,2 */
 ul ol+li      {}  /* a=0 b=0 c=0 d=3 -&gt; specificity = 0,0,0,3 */
 h1 + *[rel=up]{}  /* a=0 b=0 c=1 d=1 -&gt; specificity = 0,0,1,1 */
 ul ol li.red  {}  /* a=0 b=0 c=1 d=3 -&gt; specificity = 0,0,1,3 */
 li.red.level  {}  /* a=0 b=0 c=2 d=1 -&gt; specificity = 0,0,2,1 */
 #x34y         {}  /* a=0 b=1 c=0 d=0 -&gt; specificity = 0,1,0,0 */
 
 style=""          /* a=1 b=0 c=0 d=0 -&gt; specificity = 1,0,0,0 */
</pre>
</div>

</blockquote>
</div>
<div class="footer">
<p><b>Credits:</b> Original Code by <a href="http://www.rebelinblue.com/specificity.php" title="Stephen Ball">rebelinblue.com</a>
<br>Amended by Claire "Suzy" Campbell <a href="http://www.tanfa.co.uk" title="tanfa">tanfa.co.uk</a> : <a href="http://twitter.com/ClaireSuzy">Twitter</a></p>
<p class="viewsource"><a href="specificity.php?source">View the source</a></p>
</div>
</div>
</body>
</html>