مطالب
پلاگین جستجو با jquery و twitter bootstrap
در این مطلب با نحوه استفاده از پلاگین جستجوی سفارشی searchboxmvc.js آشنا خواهید شد. 

قبلاً در اینجا با نحوه ایجاد پلاگین jQuey آشنا شدید. روشی دیگری نیز برای ایجاد این نوع پلاگین‌ها وجود دارد و آن استفاده از widget factory موجود در پلاگین jQuery UI می‌باشد. 
برای استفاده از این پلاگین که کدهای کامل آن در فایل پیوست موجود است، ابتدا باید فایل‌های لازم را به پروژه خود اضافه کنیم:
    <link rel="stylesheet" href="@Url.Content("~/Content/bootstrap-rtl.css")" type="text/css" />
    <script type="text/javascript" src="@Url.Content("~/scripts/jquery-2.0.2.min.js")"></script>
    <script type="text/javascript" src="@Url.Content("~/scripts/jquery-ui-1.10.3.min.js")"></script>
    <script type="text/javascript" src="@Url.Content("~/scripts/bootstrap-rtl.js")"></script>
    <script type="text/javascript" src="@Url.Content("~/scripts/searchboxmvc.js")"></script>
سپس در کنترلر خود یک Action بصورت زیر ایجاد کنید:
 [HttpPost]
        public virtual ActionResult LoadData(string fieldName, string value, string stringFilterMode = "startWith")
        {
            Thread.Sleep(2000);
            var models = MakePersons();
            if (fieldName == "Id")
            {
                models = models.Where(p => p.Id == int.Parse(value)).Take(1).ToList();
            }
            else if (fieldName == "FirstName")
            {
                models = models.Where(p => p.FirstName.StartsWith(value)).ToList();
            }

            return Json(new { Status = "OK", Records = models });
        }
        private List<Person> MakePersons()
        {
            var lst = new List<Person>();
            lst.Add(new Person() { Id = 1, Code = "Uytffs-098", FirstName = "احمدرضا", LastName = "عابدزاده" });
            lst.Add(new Person() { Id = 2, Code = "fTuuuw-652", FirstName = "کریم", LastName = "باقری" });
            lst.Add(new Person() { Id = 3, Code = "Lopapo-123", FirstName = "خداداد", LastName = "عزیزی" });
            lst.Add(new Person() { Id = 4, Code = "Utppq-981", FirstName = "علی", LastName = "دایی" });
            lst.Add(new Person() { Id = 5, Code = "zttsn-471", FirstName = "علی", LastName = "کریمی" });
            lst.Add(new Person() { Id = 6, Code = "poiud-901", FirstName = "مهدی", LastName = "مهدوی کیا" });
            lst.Add(new Person() { Id = 7, Code = "wqrPoP-391", FirstName = "علیرضا", LastName = "منصوریان" });
            return lst;
        }
در ادامه در ویوی مورد نظر خود یک div ایجاد کنید. همین div خام با اعمال پلاگین بر روی آن ، بصورت یک پلاگین جستجو عمل خواهد کرد.
حال کدهای جاوا اسکریپت مورد نظر را برای اعمال پلاگین و تنظیمات موردنیاز آن به div ایجاد شده می‌نویسیم:
...
<div id="div_SearchBoxContainer">
</div>
...
@section scripts{
    <script type="text/javascript">
        $("#div_SearchBoxContainer").searchboxmvc({
            loadUrl: '@Url.Action(actionName: "LoadData", controllerName: "Home")',
            defaultStringFilterMode: "startWith",
            loadDataOnLeave: true,
            displayClass: "",
            displayNoResultClass: "",
            display: function (element, record) {
                $(element).html(record.FirstName + "  " + record.LastName);
            },
            listItemsDisplay: function (element, record, index) {
                return record.LastName + " " + record.FirstName + "(" + record.Code + ")";
            },
            fields: [
                {
                    fieldName: "Id",
                    fieldTitle: "شناسه",
                    width: 100,
                    defaultValueField: true
                },
                {
                    fieldName: "FirstName",
                    fieldTitle: "نام",
                    width: 200,
                    defaultDisplayField: true,
                    filter: true,
                    isStringType: true
                },
                {
                    fieldName: "LastName",
                    fieldTitle: "نام خانوادگی",
                    filter: false,
                    isStringType: true
                }
            ]
        });
    </script>
}

