How to replace plain URLs with links? (C# and Javascript)

Idea: In Suppo project we want to replace URLs in chat conversations by HTML links. Yes the first thing was to use google to find some recommendations or regular expressions BUT: On stackoverflow in article http://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links is written:

“First off, rolling your own regexp to parse URLs is a terrible idea. You must imagine this is a common enough problem that someone has written, debugged and tested a library for it, according to the RFCs. URIs are complex  … and there is multiple recommended libraries in Javascript (expect similar libraries in C#) like linkifyjs, anchorme.js, autolinker.js … OK so where is the problem?

The problem for me is that all of this libraries are huge … >30KB minified.

Why?

Because we are developing livechat window (Suppo) which is attached to every page of our clients and +30KB of javascript code is not acceptable when our solution has 63KB at all (included the core of jQuery library)

So what next?

Back to beginning and compose simple regular expression (is not so important to work on every URLs … suffice many of them)

C#:

public static string Linkify(this string text)
{
    // www|http|https|ftp|news|file
    text = Regex.Replace(
        text,
        @"((www\.|(http|https|ftp|news|file)+\:\/\/)[&#95;.a-z0-9-]+\.[a-z0-9\/&#95;:@=.+?,##%&~-]*[^.|\'|\# |!|\(|?|,| |>|<|;|\)])",
        "<a href=\"$1\" target=\"_blank\">$1</a>",
        RegexOptions.IgnoreCase)
        .Replace("href=\"www", "href=\"http://www");

    // mailto
    text = Regex.Replace(
        text,
        @"(([a-zA-Z0-9_\-\.])+@[a-zA-Z\ ]+?(\.[a-zA-Z]{2,6})+)",
        "<a href=\"mailto:$1\">$1</a>",
        RegexOptions.IgnoreCase);

    return text;
}

Javascript:

// www|http|https|ftp|news|file
text = text.replace(
    /((www\.|(http|https|ftp|news|file)+\:\/\/)[&#95;.a-z0-9-]+\.[a-z0-9\/&#95;:@=.+?,##%&~-]*[^.|\'|\# |!|\(|?|,| |>|<|;|\)])/gim,
    '<a href="$1" target="_blank">$1</a>');

// mailto
text = text.replace(
    /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim,
    '<a href="mailto:$1">$1</a>');

It’s simple: first part of code find www|http|https|ftp|news|file and replace it by <a href=”http(s):// and second part find email address and replace it by <a href=”mailto::.
That is all and it's less than 1KB of code.

Error 404 for .axd files (WebResource.axd & ScriptResource.axd)

If you’ve got a similar error as on following image:

ScriptResource.axd error 404

that your script resources related with components like UpdatePanel or ScriptManager are not working correctly, think about routing. It may by caused by many other reasons as wrote on similar blog post across the web e.g. wrong version of related System.Web.Extensions.dll or wrong IIS configuration or incorrect handlers defined in web.config. BUT when you are working in hybrid ASP.NET application where are .aspx + .chtml pages together (old Web Forms and new MVC) the problem will probably be routing.

Check and correct your RouteConfig.cs as on following image:

Ignore .axd Resources in RouteConfig.cs

public static void RegisterRoutes(RouteCollection routes)
{
	routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
	
	// Default MVC routing
    routes.MapRoute(
       name: "D1",
       url: "{controller}/{action}/{id}",
       defaults: new { controller = "Default", action = "Index", id = UrlParameter.Optional }
    );
}

 

Enjoy!

ASP.NET- Simple localization with free tool (Zeta Resource Editor)

When we are writing every application (software), we are localizing the resources. Every text information writing to the .resx files as follows :

Text resources in .resx filesCommonly we are using 2 languages (English as default and Slovak as optional, because Slovak is our motherly language). So in this case we need 2 resource files for as follows:

Two resource files

And what was the issue? The issue is effectiveness of translating from one language to another. This issue is in visual studio little bit complicated, because you need to synchronize every keys from one file to another. And for this purpose we are using cool tool called Zeta resource editor. Look at the following picture:
Zeta resource editor in action

Zeta resource editor provide you edit values for particular key very confortable on one screen (on one line). And the best feature is the finding of missing keys. You can easily add new value for particular missing key.
Zeta resource editor - missing keys

Is really useful in situations, when your resource files are not sync. and some keys missing in both files.

ASP.NET MVC - the value set on the field does not correspond to the value in the Model - what’s wrong?

While developing our brand new LiveChat called ”Suppo.biz”, I noticed interesting behaviour, what I never noticed before. In the functions like @Html.EditorFor, @Html.TextBoxFor … or @Html.TextArea, @Html.TextBox … When you do AJAX “postback” of your form like this:

			@using (Ajax.BeginForm(new AjaxOptions { HttpMethod = "Post", InsertionMode = InsertionMode.Replace, UpdateTargetId = "FormAjaxContainer" }))
			{
				@Html.AntiForgeryToken()
				
				<div id="FormAjaxContainer">

					<div class="form-group">
						@Html.LabelFor(model => model.WidgetThemeColor, htmlAttributes: new { @class = "control-label col-md-2" })
						<div class="col-md-10">
							@Html.ValidationMessageFor(model => model.WidgetThemeColor, "", new { @class = "text-danger" })
							<div class="input-group BGColorPicker">
								<span class="input-group-addon"><i></i></span>
								@Html.EditorFor(model => model.WidgetThemeColor, new { htmlAttributes = new { @class = "form-control" } })
							</div>
						</div>
					</div>

				</div>
			}

with value “A” on property WidgetThemeColor and on the server you change the value to value “B” you still see on the input field the value “A”. Its caused by interesting behaviour of the helper function of all controls like @Html.EditorFor, @Html.TextBox …
They first use the value of the POST request and after that the value in the model. This means that even if you modify the value of the model in your controller action if there is the same variable in the POST request your modification will be ignored and the POSTed value will be used. When you want to apply your new value from controller, you need to do this:
 
ModelState.Clear();

Or for particular property by this statement :
ModelState.Remove("WidgetThemeColor");

And the POSTed value will be ignored while rendering of the View or PartialView.

Bootstrap responsive table

When I started to decorate the table by bootstrap classes like table, table-hover, table-condensed, table-nomargin, table-striped… I expected that bootstrap will have some useful class for awesome responsive behaviour, but NO. Then … I must created some custom solution and here is :

I want to rendered table like this :

Bootstrap table with standard pc resolution

to something like this:

Bootstrap table with mobile resolution

The solution is really simple. We need to add only one class to table called e.g. table-specialresponsive and specify special attribute e.g. data-title per cell for new column header which will be displayed in row, when table header th-s will be hidden:

<table class="table table-striped table-specialresponsive">
            <thead>
                <tr>
                    <th>Column 1</th>
                    <th>Column 2</th>
                    <th>Column 3</th>
                    <th>Column 4</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td data-title="Column 1">Value 1-1</td>
                    <td data-title="Column 2">Value 1-2</td>
                    <td data-title="Column 3">Value 1-3</td>
                    <td data-title="Column 4">Value 1-4</td>
                </tr>
                <tr>
                    <td data-title="Column 1">Value 2-1</td>
                    <td data-title="Column 2">Value 2-2</td>
                    <td data-title="Column 3">Value 2-3</td>
                    <td data-title="Column 4">Value 2-4</td>
                </tr>
                <tr>
                    <td data-title="Column 1">Value 3-1</td>
                    <td data-title="Column 2">Value 3-2</td>
                    <td data-title="Column 3">Value 3-3</td>
                    <td data-title="Column 4">Value 3-4</td>
                </tr>
            </tbody>
        </table>

Plus we need some CSS code. Here is:

@media only screen and (max-width: 768px) {

    .table-specialresponsive > thead,
    .table-specialresponsive > tbody,
    .table-specialresponsive > tbody > tr,
    .table-specialresponsive > thead > tr,
    .table-specialresponsive > tfoot > tr,
    .table-specialresponsive > tbody > tr > td,
    .table-specialresponsive > thead > tr > td,
    .table-specialresponsive > tfoot > tr > td,
    .table-specialresponsive > tbody > tr > th,
    .table-specialresponsive > thead > tr > th,
    .table-specialresponsive > tfoot > tr > th
    {
        display: block;
    }

    .table-specialresponsive thead tr {
        position: absolute;
        top: -9999px;
        left: -9999px;
    }

    .table-specialresponsive tr {
        border-top: 1px solid #ccc;
    }

    .table-specialresponsive > tbody > tr > td,
    .table-specialresponsive > tfoot > tr > td {
        border: none;
        position: relative;
        padding-left: 50%;
        white-space: normal;
        text-align: left;
    }

    .table-specialresponsive > tbody > tr > td:before,
    .table-specialresponsive > tfoot > tr > td:before {
        position: absolute;
        left: 6px;
        width: 50%;
        padding-right: 10px;
        white-space: nowrap;
        text-align: left;
        font-weight: bold;
        content: attr(data-title);
    }
}

Responsive table is applicable for screen resolution less then 768 pixels .. and maybe interesting is last line content: attr(data-title) which specify the content which will be apply before every cell.

That's it

Customizácia jQuery.Validate pre bootstrap

Názov tohto príspevku som prepisoval snáď 10 krát. Pravdepodobne nie je podľa neho jasné o čo pôjde, tak to skúsim objasniť: Ak chcete validovať formulárové prvky v ASP.NET MVC a na ich design používate Bootstrap, a súčasne na validáciu na klientskej strane používate javascriptovú knižnicu jQuery.Validate tak sa určite dostanete do rovnakej situácie ako ja. jQuery Validate pridáva css triedy s názvom “input-validation-error” a “valid” priamo do input elementu (alebo iných elementov formulára) čo je síce pekné, ale nechce sa mi dizajnovať niečo, čo už bootstrap vyriešil za mňa. Ak chceme použiť korektne knižnicu bootstrap, tak tá to potrebuje mať povedané v inom formáte. (Formát validného/nevalidného prvku nájdete na bootstrap stránke v sekcii Validation states). Čo s tým? ideálne toto:

jQuery.validator.setDefaults({
    highlight: function (element, errorClass, validClass) {
        if (element.type === 'radio') {
            this.findByName(element.name).addClass(errorClass).removeClass(validClass);
        } else {
            $(element).addClass(errorClass).removeClass(validClass);
            $(element).closest('.form-group').removeClass('has-success').addClass('has-error');
        }
    },
    unhighlight: function (element, errorClass, validClass) {
        if (element.type === 'radio') {
            this.findByName(element.name).removeClass(errorClass).addClass(validClass);
        } else {
            $(element).removeClass(errorClass).addClass(validClass);
            $(element).closest('.form-group').removeClass('has-error').addClass('has-success');
        }
    }
});

Upraviť metódy highligh a unhighligh uvedeným spôsobom. (aby pridávali triedy has-success a has-error do nadradeného DIV elementu s triedou form-group).

Error code 404 pre fonty vo formáte .woff2

Browser Mozilla Firefox Developer Edition v.38 ako developerských prehliadač podporuje najnovšie features zo sveta web technológií. Toto pravidlo platí aj pre fonty v najnovšom formáte .woff2. V našom prípade išlo o fonty od bootstrap-u s názvom glyphicons. Browser nám hlásil error code 404 –  File not found.fError 404 for .woff2 file extension

Dané fonty sa samozrejme na webovom servery nachádzali. Problém je, že webový server (v tomto prípade IIS v8.5) nepozná túto koncovku (je pre neho jednoducho nová). Tak mu to musíme povedať pre danú aplikáciu nasledovne:

<system.webServer>
    <staticContent>
    	<mimeMap fileExtension=".woff2" mimeType="application/octet-stream" />
    </staticContent>       
</system.webServer>

Samozrejme túto koncovku vieme pridať aj globálne pre všetky weby na našom servery cez IIS manager, ikonka MIME Types:

IIS MIME types definition

Boostrap - @media(min-width vs. max-width)

Ak pracujeme s CSS knižnicou bootstrap, skôr či neskôr sa dostaneme do situácie, kedy potrebujeme nastaviť CSS štýl nejakému komponentu/tagu … podľa rôznej veľkosti obrazovky prehliadača. Slúži na to kľúčové slovo @media. Bootstrap rozlišuje 4 základné veľkosti (nájdeme ich aj vo vnútri knižnice):

.container {
}

@media (min-width: 768px) {
    .container {
        width: 750px;
    }
}

@media (min-width: 992px) {
    .container {
        width: 970px;
    }
}

@media (min-width: 1200px) {
    .container {
        width: 1170px;
    }
}

Základné veľkosti podľa bootstrap-u sú teda štyri:

  1. veľkosť do 768 pixelov
  2. veľkosť od 768 do 992 px
  3. veľkosť od 992 do 1200 px
  4. veľkosť nad 1200 pixelov

Toto nie je žiadne tajomstvo. No týmto blog postom by som chcel upozorniť na jednu drobnosť. A to použitie reverzného princípu pomocou @media (max-width: x). Definovanie štýlu nie od minimálnej šírky ale do maximálnej šírky. Tu treba dať pozor na jednu vec a tou je jeden pixel. Ak definujeme štýl od minimálnej šírky pomocou min-width:x tak k nemu opačná definícia pomocou max-width je max-width:x-1. Pre úvodný kus kódu by to bolo nasledovne:

@media (max-width: 767px) {
    /* Custom definitions ...*/
}

@media (max-width: 991px) {
    /* Custom definitions ...*/
}

@media (max-width: 1199px) {
    /* Custom definitions ...*/
}

A ešte jedna poznámka na záver:

  • @media (max-width: 767px) platí pre šírku od 0 do 767 pixelov 
  • @media (min-width: 768px) platí pre šírku od 768 pixelov a viac

Optimalizácia webu Turingion.com

Pre tieto účely – optimalizácia html stránok - je veľmi užitočný tool priamo od google s názvom Webmaster tool a konkrétne jeho súčasť PageSpeed Insights. Ten vie prebehnúť požadovanú url adresu a poskytnúť nielen výslednú štatistiku, ale aj návrhy ako dané nedostatky riešiť.

Turingion.com on PageSpeed Insights

Tie najdôležitejšie nedostatky sme riešili nasledovne:

1. Minifikácia Css a Javascript súborov

Minifikácia súborov Css,Js ale napr. aj HTML je vo Visual studiu skutočne jednoduchá v spojení s toolom s názvom Web Essentials. Stačí v Solution explorer nájsť požadovaný súbor a v kontextovej ponuke vybrať minifikáciu nasledovne:

Minifikácia CSS súboru pomocou Web Essentials

Tool vygeneruje rovnomenný súbor s koncovkou .min.css, resp. .min.js do rovnakého priečinka a zavesí ho ako sub-node daného súboru. Web essentials je veľmi užitočný tool Palec smerujúci nahor.
Takto minifikované súbory sme ešte pomocou tzv bundlingu (referenčná kližnica System.Web.Optimization) zabalili do jedného výstupného súboru:

public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            // CSS
            bundles.Add(new StyleBundle("~/Styles/Css").Include(
                "~/Styles/Bootstrap.min.css",
                "~/Styles/Fonts.min.css",
                "~/Styles/font-awesome.min.css",
                "~/Styles/font-iconpack.min.css",
                "~/Styles/Bootstrap-cutom.min.css",
                "~/Styles/Animate.min.css", 
                "~/Styles/Technologies.png.min.css",
                "~/Styles/Main.min.css"));

            // JS
            bundles.Add(new ScriptBundle("~/Scripts/Js").Include(
                "~/Scripts/jquery-{version}.min.js",
                "~/Scripts/jquery-Lightbox.js",
                "~/Scripts/jquery-viewportchecker.min.js",
                "~/Scripts/jquery-typed.min.js",
                "~/Scripts/modernizr-{version}.min.js",
                "~/Scripts/detectizr.min.js",
                "~/Scripts/bootstrap.min.js",
                "~/Scripts/respond.min.js",
                "~/Scripts/Site.min.js"));

            BundleTable.EnableOptimizations = true;
        }
    }

Výsledok:

Bundling pre CSS a JS fajly

 

2. Caching na strane klienta

Aby prehliadač klienta nemusel pre každú stránku ťahať po sieti aj zdroje, ktoré už má, musíme mu to povedať cez hlavičku HTTP requestu. V ASP.NET to zabezpečíme veľmi jednoducho. Do priečinkov, ktoré cheme zacacheovať na strane klienta doplníme nasledovný web.config:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <staticContent>
            <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="1.00:00:00" />
        </staticContent>
    </system.webServer>
</configuration>

V našom prípade to bolo do priečinkov Scripts a Styles s hodnotou 1.00:00:00, ktorá definuje cacheovanie na jeden deň (napr. na hodinu by to bolo 01:00:00) a následne si výsledok môžeme overiť cez firebug:

Firebug a cacheovanie na strane clienta v Turingion.com

 

3. Caching na strane servera

Na cacheovanie na strane servera sme využili tzv. OutputCache a to nasledovnou konfiguráciou vo web.configu projektu:

<system.web>
	<caching>
    	<outputCache enableOutputCache="true"></outputCache>
    	<outputCacheSettings>
    		<outputCacheProfiles>
        		<add name="Cache1Hour" location="ServerAndClient" duration="3600" enabled="true" varyByParam="*" />
            	<add name="Cache1Day" location="ServerAndClient" duration="86400" enabled="true" varyByParam="*" />
       	 </outputCacheProfiles>
    	</outputCacheSettings>
    </caching>
 </system.web>

A následne OutputCache profil použijeme na .aspx stránke:
<%@ Page Language="C#" %>
<%@ OutputCache CacheProfile="Cache1Hour" %>

Poznámka na záver : Minifikácia a Cache na strane clienta a servera výrazne, skutočne výrazne ovplyvní performance webu.