var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
child[symbol] = _clone(parent[symbol], depth - 1);
if (!descriptor.enumerable) {
Object.defineProperty(child, symbol, {
if (includeNonEnumerable) {
var allPropertyNames = Object.getOwnPropertyNames(parent);
for (var i = 0; i < allPropertyNames.length; i++) {
var propertyName = allPropertyNames[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
if (descriptor && descriptor.enumerable) {
child[propertyName] = _clone(parent[propertyName], depth - 1);
Object.defineProperty(child, propertyName, {
return _clone(parent, depth);
* Simple flat clone using prototype, accepts only objects, usefull for property
* override on FLAT configuration object (no nested props).
* USE WITH CAUTION! This may not behave as you wish if you do not know how this
clone.clonePrototype = function clonePrototype(parent) {
// private utility functions
return Object.prototype.toString.call(o);
clone.__objToStr = __objToStr;
return typeof o === 'object' && __objToStr(o) === '[object Date]';
clone.__isDate = __isDate;
return typeof o === 'object' && __objToStr(o) === '[object Array]';
clone.__isArray = __isArray;
return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
clone.__isRegExp = __isRegExp;
function __getRegExpFlags(re) {
if (re.global) flags += 'g';
if (re.ignoreCase) flags += 'i';
if (re.multiline) flags += 'm';
clone.__getRegExpFlags = __getRegExpFlags;
if (typeof module === 'object' && module.exports) {
* @extends parserlib.util.EventTarget
/* global parserlib, clone, Reporter */
var CSSLint = (function() {
embeddedRuleset = /\/\*\s*csslint([^\*]*)\*\//,
api = new parserlib.util.EventTarget();
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
* Adds a new rule to the engine.
* @param {Object} rule The rule to add.
api.addRule = function(rule) {
* Clears all rule from the engine.
api.clearRules = function() {
* Returns the rule objects.
* @return An array of rule objects.
api.getRules = function() {
return [].concat(rules).sort(function(a, b) {
return a.id > b.id ? 1 : 0;
* Returns a ruleset configuration object with all current rules.
* @return A ruleset object.
api.getRuleset = function() {
ruleset[rules[i++].id] = 1; // by default, everything is a warning
* Returns a ruleset object based on embedded rules.
* @param {String} text A string of css containing embedded rules.
* @param {Object} ruleset A ruleset object to modify.
* @return {Object} A ruleset object.
* @method getEmbeddedRuleset
function applyEmbeddedRuleset(text, ruleset) {
embedded = text && text.match(embeddedRuleset),
rules = embedded && embedded[1];
"true": 2, // true is error
"": 1, // blank is warning
"false": 0, // false is ignore
"2": 2, // explicit error
"1": 1, // explicit warning
"0": 0 // explicit ignore
rules.toLowerCase().split(",").forEach(function(rule) {
var pair = rule.split(":"),
property = pair[0] || "",
ruleset[property.trim()] = valueMap[value.trim()];
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
* Adds a new formatter to the engine.
* @param {Object} formatter The formatter to add.
api.addFormatter = function(formatter) {
// formatters.push(formatter);
formatters[formatter.id] = formatter;
* Retrieves a formatter for use.
* @param {String} formatId The name of the format to retrieve.
* @return {Object} The formatter or undefined.
api.getFormatter = function(formatId) {
return formatters[formatId];
* Formats the results in a particular format for a single file.
* @param {Object} result The results returned from CSSLint.verify().
* @param {String} filename The filename for which the results apply.
* @param {String} formatId The name of the formatter to use.
* @param {Object} options (Optional) for special output handling.
* @return {String} A formatted string for the results.
api.format = function(results, filename, formatId, options) {
var formatter = this.getFormatter(formatId),
result = formatter.startFormat();
result += formatter.formatResults(results, filename, options || {});
result += formatter.endFormat();
* Indicates if the given format is supported.
* @param {String} formatId The ID of the format to check.
* @return {Boolean} True if the format exists, false if not.
api.hasFormat = function(formatId) {
return formatters.hasOwnProperty(formatId);
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
* Starts the verification process for the given CSS text.
* @param {String} text The CSS text to verify.
* @param {Object} ruleset (Optional) List of rules to apply. If null, then
* all rules are used. If a rule has a value of 1 then it's a warning,
* a value of 2 means it's an error.
* @return {Object} Results of the verification.
api.verify = function(text, ruleset) {
parser = new parserlib.css.Parser({
// normalize line endings
lines = text.replace(/\n\r?/g, "$split$").split("$split$");
CSSLint.Util.forEach(lines, function (line, lineno) {
var allowLine = line && line.match(/\/\*[ \t]*csslint[ \t]+allow:[ \t]*([^\*]*)\*\//i),
allowRules = allowLine && allowLine[1],
allowRules.toLowerCase().split(",").forEach(function(allowRule) {
allowRuleset[allowRule.trim()] = true;
if (Object.keys(allowRuleset).length > 0) {
allow[lineno + 1] = allowRuleset;
CSSLint.Util.forEach(lines, function (line, lineno) {
// Keep oldest, "unclosest" ignore:start
if (ignoreStart === null && line.match(/\/\*[ \t]*csslint[ \t]+ignore:start[ \t]*\*\//i)) {
if (line.match(/\/\*[ \t]*csslint[ \t]+ignore:end[ \t]*\*\//i)) {
if (ignoreStart !== null && ignoreEnd !== null) {
ignore.push([ignoreStart, ignoreEnd]);
ignoreStart = ignoreEnd = null;
// Close remaining ignore block, if any
if (ignoreStart !== null) {
ignore.push([ignoreStart, lines.length]);
ruleset = this.getRuleset();
if (embeddedRuleset.test(text)) {
// defensively copy so that caller's version does not get modified
ruleset = clone(ruleset);
ruleset = applyEmbeddedRuleset(text, ruleset);
reporter = new Reporter(lines, ruleset, allow, ignore);
ruleset.errors = 2; // always report parsing errors as errors
if (ruleset.hasOwnProperty(i) && ruleset[i]) {
rules[i].init(parser, reporter);
// capture most horrible error type
reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
messages : reporter.messages,
ruleset : reporter.ruleset,
// sort by line numbers, rollups at the bottom
report.messages.sort(function (a, b) {
if (a.rollup && !b.rollup) {
} else if (!a.rollup && b.rollup) {
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
* An instance of Report is used to report results of the
* verification back to the main API.
* @param {String[]} lines The text lines of the source.
* @param {Object} ruleset The set of rules to work with, including if
* they are errors or warnings.
* @param {Object} explicitly allowed lines
* @param {[][]} ingore list of line ranges to be ignored
function Reporter(lines, ruleset, allow, ignore) {
* List of messages being reported.
* List of statistics being reported.
* Lines of code being reported on. Used to provide contextual information
* Information about the rules. Used to determine whether an issue is an
* Lines with specific rule messages to leave out of the report.
* Linesets not to include in the report.
* @param {String} message The message to store.
* @param {int} line The line number.
* @param {int} col The column number.
* @param {Object} rule The rule this message relates to.
error: function(message, line, col, rule) {
evidence: this.lines[line-1],
* @param {String} message The message to store.
* @param {int} line The line number.
* @param {int} col The column number.
* @param {Object} rule The rule this message relates to.
* @deprecated Use report instead.
warn: function(message, line, col, rule) {
this.report(message, line, col, rule);
* @param {String} message The message to store.
* @param {int} line The line number.
* @param {int} col The column number.
* @param {Object} rule The rule this message relates to.
report: function(message, line, col, rule) {
// Check if rule violation should be allowed
if (this.allow.hasOwnProperty(line) && this.allow[line].hasOwnProperty(rule.id)) {