شرح پارامترهای افزونه searchboxmvc.js 
loadUrl : آدرس اکشن متدی است که بصورت ajax ای فراخوانی شده و نتایج حاصل را بازگشت میدهد.
 نتایج حاصله باید با فرمت json بازگشت داده شوند. اگر نتایج موفقیت باشد باید بصورت  ({Json(new { Status = "OK", Records = models بازگشت داده شوند و اگر خطایی در این بین صورت گرفت مقدار Status نباید مقدار OK باشد.
پارامترهای مورد نیاز این اکشن نیز باید به ترتیب با نام های fieldName و value باشند که fieldName نام فیلدی است که جستجو بر اساس آن صورت می‌گیرد و value همان مقدار وارد شده توسط کاربر است. 
defaultStringFilterMode : اگر فیلد مورد جستجو از نوع رشته ای باشد (یعنی isStringType  آن برابر true باشد) آنگاه پارامتر سوم اکشن متد بطور خودکار مقداردهی خواهد شد. مقادیر این خاصیت میتواند startWith  یا contains و یا equal باشد.
loadDataOnLeave : اگر برابر false باشد، هربار که متن input تغییر کرد بلافاصله یک تقاضا برای یافتن مقادیر به سرور فرستاده میشود و نیازی نیست که فوکوس از کنترل خارج شود.
displayClass : نام کلاس css است که به div 3 اعمال خواهد شد.
displayNoResultClass : در صورتیکه جستجو نتیجه ای نداشته باشد این کلاس به div 3 اعمال خواهد شد.
display : یک فانکشن که برای ایجاد خروجی html برای نمایش در div 3 بکار می‌رود.
listItemsDisplay : یک فانکشن که برای ایجاد خروجی html برای آیتم‌ها بکار می‌رود.
fields : یک آرایه از فیلدهای موردنیاز پلاگین .
خاصیت‌های فیلد نیز بصورت زیر است:
fieldName : نام فیلد
fieldTitle : عنوان فیلد
defaultValueField : فیلد پیش فرض که جستجو بر اساس آن صورت می‌گیرد. اگر تعیین نشود فیلد اول آرایه به عنوان فیلد پیش فرض انتخاب خواهد شد.
defaultDisplayField : فیلد پیش فرض که برای نمایش متن div 3 بکار می‌رود(البته اگر پارامتر display تعیین نشود)
filter : اگر برابر true باشد این فیلد در لیست فیلدهای جستجو خواهد آمد و کاربر می‌تواند بر اساس آن جستجو انجام دهد.
isStringType : اگر برابر true باشد ، پارامتر سوم اکشن متد بطور خودکار مقداردهی خواهد شد.
لازم به ذکر است که این پلاگین کامل نیست و فقط برای ارائه مثال اینجا آورده شده است. هر یک از دوستان می‌توانند محتوای پلاگین را به سلیقه خود تغییر داده و پلاگین را کاملتر کنند.
sample_mvc.zip
اشتراک‌ها
جاوا اسکریپت، اجکس، جی کوئری، انگیولار و نود چیستند و کاربرد هر کدام چیست؟

Summary

JavaScript is a language written for websites to run in the client’s browser.

AJAX is a way for JavaScript to request data from a server without refreshing the page or blocking the application.

jQuery is a JavaScript library built to automate and simplify common web tasks like AJAX or animation.

Angular is a hip JavaScript framework which is made for building large, single-page web applications.

Node.js allows JavaScript to be run without a browser, and is commonly used to run web servers. 

جاوا اسکریپت، اجکس، جی کوئری، انگیولار و نود چیستند و کاربرد هر کدام چیست؟
مطالب دوره‌ها
نگاهی به SignalR Clients
در قسمت قبل موفق به ایجاد اولین Hub خود شدیم. در ادامه، برای تکمیل برنامه نیاز است تا کلاینتی را نیز برای آن تهیه کنیم.
مصرف کنندگان یک Hub می‌توانند انواع و اقسام برنامه‌های کلاینت مانند jQuery Clients و یا حتی یک برنامه کنسول ساده باشند و همچنین Hubهای دیگر نیز قابلیت استفاده از این امکانات Hubهای موجود را دارند. تیم SignalR امکان استفاده از Hubهای آن‌را در برنامه‌های دات نت 4 به بعد، برنامه‌های WinRT، ویندوز فون 8، سیلورلایت 5، jQuery و همچنین برنامه‌های CPP نیز مهیا کرده‌اند. به علاوه گروه‌های مختلف نیز با توجه به سورس باز بودن این مجموعه، کلاینت‌های iOS Native، iOS via Mono و Android via Mono را نیز به این لیست اضافه کرده‌اند.


بررسی کلاینت‌های jQuery

با توجه به پروتکل مبتنی بر JSON سیگنال‌آر، استفاده از آن در کتابخانه‌های جاوا اسکریپتی همانند jQuery نیز به سادگی مهیا است. برای نصب آن نیاز است در کنسول پاور شل نوگت، دستور زیر را صادر کنید:
 PM> Install-Package Microsoft.AspNet.SignalR.JS
برای نمونه به solution پروژه قبل، یک برنامه وب خالی دیگر را اضافه کرده و سپس دستور فوق را بر روی آن اجرا نمائید. در این حالت فقط باید دقت داشت که فرامین بر روی کدام پروژه اجرا می‌شوند:


با استفاده از افزونه SignalR jQuery، به دو طریق می‌توان به یک Hub اتصال برقرار کرد:
الف) استفاده از فایل proxy تولید شده آن (این فایل، در زمان اجرای برنامه تولید می‌شود و یا امکان استفاده از آن به کمک ابزارهای کمکی نیز وجود دارد)
نمونه‌ای از آن‌را در قسمت قبل ملاحظه کردید؛ همان فایل تولید شده در مسیر /signalr/hubs برنامه. به نوعی به آن Service contract نیز گفته می‌شود (ارائه متادیتا و قراردادهای کار با یک سرویس Hub). این فایل همانطور که عنوان شد به صورت پویا در زمان اجرای برنامه ایجاد می‌شود.
امکان تولید آن توسط برنامه کمکی signalr.exe نیز وجود دارد؛ برای دریافت آن می‌توان از طریق NuGet اقدام کرد (بسته Microsoft.AspNet.SignalR.Utils) که نهایتا در پوشه packages قرار خواهد گرفت. نحوه استفاده از آن نیز به صورت زیر است:
 Signalr.exe ghp http://localhost/
در این دستور ghp مخفف generate hub proxy است و نهایتا فایلی را به نام server.js تولید می‌کند.

ب) بدون استفاده از فایل proxy و به کمک روش late binding (انقیاد دیر هنگام)

برای کار با یک Hub از طریق jQuery مراحل ذیل باید طی شوند:
1) ارجاعی به Hub باید مشخص شود.
2) روال‌های رخدادگردان تنظیم گردند.
3) اتصال به Hub برقرار گردد.
4) متدی فراخوانی شود.

در اینجا باید دقت داشت که امکانات Hub به صورت خواص
 $.connection
در سمت کلاینت جی‌کوئری، در دسترس خواهند بود. برای مثال:
 $.connection.chatHub
و نام‌های بکارگرفته شده در اینجا مطابق روش‌های متداول نام گذاری در جاوا اسکریپت، camel case هستند.

خوب، تا اینجا فرض بر این است که یک پروژه خالی ASP.NET را آغاز و سپس فرمان نصب Microsoft.AspNet.SignalR.JS را نیز همانطور که عنوان شد، صادر کرده‌اید. در ادامه یک فایل ساده html را به نام chat.htm، به این پروژه جدید اضافه کنید (برای استفاده از کتابخانه جاوا اسکریپتی SignalR الزامی به استفاده از صفحات کامل پروژه‌های وب نیست).
<!DOCTYPE>
<html>
<head>
    <title></title>
    <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.signalR-1.0.1.min.js" type="text/javascript"></script>
    <script src="http://localhost:1072/signalr/hubs" type="text/javascript"></script>
</head>
<body>
    <div>
        <input id="txtMsg" type="text" /><input id="send" type="button" value="send msg" />
        <ul id="messages">
        </ul>
    </div>
    <script type="text/javascript">
        $(function () {
            var chat;
            $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ می‌کند
            chat = $.connection.chat; //این نام مستعار پیشتر توسط ویژگی نام هاب تنظیم شده است
            chat.client.hello = function (message) {
                //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است
                //به این ترتیب سرور می‌تواند کلاینت را فراخوانی کند
                $("#messages").append("<li>" + message + "</li>");
            };
            $.connection.hub.start(/*{ transport: 'longPolling' }*/); // فاز اولیه ارتباط را آغاز می‌کند

            $("#send").click(function () {
                // Hub's `SendMessage` should be camel case here
                chat.server.sendMessage($("#txtMsg").val());
            });
        });
    </script>
