ASP.NET, ASP.MVC - LESS CSS


Páči sa mi myšlienka Less CSS, preto som sa rozhodol urobiť alternatívu pre ASP.NET a ASP.MVC. Na internete som hľadal niečo podobné pre .NET, ale nič som poriadneho, malého a kompaktného nenašiel. A čo si človek nenapíše, to nemá.

 


Moja predstava Less

@border-radius(radius) { -moz-border-radius:@radius; border-radius:@radius; -webkit-border-radius:@radius }
@shadow(x,y,size,color) { -moz-box-shadow:@x @y @size @color; -webkit-box-shadow:@x @y @size @color; box-shadow:@x @y @size @color }
@red { color: Red }
@font-arial { font:normal 12px Arial }
@font-arial { font:normal 12px Arial }
@size { 12px }

div { @font-arial-neviem; @shadow(1px,1px,2px,#303030); }
p { @border-radius(5px); }
span { @red; @shadow(1px,1px,2px,rgba(1,1,1,0.5)); }
table { font-size:@size; }

Výsledok spracovania

div { font:normal 12px Arial; -moz-box-shadow:1px 1px 2px #303030;-webkit-box-shadow:1px 1px 2px #303030;box-shadow:1px 1px 2px #303030; }
p { -moz-border-radius:5px;border-radius:5px;-webkit-border-radius:5px; }
span { color: Red; -moz-box-shadow:1px 1px 2px rgba(1,1,1,0.5);-webkit-box-shadow:1px 1px 2px rgba(1,1,1,0.5);box-shadow:1px 1px 2px rgba(1,1,1,0.5); }
table { font-size:12px; }
Trieda obsahuje vlastnosť na zmenšenie veľkosti CSS, tu je výsledok:
div{font:normal 12px Arial;-moz-box-shadow:1px 1px 2px #303030;-webkit-box-shadow:1px 1px 2px #303030;box-shadow:1px 1px 2px #303030;}p{-moz-border-radius:5px;border-radius:5px;-webkit-border-radius:5px;}span{color:Red;-moz-box-shadow:1px 1px 2px rgba(1,1,1,0.5);-webkit-box-shadow:1px 1px 2px rgba(1,1,1,0.5);box-shadow:1px 1px 2px rgba(1,1,1,0.5);}table{font-size:12px;}

Trieda na spracovanie CSS

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

public class Less
{
    private class LessValue
    {
        private class LessParam
        {
            public string Name { get; set; }
            public string Value { get; set; }
        }

        public int Index { get; set; }
        public string Value { get; set; }
        public string Name { get; set; }
        public bool IsVariable { get; set; }
        public bool IsFunction { get; set; }
        public bool IsProblem { get; set; }

        private string[] Split(string value)
        {
            var index = 0;
            var l = new List<string>(5);
            var skip = false;
            var sb = new StringBuilder();

            while (index < value.Length)
            {
                var c = value[index++];

                if (c == '(' || c == '{')
                    skip = true;
                else if (c == ')' || c == '}')
                    skip = false;

                if (!skip && c == ',')
                {
                    l.Add(sb.ToString());
                    sb.Clear();
                }
                else
                    sb.Append(c);
            }

            if (sb.Length > 0)
                l.Add(sb.ToString());

            return l.ToArray();
        }

        public string GetValue(LessValue less)
        {
            if (less == null)
                return "";

            if (IsVariable)
                return "";

            var value = "";

            if (IsFunction)
            {
                var param = new List<LessParam>(5);

                var beg = less.Value.IndexOf('(') + 1;
                var end = less.Value.LastIndexOf(')');

                foreach (var p in less.Value.Substring(beg, end - beg).Split(','))
                {
                    param.Add(new LessParam() { Name = p.Trim() });
                }

                beg = Value.IndexOf('(') + 1;
                end = Value.LastIndexOf(')');

                var index = 0;
                foreach (var p in Split(Value.Substring(beg, end - beg)))
                    param[index++].Value = p.Trim();

                beg = less.Value.IndexOf('{') + 1;
                end = less.Value.LastIndexOf('}');
                index = 0;

                var sb = new System.Text.StringBuilder();

                foreach (var p in less.Value.Substring(beg, end - beg).Split(';'))
                {
                    value = p.Trim();
                    if (string.IsNullOrEmpty(value))
                        continue;

                    foreach (var j in param)
                        value = value.Replace("@" + j.Name, j.Value);

                    if (sb.Length > 0)
                        sb.Append(';');
                    sb.Append(value);
                }
                return sb.ToString();
            }

            value = less.Value.Substring(less.Name.Length).Trim();

            if ((value[0] == '{') && (value[value.Length - 1] == '}'))
                value = value.Substring(1, value.Length - 2).Trim();

            return value;
        }
    }

    private static LessValue getValue(LessValue prev, string value)
    {
        var index = 0;
        if (prev != null)
            index = prev.Index + prev.Value.Length;

        var beg = false;
        var copy = false;

        var param = 0;
        var val = 0;

        var sb = new System.Text.StringBuilder();
        var less = new LessValue();

        while (index < value.Length)
        {
            var c = value[index];

            if (c == '@' && !less.IsFunction)
            {
                beg = true;
                copy = true;
                less.Index = index;
            }
            else if (beg)
            {
                var charindex = Convert.ToInt32(c);

                if (charindex == 40)
                    param++;
                else if (charindex == 41)
                    param--;

                var next = false;
                if (charindex == 123)
                {
                    if (val == 0)
                        less.IsVariable = true;
                    val++;
                    next = true;
                }
                else if (charindex == 125)
                {
                    if (val == 0)
                    {
                        index++;
                        continue;
                    }
                    val--;
                    next = true;
                }

                if (charindex == 32 || charindex == 41)
                {
                    next = true;
                }
                else if (param == 0 && val == 0 && !next)
                {
                    next = (charindex >= 65 && charindex <= 90) || (charindex >= 97 && charindex <= 122) || charindex == 45;
                }
                else if (param > 0 && val == 0)
                {
                    next = charindex != 41;
                    less.IsFunction = true;
                }
                else if (val > 0 && param == 0)
                {
                    next = true;
                }
                copy = next;
            }

            if (beg && copy)
            {
                sb.Append(c);
            }
            else if (beg)
            {
                if (copy)
                    sb.Append(c);

                less.Value = sb.ToString().Trim();

                if (less.IsFunction)
                    less.Name = less.Value.Substring(0, less.Value.IndexOf('(')).Trim();
                else if (less.IsVariable)
                    less.Name = less.Value.Substring(0, less.Value.IndexOf('{')).Trim();
                else
                    less.Name = less.Value.Trim();

                if (less.Name == "@font-face")
                    less.IsProblem = true;

                return less;
            }
            index++;
        }
        return null;
    }

    public static string Compile(string CSS, bool minify)
    {
        var l = new List<LessValue>(10);

        var less = getValue(null, CSS);
        if (less != null)
        {
            l.Add(less);
            while (less != null)
            {
                less = getValue(less, CSS);
                if (less != null)
                    l.Add(less);
            }
        }

        if (l.Count > 0)
        {
            var variables = l.Where(n => n.IsVariable && !n.IsProblem).ToList();

            foreach (var m in variables)
                CSS = CSS.Replace(m.Value, "");

            foreach (var m in l.Where(n => !n.IsVariable))
            {
                var value = m.GetValue(variables.Where(n => n.Name == m.Name).FirstOrDefault());
                CSS = CSS.Replace(m.Value, value);
            }
        }

        if (minify)
        {
            var reg1 = new Regex(@"\n|\s{2,}", System.Text.RegularExpressions.RegexOptions.Compiled);
            var reg2 = new Regex(@"\s?\{\s{1,}");
            var reg3 = new Regex(@"\s?\}\s{1,}");
            var reg4 = new Regex(@"\s?\:\s{1,}");
            var reg5 = new Regex(@"\s?\;\s{1,}");
            return reg5.Replace(reg4.Replace(reg3.Replace(reg2.Replace(reg1.Replace(CSS, ""), "{"), "}"), ":"), ";");
        }
        return CSS.Trim();
    }
}
Less.txt 1 kB


Handler pre ASP.NET a ASP.MVC

Posledná vec čo treba v Handleri urobiť je nastaviť Cacheovanie.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Handlers
{
    public class CSS : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            var path = "~" + context.Request.RawUrl.Replace('/', '\\');

            context.Response.ContentType = "text/css";
            context.Response.ContentEncoding = System.Text.Encoding.UTF8;

            if (System.IO.File.Exists(context.Server.MapPath(path)))
                context.Response.BinaryWrite(System.Text.Encoding.UTF8.GetBytes(Less.Compile(System.IO.File.ReadAllText(context.Server.MapPath(path)), true)));
            else
                context.Response.StatusCode = 404;
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

Web.config

<system.webServer> 
    <handlers>
        <add name="CSS" verb="*" path="/styles/*.css" type="Handlers.CSS" />
    </handlers>

 
Peter Širka
pred 4 mesiacmi, 17.01.2012

Diskusia (6)

1
Neviem kde si hladal na nugete je toho kopa (http://www.dotlesscss.org/, http://getcassette.net/, tiez je moznost pouzit priamo js implementaciu).

A k tvojej implementacii, nevidim tam ani dedicnost ani kniznice na pracu s farbou, atd. Okrem toho to hlavne, teda podporu verzionovania s eternal cache by si mohol prihodit:)

A pokial sa jedna o mna, viac sa mi paci scss a pouzivam vs tool, ktory css skompiluje len raz http://visualstudiogallery.msdn.microsoft.com/2b96d16a-c986-4501-8f97-8008f9db141a
 
vlko, pred 4 mesiacmi
2
Vlko potreboval som niečo malé a kompaktné. Môj kód si vieš krásne ohýbať a keď sa niečo pokazí alebo nebude fungovať, jednoducho to opravíš. Chceš dorobiť cache? V mojom kóde nie je problém.. Chceš dorobiť verziovanie? V mojom kóde nie je problém...

Môj nadpis "Moja predstava Less" hovorí o všetkom, potreboval som doslova replacovací nástroj, lebo písať zakaždým -moz, -webkit, -ms bolo na obtiaž. Myslím si, že vymýšľať scriptovanie v CSS, dedičnosť, podmienky je naozaj na hlavu.... (viď. http://lesscss.org/). Robím weby veľmi dlho a nepotreboval som to a ani dúfam potrebovať nebudem.

Dušan mi akurát posielal pred hodinou getcassette.net a ako som mu napísal, vypadá to zaujímavo.
PS: ale ďakujem za komentár.
 
Peter Širka, pred 4 mesiacmi
3
sorry, nemyslel som dedicnost ale nested rules, ktore su klucovou vlastnostou less/sass a podla mna ich nepotrebujes iba preto, ze ich nepoznas, pretoze vedia brutalne zprehladnit citanie less/sass a najma jednoduchsie identifikovat opakujuce sa casti, co opat vedie k cistejsiemu css.
 
vlko, pred 4 mesiacmi
4
Nejake komentare si mohol do toho kodu dat :)

takyto kod by sa mi veru nechcelo ohybat, pokial som ho nepisal ja :)
 
Liero, pred 4 mesiacmi
5
Liero načo commenty? Však to má 260 riadkov. Človek, ktorý si to otvorí vo Visual Studio hneď pochopí o čo sa jedná. Je to jednoduchý parser.
 
Peter Širka, pred 4 mesiacmi
6
Ja pouzivam sass (je kopa extensions pre VS).
 
Anonym, pred 4 mesiacmi

Nevyplnil si všetky povinné políčka alebo si ich vyplnil chybne. Neojebávaj môj systém.
Skontroluj či si zadal meno a komentár.
Tvoj komentár bol úspešne odoslaný.
Ďakujem.

Meno:

Komentár:

Ďalšie blogy
Dynamické vytvorenie subdomény v ASP.NET / MVC
Dynamické vytvorenie subdomény v ASP.NET / MVC
Bolo to už dávno, čo nás na firme žralo, že naša konkurencia vedela riešiť dynamicky vytvárenie subdomén alebo domén 3 úrovne. Google ich lepšie indexoval (tak sa nám to zdalo) a riešenie prišlo až v IIS 7.
Prečítať 9
ASP.MVC - Session troška inak a prihlásený užívateľ
ASP.MVC - Session troška inak a prihlásený užívateľ
V dávnej minulosti som používal vstavaného Session providera. Raz mi praskli nervy a rozhodol som sa experimentovať. Stálo to za to. Ten kto neexperimentuje ten nemá a ja experimentujem len pre moje potreby.
Prečítať 21
C# (2) funkcie na ktoré som skutočne pyšný
C# (2) funkcie na ktoré som skutočne pyšný
Každý kóder určite vymyslel nejaký kód, na ktorý je pyšný. Ja som pyšný na 2 funkcie, ktoré používam v našich projektoch denno-denne. Niekomu sa budú zdať smiešne, no pre mňa sú jedinečné.
Prečítať 14
ASP.MVC, ASP.NET Facebook like count
ASP.MVC, ASP.NET Facebook like count
Jednoduchý kód na zistenie počtu like-kov z Facebooku na zadanú URL adresu. Je možné získať ešte ďalšie zaujímavé počty, napríklad počet zdielaní, počet komentárov, atď..
Prečítať
jQuery webcam plugin / ASP.NET, ASP.MVC
jQuery webcam plugin / ASP.NET, ASP.MVC
Pre náš webový projekt som použil jQuery webcam plugin, ktorý spustí Flash animáciu a odfotí užívateľa. Následne spracovaný obrázok odošle na server. Problém je, že odosiela data po pixeli.
Prečítať