CSS vs SASS & LESS

Benchmark created by Awsome Media on


Test runner

Ready to run.

Testing in
TestOps/sec
CSS
(function(scope) {

  // imports

  var api = scope.api;
  var isBase = scope.isBase;
  var extend = scope.extend;

  var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;

  // prototype api

  var prototype = {

    register: function(name, extendeeName) {
      // build prototype combining extendee, Polymer base, and named api
      this.buildPrototype(name, extendeeName);
      // register our custom element with the platform
      this.registerPrototype(name, extendeeName);
      // reference constructor in a global named by 'constructor' attribute
      this.publishConstructor();
    },

    buildPrototype: function(name, extendeeName) {
      // get our custom prototype (before chaining)
      var extension = scope.getRegisteredPrototype(name);
      // get basal prototype
      var base = this.generateBasePrototype(extendeeName);
      // implement declarative features
      this.desugarBeforeChaining(extension, base);
      // join prototypes
      this.prototype = this.chainPrototypes(extension, base);
      // more declarative features
      this.desugarAfterChaining(name, extendeeName);
    },

    desugarBeforeChaining: function(prototype, base) {
      // back reference declaration element
      // TODO(sjmiles): replace `element` with `elementElement` or `declaration`
      prototype.element = this;
      // transcribe `attributes` declarations onto own prototype's `publish`
      this.publishAttributes(prototype, base);
      // `publish` properties to the prototype and to attribute watch
      this.publishProperties(prototype, base);
      // infer observers for `observe` list based on method names
      this.inferObservers(prototype);
      // desugar compound observer syntax, e.g. 'a b c' 
      this.explodeObservers(prototype);
    },

    chainPrototypes: function(prototype, base) {
      // chain various meta-data objects to inherited versions
      this.inheritMetaData(prototype, base);
      // chain custom api to inherited
      var chained = this.chainObject(prototype, base);
      // x-platform fixup
      ensurePrototypeTraversal(chained);
      return chained;
    },

    inheritMetaData: function(prototype, base) {
      // chain observe object to inherited
      this.inheritObject('observe', prototype, base);
      // chain publish object to inherited
      this.inheritObject('publish', prototype, base);
      // chain reflect object to inherited
      this.inheritObject('reflect', prototype, base);
      // chain our lower-cased publish map to the inherited version
      this.inheritObject('_publishLC', prototype, base);
      // chain our instance attributes map to the inherited version
      this.inheritObject('_instanceAttributes', prototype, base);
      // chain our event delegates map to the inherited version
      this.inheritObject('eventDelegates', prototype, base);
    },

    // implement various declarative features
    desugarAfterChaining: function(name, extendee) {
      // build side-chained lists to optimize iterations
      this.optimizePropertyMaps(this.prototype);
      this.createPropertyAccessors(this.prototype);
      // install mdv delegate on template
      this.installBindingDelegate(this.fetchTemplate());
      // install external stylesheets as if they are inline
      this.installSheets();
      // adjust any paths in dom from imports
      this.resolveElementPaths(this);
      // compile list of attributes to copy to instances
      this.accumulateInstanceAttributes();
      // parse on-* delegates declared on `this` element
      this.parseHostEvents();
      //
      // install a helper method this.resolvePath to aid in 
      // setting resource urls. e.g.
      // this.$.image.src = this.resolvePath('images/foo.png')
      this.addResolvePathApi();
      // under ShadowDOMPolyfill, transforms to approximate missing CSS features
      if (hasShadowDOMPolyfill) {
        WebComponents.ShadowCSS.shimStyling(this.templateContent(), name,
          extendee);
      }
      // allow custom element access to the declarative context
      if (this.prototype.registerCallback) {
        this.prototype.registerCallback(this);
      }
    },

    // if a named constructor is requested in element, map a reference
    // to the constructor to the given symbol
    publishConstructor: function() {
      var symbol = this.getAttribute('constructor');
      if (symbol) {
        window[symbol] = this.ctor;
      }
    },

    // build prototype combining extendee, Polymer base, and named api
    generateBasePrototype: function(extnds) {
      var prototype = this.findBasePrototype(extnds);
      if (!prototype) {
        // create a prototype based on tag-name extension
        var prototype = HTMLElement.getPrototypeForTag(extnds);
        // insert base api in inheritance chain (if needed)
        prototype = this.ensureBaseApi(prototype);
        // memoize this base
        memoizedBases[extnds] = prototype;
      }
      return prototype;
    },

    findBasePrototype: function(name) {
      return memoizedBases[name];
    },

    // install Polymer instance api into prototype chain, as needed 
    ensureBaseApi: function(prototype) {
      if (prototype.PolymerBase) {
        return prototype;
      }
      var extended = Object.create(prototype);
      // we need a unique copy of base api for each base prototype
      // therefore we 'extend' here instead of simply chaining
      api.publish(api.instance, extended);
      // TODO(sjmiles): sharing methods across prototype chains is
      // not supported by 'super' implementation which optimizes
      // by memoizing prototype relationships.
      // Probably we should have a version of 'extend' that is 
      // share-aware: it could study the text of each function,
      // look for usage of 'super', and wrap those functions in
      // closures.
      // As of now, there is only one problematic method, so 
      // we just patch it manually.
      // To avoid re-entrancy problems, the special super method
      // installed is called `mixinSuper` and the mixin method
      // must use this method instead of the default `super`.
      this.mixinMethod(extended, prototype, api.instance.mdv, 'bind');
      // return buffed-up prototype
      return extended;
    },

    mixinMethod: function(extended, prototype, api, name) {
      var $super = function(args) {
        return prototype[name].apply(this, args);
      };
      extended[name] = function() {
        this.mixinSuper = $super;
        return api[name].apply(this, arguments);
      }
    },

    // ensure prototype[name] inherits from a prototype.prototype[name]
    inheritObject: function(name, prototype, base) {
      // require an object
      var source = prototype[name] || {};
      // chain inherited properties onto a new object
      prototype[name] = this.chainObject(source, base[name]);
    },

    // register 'prototype' to custom element 'name', store constructor 
    registerPrototype: function(name, extendee) {
      var info = {
        prototype: this.prototype
      }
      // native element must be specified in extends
      var typeExtension = this.findTypeExtension(extendee);
      if (typeExtension) {
        info.extends = typeExtension;
      }
      // register the prototype with HTMLElement for name lookup
      HTMLElement.register(name, this.prototype);
      // register the custom type
      this.ctor = document.registerElement(name, info);
    },

    findTypeExtension: function(name) {
      if (name && name.indexOf('-') < 0) {
        return name;
      } else {
        var p = this.findBasePrototype(name);
        if (p.element) {
          return this.findTypeExtension(p.element.extends);
        }
      }
    }

  };

  // memoize base prototypes
  var memoizedBases = {};

  // implementation of 'chainObject' depends on support for __proto__
  if (Object.__proto__) {
    prototype.chainObject = function(object, inherited) {
      if (object && inherited && object !== inherited) {
        object.__proto__ = inherited;
      }
      return object;
    }
  } else {
    prototype.chainObject = function(object, inherited) {
      if (object && inherited && object !== inherited) {
        var chained = Object.create(inherited);
        object = extend(chained, object);
      }
      return object;
    }
  }

  // On platforms that do not support __proto__ (versions of IE), the prototype
  // chain of a custom element is simulated via installation of __proto__.
  // Although custom elements manages this, we install it here so it's
  // available during desugaring.

  function ensurePrototypeTraversal(prototype) {
    if (!Object.__proto__) {
      var ancestor = Object.getPrototypeOf(prototype);
      prototype.__proto__ = ancestor;
      if (isBase(ancestor)) {
        ancestor.__proto__ = Object.getPrototypeOf(ancestor);
      }
    }
  }

  // exports

  api.declaration.prototype = prototype;

})(Polymer);
ready
SASS
CodeMirror.defineMode("less", function(p) {
  function b(a, b) {
    d = b;
    return a
  }

  function g(a, e) {
    var c = a.next();
    if ("@" == c) return a.eatWhile(/[\w\-]/), b("meta", a.current());
    if ("/" == c && a.eat("*")) return e.tokenize = k, k(a, e);
    if ("<" == c && a.eat("!")) return e.tokenize = l, l(a, e);
    if ("=" == c) b(null, "compare");
    else {
      if ("|" == c && a.eat("=")) return b(null, "compare");
      if ('"' == c || "'" == c) return e.tokenize = q(c), e.tokenize(a, e);
      if ("/" == c) {
        if (a.eat("/")) return e.tokenize = m, m(a, e);
        if ("string" == d || "(" == d) return b("string", "string");
        if (void 0 !=
          e.stack[e.stack.length - 1]) return b(null, c);
        a.eatWhile(/[\a-zA-Z0-9\-_.\s]/);
        if (/\/|\)|#/.test(a.peek() || a.eatSpace() && ")" == a.peek()) || a.eol()) return b("string", "string")
      } else {
        if ("!" == c) return a.match(/^\s*\w*/), b("keyword", "important");
        if (/\d/.test(c)) return a.eatWhile(/[\w.%]/), b("number", "unit");
        if (/[,+<>*\/]/.test(c)) return "=" == a.peek() || "a" == d ? b("string", "string") : "," === c ? b(null, c) : b(null, "select-op");
        if (/[;{}:\[\]()~\|]/.test(c)) {
          if (":" == c) return a.eatWhile(/[a-z\\\-]/), h.test(a.current()) ? b("tag",
            "tag") : ":" == a.peek() ? (a.next(), a.eatWhile(/[a-z\\\-]/), a.current().match(/\:\:\-(o|ms|moz|webkit)\-/) ? b("string", "string") : h.test(a.current().substring(1)) ? b("tag", "tag") : b(null, c)) : b(null, c);
          if ("~" == c) {
            if ("r" == d) return b("string", "string")
          } else return b(null, c)
        } else {
          if ("." == c) {
            if ("(" == d || "string" == d) return b("string", "string");
            a.eatWhile(/[\a-zA-Z0-9\-_]/);
            " " == a.peek() && a.eatSpace();
            return ")" == a.peek() ? b("number", "unit") : b("tag", "tag")
          }
          if ("#" == c) {
            a.eatWhile(/[A-Za-z0-9]/);
            if (4 == a.current().length ||
              7 == a.current().length) {
              if (null != a.current().match(/[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/, !1)) {
                if (a.current().substring(1) != a.current().match(/[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/, !1)) return b("atom", "tag");
                a.eatSpace();
                return /[\/<>.(){!$%^&*_\-\\?=+\|#'~`]/.test(a.peek()) ? b("atom", "tag") : "}" == a.peek() ? b("number", "unit") : /[a-zA-Z\\]/.test(a.peek()) ? b("atom", "tag") : a.eol() ? b("atom", "tag") : b("number", "unit")
              }
              a.eatWhile(/[\w\\\-]/);
              return b("atom", "tag")
            }
            a.eatWhile(/[\w\\\-]/);
            return b("atom", "tag")
          }
          if ("&" == c) return a.eatWhile(/[\w\-]/),
          b(null, c);
          a.eatWhile(/[\w\\\-_%.{]/);
          if ("string" == d) return b("string", "string");
          if (null != a.current().match(/(^http$|^https$)/)) return a.eatWhile(/[\w\\\-_%.{:\/]/), b("string", "string");
          if ("<" == a.peek() || ">" == a.peek() || "+" == a.peek()) return b("tag", "tag");
          if (/\(/.test(a.peek())) return b(null, c);
          if ("/" == a.peek() && void 0 != e.stack[e.stack.length - 1]) return b("string", "string");
          if (a.current().match(/\-\d|\-.\d/)) return b("number", "unit");
          if (/\/|[\s\)]/.test(a.peek() || a.eol() || a.eatSpace() && "/" == a.peek()) && -1 !== a.current().indexOf(".")) {
            if ("{" == a.current().substring(a.current().length - 1, a.current().length)) return a.backUp(1), b("tag", "tag");
            a.eatSpace();
            return /[{<>.a-zA-Z\/]/.test(a.peek()) || a.eol() ? b("tag", "tag") : b("string", "string")
          }
          if (a.eol() || "[" == a.peek() || "#" == a.peek() || "tag" == d) return "{" == a.current().substring(a.current().length - 1, a.current().length) && a.backUp(1), b("tag", "tag");
          if ("compare" == d || "a" == d || "(" == d) return b("string", "string");
          if ("|" == d || "-" == a.current() || "[" == d) return "|" == d ? b("tag",
            "tag") : b(null, c);
          if (":" == a.peek()) {
            a.next();
            if (c = ":" == a.peek() ? !0 : !1) a.backUp(1);
            else {
              var f = a.pos,
                g = a.current().length;
              a.eatWhile(/[a-z\\\-]/);
              var n = a.pos;
              if (null != a.current().substring(g - 1).match(h)) return a.backUp(n - (f - 1)), b("tag", "tag");
              a.backUp(n - (f - 1))
            }
            return c ? b("tag", "tag") : b("variable", "variable")
          }
          return "font-family" === e.stack[e.stack.length - 1] ? b(null, null) : "{" === e.stack[e.stack.length - 1] || "select-op" === d || "rule" === e.stack[e.stack.length - 1] && "," === d ? b("tag", "tag") : b("variable", "variable")
        }
      }
    }
  }

  function m(a, e) {
    a.skipToEnd();
    e.tokenize = g;
    return b("comment", "comment")
  }

  function k(a, e) {
    for (var c = !1, d; null != (d = a.next());) {
      if (c && "/" == d) {
        e.tokenize = g;
        break
      }
      c = "*" == d
    }
    return b("comment", "comment")
  }

  function l(a, e) {
    for (var c = 0, d; null != (d = a.next());) {
      if (2 <= c && ">" == d) {
        e.tokenize = g;
        break
      }
      c = "-" == d ? c + 1 : 0
    }
    return b("comment", "comment")
  }

  function q(a) {
    return function(e, c) {
      for (var d = !1, h; null != (h = e.next()) && (h != a || d);) d = !d && "\\" == h;
      d || (c.tokenize = g);
      return b("string", "string")
    }
  }
  var r = p.indentUnit,
    d, h = /(^\: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$|^\:link|^\:visited$|^\:active$|^\:hover$|^\:focus$|^\:target$|^\:lang$|^\:enabled^\:disabled$|^\:checked$|^\:first\-line$|^\:first\-letter$|^\:before$|^\:after$|^\:not$|^\:required$|^\:invalid$)/;
  return {
    startState: function(a) {
      return {
        tokenize: g,
        baseIndent: a || 0,
        stack: []
      }
    },
    token: function(a, b) {
      if (a.eatSpace()) return null;
      var c = b.tokenize(a, b),
        f = b.stack[b.stack.length - 1];
      "hash" == d && "rule" == f ? c = "atom" : "variable" == c && ("rule" == f ? c = null : f && "@media{" != f || (c = "when" == a.current() ? "variable" : /[\s,|\s\)|\s]/.test(a.peek()) ? "tag" : d));
      "rule" == f && /^[\{\};]$/.test(d) && b.stack.pop();
      "{" == d ? "@media" == f ? b.stack[b.stack.length - 1] = "@media{" : b.stack.push("{") : "}" == d ? b.stack.pop() : "@media" == d ? b.stack.push("@media") :
        "font-family" === a.current() ? b.stack[b.stack.length - 1] = "font-family" : "{" == f && "comment" != d && "tag" !== d ? b.stack.push("rule") : ":" === a.peek() && null === a.current().match(/@|#/) && (c = d);
      return c
    },
    indent: function(a, b) {
      var c = a.stack.length;
      /^\}/.test(b) && (c -= "rule" == a.stack[a.stack.length - 1] ? 2 : 1);
      return a.baseIndent + c * r
    },
    electricChars: "}"
  }
});
CodeMirror.defineMIME("text/x-less", "less");
CodeMirror.mimeModes.hasOwnProperty("text/css") || CodeMirror.defineMIME("text/css", "less");
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.

  • Revision 1: published by Awsome Media on