</body>
</html>
کدهای آن‌را به نحو فوق تغییر دهید.
توضیحات:
همانطور که ملاحظه می‌کنید ابتدا ارجاعاتی به jquery و jquery.signalR-1.0.1.min.js اضافه شده‌اند. سپس نیاز است مسیر دقیق فایل پروکسی هاب خود را نیز مشخص کنیم. اینکار با تعریف مسیر signalr/hubs انجام شده است.
<script src="http://localhost:1072/signalr/hubs" type="text/javascript"></script>
در ادامه توسط تنظیم connection.hub.logging سبب خواهیم شد تا اطلاعات بیشتری در javascript console مرورگر لاگ شود.
سپس ارجاعی به هاب تعریف شده، تعریف گردیده است. اگر از قسمت قبل به خاطر داشته باشید، توسط ویژگی HubName، نام chat را برگزیدیم. بنابراین connection.chat ذکر شده دقیقا به این هاب اشاره می‌کند.
سپس سطر chat.client.hello مقدار دهی شده است. متد hello، متدی dynamic و تعریف شده در سمت هاب برنامه است. به این ترتیب می‌توان به پیام‌های رسیده از طرف سرور گوش فرا داد. در اینجا، این پیام‌ها، به li ایی با id مساوی messages اضافه می‌شوند.
سپس توسط فراخوانی متد connection.hub.start، فاز negotiation شروع می‌شود. در اینجا حتی می‌توان نوع transport را نیز صریحا انتخاب کرد که نمونه‌ای از آن را به صورت کامنت شده جهت آشنایی با نحوه تعریف آن مشاهده می‌کنید. مقادیر قابل استفاده در آن به شرح زیر هستند:
 - webSockets
- forverFrame
- serverSentEvents
- longPolling
سپس به رویدادهای کلیک دکمه send گوش فرا داده و در این حین، اطلاعات TextBox ایی با id مساوی txtMsg را به متد SendMessage هاب خود ارسال می‌کنیم. همانطور که پیشتر نیز عنوان شد، در سمت کلاینت، تعریف متد SendMessage باید camel case باشد.

اکنون به صورت جداگانه یکبار برنامه hub را در مرورگر باز کنید. سپس بر روی فایل chat.htm کلیک راست کرده و گزینه مشاهده آن را در مرورگر نیز انتخاب نمائید (گزینه View in browser منوی کلیک راست).

خوب! پروژه کار نمی‌کند! برای اینکه مشکلات را بهتر بتوانید مشاهده کنید نیاز است به JavaScript Console مرورگر خود مراجعه نمائید. برای مثال در مرورگر کروم دکمه F12 را فشرده و برگه Console آن‌را باز کنید. در اینجا اعلام می‌کند که فاز negotiation قابل انجام نیست؛ چون مسیر پیش فرضی را که انتخاب کرده است، همین مسیر پروژه دومی است که اضافه کرده‌ایم (کلاینت ما در پروژه دوم قرار دارد و نه در همان پروژه اول هاب).
برای اینکه مسیر دقیق hub را در این حالت مشخص کنیم، سطر زیر را به ابتدای کدهای جاوا اسکریپتی فوق اضافه نمائید:
 $.connection.hub.url = 'http://localhost:1072/signalr'; //چون در یک پروژه دیگر قرار داریم
اکنون اگر مجدا سعی کنید، باز هم برنامه کار نمی‌کند و پیام می‌دهد که امکان دسترسی به این سرویس از خارج از دومین آن میسر نیست. برای اینکه این مجوز را صادر کنیم نیاز است تنظیمات مسیریابی پروژه هاب را به نحو ذیل ویرایش نمائیم:
using System;
using System.Web;
using System.Web.Routing;
using Microsoft.AspNet.SignalR;

namespace SignalR02
{
    public class Global : HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            // Register the default hubs route: ~/signalr
            RouteTable.Routes.MapHubs(new HubConfiguration
            {
                EnableCrossDomain = true
            });
        }
    }
}
با تنظیم EnableCrossDomain به true اینبار فاز‌های آغاز ارتباط با سرور برقرار می‌شوند:
 SignalR: Auto detected cross domain url. jquery.signalR-1.0.1.min.js:10
SignalR: Negotiating with 'http://localhost:1072/signalr/negotiate'. jquery.signalR-1.0.1.min.js:10
SignalR: SignalR: Initializing long polling connection with server. jquery.signalR-1.0.1.min.js:10
SignalR: Attempting to connect to 'http://localhost:1072/signalr/connect?transport=longPolling&connectionToken…NRh72omzsPkKqhKw2&connectionData=%5B%7B%22name%22%3A%22chat%22%7D%5D&tid=3' using longPolling. jquery.signalR-1.0.1.min.js:10
SignalR: Longpolling connected jquery.signalR-1.0.1.min.js:10
مطابق این لاگ‌ها ابتدا فاز negotiation انجام می‌شود. سپس حالت long polling را به صورت خودکار انتخاب می‌کند.

در برگه شبکه، مطابق شکل فوق، امکان آنالیز اطلاعات رد و بدل شده مهیا است. برای مثال در حالتیکه سرور پیام دریافتی را به کلیه کلاینت‌ها ارسال می‌کند، نام متد و نام هاب و سایر پارامترها در اطلاعات به فرمت JSON آن به خوبی قابل مشاهده هستند.

یک نکته:
اگر از ویندوز 8 (یعنی IIS8) و VS 2012 استفاده می‌کنید، برای استفاده از حالت Web socket، ابتدا فایل وب کانفیگ برنامه را باز کرده و در قسمت httpRunTime، مقدار ویژگی targetFramework را بر روی 4.5 تنظیم کنید. اینبار اگر مراحل negotiation را بررسی کنید در همان مرحله اول برقراری اتصال، از روش Web socket استفاده گردیده است.


تمرین 1
به پروژه ساده و ابتدایی فوق یک تکست باکس دیگر به نام Room را اضافه کنید؛ به همراه دکمه join. سپس نکات قسمت قبل را در مورد الحاق به یک گروه و سپس ارسال پیام به اعضای گروه را پیاده سازی نمائید. (تمام نکات آن با مطلب فوق پوشش داده شده است و در اینجا باید صرفا فراخوانی متدهای عمومی دیگری در سمت هاب، صورت گیرد)

تمرین 2
در انتهای قسمت دوم به نحوه ارسال پیام از یک هاب به هابی دیگر اشاره شد. این MonitorHub را ایجاد کرده و همچنین یک کلاینت جاوا اسکریپتی را نیز برای آن تهیه کنید تا بتوان اتصال و قطع اتصال کلیه کاربران سیستم را مانیتور و مشاهده کرد.


پیاده سازی کلاینت jQuery بدون استفاده از کلاس Proxy

در مثال قبل، از پروکسی پویای مهیای در آدرس signalr/hubs استفاده کردیم. در اینجا قصد داریم، بدون استفاده از آن نیز کار برپایی کلاینت را بررسی کنیم.
بنابراین یک فایل جدید html را مثلا به نام chat_np.html به پروژه دوم برنامه اضافه کنید. سپس محتویات آن‌را به نحو زیر تغییر دهید:
<!DOCTYPE>
<html>
<head>
    <title></title>
    <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.signalR-1.0.1.min.js" type="text/javascript"></script>
