UNPKG

specificity-graph

Version:

Generate an interactive Specificity Graph for your CSS.

2,226 lines (1,780 loc) 485 kB
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.specificityGraph=e()}}(function(){var _defi_,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ 'use strict'; var generateCssData = _dereq_('./generateCssData'); var lineChart = _dereq_('./lineChart'); var _create = function(css, opts){ var data = generateCssData(css); lineChart.create(data,opts); } var _update = function(css, opts){ var data = generateCssData(css); lineChart.update(data); } module.exports = { create: _create, update: _update, draw: lineChart.create, nextFocus: lineChart.nextFocus, prevFocus: lineChart.prevFocus }; },{"./generateCssData":2,"./lineChart":3}],2:[function(_dereq_,module,exports){ 'use strict'; var cssParse = _dereq_('css-parse'); var unminify = _dereq_('cssbeautify'); var specificity = _dereq_('specificity'); var generateCssData = function(origCss) { var testCSS = unminify(origCss); var astObj = cssParse(testCSS, {silent: true}); var results = [], selectorIndex = 0; var specSum = function(selector){ var specs = specificity.calculate(selector)[0].specificity.split(','); var sum = (parseInt(specs[0])*1000) + (parseInt(specs[1])*100) + (parseInt(specs[2])*10) + (parseInt(specs[3])); return sum; } astObj.stylesheet.rules.map(function(rule){ var selectors = rule.selectors; if(typeof selectors === "undefined") { return; } var line = rule.position.start.line; selectors.forEach(function(selector){ if(selector.length === 0) return; results.push({ selectorIndex: selectorIndex, line: line, specificity: specSum(selector), selectors: selector }); selectorIndex++; }); }); return results; } module.exports = generateCssData; },{"css-parse":7,"cssbeautify":30,"specificity":32}],3:[function(_dereq_,module,exports){ 'use strict'; var d3 = _dereq_('d3'); if (typeof window === "undefined") { console.log('d3 requires a browser to run. EXITING'); throw new Error; } var _state = { data: {}, vis: undefined, focus : undefined, width: 1000, height: 400, padding: { top: 40, right: 60, bottom: 40, left: 60 }, min_val: 0, //same for x/y max_val_y: 100, d3_x : undefined, d3_y : undefined, data_attribute_name_x : 'selectorIndex', data_attribute_name_y : 'specificity', active_index : 0 }; var lineFunc = d3.svg.line() .x(function(d,idx) { return _state.d3_x(d[_state.data_attribute_name_x]); }) .y(function(d) { return _state.d3_y(d[_state.data_attribute_name_y]); }) .interpolate('linear'); var _updateFocus = function(index){ var index = Math.min(Math.max(0, index), _state.data.length-1); _state.active_index = index; _state.focus.style('display', null); var d = _state.data[_state.active_index]; _state.focus.attr('transform', 'translate(' + _state.d3_x(d[_state.data_attribute_name_x]) + ',' + _state.d3_y(d[_state.data_attribute_name_y]) + ')'); var t = _state.focus.select('.js-focus-text'); t.text(d.selectors + ': ' + d.specificity); var w = t[0][0].getBBox().width + 20; _state.focus.select('.js-focus-text-background') .attr('width', w) .attr('x', -w/2); } var _create = function(data, opts){ _state.data = data; var opts = opts || {}; _state.width = opts.width || _state.width, _state.height = opts.height || _state.height, _state.data_attribute_name_x = opts.xProp || _state.data_attribute_name_x; _state.data_attribute_name_y = opts.yProp || _state.data_attribute_name_y; var svgSelector = opts.svgSelector || '.js-graph'; _state.vis = d3.select(svgSelector); _update(_state.data); // below elements don't change based on data var xAxis = d3.svg.axis() .scale(_state.d3_x) .tickSize(0); var yAxis = d3.svg.axis() .scale(_state.d3_y) .tickSize(0) .orient('left'); _state.vis.append('svg:g') .attr('class', 'axis' + (opts.showTicks ? '--show-ticks' : '')) .attr('transform', 'translate(0,' + (_state.height - _state.padding.bottom) + ')') .call(xAxis) // Move the ticks out from the axis line .selectAll("text") .attr("transform", 'translate(0,' + (opts.showTicks ? 4 : 0) + ')'); _state.vis.append('svg:g') .attr('class', 'axis' + (opts.showTicks ? '--show-ticks' : '')) .attr('transform', 'translate(' + (_state.padding.left) + ',0)') .call(yAxis) // Move the ticks out from the axis line .selectAll("text") .attr("transform", 'translate(' + (opts.showTicks ? -4 : 0) + ', 0)'); // x domain label _state.vis.append('svg:text') .attr('class', 'domain-label') .attr('text-anchor', 'middle') .attr('x', (_state.width/2)) .attr('y', _state.height) .attr('transform', 'translate(0,' + (-_state.padding.bottom+(opts.showTicks ? 34 : 24)) + ')') .text('Location in stylesheet'); // y domain label _state.vis.append('svg:text') .attr('class', 'domain-label') .attr('text-anchor', 'middle') .attr('transform', 'translate('+(_state.padding.left-(opts.showTicks ? 34 : 16)) + ','+(_state.height/2)+')rotate(-90)') .text('Specificity'); // handle on mouseover focus circle and info text _state.focus = _state.vis.append('svg:g') .attr('class', 'focus') .style('display', 'none'); _state.focus.append('svg:circle') .attr('r', 4.5); _state.focus.append('svg:rect') .attr('class', 'focus-text-background js-focus-text-background') .attr('width',300) .attr('height',20) .attr('y', '-30') .attr('ry', '14') .attr('rx', '4'); _state.focus.append('svg:text') .attr('class', 'focus-text js-focus-text') .attr('text-anchor', 'middle') .attr('dy', '0.35em') .attr('y', '-20'); _state.vis.append('svg:rect') .attr('class', 'overlay') .attr('width', _state.width) .attr('height', _state.height) .on('mouseout', function() { _state.focus.style('display', 'none'); }) .on('mousemove', mousemove); var bisectX = d3.bisector(function(d) { return d[_state.data_attribute_name_x]; }).right; function mousemove() { var x0 = _state.d3_x.invert(d3.mouse(this)[0]), i = bisectX(_state.data, x0), d0 = _state.data[i - 1], d1 = _state.data[i], newIndex; //check which value we're closer to (if within bounds) if(typeof d0 === 'undefined'){ if(typeof d1 === 'undefined'){ return; } newIndex = i; } else if(typeof d1 ==='undefined'){ newIndex = i -1; } else { if(x0 - d0[_state.data_attribute_name_x] > d1[_state.data_attribute_name_x] - x0){ newIndex = i; } else { newIndex = i -1; } } _updateFocus(newIndex); } } var _update = function(data){ _state.data = data; _state.max_val_y = Math.max(100, d3.max(_state.data, function(d) { return d[_state.data_attribute_name_y]; })); _state.d3_x = d3.scale.linear() .range([_state.padding.left, _state.width - _state.padding.right]) .domain([_state.min_val, d3.max(_state.data, function(d,idx) { return d[_state.data_attribute_name_x]; })]); _state.d3_y = d3.scale.linear() .range([_state.height - _state.padding.top, _state.padding.bottom]) .domain([_state.min_val, _state.max_val_y]); //TODO: transition path, instead of remove hack if(document.querySelectorAll('.line-path').length === 0) { _state.vis.append('svg:path') .attr('d', lineFunc(_state.data)) .attr('class', 'line-path'); } else { _state.vis.insert('svg:path', '.line-path') .attr('d', lineFunc(_state.data)) .attr('class', 'line-path'); document.querySelector('.line-path:last-of-type').remove(); } } var _nextFocus = function(){ _updateFocus(_state.active_index + 1); } var _prevFocus = function(){ _updateFocus(_state.active_index - 1); } module.exports = { create: _create, update: _update, nextFocus: _nextFocus, prevFocus: _prevFocus }; },{"d3":31}],4:[function(_dereq_,module,exports){ },{}],5:[function(_dereq_,module,exports){ (function (process){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. // resolves . and .. elements in a path array with directory names there // must be no slashes, empty elements, or device names (c:\) in the array // (so also no leading and trailing slashes - it does not distinguish // relative and absolute paths) function normalizeArray(parts, allowAboveRoot) { // if the path tries to go above the root, `up` ends up > 0 var up = 0; for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i]; if (last === '.') { parts.splice(i, 1); } else if (last === '..') { parts.splice(i, 1); up++; } else if (up) { parts.splice(i, 1); up--; } } // if the path is allowed to go above the root, restore leading ..s if (allowAboveRoot) { for (; up--; up) { parts.unshift('..'); } } return parts; } // Split a filename into [root, dir, basename, ext], unix version // 'root' is just a slash, or nothing. var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; var splitPath = function(filename) { return splitPathRe.exec(filename).slice(1); }; // path.resolve([from ...], to) // posix version exports.resolve = function() { var resolvedPath = '', resolvedAbsolute = false; for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = (i >= 0) ? arguments[i] : process.cwd(); // Skip empty and invalid entries if (typeof path !== 'string') { throw new TypeError('Arguments to path.resolve must be strings'); } else if (!path) { continue; } resolvedPath = path + '/' + resolvedPath; resolvedAbsolute = path.charAt(0) === '/'; } // At this point the path should be resolved to a full absolute path, but // handle relative paths to be safe (might happen when process.cwd() fails) // Normalize the path resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { return !!p; }), !resolvedAbsolute).join('/'); return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; }; // path.normalize(path) // posix version exports.normalize = function(path) { var isAbsolute = exports.isAbsolute(path), trailingSlash = substr(path, -1) === '/'; // Normalize the path path = normalizeArray(filter(path.split('/'), function(p) { return !!p; }), !isAbsolute).join('/'); if (!path && !isAbsolute) { path = '.'; } if (path && trailingSlash) { path += '/'; } return (isAbsolute ? '/' : '') + path; }; // posix version exports.isAbsolute = function(path) { return path.charAt(0) === '/'; }; // posix version exports.join = function() { var paths = Array.prototype.slice.call(arguments, 0); return exports.normalize(filter(paths, function(p, index) { if (typeof p !== 'string') { throw new TypeError('Arguments to path.join must be strings'); } return p; }).join('/')); }; // path.relative(from, to) // posix version exports.relative = function(from, to) { from = exports.resolve(from).substr(1); to = exports.resolve(to).substr(1); function trim(arr) { var start = 0; for (; start < arr.length; start++) { if (arr[start] !== '') break; } var end = arr.length - 1; for (; end >= 0; end--) { if (arr[end] !== '') break; } if (start > end) return []; return arr.slice(start, end - start + 1); } var fromParts = trim(from.split('/')); var toParts = trim(to.split('/')); var length = Math.min(fromParts.length, toParts.length); var samePartsLength = length; for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i; break; } } var outputParts = []; for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push('..'); } outputParts = outputParts.concat(toParts.slice(samePartsLength)); return outputParts.join('/'); }; exports.sep = '/'; exports.delimiter = ':'; exports.dirname = function(path) { var result = splitPath(path), root = result[0], dir = result[1]; if (!root && !dir) { // No dirname whatsoever return '.'; } if (dir) { // It has a dirname, strip trailing slash dir = dir.substr(0, dir.length - 1); } return root + dir; }; exports.basename = function(path, ext) { var f = splitPath(path)[2]; // TODO: make this comparison case-insensitive on windows? if (ext && f.substr(-1 * ext.length) === ext) { f = f.substr(0, f.length - ext.length); } return f; }; exports.extname = function(path) { return splitPath(path)[3]; }; function filter (xs, f) { if (xs.filter) return xs.filter(f); var res = []; for (var i = 0; i < xs.length; i++) { if (f(xs[i], i, xs)) res.push(xs[i]); } return res; } // String.prototype.substr - negative index don't work in IE8 var substr = 'ab'.substr(-1) === 'b' ? function (str, start, len) { return str.substr(start, len) } : function (str, start, len) { if (start < 0) start = str.length + start; return str.substr(start, len); } ; }).call(this,_dereq_('_process')) },{"_process":6}],6:[function(_dereq_,module,exports){ // shim for using process in browser var process = module.exports = {}; process.nextTick = (function () { var canSetImmediate = typeof window !== 'undefined' && window.setImmediate; var canMutationObserver = typeof window !== 'undefined' && window.MutationObserver; var canPost = typeof window !== 'undefined' && window.postMessage && window.addEventListener ; if (canSetImmediate) { return function (f) { return window.setImmediate(f) }; } var queue = []; if (canMutationObserver) { var hiddenDiv = document.createElement("div"); var observer = new MutationObserver(function () { var queueList = queue.slice(); queue.length = 0; queueList.forEach(function (fn) { fn(); }); }); observer.observe(hiddenDiv, { attributes: true }); return function nextTick(fn) { if (!queue.length) { hiddenDiv.setAttribute('yes', 'no'); } queue.push(fn); }; } if (canPost) { window.addEventListener('message', function (ev) { var source = ev.source; if ((source === window || source === null) && ev.data === 'process-tick') { ev.stopPropagation(); if (queue.length > 0) { var fn = queue.shift(); fn(); } } }, true); return function nextTick(fn) { queue.push(fn); window.postMessage('process-tick', '*'); }; } return function nextTick(fn) { setTimeout(fn, 0); }; })(); process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.binding = function (name) { throw new Error('process.binding is not supported'); }; // TODO(shtylman) process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; },{}],7:[function(_dereq_,module,exports){ module.exports = _dereq_('css').parse; },{"css":8}],8:[function(_dereq_,module,exports){ exports.parse = _dereq_('./lib/parse'); exports.stringify = _dereq_('./lib/stringify'); },{"./lib/parse":9,"./lib/stringify":13}],9:[function(_dereq_,module,exports){ // http://www.w3.org/TR/CSS21/grammar.html // https://github.com/visionmedia/css-parse/pull/49#issuecomment-30088027 var commentre = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g module.exports = function(css, options){ options = options || {}; /** * Positional. */ var lineno = 1; var column = 1; /** * Update lineno and column based on `str`. */ function updatePosition(str) { var lines = str.match(/\n/g); if (lines) lineno += lines.length; var i = str.lastIndexOf('\n'); column = ~i ? str.length - i : column + str.length; } /** * Mark position and patch `node.position`. */ function position() { var start = { line: lineno, column: column }; return function(node){ node.position = new Position(start); whitespace(); return node; }; } /** * Store position information for a node */ function Position(start) { this.start = start; this.end = { line: lineno, column: column }; this.source = options.source; } /** * Non-enumerable source string */ Position.prototype.content = css; /** * Error `msg`. */ function error(msg) { if (options.silent === true) { return false; } var err = new Error(options.source + ':' + lineno + ':' + column + ': ' + msg); err.reason = msg; err.filename = options.source; err.line = lineno; err.column = column; err.source = css; throw err; } /** * Parse stylesheet. */ function stylesheet() { return { type: 'stylesheet', stylesheet: { rules: rules() } }; } /** * Opening brace. */ function open() { return match(/^{\s*/); } /** * Closing brace. */ function close() { return match(/^}/); } /** * Parse ruleset. */ function rules() { var node; var rules = []; whitespace(); comments(rules); while (css.length && css.charAt(0) != '}' && (node = atrule() || rule())) { if (node !== false) { rules.push(node); comments(rules); } } return rules; } /** * Match `re` and return captures. */ function match(re) { var m = re.exec(css); if (!m) return; var str = m[0]; updatePosition(str); css = css.slice(str.length); return m; } /** * Parse whitespace. */ function whitespace() { match(/^\s*/); } /** * Parse comments; */ function comments(rules) { var c; rules = rules || []; while (c = comment()) { if (c !== false) { rules.push(c); } } return rules; } /** * Parse comment. */ function comment() { var pos = position(); if ('/' != css.charAt(0) || '*' != css.charAt(1)) return; var i = 2; while ("" != css.charAt(i) && ('*' != css.charAt(i) || '/' != css.charAt(i + 1))) ++i; i += 2; if ("" === css.charAt(i-1)) { return error('End of comment missing'); } var str = css.slice(2, i - 2); column += 2; updatePosition(str); css = css.slice(i); column += 2; return pos({ type: 'comment', comment: str }); } /** * Parse selector. */ function selector() { var m = match(/^([^{]+)/); if (!m) return; /* @fix Remove all comments from selectors * http://ostermiller.org/findcomment.html */ return trim(m[0]) .replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '') .replace(/(?:"[^"]*"|'[^']*')/g, function(m) { return m.replace(/,/g, '\u200C'); }) .split(/\s*(?![^(]*\)),\s*/) .map(function(s) { return s.replace(/\u200C/g, ','); }); } /** * Parse declaration. */ function declaration() { var pos = position(); // prop var prop = match(/^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/); if (!prop) return; prop = trim(prop[0]); // : if (!match(/^:\s*/)) return error("property missing ':'"); // val var val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/); var ret = pos({ type: 'declaration', property: prop.replace(commentre, ''), value: val ? trim(val[0]).replace(commentre, '') : '' }); // ; match(/^[;\s]*/); return ret; } /** * Parse declarations. */ function declarations() { var decls = []; if (!open()) return error("missing '{'"); comments(decls); // declarations var decl; while (decl = declaration()) { if (decl !== false) { decls.push(decl); comments(decls); } } if (!close()) return error("missing '}'"); return decls; } /** * Parse keyframe. */ function keyframe() { var m; var vals = []; var pos = position(); while (m = match(/^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/)) { vals.push(m[1]); match(/^,\s*/); } if (!vals.length) return; return pos({ type: 'keyframe', values: vals, declarations: declarations() }); } /** * Parse keyframes. */ function atkeyframes() { var pos = position(); var m = match(/^@([-\w]+)?keyframes */); if (!m) return; var vendor = m[1]; // identifier var m = match(/^([-\w]+)\s*/); if (!m) return error("@keyframes missing name"); var name = m[1]; if (!open()) return error("@keyframes missing '{'"); var frame; var frames = comments(); while (frame = keyframe()) { frames.push(frame); frames = frames.concat(comments()); } if (!close()) return error("@keyframes missing '}'"); return pos({ type: 'keyframes', name: name, vendor: vendor, keyframes: frames }); } /** * Parse supports. */ function atsupports() { var pos = position(); var m = match(/^@supports *([^{]+)/); if (!m) return; var supports = trim(m[1]); if (!open()) return error("@supports missing '{'"); var style = comments().concat(rules()); if (!close()) return error("@supports missing '}'"); return pos({ type: 'supports', supports: supports, rules: style }); } /** * Parse host. */ function athost() { var pos = position(); var m = match(/^@host */); if (!m) return; if (!open()) return error("@host missing '{'"); var style = comments().concat(rules()); if (!close()) return error("@host missing '}'"); return pos({ type: 'host', rules: style }); } /** * Parse media. */ function atmedia() { var pos = position(); var m = match(/^@media *([^{]+)/); if (!m) return; var media = trim(m[1]); if (!open()) return error("@media missing '{'"); var style = comments().concat(rules()); if (!close()) return error("@media missing '}'"); return pos({ type: 'media', media: media, rules: style }); } /** * Parse custom-media. */ function atcustommedia() { var pos = position(); var m = match(/^@custom-media (--[^\s]+) *([^{;]+);/); if (!m) return; return pos({ type: 'custom-media', name: trim(m[1]), media: trim(m[2]) }); } /** * Parse paged media. */ function atpage() { var pos = position(); var m = match(/^@page */); if (!m) return; var sel = selector() || []; if (!open()) return error("@page missing '{'"); var decls = comments(); // declarations var decl; while (decl = declaration()) { decls.push(decl); decls = decls.concat(comments()); } if (!close()) return error("@page missing '}'"); return pos({ type: 'page', selectors: sel, declarations: decls }); } /** * Parse document. */ function atdocument() { var pos = position(); var m = match(/^@([-\w]+)?document *([^{]+)/); if (!m) return; var vendor = trim(m[1]); var doc = trim(m[2]); if (!open()) return error("@document missing '{'"); var style = comments().concat(rules()); if (!close()) return error("@document missing '}'"); return pos({ type: 'document', document: doc, vendor: vendor, rules: style }); } /** * Parse font-face. */ function atfontface() { var pos = position(); var m = match(/^@font-face */); if (!m) return; if (!open()) return error("@font-face missing '{'"); var decls = comments(); // declarations var decl; while (decl = declaration()) { decls.push(decl); decls = decls.concat(comments()); } if (!close()) return error("@font-face missing '}'"); return pos({ type: 'font-face', declarations: decls }); } /** * Parse import */ var atimport = _compileAtrule('import'); /** * Parse charset */ var atcharset = _compileAtrule('charset'); /** * Parse namespace */ var atnamespace = _compileAtrule('namespace'); /** * Parse non-block at-rules */ function _compileAtrule(name) { var re = new RegExp('^@' + name + ' *([^;\\n]+);'); return function() { var pos = position(); var m = match(re); if (!m) return; var ret = { type: name }; ret[name] = m[1].trim(); return pos(ret); } } /** * Parse at rule. */ function atrule() { if (css[0] != '@') return; return atkeyframes() || atmedia() || atcustommedia() || atsupports() || atimport() || atcharset() || atnamespace() || atdocument() || atpage() || athost() || atfontface(); } /** * Parse rule. */ function rule() { var pos = position(); var sel = selector(); if (!sel) return error('selector missing'); comments(); return pos({ type: 'rule', selectors: sel, declarations: declarations() }); } return addParent(stylesheet()); }; /** * Trim `str`. */ function trim(str) { return str ? str.replace(/^\s+|\s+$/g, '') : ''; } /** * Adds non-enumerable parent node reference to each node. */ function addParent(obj, parent) { var isNode = obj && typeof obj.type === 'string'; var childParent = isNode ? obj : parent; for (var k in obj) { var value = obj[k]; if (Array.isArray(value)) { value.forEach(function(v) { addParent(v, childParent); }); } else if (value && typeof value === 'object') { addParent(value, childParent); } } if (isNode) { Object.defineProperty(obj, 'parent', { configurable: true, writable: true, enumerable: false, value: parent || null }); } return obj; } },{}],10:[function(_dereq_,module,exports){ /** * Expose `Compiler`. */ module.exports = Compiler; /** * Initialize a compiler. * * @param {Type} name * @return {Type} * @api public */ function Compiler(opts) { this.options = opts || {}; } /** * Emit `str` */ Compiler.prototype.emit = function(str) { return str; }; /** * Visit `node`. */ Compiler.prototype.visit = function(node){ return this[node.type](node); }; /** * Map visit over array of `nodes`, optionally using a `delim` */ Compiler.prototype.mapVisit = function(nodes, delim){ var buf = ''; delim = delim || ''; for (var i = 0, length = nodes.length; i < length; i++) { buf += this.visit(nodes[i]); if (delim && i < length - 1) buf += this.emit(delim); } return buf; }; },{}],11:[function(_dereq_,module,exports){ /** * Module dependencies. */ var Base = _dereq_('./compiler'); var inherits = _dereq_('inherits'); /** * Expose compiler. */ module.exports = Compiler; /** * Initialize a new `Compiler`. */ function Compiler(options) { Base.call(this, options); } /** * Inherit from `Base.prototype`. */ inherits(Compiler, Base); /** * Compile `node`. */ Compiler.prototype.compile = function(node){ return node.stylesheet .rules.map(this.visit, this) .join(''); }; /** * Visit comment node. */ Compiler.prototype.comment = function(node){ return this.emit('', node.position); }; /** * Visit import node. */ Compiler.prototype.import = function(node){ return this.emit('@import ' + node.import + ';', node.position); }; /** * Visit media node. */ Compiler.prototype.media = function(node){ return this.emit('@media ' + node.media, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}'); }; /** * Visit document node. */ Compiler.prototype.document = function(node){ var doc = '@' + (node.vendor || '') + 'document ' + node.document; return this.emit(doc, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}'); }; /** * Visit charset node. */ Compiler.prototype.charset = function(node){ return this.emit('@charset ' + node.charset + ';', node.position); }; /** * Visit namespace node. */ Compiler.prototype.namespace = function(node){ return this.emit('@namespace ' + node.namespace + ';', node.position); }; /** * Visit supports node. */ Compiler.prototype.supports = function(node){ return this.emit('@supports ' + node.supports, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}'); }; /** * Visit keyframes node. */ Compiler.prototype.keyframes = function(node){ return this.emit('@' + (node.vendor || '') + 'keyframes ' + node.name, node.position) + this.emit('{') + this.mapVisit(node.keyframes) + this.emit('}'); }; /** * Visit keyframe node. */ Compiler.prototype.keyframe = function(node){ var decls = node.declarations; return this.emit(node.values.join(','), node.position) + this.emit('{') + this.mapVisit(decls) + this.emit('}'); }; /** * Visit page node. */ Compiler.prototype.page = function(node){ var sel = node.selectors.length ? node.selectors.join(', ') : ''; return this.emit('@page ' + sel, node.position) + this.emit('{') + this.mapVisit(node.declarations) + this.emit('}'); }; /** * Visit font-face node. */ Compiler.prototype['font-face'] = function(node){ return this.emit('@font-face', node.position) + this.emit('{') + this.mapVisit(node.declarations) + this.emit('}'); }; /** * Visit host node. */ Compiler.prototype.host = function(node){ return this.emit('@host', node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}'); }; /** * Visit custom-media node. */ Compiler.prototype['custom-media'] = function(node){ return this.emit('@custom-media ' + node.name + ' ' + node.media + ';', node.position); }; /** * Visit rule node. */ Compiler.prototype.rule = function(node){ var decls = node.declarations; if (!decls.length) return ''; return this.emit(node.selectors.join(','), node.position) + this.emit('{') + this.mapVisit(decls) + this.emit('}'); }; /** * Visit declaration node. */ Compiler.prototype.declaration = function(node){ return this.emit(node.property + ':' + node.value, node.position) + this.emit(';'); }; },{"./compiler":10,"inherits":15}],12:[function(_dereq_,module,exports){ /** * Module dependencies. */ var Base = _dereq_('./compiler'); var inherits = _dereq_('inherits'); /** * Expose compiler. */ module.exports = Compiler; /** * Initialize a new `Compiler`. */ function Compiler(options) { options = options || {}; Base.call(this, options); this.indentation = options.indent; } /** * Inherit from `Base.prototype`. */ inherits(Compiler, Base); /** * Compile `node`. */ Compiler.prototype.compile = function(node){ return this.stylesheet(node); }; /** * Visit stylesheet node. */ Compiler.prototype.stylesheet = function(node){ return this.mapVisit(node.stylesheet.rules, '\n\n'); }; /** * Visit comment node. */ Compiler.prototype.comment = function(node){ return this.emit(this.indent() + '/*' + node.comment + '*/', node.position); }; /** * Visit import node. */ Compiler.prototype.import = function(node){ return this.emit('@import ' + node.import + ';', node.position); }; /** * Visit media node. */ Compiler.prototype.media = function(node){ return this.emit('@media ' + node.media, node.position) + this.emit( ' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit( this.indent(-1) + '\n}'); }; /** * Visit document node. */ Compiler.prototype.document = function(node){ var doc = '@' + (node.vendor || '') + 'document ' + node.document; return this.emit(doc, node.position) + this.emit( ' ' + ' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit( this.indent(-1) + '\n}'); }; /** * Visit charset node. */ Compiler.prototype.charset = function(node){ return this.emit('@charset ' + node.charset + ';', node.position); }; /** * Visit namespace node. */ Compiler.prototype.namespace = function(node){ return this.emit('@namespace ' + node.namespace + ';', node.position); }; /** * Visit supports node. */ Compiler.prototype.supports = function(node){ return this.emit('@supports ' + node.supports, node.position) + this.emit( ' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit( this.indent(-1) + '\n}'); }; /** * Visit keyframes node. */ Compiler.prototype.keyframes = function(node){ return this.emit('@' + (node.vendor || '') + 'keyframes ' + node.name, node.position) + this.emit( ' {\n' + this.indent(1)) + this.mapVisit(node.keyframes, '\n') + this.emit( this.indent(-1) + '}'); }; /** * Visit keyframe node. */ Compiler.prototype.keyframe = function(node){ var decls = node.declarations; return this.emit(this.indent()) + this.emit(node.values.join(', '), node.position) + this.emit( ' {\n' + this.indent(1)) + this.mapVisit(decls, '\n') + this.emit( this.indent(-1) + '\n' + this.indent() + '}\n'); }; /** * Visit page node. */ Compiler.prototype.page = function(node){ var sel = node.selectors.length ? node.selectors.join(', ') + ' ' : ''; return this.emit('@page ' + sel, node.position) + this.emit('{\n') + this.emit(this.indent(1)) + this.mapVisit(node.declarations, '\n') + this.emit(this.indent(-1)) + this.emit('\n}'); }; /** * Visit font-face node. */ Compiler.prototype['font-face'] = function(node){ return this.emit('@font-face ', node.position) + this.emit('{\n') + this.emit(this.indent(1)) + this.mapVisit(node.declarations, '\n') + this.emit(this.indent(-1)) + this.emit('\n}'); }; /** * Visit host node. */ Compiler.prototype.host = function(node){ return this.emit('@host', node.position) + this.emit( ' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit( this.indent(-1) + '\n}'); }; /** * Visit custom-media node. */ Compiler.prototype['custom-media'] = function(node){ return this.emit('@custom-media ' + node.name + ' ' + node.media + ';', node.position); }; /** * Visit rule node. */ Compiler.prototype.rule = function(node){ var indent = this.indent(); var decls = node.declarations; if (!decls.length) return ''; return this.emit(node.selectors.map(function(s){ return indent + s }).join(',\n'), node.position) + this.emit(' {\n') + this.emit(this.indent(1)) + this.mapVisit(decls, '\n') + this.emit(this.indent(-1)) + this.emit('\n' + this.indent() + '}'); }; /** * Visit declaration node. */ Compiler.prototype.declaration = function(node){ return this.emit(this.indent()) + this.emit(node.property + ': ' + node.value, node.position) + this.emit(';'); }; /** * Increase, decrease or return current indentation. */ Compiler.prototype.indent = function(level) { this.level = this.level || 1; if (null != level) { this.level += level; return ''; } return Array(this.level).join(this.indentation || ' '); }; },{"./compiler":10,"inherits":15}],13:[function(_dereq_,module,exports){ /** * Module dependencies. */ var Compressed = _dereq_('./compress'); var Identity = _dereq_('./identity'); /** * Stringfy the given AST `node`. * * Options: * * - `compress` space-optimized output * - `sourcemap` return an object with `.code` and `.map` * * @param {Object} node * @param {Object} [options] * @return {String} * @api public */ module.exports = function(node, options){ options = options || {}; var compiler = options.compress ? new Compressed(options) : new Identity(options); // source maps if (options.sourcemap) { var sourcemaps = _dereq_('./source-map-support'); sourcemaps(compiler); var code = compiler.compile(node); compiler.applySourceMaps(); var map = options.sourcemap === 'generator' ? compiler.map : compiler.map.toJSON(); return { code: code, map: map }; } var code = compiler.compile(node); return code; }; },{"./compress":11,"./identity":12,"./source-map-support":14}],14:[function(_dereq_,module,exports){ /** * Module dependencies. */ var SourceMap = _dereq_('source-map').SourceMapGenerator; var SourceMapConsumer = _dereq_('source-map').SourceMapConsumer; var sourceMapResolve = _dereq_('source-map-resolve'); var urix = _dereq_('urix'); var fs = _dereq_('fs'); var path = _dereq_('path'); /** * Expose `mixin()`. */ module.exports = mixin; /** * Mixin source map support into `compiler`. * * @param {Compiler} compiler * @api public */ function mixin(compiler) { compiler._comment = compiler.comment; compiler.map = new SourceMap(); compiler.position = { line: 1, column: 1 }; compiler.files = {}; for (var k in exports) compiler[k] = exports[k]; } /** * Update position. * * @param {String} str * @api private */ exports.updatePosition = function(str) { var lines = str.match(/\n/g); if (lines) this.position.line += lines.length; var i = str.lastIndexOf('\n'); this.position.column = ~i ? str.length - i : this.position.column + str.length; }; /** * Emit `str`. * * @param {String} str * @param {Object} [pos] * @return {String} * @api private */ exports.emit = function(str, pos) { if (pos) { var sourceFile = urix(pos.source || 'source.css'); this.map.addMapping({ source: sourceFile, generated: { line: this.position.line, column: Math.max(this.position.column - 1, 0) }, original: { line: pos.start.line, column: pos.start.column - 1 } }); this.addFile(sourceFile, pos); } this.updatePosition(str); return str; }; /** * Adds a file to the source map output if it has not already been added * @param {String} file * @param {Object} pos */ exports.addFile = function(file, pos) { if (typeof pos.content !== 'string') return; if (Object.prototype.hasOwnProperty.call(this.files, file)) return; this.files[file] = pos.content; }; /** * Applies any original source maps to the output and embeds the source file * contents in the source map. */ exports.applySourceMaps = function() { Object.keys(this.files).forEach(function(file) { var content = this.files[file]; this.map.setSourceContent(file, content); if (this.options.inputSourcemaps !== false) { var originalMap = sourceMapResolve.resolveSync( content, file, fs.readFileSync); if (originalMap) { var map = new SourceMapConsumer(originalMap.map); var relativeTo = originalMap.sourcesRelativeTo; this.map.applySourceMap(map, file, urix(path.dirname(relativeTo))); } } }, this); }; /** * Process comments, drops sourceMap comments. * @param {Object} node */ exports.comment = function(node) { if (/^# sourceMappingURL=/.test(node.comment)) return this.emit('', node.position); else return this._comment(node); }; },{"fs":4,"path":5,"source-map":19,"source-map-resolve":18,"urix":29}],15:[function(_dereq_,module,exports){ if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module module.exports = function inherits(ctor, superCtor) { ctor.super_ = superCtor ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); }; } else { // old school shim for old browsers module.exports = function inherits(ctor, superCtor) { ctor.super_ = superCtor var TempCtor = function () {} TempCtor.prototype = superCtor.prototype ctor.prototype = new TempCtor() ctor.prototype.constructor = ctor } } },{}],16:[function(_dereq_,module,exports){ // Copyright 2014 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) void (function(root, factory) { if (typeof _defi_ === "function" && _defi_.amd) { _defi_(factory) } else if (typeof exports === "object") { module.exports = factory() } else { root.resolveUrl = factory() } }(this, function() { function resolveUrl(/* ...urls */) { var numUrls = arguments.length if (numUrls === 0) { throw new Error("resolveUrl requires at least one argument; got none.") } var base = document.createElement("base") base.href = arguments[0] if (numUrls === 1) { return base.href } var head = document.getElementsByTagName("head")[0] head.insertBefore(base, head.firstChild) var a = document.createElement("a") var resolved for (var index = 1; index < numUrls; index++) { a.href = arguments[index] resolved = a.href base.href = resolved } head.removeChild(base) return resolved } return resolveUrl })); },{}],17:[function(_dereq_,module,exports){ // Copyright 2014 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) void (function(root, factory) { if (typeof _defi_ === "function" && _defi_.amd) { _defi_(factory) } else if (typeof exports === "object") { module.exports = factory() } else { root.sourceMappingURL = factory() } }(this, function() { var innerRegex = /[#@] sourceMappingURL=([^\s'"]*)/ var regex = RegExp( "(?:" + "/\\*" + "(?:\\s*\r?\n(?://)?)?" + "(?:" + innerRegex.source + ")" + "\\s*" + "\\*/" + "|" + "//(?:" + innerRegex.source + ")" + ")" + "\\s*$" ) return { regex: regex, _innerRegex: innerRegex, getFrom: function(code) { var match = code.match(regex) return (match ? match[1] || match[2] || "" : null) }, existsIn: function(code) { return regex.test(code) }, removeFrom: function(code) { return code.replace(regex, "") }, insertBefore: function(code, string) { var match = code.match(regex) if (match) { return code.slice(0, match.index) + string + code.slice(match.index) } else { return code + string } } } })); },{}],18:[function(_dereq_,module,exports){ // Copyright 2014 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) // Note: source-map-resolve.js is generated from source-map-resolve-node.js and // source-map-resolve-template.js. Only edit the two latter files, _not_ // source-map-resolve.js! void (function(root, factory) { if (typeof _defi_ === "function" && _defi_.amd) { _defi_(["source-map-url", "resolve-url"], factory) } else if (typeof exports === "object") { var sourceMappingURL = _dereq_("source-map-url") var resolveUrl = _dereq_("resolve-url") module.exports = factory(sourceMappingURL, resolveUrl) } else { root.sourceMapResolve = factory(root.sourceMappingURL, root.resolveUrl) } }(this, function(sourceMappingURL, resolveUrl) { function callbackAsync(callback, error, result) { setImmediate(function() { callback(error, result) }) } function parseMapToJSON(string) { return JSON.parse(string.replace(/^\)\]\}'/, "")) } function resolveSourceMap(code, codeUrl, read, callback) { var mapData try { mapData = resolveSourceMapHelper(code, codeUrl) } catch (error) { return callbackAsync(callback, error) } if (!mapData || mapData.map) { return callbackAsync(callback, null, mapData) } read(mapData.url, function(error, result) { if (error) { return callback(error) } try { mapData.map = parseMapToJSON(String(result)) } catch (error) { return callback(error) } callback(null, mapData) }) } function resolveSourceMapSync(code, codeUrl, read) { var mapData = resolveSourceMapHelper(code, codeUrl) if (!mapData || mapData.map) { return mapData } mapData.map = parseMapToJSON(String(read(mapData.url))) return mapData } var dataUriRegex = /^data:([^,;]*)(;[^,;]*)*(?:,(.*))?$/ var jsonMimeTypeRegex = /^(?:application|text)\/json$/ function resolveSourceMapHelper(code, codeUrl) { var url = sourceMappingURL.getFrom(code) if (!url) { return null } var dataUri = url.match(dataUriRegex) if (dataUri) { var mimeType = dataUri[1] var lastParameter = dataUri[2] var encoded = dataUri[3] if (!jsonMimeTypeRegex.test(mimeType)) { throw new Error("Unuseful data uri mime type: " + (mimeType || "text/plain")) } return { sourceMappingURL: url, url: null, sourcesRelativeTo: codeUrl, map: parseMapToJSON(lastParameter === ";base64" ? atob(encoded) : decodeURIComponent(encoded)) } } var mapUrl = resolveUrl(codeUrl, url) return { sourceMappingURL: url, url: mapUrl, sourcesRelativeTo: mapUrl, map: null } } function resolveSources(map, mapUrl, read, options, callback) { if (typeof options === "function") { callback = options options = {} } var pending = map.sources.length var errored = false var result = { sourcesResolved: [], sourcesContent: [] } var done = function(error) { if (errored) { return } if (error) { errored = true return callback(error) } pending-- if (pending === 0) { callback(null, result) } } resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) { result.sourcesResolved[index] = fullUrl if (typeof sourceContent === "string") { result.sourcesContent[index] = sourceContent callbackAsync(done, null) } else { read(fullUrl, function(error, source) { result.sourcesContent[index] = String(source) done(error) }) } }) } function resolveSourcesSync(map, mapUrl, read, options) { var result = { sourcesResolved: [], sourcesContent: [] } resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) { result.sourcesResolved[index] = fullUrl if (read !== null) { if (typeof sourceContent === "string") { result.sourcesContent[index] = sourceContent } else { result.sourcesContent[index] = String(read(fullUrl)) } } }) return result } var endingSlash = /\/?$/ function resolveSourcesHelper(map, mapUrl, options, fn) { options = options || {} var fullUrl var sourceContent for (var index = 0, len = map.sources.length; index < len; index++) { if (map.sourceRoot && !options.ignoreSourceRoot) { // Make sure that the sourceRoot ends with a slash, so that `/scripts/subdir` becomes // `/scripts/subdir/<source>`, not `/scripts/<source>`. Pointing to a file as source root // does not make sense. fullUrl = resolveUrl(mapUrl, map.sourceRoot.replace(endingSlash, "/"), map.sources[index]) } else { fullUrl = resolveUrl(mapUrl, map.sources[index]) } sourceContent = (map.sourcesContent || [])[index] fn(fullUrl, sourceContent, index) } } function resolve(code, codeUrl, read, options, callback) { if (typeof options === "function") { callback = options options = {} } resolveSourceMap(code, codeUrl, read