{"size": 6746, "ext": "js", "max_stars_repo_path": "src/specificity.js", "max_stars_repo_name": "eprev/specificity", "max_stars_repo_stars_event_min_datetime": "2015-02-05T10:39:24.000Z", "max_stars_repo_stars_event_max_datetime": "2017-11-13T14:01:54.000Z", "max_issues_repo_path": "src/specificity.js", "max_issues_repo_name": "eprev/specificity", "max_issues_repo_issues_event_min_datetime": "2015-01-24T10:19:50.000Z", "max_issues_repo_issues_event_max_datetime": "2015-05-28T18:57:40.000Z", "max_forks_repo_path": "src/specificity.js", "max_forks_repo_name": "eprev/specificity", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": 25, "max_issues_count": 10, "max_forks_count": null, "avg_line_length": 28.4641350211, "max_line_length": 99, "alphanum_fraction": 0.3879335903}
'use strict';

var fs = require('fs'),
    postcss = require('postcss'),
    path = require('path');

exports.parse = function (files, options) {

    if (!options.cwd) {
        options.cwd = '';
    }

    function cmp(prop){
        return function(a, b) {
            return a[prop] >= b[prop] ? 1 : -1;
        };
    }

    var multiple = files.length > 1,
        result = {
            files  : {
                '*': {
                    series: []
                }
            },
            options: options
        };

    var uniqueSelectors = {};

    var series = result.files['*'].series;

    files.forEach(function (file) {
        var css = fs.readFileSync(path.join(options.cwd, file), 'utf8'),
            root = postcss.parse(css, { safe: true });
        if (multiple) {
            result.files[file] = {
                series: []
            };
        }
        root.eachRule(function (rule) {
            if (!(rule.parent.type === 'atrule' && rule.parent.name.indexOf('keyframes') !== -1)) {
                var important = 0;
                rule.eachDecl(function (decl) {
                    if (decl.important) {
                        important++;
                    }
                });
                rule.selectors.forEach(function (selector) {
                    if (options.uniqueSelectors && uniqueSelectors[selector]) {
                        uniqueSelectors[selector]++;
                    } else {
                        uniqueSelectors[selector] = 1;
                        var weight = getSpecificity(selector);
                        var o = {
                            selector: selector,
                            important: important,
                            weight: weight,
                            file: file,
                            start: rule.source.start,
                            end: rule.source.end
                        };
                        series.push(o);
                    }
                });
            }
        });
    });

    series = series.sort(cmp('weight'));

    if (multiple) {
        series.forEach(function(o) {
            result.files[o.file].series.push(o);
        });
        for (var i in result.files) {
            profile(result.files[i]);
        }
    } else {
        profile(result.files['*']);
    }

    function median(series) {
        var n = series.length;
        if (n % 2) {
            return series[ (n + 1) / 2 ];
        } else {
            var a = series[ n/2 - 1 ],
                b = series[ n/2 ];
            if (Array.isArray(a)) {
                return [0, 1, 2].map(function (i) {
                    return (a[i] + b[i]) / 2;
                });
            } else {
                return (a + b) / 2;
            }
        }
    }

    function profile(data) {
        data['important'] = {
                'min'  : Infinity,
                'max'  : 0,
                'avg'  : 0,
                'med'  : 0
            };
        data['weight_a'] = {
                'min'  : Infinity,
                'max'  : 0,
                'avg'  : 0,
                'med'  : 0
            };
        data['weight_b'] = {
                'min'  : Infinity,
                'max'  : 0,
                'avg'  : 0,
                'med'  : 0
            };
        data['weight_c'] = {
                'min'  : Infinity,
                'max'  : 0,
                'avg'  : 0,
                'med'  : 0
            };
        data['weight'] = {
                'min'  : [Infinity, Infinity, Infinity],
                'max'  : [0, 0, 0],
                'avg'  : [0, 0, 0],
                'med'  : [0, 0, 0]
            };
        function check(v, prop) {
            if (v > data[prop].max) {
                data[prop].max = v;
            }
            if (v < data[prop].min) {
                data[prop].min = v;
            }
            if (Array.isArray(v)) {
                data[prop].avg[0] += v[0];
                data[prop].avg[1] += v[1];
                data[prop].avg[2] += v[2];
            } else {
                data[prop].avg += v;
            }
        }
        function fin(prop) {
            if (Array.isArray(data[prop].avg)) {
                data[prop].avg[0] /= n;
                data[prop].avg[1] /= n;
                data[prop].avg[2] /= n;
            } else {
                data[prop].avg /= n;
            }
        }
        var distrib = data.distrib = {};
        var n = data.series.length;
        var a = [], b = [], c = [], abc = [];
        data.series.forEach(function (o) {
            check(o.important, 'important');
            check(o.weight[0], 'weight_a');
            check(o.weight[1], 'weight_b');
            check(o.weight[2], 'weight_c');
            check(o.weight,    'weight');
            a.push(o.weight[0]);
            b.push(o.weight[1]);
            c.push(o.weight[2]);
            abc.push(o.weight);
            var sw = o.weight.join(',');
            if (distrib[sw]) {
                distrib[sw]++;
            } else {
                distrib[sw] = 1;
            }
        });
        if (data.series.length) {
            fin('important');
            fin('weight_a');
            fin('weight_b');
            fin('weight_c');
            fin('weight');
            data.weight_a.med = median(a);
            data.weight_b.med = median(b);
            data.weight_c.med = median(c);
            data.weight.med   = median(abc);
        }
    }

    if (options.uniqueSelectors) {
        result.uniqueSelectors = uniqueSelectors;
    }

    return result;
};

/* exported getSpecificity */

var getSpecificity = exports.getSpecificity = function (selector) {
    var m, re, a = 0, b = 0, c = 0;
    // Pseudo-class :not is not considered as a pseudo-class
    selector = selector.replace(/:not\(([^)]*)\)/g, ' $1 ');
    // Count :before and :after as pseudo-elements
    re = /:(?!:)(before|after)+/g;
    m = selector.match(re);
    if (m) {
        c += m.length;
        selector = selector.replace(re, '');
    }
    // Count pseudo-elements
    re = /:(?=:)[A-Za-z0-9_-]+/g;
    m = selector.match(re);
    if (m) {
        c += m.length;
        selector = selector.replace(re, '');
    }
    // Count ID attributes
    re = /(#[A-Za-z0-9_-]+)/g;
    m = selector.match(re);
    if (m) {
        a += m.length;
        selector = selector.replace(re, '');
    }
    // Count attributes and pseudo-classes
    re = /(\[[^\]]*\]|(:|\.)[A-Za-z0-9_-]+)/g,
    m = selector.match(re);
    if (m) {
        b += m.length;
        selector = selector.replace(re, '');
    }
    // Count elements
    re = /(\w+)/g;
    m = selector.match(re);
    if (m) {
        c += m.length;
    }
    return [ a, b, c ];
};