</head>
<body>
    <div>
        <input id="txtMsg" type="text" /><input id="send" type="button" value="send msg" />
        <ul id="messages">
        </ul>
    </div>
    <script type="text/javascript">
        $(function () {                        
            $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ می‌کند

            var connection = $.hubConnection();
            connection.url = 'http://localhost:1072/signalr'; //چون در یک پروژه دیگر قرار داریم
            var proxy = connection.createHubProxy('chat');

            proxy.on('hello', function (message) {
                //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است
                //به این ترتیب سرور می‌تواند کلاینت را فراخوانی کند
                $("#messages").append("<li>" + message + "</li>");
            });

            $("#send").click(function () {
                // Hub's `SendMessage` should be camel case here
                proxy.invoke('sendMessage', $("#txtMsg").val());
            });

            connection.start();
        });
    </script>
</body>
</html>
در اینجا سطر مرتبط با تعریف مسیر اسکریپت‌های پویای signalr/hubs را دیگر در ابتدای فایل مشاهده نمی‌کنید. کار تشکیل proxy اینبار از طریق کدنویسی صورت گرفته است. پس از ایجاد پروکسی، برای گوش فرا دادن به متدهای فراخوانی شده از طرف سرور از متد proxy.on و نام متد فراخوانی شده سمت سرور استفاده می‌کنیم و یا برای ارسال اطلاعات به سرور از متد proxy.invoke به همراه نام متد سمت سرور استفاده خواهد شد.


کلاینت‌های دات نتی SignalR
تا کنون Solution ما حاوی یک پروژه Hub و یک پروژه وب کلاینت جی‌کوئری است. به همین Solution، یک پروژه کلاینت کنسول ویندوزی را نیز اضافه کنید.
سپس در خط فرمان پاور شل نوگت دستور زیر را صادر نمائید تا فایل‌های مورد نیاز به پروژه کنسول اضافه شوند:
 PM> Install-Package Microsoft.AspNet.SignalR.Client
در اینجا نیز باید دقت داشت تا دستور بر روی default project صحیحی اجرا شود (حالت پیش فرض، اولین پروژه موجود در solution است).
پس از نصب آن اگر به پوشه packages مراجعه کنید، نگارش‌های مختلف آن‌را مخصوص سیلورلایت، دات نت‌های 4 و 4.5، WinRT و ویندوز فون8 نیز می‌توانید در پوشه Microsoft.AspNet.SignalR.Client ملاحظه نمائید. البته در ابتدای نصب، انتخاب نگارش مناسب، بر اساس نوع پروژه جاری به صورت خودکار صورت می‌گیرد.
مدل برنامه نویسی آن نیز بسیار شبیه است به حالت عدم استفاده از پروکسی در حین استفاده از jQuery که در قسمت قبل بررسی گردید و شامل این مراحل است:
1) یک وهله از شیء HubConnection را ایجاد کنید.
2) پروکسی مورد نیاز را جهت اتصال به Hub از طریق متد CreateProxy تهیه کنید.
3) رویدادگردان‌ها را همانند نمونه کدهای جاوا اسکریپتی قسمت قبل، توسط متد On تعریف کنید.
4) به کمک متد Start، اتصال را آغاز نمائید.
5) متدها را به کمک متد Invoke فراخوانی نمائید.

using System;
using Microsoft.AspNet.SignalR.Client.Hubs;

namespace SignalR02.WinClient
{
    class Program
    {
        static void Main(string[] args)
        {
            var hubConnection = new HubConnection(url: "http://localhost:1072/signalr");
            var chat = hubConnection.CreateHubProxy(hubName: "chat");

            chat.On<string>("hello", msg => {
                Console.WriteLine(msg);
            });

            hubConnection.Start().Wait();

            chat.Invoke<string>("sendMessage", "Hello!");

            Console.WriteLine("Press a key to terminate the client...");
            Console.Read();
        }
    }
}
نمونه‌ای از این پیاده سازی را در کدهای فوق ملاحظه می‌کنید که از لحاظ طراحی آنچنان تفاوتی با نمونه ذهنی جاوا اسکریپتی ندارد.

نکته مهم
کلیه فراخوانی‌هایی که در اینجا ملاحظه می‌کنید غیرهمزمان هستند.
به همین جهت پس از متد Start، متد Wait ذکر شده‌است تا در این برنامه ساده، پس از برقراری کامل اتصال، کار invoke صورت گیرد و یا زمانیکه callback تعریف شده توسط متد chat.On فراخوانی می‌شود نیز این فراخوانی غیرهمزمان است و خصوصا اگر نیاز است رابط کاربری برنامه را در این بین به روز کنید باید به نکات به روز رسانی رابط کاربری از طریق یک ترد دیگر دقت داشت.
مطالب
Gulp #5

در مقالات قبلی به طور کامل با گالپ آشنا شدیم و گفتیم که می‌تواند ما را در بهینه سازی ورک فلویمان کمک کند. در این قسمت یاد خواهیم گرفت که چگونه تجربه‌ی کاربری بهتری را از سرعت بارگذاری سایتمان ایجاد کنیم.

افزایش کارآیی Performance وب با گالپ

برای اینکه بفهمیم چه کارهایی می‌تواند سایت یا اپلیکیشن ما را کاراتر کند، از Developer tools با زدن Ctrl+Shifi+I درون گوگل کروم، کار خود را شروع می‌کنیم. به برگه‌ی Audits می‌رویم و دکمه‌ی Run را با تنظیمات پیش فرض می‌زنیم. نتایج آن بعد از اندکی صبر، برای من به صورت شکل زیر است:

ما قصد داریم بدانیم گالپ چه ابزاهایی را برای راه حل‌های داده شده توسط مرورگر دارد؟

۱− کنار هم قرار دادن و فشرده کردن فایل‌های جاوا اسکریپت

پلاگین‌های گالپ برای اینکار،  gulp-concat و gulp-uglify هستند. آنها را در مسیر ریشه‌ی پروژه نصب می‌کنیم.
npm install --save-dev gulp-uglify gulp-concat
بعد از نصب، فایل gulpfile.js را به صورت زیر ویرایش و تسک js را به آن اضافه می‌کنیم.
// first load all required js files
// concat them in to  script.min.js
// and minify it.
gulp.task('js', function() {
    return gulp.src([
            config.bowerDir + '/jquery/dist/jquery.min.js', // این فایل وابستگی فایل‌های زیر است 
            config.bowerDir + '/materialize/dist/js/materialize.min.js',
            './resources/js/app.js'
        ])
        .pipe(concat('script.min.js'))
        .pipe(uglify())
        .pipe(size())
        .pipe(gulp.dest('./public/js'));
});

خروجی:

فشرده کردن جاوا اسکریپت، حجم فایل‌ها را ۳۰ تا ۹۰ درصد کاهش می‌دهد.

۲− حذف سکلتور‌های بدون استفاده css

همانگونه که در عکس اول آمده، ۹۳ درصد سلکتور‌ها در این صفحه بلا استفاده هستند! و این یعنی کاهش فوق العاده زیاد حجم فایل فشرده شده css. به طور معمول، توسعه دهندگان ۸۵ درصد حجم فایل css خود را می‌توانند با این کار کاهش دهند (البته بیشتر این اتفاق هنگام استفاده از فریک ورک‌هایی مانند bootstrap,... می‌افتد) برای این کار از پلاگین gulp-uncss استفاده می‌کنیم. نصب:
npm install gulp-uncss --save-dev
سپس تسک مربوطه را می‌نویسم:
gulp.task('css', function() {
    return sass(config.sassPath + '/style.scss', {
            style: 'compressed',
            loadPath: [
                './resources/sass',
                config.bowerDir + '/materialize/sass'
            ]
        })
        .on('error', util.log)
        .pipe(size())
        .pipe(uncss({
            html: ['./index.html', './posts.html']
        }))
        .pipe(gulp.dest('./public/css'))
        .pipe(size())
        .pipe(connect.reload());
});
نتیجه:

نتیجه فوق العاده است! ۸۷ درصد کاهش حجم css! اما ممکن است بعضی از استایل‌های شما توسط javascript به صفحه تزریق شوند. در این صورت نباید سکلتور‌های لازم را حذف کرد و آنها را داخل آرایه‌ی ignore قرار می‌دهیم. برای این منظور، تسک بالا را به صورت زیر به روز رسانی می‌کنیم.
gulp.task('css', function() {
    return sass(config.sassPath + '/style.scss', {
            style: 'compressed',
            loadPath: [
                './resources/sass',
                config.bowerDir + '/materialize/sass'
            ]
        })
        .on('error', util.log)
        .pipe(size())
        .pipe(uncss({
            html: ['./index.html', './posts.html'],
            timeout : 2000, // wait for load js files
              ignore: [ 
                ".waves-ripple ",
                ".drag-target",
                "#sidenav-overlay",
                ".waves-effect",
                ".waves-effect .waves-ripple",
                ".waves-effect.waves-pinck .waves-ripple",
                ".waves-block.waves-light"
           ]
        }))
        .pipe(minifyCss())
        .pipe(size())
        .pipe(gulp.dest('./public/css'))
        .pipe(connect.reload());
});
بعد از اینکار حجم فایل css من کمی افزایش پیدا کرد ولی بعد از فشرده کردن، نهایتا به حدود ۱۴KB رسید و این یعنی ۸۷ درصد کاهش حجم فایل css؛ تنها بعد از حذف سکلتور‌های اضافی و فشرده کردن آنها. می‌توانید پلاگین‌های بیشتری را در اینجا ببینید و استفاده کنید. 

Gist 
مطالب
Vue.js - نحوه‌ی استفاده از list item ها و تخصیص item - قسمت چهارم
در این قسمت به بحث item list‌‌ها پرداخته می‌شود. روند کلی بدین صورت است که ابتدا یک آرایه را ایجاد کرده (با مقادیر مشخص) و سپس درون تگ مورد نظری از صفحه، آرایه را فراخوانی می‌کنیم.
برای این منظور ابتدا یک vue جدید را ساخته و سپس آرایه مورد نظر را تعریف کرده و آیتم‌های آرایه را مشخص می‌کنیم. به صورت زیر:
     var dotnettips = new Vue({
         el: '#dotnettips',
         data: {
            items: [
                  { message: 'one' },
                  { message: 'two' }
    ]
  }
});
در کد بالا یک ویژگی با نام dotnettips تعریف شده و مقادیری به آن اختصاص داده شده است.
حال زمان آن فرارسیده است تا بدنه اصلی ساختار صفحه را بنویسیم.
<html>
    <body>
    <ul id="dotnettips">
       <li v-for="item in items">
           {{ item.message }}
       </li>
    </ul>
  
    <script src="https://unpkg.com/vue@2.2.6">
    </script>

    <script type="text/javascript">
         var dotnettips = new Vue({
         el: '#dotnettips',
         data: {
            items: [
                  { message: 'one' },
                  { message: 'two' }
            ]
          }
        });
        </script>      
        </body>
</html>
برای نمایش خروجی کار به صورت دلخواه، ابتدا یک تگ ul و درون آن تگ li را ایجاد کردیم و سپس با استفاده از دستور v-for، اقدام به فراخوانی list item مورد نظر کردیم و سپس ویژگی مورد نظر را که همان item.message می‌باشد، درون علامت {{ }} قرار دادیم، تا خروجی نهایی به نمایش درآید.
 .حال با اجرای کد فوق تمامی آیتم‌های مورد نظر روی صفحه به نمایش در می‌آیند 
.دقت نمائید که کدهای فوق و بدنه اسکریپت به دلخواه نوشته شده‌است و شما طبق نیاز خود می‌توانید آن‌ها را تغییر دهید
مطالب
راه‌های متفاوت رندر لایه‌ها در ASP.NET MVC
در MVC لایه‌ها (Layouts) مانند Master Page‌ها در وب فرم عمل می‌کنند. این به ما کمک می‌کند تا بتوانیم از تکرار کدها پرهیز کنیم و سریعتر صفحات خودمان را گسترش دهیم. مثل Master Page‌ها، این صفحات هم (Layouts) می‌تواند شامل قالب‌های CSS مختلف، کدهای Javascript مختلف و قالب بندی‌های مختلفی باشند.
در میان View‌های یک برنامه MVC فایلی را به عنوان    _ViewStart  داریم که وظیفه‌ی آن نگهداری قالب اصلی برنامه‌ی ما است.
در این مقاله سعی شده است تا راه‌های موجود برای استفاده از این قالب‌ها را در یک برنامه MVC، بررسی کنیم.
فرض ما بر این است که می‌خواهیم لایه‌ای را که در تصویر زیر می‌بینید، مورد استفاده قرار دهیم:

روش شماره 1 : استفاده از  _ViewStart موجود در ریشه‌ی پوشه Views

با استفاده از کد زیر می‌توانیم فایل پیش فرضی را که قرار است رندر شود، تغییر دهیم:

@{
 var controller = HttpContext.Current.Request.RequestContext.RouteData.Values["Controller"].ToString();
 
 string layout = "";
 if (controller == "Admin")
 {
 layout = "~/Views/Shared/_AdminLayout.cshtml";
 }
 else
 {
 layout = "~/Views/Shared/_Layout.cshtml";
 }
 
 Layout = layout;
}


روش شماره 2 : مشخص کردن لایه در Action

همچنین می‌توانیم فایل مورد نظر را در اکشن خودمان، بازنویسی (override) کنیم:

public ActionResult Index()
{
 RegisterModel model = new RegisterModel();
 //TO DO:
 return View("Index", "_AdminLayout", model);
}

روش شماره 3 : مشخص کردن لایه به ازای هر View

می‌توانیم در هر View هم لایه مربوط به آن را مشخص کنیم:

@{
 Layout = "~/Views/Shared/_AdminLayout.cshtml";
}

روش شماره 4 : اضافه کردن فایل _ViewStart به ازای هر کنترلر

همانطور که در تصویر زیر می‌بینید آخرین روش این هست که می‌توانید فایل‌های _ViewStart مختلفی را به ازای هر کنترلر، داخل پوشه View مربوطه قرار بدید تا سیستم از آن استفاده کند. 

مطالب
افزونه farsiInput جهت ورودی فقط فارسی در صفحات وب
گاهی از اوقات نیاز است کاربر در یک جعبه متنی، فقط متن فارسی وارد کند؛ حتی اگر صفحه کلید او فارسی نباشد و یا بنابر درخواست او، جهت بالا رفتن سرعت ورود اطلاعات یک چنین قابلیتی نیاز می‌شود. چندین سال قبل farsitype.js اینکار را انجام می‌داد. این اسکریپت با مرورگرهای جدید سازگار نیست و برای نمونه initKeyEvent آن در نگارش‌های قدیمی فایرفاکس کار می‌کرد، در کروم هیچ وقت پشتیبانی نشد (به نام initKeyboardEvent موجوداست؛ اما برای جایگزین کردن حروف عمل نمی‌کند) و مدتی است که فایرفاکس هم به دلایل امنیتی آن‌را غیرفعال کرده است.
به همین جهت افزونه farsiInput، که کدهای آن‌را در ادامه مشاهده می‌کنید، تهیه گردید. این افزونه تا این تاریخ با IE، فایرفاکس، کروم و اپرا سازگار است و توسط آن کاربر بدون نیاز به داشتن یک صفحه کلید فارسی می‌تواند فارسی تایپ کند. برای سوئیچ به حالت انگلیسی، دکمه Scroll lock باید روشن شود و این مورد توسط پارامتر changeLanguageKey قابل تغییر است.
// <![CDATA[
(function ($) {
    $.fn.farsiInput = function (options) {
        var defaults = {
            changeLanguageKey: 145 /* Scroll lock */
        };
        var options = $.extend(defaults, options);

        var lang = 'fa';

        var keys = new Array(1711, 0, 0, 0, 0, 1608, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1705, 1572, 0, 1548,
                             1567, 0, 1616, 1571, 8250, 0, 1615, 0, 0, 1570, 1577, 0, 0, 0, 1569, 1573, 0, 0, 1614, 1612, 1613, 0, 0,
                             8249, 1611, 171, 0, 187, 1580, 1688, 1670, 0, 1600, 1662, 1588, 1584, 1586, 1740, 1579, 1576, 1604, 1575,
                             1607, 1578, 1606, 1605, 1574, 1583, 1582, 1581, 1590, 1602, 1587, 1601, 1593, 1585, 1589, 1591, 1594, 1592);

        var substituteChar = function (charCode, e) {
            if (navigator.appName == "Microsoft Internet Explorer") {
                window.event.keyCode = charCode;
            }
            else {
                insertAtCaret(String.fromCharCode(charCode), e);
            }
        };

        var insertAtCaret = function (str, e) {
            var obj = e.target;
            var startPos = obj.selectionStart;
            var endPos = obj.selectionEnd;
            var scrollTop = obj.scrollTop;
            obj.value = obj.value.substring(0, startPos) + str + obj.value.substring(endPos, obj.value.length);
            obj.focus();
            obj.selectionStart = startPos + str.length;
            obj.selectionEnd = startPos + str.length;
            obj.scrollTop = scrollTop;
            e.preventDefault();
        };

        var keyDown = function (e) {
            var evt = e || window.event;
            var key = evt.keyCode ? evt.keyCode : evt.which;
            if (key == options.changeLanguageKey) {
                lang = (lang == 'en') ? 'fa' : 'en';
                return true;
            }
        };

        var fixYeKeHalfSpace = function (key, evt) {
            var originalKey = key;
            var arabicYeCharCode = 1610;
            var persianYeCharCode = 1740;
            var arabicKeCharCode = 1603;
            var persianKeCharCode = 1705;
            var halfSpace = 8204;

            switch (key) {
                case arabicYeCharCode:
                    key = persianYeCharCode;
                    break;
                case arabicKeCharCode:
                    key = persianKeCharCode;
                    break;
            }

            if (evt.shiftKey && key == 32) {
                key = halfSpace;
            }

            if (originalKey != key) {
                substituteChar(key, evt);
            }
        };

        var keyPress = function (e) {
            if (lang != 'fa')
                return;

            var evt = e || window.event;
            var key = evt.keyCode ? evt.keyCode : evt.which;
            fixYeKeHalfSpace(key, evt);
            var isNotArrowKey = (evt.charCode != 0) && (evt.which != 0);
            if (isNotArrowKey && (key > 38) && (key < 123)) {
                var pCode = (keys[key - 39]) ? (keys[key - 39]) : key;
                substituteChar(pCode, evt);
            }
        }

        return this.each(function () {
            var input = $(this);
            input.keypress(function (e) {
                keyPress(e);
            });
            input.keydown(function (e) {
                keyDown(e);
            });
        });
    };
})(jQuery);
// ]]>
مثالی از نحوه بکارگیری آن:
<html>
<head>
    <title>تکست باکس فارسی</title>
    <script type="text/javascript" src="jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="jquery.farsiInput.js"></script>
    <style type="text/css">
        input, textarea
        {
            font-family: tahoma;
            font-size: 9pt;
        }
    </style>
</head>
<body>
    <input dir="rtl" id='text1' />
    <br />
    <textarea dir="rtl" id='text2' rows="15" cols="84"></textarea>
    <script type="text/javascript">
        $(function () {
            $("#text1, #text2").farsiInput();
        });
    </script>
</body>
</html>

دریافت کدهای کامل افزونه farsiInput
farsi_input.zip
 
مطالب
نمایش خودکار مقدار یکDropDownList با کمک jQuery

نیاز بود هنگام انتخاب یک آیتم دراپ داون لیست در کل برنامه و تمامی دراپ داون‌های آن، مقدار آن‌ها نیز به صورت یک برچسب در کنار آن نمایش داده شود.
برای مثال در لیست زیر:

<asp:DropDownList ID="ddlActive" runat="server">
<asp:ListItem Value="Active">فعال</asp:ListItem>
<asp:ListItem Value="Inactive">غیرفعال</asp:ListItem>
</asp:DropDownList>
اگر آیتم فعال انتخاب شد، مقدار active نیز کنار آن نمایش داده شود و الی آخر.

راه حل اول:
در تمام صفحات به ازای تک تک دراپ داون‌ها یک label اضافه کنیم و همچنین کدهای تمام قسمت‌های برنامه را نیز اصلاح کنیم تا این مورد را لحاظ کند.

راه دوم:
یک کنترل دراپ داون سفارشی را با خاصیت مورد نظر (همراه بودن با یک لیبل) ایجاد کرده و سپس تمام فرم‌ها را باید اصلاح کرد تا از این کنترل جدید استفاده کنند.

راه سوم:
استفاده از jQuery برای اعمال این مهم به کل برنامه بدون نیاز به تغییرات اساسی در آن (و همچنین سازگاری با تمام مرورگرها):

//فقط در این محدوده
$("#mainFormReq select").change(function() {
var currentId = $(this).attr("id"); //آی دی شیء جاری
var val = $(this).val(); //مقدار
var text = $('#' + currentId + ' option:selected').text(); //متن
$("#lbl" + currentId).remove(); //اگر نمونه‌ی قبلی موجود است حذف شود
if (val && (val.length > 0) && (text != val)) {
//اگر متن و مقدار یکی نیست نمایش داده شود
$(this).after('<label id="lbl' + currentId + '">' + val + '</label>');
}
});
توضیحات:
در یک محدوده مشخص شده با ID مساوی mainFormReq (مثلا استفاده از master page ها و نسبت دادن این ID به content آن)، به دنبال تمام select های موجود در آن ناحیه می‌گردیم (اگر mainFormReq حذف شود، این جستجو در کل صفحه صورت خواهد گرفت) و تغییرات آن‌ها را تحت نظر قرار خواهیم داد.
سپس آی دی این کنترل انتخابی را دریافت می‌کنیم (از این ID برای تولید ID برچسب مورد نظر استفاده خواهیم کرد).
در ادامه مقدارهای text و value گزینه انتخابی دریافت می‌شوند (+).
سپس بررسی خواهیم کرد که آیا برچسبی با ID مشخص شده ما وجود دارد (در صورت انتخاب آیتم‌های دیگر، نباید برچسبی غیر منحصربفرد و تکراری در صفحه ایجاد کرد)
در ادامه اگر این مقدار null نبود و همچنین مقدار text و value هم یکی نبودند (اگر یکی بودند لزوم وجود این برچسب بی معنا است)، با استفاده از متد after کتابخانه jQuery یک برچسب را تولید و مقدار مورد نظر را پس از محل نمایش دراپ داون خود، نمایش خواهیم داد.

بهبود کد:
صورت مساله: اکنون نیاز است بجز ناحیه mainFormReq، به سه ناحیه دیگر نیز این تغییرات اعمال گردد. آیا باید همین مقدار کد را سه بار دیگر copy/paste کرد؟
روش صحیح انجام اینکار در jQuery ، نوشتن یک افزونه بر اساس کدهای فوق است که روش انجام آن به صورت زیر می‌باشد (+):

//<![CDATA[
(function($) {
$.fn.dropdownlabel = function() {
return this.change(function() {
var obj = $(this);
var currentId = obj.attr("id"); //آی دی شیء جاری
var val = obj.val(); //مقدار
var text = $('#' + currentId + ' option:selected').text(); //متن
$("#lbl" + currentId).remove(); //اگر نمونه‌ی قبلی موجود است حذف شود
if (val && (val.length > 0) && (text != val)) {
//اگر متن و مقدار یکی نیست نمایش داده شود
obj.after('<label id="lbl' + currentId + '">' + val + '</label>');
}
});
};
})(jQuery);
//]]>
و در نهایت نحوه استفاده از آن (فایلی به نام jquery.dropdownlabel.js ) به صورت زیر خواهد بود:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TestDropdownlabel.aspx.cs"
Inherits="testWebForms87.TestDropdownlabel" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>

<script src="jquery.min.js" type="text/javascript"></script>
<script src="jquery.dropdownlabel.js" type="text/javascript"></script>

<script type="text/javascript">
$(document).ready(function() {
$("#mainFormReq select").dropdownlabel();
});
</script>

</head>
<body>
<form id="form1" runat="server">
<div id="mainFormReq">
<asp:DropDownList ID="ddlActive" runat="server">
<asp:ListItem Value=""></asp:ListItem>
<asp:ListItem Value="Active">فعال</asp:ListItem>
<asp:ListItem Value="Inactive">غیرفعال</asp:ListItem>
</asp:DropDownList>
</div>
</form>
</body>
</html>

مطالب
توسعه سرویس‌های Angular به روش OOP
یک نکته‌ای که در توسعه سیستم‌ها و نرم افزار‌ها تاکید فراوانی به آن می‌شود استفاده مجدد از کد‌های نوشته شده قبلی است. یعنی تا جای ممکن باید ساختار پروژه به گونه‌ای نوشته شود که از تکرار کد‌ها در جای جای پروژه جلوگیری شود. این مورد به خوبی در زبان‌های شیء‌گرا نظیر #C رعایت می‌شود اما در پروژه‌هایی که مبتنی بر Javascript هستند نظیر angular، باید با استفاده از خاصیت prototype جاوا اسکریپ این مورد را رعایت نمود. در  مقاله  Dr. Axel Rauschmayer،  قدم به قدم و به خوبی روش‌های وراثت در Javascript توضیح داده شده است.
در این پست با روش‌های وراثت در کنترلر‌های انگولاری آشنا شدید. این وراثت محدود به ارث بری scope‌ها می‌شود. اما یکی از بخش‌های بسیار مهم پروژه‌های انگولار نوشتن سرویس‌هایی با قابلیت توسعه مجدد در سایر بخش‌های پروژه می‌باشد. معادل آن، مفهوم Overriding در OOP است. با ذکر مثالی این مورد را با هم بررسی خواهیم کرد.
ابتدا یک سرویس به نام BaseService ایجاد کنید:
angular.module('myApp').service('BaseService', function() {

    var BaseService = function(title) {
        this.title = title;
    };

    BaseService.prototype.getMessage = function() {
        var self = this;
        return 'Hello ' + self.title;
    };

    return BaseService;
});
سرویس بالا دارای سازنده‌ای است که مقدار title باید در اختیار آن قرار گیرد. با استفاده از خاصیت prototype تابعی تعریف می‌کنیم که این تابع خروجی مورد نظر را برای ما تامین خواهد نمود.
حال اگر ماژول و کنترلری جهت نمایش خروجی به صورت زیر ایجاد کنیم:
var app= angular.module('myApp', []);


app.controller('myCtrl', function ($scope,BaseService) {

    var instance = new BaseService('Masoud');
    $scope.title = instance.getMessage();

});
با کدهای Html زیر:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" ng-app="myApp">
<head>
    <title></title>
</head>
    <body ng-controller="myCtrl">

        <div>
            {{title}}
        </div>

    </body>
<script src="Scripts/jquery-2.1.1.min.js"></script>
<script src="Scripts/angular.js"></script>
<script src="App/app.js"></script>
</html>
در نهایت خروجی به صورت زیر قابل مشاهده است:

تا اینجای کار روال معمول تعاریف سرویس در انگولار بوده است. اما قصد داریم سرویس جدیدی را ایجاد نمایم تا خروجی سرویس قبلی را اندکی تغییر دهد. به جای اینکه سرویس قبلی را تغییر دهیم یا بدتر از آن سرویس جدیدی بسازیم و کدهای قبلی را در آن کپی کنیم کافیست به صورت زیر عمل نماییم:

app.service('ExtService', function(BaseService) {

    var ExtService = function() {
        BaseService.apply(this, arguments);
    };

    ExtService.prototype = new BaseService();
    
    ExtService.prototype.getMessage = function() {
        var self = this;
        return BaseService.prototype.getMessage.apply(this, arguments) + ' From Ext Service';
        
    };

    return ExtService;
});
حال می‌توان کنترلر را به صورت زیر بازنویسی کرد.
app.controller('myCtrl', function ($scope,BaseService , ExtService) {

    var baseInstance = new BaseService('Masoud');
    var extInstance = new ExtService('Dotnettips');
    $scope.title = baseInstance.getMessage() + ' and ' + extInstance.getMessage();

});
در کنترلر بالا هر دو سرویس تزریق شده‌اند. خروجی سرویس دوم متن From Ext Service را نیز به همراه خواهد داشت. پس از اجرای برنامه خروجی زیر قابل مشاهده است:

مطالب
افزونه rtlColResizable جهت تغییر سایز ستون‌های جدول‌های راست به چپ HTML
برای تغییر سایز ستون‌های جداول HTML با استفاده از ماوس، افزونه‌های زیادی تدارک دیده شده است که از جمله مطرح‌ترین آن‌ها می‌توان به colResizable اشاره کرد. حتی اگر از DataGrid‌های مطرح وب هم استفاده کرده باشید، اکثر آن‌ها از تغییر سایز ستون‌ها توسط کاربر پشتیبانی می‌کنند. اما مشکل بزرگی که در همه‌ی آن‌ها مشترک است  این است که فقط از چیدمان‌های چپ به راست پشتیبانی می‌کنند و به محض اینکه شما ساختار راست به چپ را به جدول مورد نظر اعمال کنید، عملکرد تغییر سایز ستون‌ها با اشکال مواجه می‌شود.
به همین جهت برای تغییر سایز ستون‌ها توسط کاربر، افزونه ای برای jQuery تدارک دیدم که با جداول معمول HTML که همان table‌ها هستند سازگار است و به راحتی به هر جدولی می‌توان آن را اعمال کرد.
 
کدهای افزونه‌ی rtlColResizable:
(function ($, undefined) {

    $.fn.extend({
        'rtlColResizable': function (options) {
            var defaults = {
                //Default values for the plugin's options here
            };

            options = $.extend(defaults, options);

            var isMouseButtonPressed;
            var $resizingElement = undefined;
            var resizingElementStartWidth;
            var mouseCursorStartX;
            var isCursorInResizingPosition;

            var addResizingCursorStyle = function ($element) {
                $element.css({
                    'cursor': 'col-resize',
                    'user-select': 'none',
                    '-o-user-select': 'none',
                    '-ms-user-select': 'none',
                    '-moz-user-select': 'none',
                    '-khtml-user-select': 'none',
                    '-webkit-user-select': 'none',
                });
            };

            var removeResizingCursorStyle = function ($element) {
                $element.css({
                    'cursor': 'default',
                    'user-select': 'text',
                    '-o-user-select': 'text',
                    '-ms-user-select': 'text',
                    '-moz-user-select': 'text',
                    '-khtml-user-select': 'text',
                    '-webkit-user-select': 'text',
                });
            };

            var canResize = function (e) {
                return (e.offsetX || e.clientX - $(e.target).offset().left) < 10;
            };

            return this.each(function () {

                var opts = options;
                var tableColumns = $(this).find('th');

                tableColumns.filter(':not(:last-child)').mousedown(function (e) {

                    $resizingElement = $(this);
                    isMouseButtonPressed = true;
                    mouseCursorStartX = e.pageX;
                    resizingElementStartWidth = $resizingElement.width();

                });

                tableColumns.mousemove(function (e) {

                    if (canResize(e)) {
                        addResizingCursorStyle($(e.target));
                        isCursorInResizingPosition = true;

                    } else if (!isMouseButtonPressed) {
                        removeResizingCursorStyle($(e.target));
                        isCursorInResizingPosition = false;
                    }

                    if (isCursorInResizingPosition && isMouseButtonPressed) {

                        $resizingElement.width(resizingElementStartWidth + (mouseCursorStartX - e.pageX));
                    }

                });

                $(document).mouseup(function () {
                    if (isMouseButtonPressed) {
                        removeResizingCursorStyle($resizingElement);
                        isMouseButtonPressed = false;
                    }

                });

            });
        }
    });

})(jQuery);
 
نحوه‌ی استفاده:
این افزونه با تگ Table در HTML سازگار است. فقط تنها موردی که باید رعایت شود این است که در هنگام تعریف ساختار جدول، باید استاندارد تعریف ستون‌ها یا همان Header‌ها را رعایت کنید و از تگ‌های thead و th استفاده کنید.
نمونه ای از نحوه‌ی استفاده از آن را در کدهای زیر می‌بینید:
<!DOCTYPE html>
<html>
<head>
    <title>rtlColResizable Sample</title>
</head>
<body>
    <table id="myTable">
        <thead>
            <tr>
                <th>ستون1</th> <!-- نام ستون‌ها را حتما در این تگ باید تعریف کنید -->
                <th>ستون2</th>
                <th>ستون3</th>
            </tr>
        </thead>
        <tr>
            <td>داده مربوط به ستون 1</td>
            <td>داده مربوط به ستون 2 </td>
            <td>داده مربوط به ستون 3</td>
        </tr>
    </table>

    <script type="text/javascript" src="jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="jquery.rtlColResizable.js"></script>
    <script>

        $('table#myTable').rtlColResizable();

    </script>
</body>
</html>
     
دریافت نمونه کدی از نحوه‌ی استفاده از این افزونه