مطالب دوره‌ها
بررسی قسمت‌های مختلف قالب پروژه WPF Framework تهیه شده
پس از ایجاد یک Solution جدید توسط قالب WPF Framework، هشت پروژه به صورت خودکار اضافه خواهند شد:



1) پروژه ریشه که بسته به نامی که در ابتدای کار انتخاب می‌کنید، تغییر نام خواهد یافت.
برای مثال اگر نام وارد شده در ابتدای کار MyWpfFramework باشد، این پروژه ریشه نیز، MyWpfFramework نام خواهد داشت. از آن صرفا جهت افزودن Viewهای برنامه استفاده می‌کنیم. کلیه Viewها در پوشه View قرار خواهند گرفت و با توجه به ساختار خاصی که در اینجا انتخاب شده، این Viewها باید از نوع Page انتخاب شوند تا با سیستم راهبری فریم ورک هماهنگ کار کنند.
در داخل پوشه Views، هر بخش از برنامه را می‌توان داخل یک زیر پوشه قرار داد. برای مثال قسمت Login سیستم، دارای سه صفحه ورود، نمایش پیام خوش آمد و نمایش صفحه عدم دسترسی است.
متناظر با هر Page اضافه شده، در پروژه MyWpfFramework.Infrastructure یک ViewModel در صورت نیاز اضافه خواهد شد. قرار داد ما در اینجا ترکیب نام View به علاوه کلمه ViewModel است. برای مثال اگر نام View اضافه شده به پروژه ریشه برنامه، LoginPage است، نام ViewModel متناظر با آن باید LoginPageViewModel باشد تا به صورت خودکار توسط برنامه ردیابی و وهله سازی گردد.
این پروژه از کتابخانه MahApps.Metro استفاده می‌کند و اگر به فایل MainWindow.xaml.cs آن مراجعه کنید، ارث بری پنجره اصلی برنامه را از کلاس MetroWindow مشاهده خواهید نمود. این فایل‌ها نیازی به تغییر خاصی نداشته و به همین نحو در این قالب قابل استفاده هستند.
و در پوشه Resources آن یک سری قلم و آیکون را می‌توانید مشاهده نمائید.

2) پروژه MyWpfFramework.Common
در این پروژه کلاس‌هایی قرار می‌گیرند که قابلیت استفاده در انواع و اقسام پروژه‌های WPF را دارند و الزاما وابسته به پروژه جاری نیستند. یک سری کلاس‌های کمکی در این پروژه Common قرار گرفته‌اند و قسمت‌های مختلف سیستم را تغذیه می‌کنند؛ مانند خواندن اطلاعات از فایل کانفیگ، هش کردن کلمه عبور، یک سری متد عمومی برای کار با EF، کلاس‌های عمومی مورد نیاز در حین استفاده از الگوی MVVM، اعتبارسنجی و امثال آن.
در این پروژه از کلاس PageAuthorizationAttribute آن جهت مشخص سازی وضعیت دسترسی به صفحات تعریف شده در پروژه ریشه استفاده خواهد شد.
نمونه‌ای از آن‌را برای مثال با مراجعه به سورس صفحه About.xaml.cs می‌توانید مشاهده کنید که در آن AuthorizationType.AllowAnonymous تنظیم شده و به این ترتیب تمام کاربران اعتبارسنجی نشده می‌توانند این صفحه را مشاهده کنند.
همچنین در این پروژه کلاس BaseViewModel قرار دارد که جهت مشخص سازی کلیه کلاس‌های ViewModel برنامه باید مورد استفاده قرار گیرد. سیستم طراحی شده، به کمک این کلاس پایه است که می‌تواند به صورت خودکار ViewModelهای متناظر با Viewها را یافته و وهله سازی کند (به همراه تمام وابستگی‌های تزریق شده به آن‌ها).
به علاوه کلاس DataErrorInfoBase آن برای یکپارچه سازی اعتبارسنجی با EF طراحی شده است. اگر به کلاس BaseEntity.cs مراجعه کنید که در پروژه MyWpfFramework.DomainClasses قرار دارد، نحوه استفاده آن‌را ملاحظه خواهید نمود. به این ترتیب حجم بالایی از کدهای تکرای، کپسوله شده و قابلیت استفاده مجدد را پیدا می‌کنند.
قسمت‌های دیگر پروژه Common، برای ثبت وقایع برنامه مورد استفاده قرار می‌گیرند. استفاده از آن‌ها را در فایل App.xaml.cs پروژه ریشه برنامه ملاحظه می‌کنید و نیاز به تنظیم خاص دیگری در اینجا وجود ندارد.

3) پروژه MyWpfFramework.DataLayer
کار تنظیمات EF در اینجا انجام می‌شود (و قسمت عمده‌ای از آن انجام شده است). تنها کاری که در آینده برای استفاده از آن نیاز است انجام شود، مراجعه به کلاس MyWpfFrameworkContext.cs و افزودن DbSetهای لازم است. همچنین اگر نیاز به تعریف نگاشت‌های اضافه‌تری وجود داشت، می‌توان از پوشه Mappings آن استفاده کرد.
در این پروژه الگوی واحد کار پیاده سازی شده است و همچنین سعی شده تمام کلاس‌های آن دارای کامنت‌های کافی جهت توضیح قسمت‌های مختلف باشند.
کلاس MyDbContextBase به اندازه کافی غنی سازی شده‌است، تا در وقت شما، در زمینه تنظیم مباحثی مانند اعتبارسنجی و نمایش پیغام‌های لازم به کاربر، یک دست سازی ی و ک ورودی در برنامه و بسیاری از نکات ریز دیگر صرفه جویی شود.
در اینجا از خاصیت ContextHasChanges جهت بررسی وضعیت Context جاری و نمایش پیغامی به کاربر در مورد اینکه آیا مایل هستید تغییرات را ذخیره کنید یا خیر استفاده می‌شود.
در متد auditFields آن یک سری خاصیت کلاس BaseEntity که پایه تمامی کلاس‌های Domain model برنامه خواهد بود به صورت خودکار مقدار دهی می‌شوند. مثلا این رکورد را چه کسی ثبت کرده یا چه کسی ویرایش و در چه زمانی. به این ترتیب دیگر نیازی نیست تا در برنامه نگران تنظیم و مقدار دهی آن‌ها بود.
کلاس MyWpfFrameworkMigrations به حالت AutomaticMigrationsEnabled تنظیم شده است و ... برای یک برنامه دسکتاپ WPF کافی و مطلوب است و ما را از عذاب به روز رسانی دستی ساختار بانک اطلاعاتی برنامه با تغییرات مدل‌ها، رها خواهد ساخت. عموما برنامه‌های دسکتاپ پس از طراحی، آنچنان تغییرات گسترده‌ای ندارند و انتخاب حالت Automatic در اینجا می‌تواند کار توزیع آن‌را نیز بسیار ساده کند. از این جهت که بانک اطلاعاتی انتخابی از نوع SQL Server CE نیز عمدا این هدف را دنبال می‌کند: عدم نیاز به نگهداری و وارد شدن به جزئیات نصب یک بانک اطلاعاتی بسیار پیشرفته مانند نگارش‌های کامل SQL Server. هرچند زمانیکه با EF کار می‌کنیم، سوئیچ به بانک‌های اطلاعاتی صرفا با تغییر رشته اتصالی فایل app.config برنامه اصلی و مشخص سازی پروایدر مناسب قابل انجام خواهد بود.
در فایل MyWpfFrameworkMigrations، توسط متد addRolesAndAdmin کاربر مدیر سیستم در آغاز کار ساخت بانک اطلاعاتی به صورت خودکار افزوده خواهد شد.


4) پروژه MyWpfFramework.DomainClasses
کلیه کلاس‌های متناظر با جداول بانک اطلاعاتی در پروژه MyWpfFramework.DomainClasses قرار خواهند گرفت. نکته مهمی که در اینجا باید رعایت شود، مزین کردن این کلاس‌ها به کلاس پایه BaseEntity می‌باشد که نمونه‌ای از آن‌را در کلاس User پروژه می‌توانید ملاحظه کنید.
BaseEntity چند کار را با هم انجام می‌دهد:
- اعمال خودکار DataErrorInfoBase جهت یکپارچه سازی سیستم اعتبارسنجی EF با WPF (برای مثال به این ترتیب خطاهای ذکر شده در ویژگی‌های خواص کلاس‌ها توسط WPF نیز خوانده خواهند شد)
- اعمال ImplementPropertyChanged به کلاس‌های دومین برنامه. به این ترتیب برنامه کمکی Fody که کار Aspect oriented programming را انجام می‌دهد، اسمبلی برنامه را ویرایش کرده و متدها و تغییرات لازم جهت پیاده سازی INotifyPropertyChanged را اضافه می‌کند. به این ترتیب به کلاس‌های دومین بسیار تمیزی خواهیم رسید با حداقل نیاز به تغییرات و نگهداری ثانویه.
- فراهم آوردن فیلدهای مورد نیاز جهت بازرسی سیستم؛ مانند اینکه چه کسی یک رکورد را ثبت کرده یا ویرایش و در چه زمانی

نقش‌های سیستم در کلاس SystemRole تعریف می‌شوند. به ازای هر نقش جدیدی که نیاز بود، تنها کافی است یک خاصیت bool را در اینجا اضافه کنید. سپس نام این خاصیت در ویژگی PageAuthorizationAttribute به صورت خودکار قابل استفاده خواهد بود. برای مثال به پروژه ریشه مراجعه و به فایل AddNewUser.xaml.cs دقت کنید؛ چنین تعریفی را در بالای کلاس مرتبط مشاهده خواهید کرد:
 [PageAuthorization(AuthorizationType.ApplyRequiredRoles, "IsAdmin, CanAddNewUser")]
در اینجا AuthorizationType سه حالت را می‌تواند داشته باشد:
    /// <summary>
    /// وضعیت اعتبار سنجی صفحه را مشخص می‌کند
    /// </summary>
    public enum AuthorizationType
    {
        /// <summary>
        /// همه می‌توانند بدون اعتبار سنجی، دسترسی به این صفحات داشته باشند
        /// </summary>
        AllowAnonymous,

        /// <summary>
        /// کاربران وارد شده به سیستم بدون محدودیت به این صفحات دسترسی خواهند داشت
        /// </summary>
        FreeForAuthenticatedUsers,

        /// <summary>
        /// بر اساس نام نقش‌هایی که مشخص می‌شوند تصمیم گیری خواهد شد
        /// </summary>
        ApplyRequiredRoles
    }
اگر حالت ApplyRequiredRoles را انتخاب کردید، در پارامتر اختیاری دوم ویژگی PageAuthorization نیاز است نام یک یا چند خاصیت کلاس SystemRole را قید کنید. بدیهی است کاربر متناظر نیز باید دارای این نقش‌ها باشد تا بتواند به این صفحه دسترسی پیدا کند، یا خیر.


5) پروژه MyWpfFramework.Models
در پروژه MyWpfFramework.Models کلیه Modelهای مورد استفاده در UI که الزاما قرار نیست در بانک اطلاعاتی قرارگیرند، تعریف خواهند شد. برای نمونه مدل صفحه لاگین در آن قرار دارد و ذکر دو نکته در آن حائز اهمیت است:
 [ImplementPropertyChanged] // AOP
public class LoginPageModel : DataErrorInfoBase
- ویژگی ImplementPropertyChanged کار پیاده سازی INotifyPropertyChanged را به صورت خودکار سبب خواهد شد.
- کلاس پایه DataErrorInfoBase سبب می‌شود تا مثلا در اینجا اگر از ویژگی Required استفاده کردید، اطلاعات آن توسط برنامه خوانده شود و با WPF یکپارچه گردد.


6) پروژه MyWpfFramework.Infrastructure.csproj
در پروژه MyWpfFramework.Infrastructure.csproj تعاریف ViewModelهای برنامه اضافه خواهند شد.
این پروژه دارای یک سری کلاس پایه است که تنظیمات IoC برنامه را انجام می‌دهد. برای مثال FrameFactory.cs آن یک کنترل Frame جدید را ایجاد کرده است که کار تزریق وابستگی‌ها را به صورت خودکار انجام خواهد داد. فایل IocConfig آن جایی است که کار سیم کشی کلاس‌های لایه سرویس و اینترفیس‌های متناظر با آن‌ها انجام می‌شود. البته پیش فرض‌های آن را اگر رعایت کنید، نیازی به تغییری در آن نخواهید داشت. برای مثال در آن scan.TheCallingAssembly قید شده است. در این حالت اگر نام کلاس لایه سرویس شما Test و نام اینترفیس متناظر با آن ITest باشد، به صورت خودکار به هم متصل خواهند شد.
همانطور که پیشتر نیز عنوان شد، در پوشه ViewModels آن، به ازای هر View یک ViewModel خواهیم داشت که نام آن مطابق قرار داد، نام View مدنظر به همراه کلمه ViewModel باید درنظر گرفته شود تا توسط برنامه شناخته شده و مورد استفاده قرار گیرد. همچنین هر ViewModel نیز باید دارای کلاس پایه BaseViewModel باشد تا توسط IoC Container برنامه جهت تزریق وابستگی‌های خودکار در سازنده‌های کلاس‌ها شناسایی شده و وهله سازی گردد.


7) پروژه MyWpfFramework.ServiceLayer
کلیه کلاس‌های لایه سرویس که منطق تجاری برنامه را پیاده سازی می‌کنند (خصوصا توسط EF) در این لایه قرار خواهند گرفت. در اینجا دو نمونه سرویس کاربران و سرویس عمومی AppContextService را ملاحظه می‌کنید.
سرویس AppContextService قلب سیستم اعتبارسنجی سیستم است و در IocConfig برنامه به صورت سینگلتون تعریف شده است. چون در برنامه‌های دسکتاپ در هر لحظه فقط یک نفر وارد سیستم می‌شود و نیاز است تا پایان طول عمر برنامه، اطلاعات لاگین و نقش‌های او را در حافظه نگه داری کرد.


8) پروژه MyWpfFramework.Tests
یک پروژه خالی Class library هم در اینجا جهت تعریف آزمون‌های واحد سیستم درنظر گرفته شده است.

 
مطالب
فشرده سازی حجم فایل‌های PDF توسط iTextSharp
پیشتر در سایت جاری مطلبی را در مورد «بهینه سازی حجم فایل PDF تولیدی در حین کار با تصاویر در iTextSharp» مطالعه کرده‌اید. خلاصه آن به این نحو است که می‌توان در یک فایل PDF، ده‌ها تصویر را که تنها به یک فایل فیزیکی اشاره می‌کنند قرار داد. به این ترتیب حجم فایل نهایی تا حد بسیار قابل ملاحظه‌ای کاهش می‌یابد. البته آن مطلب در مورد تولید یک فایل PDF جدید صدق می‌کند. اما در مورد فایل‌های PDF موجود و از پیش آماده شده چطور؟


سؤال: آیا در فایل PDF ما تصاویر تکراری وجود دارند؟

نحوه یافتن تصاویر تکراری موجود در یک فایل PDF را به کمک iTextSharp در کدهای ذیل ملاحظه می‌کنید:
        public static int FindDuplicateImagesCount(string pdfFileName)
        {
            int count = 0;
            var pdf = new PdfReader(pdfFileName);

            var md5 = new MD5CryptoServiceProvider();
            var enc = new UTF8Encoding();
            var imagesHashList = new List<string>();

            int intPageNum = pdf.NumberOfPages;
            for (int i = 1; i <= intPageNum; i++)
            {
                var page = pdf.GetPageN(i);
                var resources = PdfReader.GetPdfObject(page.Get(PdfName.RESOURCES)) as PdfDictionary;
                if (resources == null) continue;

                var xObject = PdfReader.GetPdfObject(resources.Get(PdfName.XOBJECT)) as PdfDictionary;
                if (xObject == null) continue;

                foreach (var name in xObject.Keys)
                {
                    var pdfObject = xObject.Get(name);
                    if (!pdfObject.IsIndirect()) continue;

                    var imgObject = PdfReader.GetPdfObject(pdfObject) as PdfDictionary;
                    if (imgObject == null) continue;

                    var subType = PdfReader.GetPdfObject(imgObject.Get(PdfName.SUBTYPE)) as PdfName;
                    if (subType == null) continue;

                    if (!PdfName.IMAGE.Equals(subType)) continue;

                    byte[] imageBytes = PdfReader.GetStreamBytesRaw((PRStream)imgObject);
                    var md5Hash = enc.GetString(md5.ComputeHash(imageBytes));

                    if (!imagesHashList.Contains(md5Hash))
                    {
                        imagesHashList.Add(md5Hash);
                    }
                    else
                    {
                        Console.WriteLine("Found duplicate image @page: {0}.", i);
                        count++;
                    }
                }
            }

            pdf.Close();
            return count;
        }
در این کد، از قابلیت‌های سطح پایین PdfReader استفاده شده است. یک فایل PDF از پیش آماده، توسط این شیء گشوده شده و سپس محتویات تصاویر آن یافت می‌شوند. در ادامه هش MD5 آن‌ها محاسبه و با یکدیگر مقایسه می‌شوند. اگر هش تکراری یافت شد، یعنی تصویر یافت شده تکراری است و این فایل قابلیت بهینه سازی و کاهش حجم (قابل ملاحظه‌ای) را دارا می‌باشد.


سؤال: چگونه اشیاء تکراری یک فایل PDF را حذف کنیم؟

کلاسی در iTextSharp به نام PdfSmartCopy وجود دارد که شبیه به عملیات فوق را انجام داده و یک کپی سبک از هر صفحه را تهیه می‌کند. سپس می‌توان این کپی‌ها را کنار هم قرار داد و فایل اصلی را مجددا بازسازی کرد:
    public class PdfSmartCopy2 : PdfSmartCopy
    {
        public PdfSmartCopy2(Document document, Stream os)
            : base(document, os)
        { }

        /// <summary>
        /// This is a forgotten feature in iTextSharp 5.3.4. 
        /// Actually its PdfSmartCopy is useless without this!
        /// </summary>
        protected override PdfIndirectReference CopyIndirect(PRIndirectReference inp, bool keepStructure, bool directRootKids)
        {
            return base.CopyIndirect(inp);
        }
    }

        public static void RemoveDuplicateObjects(string inFile, string outFile)
        {
            var document = new Document();
            var copy = new PdfSmartCopy2(document, new FileStream(outFile, FileMode.Create));
            document.Open();

            var reader = new PdfReader(inFile);

            var n = reader.NumberOfPages;
            for (int page = 0; page < n; )
            {
                copy.AddPage(copy.GetImportedPage(reader, ++page));
            }
            copy.FreeReader(reader);            

            document.Close();
        }
به نظر در نگارش iTextSharp 5.3.4 نویسندگان این کتابخانه اندکی فراموش کرده‌اند که باید تعدادی متد دیگر را نیز override کنند! به همین جهت کلاس PdfSmartCopy2 را مشاهده می‌کنید (اگر از نگارش‌های پایین‌تر استفاده می‌کنید، نیازی به آن نیست).
استفاده از آن هم ساده است. در متد RemoveDuplicateObjects، ابتدا هر صفحه موجود توسط متد GetImportedPage دریافت شده و به وهله‌ای از PdfSmartCopy اضافه می‌شود. در پایان کار، فایل نهایی تولیدی، حاوی عناصر تکراری نخواهد بود. احتمالا برنامه‌های PDF compressor تجاری را در گوشه و کنار اینترنت دیده‌اید. متد RemoveDuplicateObjects دقیقا همان کار را انجام می‌دهد. 
اگر علاقمند هستید که متد فوق را آزمایش کنید یک فایل جدید PDF را به صورت زیر ایجاد نمائید:
        private static void CreateTestFile()
        {
            using (var pdfDoc = new Document(PageSize.A4))
            {
                var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream("Test.pdf", FileMode.Create));
                pdfDoc.Open();

                var table = new PdfPTable(new float[] { 1, 2 });
                table.AddCell(Image.GetInstance("01.png"));
                table.AddCell(Image.GetInstance("01.png"));
                pdfDoc.Add(table);
            }
        }
در این فایل دو وهله از تصویر 01.png به صفحه اضافه شده‌اند. بنابراین دقیقا دو تصویر در فایل نهایی تولیدی وجود خواهد داشت.
سپس متد RemoveDuplicateObjects را روی test.pdf تولید شده فراخوانی کنید. حجم فایل حاصل تقریبا نصف خواهد شد. از این جهت که PdfSmartCopy توانسته است بر اساس هش MD5 موجود در فایل PDF نهایی، موارد تکراری را یافته و ارجاعات را تصحیح کند.
در شکل زیر ساختار فایل test.pdf اصلی را ملاحظه می‌کنید. در اینجا img1 و img0 به دو stream متفاوت اشاره می‌کنند:


در شکل زیر همان test.pdf را پس از بکارگیری PDFSmartCopy ملاحظه می‌کنید:

اینبار دو تصویر داریم که هر دو به یک stream اشاره می‌کنند. تصاویر فوق به کمک برنامه iText RUPS تهیه شده‌اند.

پاسخ به بازخورد‌های پروژه‌ها
ارسال به JsonResult
ممنون از راهنمایی شما
بالاخره بکمک اسکریپت FileSaver تونستم فایل Pdf فلش کنم البته responseType  از نوع   arraybuffer  انتخاب کردم
public ActionResult PdfReport(ReportViewModel model)
        {
            var file = new GeneratePdfReport()
                .CreatePdfReport(AutoMapperHelper
                .Map<ReportViewModel, Report>(model));
            _files.Add(file.PdfStreamOutput);


            var stream = new MemoryStream();
            new MergePdfDocuments
            {
                WriterCustomizer = info =>
                {
                    info.ImportedPage.PdfWriter.CloseStream = false;
                },
                InputFileStreams = _files,
                OutputFileStream = stream,
                AttachmentsBookmarkLabel = "Attachment(s) ",
            }
            .PerformMerge();

            stream.Flush();
            stream.Position = 0;
            return File(stream, "application/pdf", null);
        }
اسکریپت:
$http({
            url: "Report/PdfReport",
            method: "POST",
            responseType: "arraybuffer",
            data: json,
            headers: { "Content-type": "application/json" }
        }).success(function (data) {
            var blob = new Blob([data], { type: "application/pdf" });
            $scope.progressbar.complete();
            saveAs(blob, "report.pdf");
        }

مطالب
جایگزین کردن jQuery با JavaScript خالص - قسمت اول - یافتن عناصر
jQuery سال‌ها به عنوان جزء اصلی توسعه‌ی برنامه‌های وب مطرح بوده‌است و برای بسیاری از توسعه دهندگان وب، یک پیشنیاز پیش‌فرض محسوب می‌شود؛ ساده‌است، قابل فهم است و به آن اطمینان داریم. زمانیکه از آن استفاده می‌کنیم دیگر نیازی نیست تا آنچنان به DOM، باگ‌های مرورگرها و یا رفتارهای متفاوت آن‌ها فکر کنیم. jQuery تمام این مشکلات را برای ما حل می‌کند. اما ... اگر روزی باگی در jQuery وجود داشت، نیاز به امکاناتی بود که هنوز در jQuery ظاهر نشده‌اند و یا حتی اجازه‌ی استفاده‌ی از jQuery را نداشته باشیم، در این حالت ... وحشت زده و تقریبا بدون هیچ نوع آمادگی به نظر خواهیم رسید.
خالق جی‌کوئری (John Resig)، این کتابخانه را در سال‌های 2006 زمانیکه Internet Explorer نگارش‌های 6 و 7 بیش از 60 درصد بازار مرورگرها را به خود اختصاص داده بودند، ارائه داد. بله؛ در آْن زمان JavaScript Web API بسیار خام، پایداری مرورگرها بسیار پایین و تطابق با استانداردهای وب در بین مرورگرهای مختلف نیز بسیار پایین بود. بنابراین علت محبوبیت کتابخانه‌ای که در این شرایط، تجربه‌ی کاری یکدستی را در بین مرورگرهای مختلف ارائه می‌داد، کاملا واضح بود. اما ... اکنون سال 2018 است و حتی مایکروسافت هم دیگر از نگارش‌های مختلف IE پشتیبانی نمی‌کند. DOM API موجود در مرورگرهای مدرن بسیار توانمند شده‌اند و در بین انواع و اقسام آن‌ها یکدست عمل می‌کنند. حتی اگر دلیل استفاده‌ی از jQuery ایجاد ساده‌تر حلقه‌ها بر روی اشیاء جاوا اسکریپتی باشد (رفع کمبودهای جاوا اسکریپت)، از زمان IE 9 به بعد، متدهای forEach و Object.keys به صورت توکار در جاوا اسکریپت وجود دارند و یا اگر نیاز به inArray.$ داشته باشید، متد Array.prototype.indexOf مدت‌ها است که جزئی از ES5 است. به همین جهت است که این روزها اخباری را مانند «GitHub نیز جی‌کوئری را کنار گذاشت» زیاد می‌شنوید. نه فقط کنار گذاشتن jQuery یک وابستگی ثالث را از برنامه حذف می‌کند، بلکه کار مستقیم با native API مرورگرها همواره به مراتب سریعتر است از کتابخانه‌هایی که سطح بالایی از abstraction آن‌ها را ارائه می‌دهند.


یافتن عناصر توسط JavaScript خالص

زمانیکه نیاز به انتخاب عناصری در صفحه باشند، بلافاصله ذهن ما به سمت ('myElement#')$ و ('myElement.')$ جی‌کوئری، معطوف می‌شود. اما ... این روزها برای انجام این نوع کارها واقعا نیازی به jQuery نیست!


یافتن عناصر بر اساس ID آن‌ها
 <div id="my-element-id"></div>
اگر بخواهیم این شیء div را بر اساس ID آن در صفحه بیابیم، روش کار آن با jQuery به صورت زیر است:
 var result = $('#my-element-id');
در اینجا این ID selector string، یک استاندارد W3C CSS1 است.
انجام این کار توسط web API و یا همان JavaScript خالص، چنین شکلی را دارد:
 var result = document.getElementById('my-element-id');
و جالب است بدانید این روش از زمان IE 5.5 وجود داشته‌است.
روش دیگر انجام اینکار با JavaScript به صورت زیر است:
  var result = document.querySelector('#my-element-id');
این روش و متد querySelector که بسیار شبیه به نمونه‌ی جی‌کوئری ارائه شده‌است، از زمان IE 8.0 قابل استفاده‌است.
در هر دو حالت، خروجی مقایسه‌ی ذیل، true است:
 result.id === 'my-element-id'; // returns true


یافتن عناصر بر اساس کلاس‌های CSS

<span class="some-class"></span>
با جی‌کوئری:
 var result = $('.some-class');
با جاوا اسکریپت خالص از زمان IE 8.0
  var result = document.getElementsByClassName('some-class');
و یا توسط querySelectorAll که شبیه به نمونه‌ی jQuery است و نیاز به پیشوند . را دارد:
 var result = document.querySelectorAll('.some-class');
در هر دو حالت، خروجی بازگشت داده شده، یک آرایه است:
 result[0].className === 'some-class'; // returns true


یافتن عناصر بر اساس تگ‌های عناصر

 <code>Console.WriteLine("Hello world!");</code>
با جی‌کوئری:
 var result = $('code');
با جاوا اسکریپت:
 var result = document.getElementsByTagName('code');
و یا
 var result = document.querySelectorAll('code');
در تمام این حالات، خروجی ارائه شده یک آرایه است:
 result[0].tagName === 'code'; // returns true



یافتن عناصر بر اساس کلاس نماها (Pseudo-classes)

Pseudo-classes از زمان ابتدایی‌ترین پیش‌نویس استاندارد CSS وجود داشته‌اند. برای مثال visited: یک Pseudo-classes است و به لینک‌های بازدید شده‌ی توسط کاربر اشاره می‌کند و یا focus: به المانی اشاره می‌کند که هم اکنون دارای focus است.
  <form>
     <label>Full Name
        <input name="full-name">
     </label>
     <label>Company
        <input name="company">
     </label>
  </form>
در این مثال اگر بخواهیم تکست باکسی را بیابیم که دارای focus است، روش جی‌کوئری آن به صورت زیر است:
  var focusedInputs = $('INPUT:focus');
و روش جاوا اسکریپتی آن به این صورت:
 var companyInput = document.querySelector('INPUT:focus');
کاری که در اینجا انجام شده ترکیب یک tag name و یک pseudo-class modifier است که جزئی از استاندارد CSS می‌باشد. بنابراین روش جی‌کوئری، چیزی بیشتر از انتقال این استاندارد به توابع بومی مرورگر نیست.


یافتن عناصر بر اساس ارتباط والد و فرزندی آن‌ها
  <div>
     <a href="https://www.dntips.ir">
        <span>Go to site</span>
     </a>
     <p>Some text</p>
     Some other text
  </div>
یافتن والدها:
روش یافتن والد anchor tag در جی‌کوئری توسط متد parent؛ با فرض اینکه a$ به شیء anchor اشاره می‌کند:
 var $result = $a.parent();
و در جاوا اسکریپت توسط خاصیت parentNode:
 var result = a.parentNode;

یافتن فرزندان:
در جی‌کوئری:
 var result = $('#myParent').children();
و برای یافتن فرزندان یک المان توسط CSS 2 child selectors:
 var result = document.querySelectorAll('DIV > *');
خروجی این کوئری، المان‌های a و p هستند و یا اگر فقط بخواهیم pها را انتخاب کنیم:
  var result = document.querySelectorAll('DIV > P');
روش دیگر انجام اینکار استفاده از خاصیت childNodes یک المان است:
 var result = document.getElementById('myParent').childNodes;
var result = div.childNodes;
البته این خاصیت آرایه‌ای، Text و Comments را هم علاوه بر عناصر بازگشت می‌دهد. البته اگر می‌خواهید آن‌ها را حذف کنید، از خاصیت children استفاده کنید:
 var result =document.getElementById('myParent').children;
و یا یافتن تمام المان‌های anchor ذیل المانی با Id مساوی myParent:
 var result =document.querySelectorAll('#myParent A');



جستجوی عناصر با صرفنظر کردن از بعضی از آن‌ها
  <ul role="menu">
     <li>choice 1</li>
     <li class="active">choice 2</li>
     <li>choice 3</li>
  </ul>
در این مثال گزینه‌ی دوم دارای class مساوی active است. اگر بخواهیم تمام liهایی را که دارای این کلاس نیستند، انتخاب کنیم، در جی‌کوئری خواهیم داشت:
 var $result = $('UL LI').not('.active');
و در جاوا اسکریپت:
 var result = document.querySelectorAll('UL LI:not(.active)');
هرچند JavaScript دارای متد not جی‌کوئری نیست، اما می‌توان از W3C CSS3 negation pseudo-class بجای آن استفاده کرد. مزیت آن، استاندارد بودن و عدم نیاز به کتابخانه‌ای ثالث برای تدارک آن است.


انتخاب چندین المان با هم

  <div id="link-container">
     <a href="https://github.com/VahidN">GitHub</a>
  </div>
  <ol>
     <li>one</li>
     <li>two</li>
  </ol>
  <span class="my-name">VahidN</span>
در اینجا می‌خواهیم المان‌های link-container، my-name و لیست مرتب شده را بدون نوشتن حلقه‌ای انتخاب کنیم. روش انجام اینکار در jQuery به صورت زیر است:
 var $result = $('#link-container, .my-name, OL');
و در جاوا اسکریپت خواهیم داشت:
 var result = document.querySelectorAll('#link-container, .my-name, OL');

یافتن گروهی از المان‌ها بر اساس نوع آن‌ها:
 var result = document.querySelectorAll(
 'BUTTON[type="submit"], INPUT[type="submit"]'
);
در اینجا تمام المان‌های ورودی از نوع <"button type="submit> و <"input type="submit> را بازگشت می‌دهد.


جایگزین کردن $ جی‌کوئری با جاوا اسکریپت

تا اینجا حتما به شباهت کدهای خالص جاوا اسکریپت و jQuery دقت کرده‌اید. اگر بخواهیم برای $ جی‌کوئری، یک معادل جاوا اسکریپتی تهیه کنیم، به قطعه کد زیر خواهیم رسید:
window.$ = function(selector) {
     return document.querySelectorAll(selector);
};
و نحوه‌ی استفاده‌ی از آن نیز همانند قبل است:
  $('.some-class');
  $('#some-id');
  $('.some-parent > .some-child');
  $('UL LI:not(.active)');
مطالب
پالایش درخواست ها در IIS

شاید شما هم قصد داشته باشید تا از برخی درخواست‌ها به وب سایت یا اپلیکیشن خود ممانعت عمل بیاورید. نظیر درخواست‌های SQL Injection یا برخی Query String‌های خاص یا برخی درخواست‌های مزاحم.

یکی از مزاحمت هایی که گریبانگیر وب سایت هاست، Bot‌های متفاوتی است که برای کپی اطلاعات، درج کامنت به صورت خودکار و مواردی از این دست، به آنها مراجعه میکنند. شاید در نگاه اول بد نباشد که این Bot‌ها به سراغ وب سایت ما بیایند و باعث افزایش تعداد ویزیت سایتمان شوند؛ ولی ضررهای ناشی از کپی و سرقت مطالب سایت، آنهم با سرعت بالا، بیشتر از منافع ناشی از بالا رفتن رنک سایت است. به طور مثال همین سایت NET Tips. دارای تعداد زیادی مقالات مفید است که افراد متعددی در نگارش و تهیه آن‌ها زحمت کشیده اند، یا وب سایتی برای جلب اعتماد مشتریان جهت درج اطلاعاتشان و یا آگهی هایشان زحمت زیادی کشیده است، Bot‌های آماده‌ی زیادی وجود دارد که با چند دقیقه صرف وقت جهت تنظیم شدن آماده میشوند تا مطالب را طبق ساختار تعیین شده، مورد به مورد کپی کنند.

برای خلاصی از این موارد  روش‌های متعددی وجود دارد که از جمله آنها می‌توان به تنظیمات فایل htaccess در وب سرورهایی نظیر Apache و یا web.config در IIS اشاره کرد. در این مقاله این امکان را با IIS مرور میکنیم و برای فعال سازی آن کافی است در:

  • IIS 7.5 و بالاتر، همراه با انتخاب Request Filtering در مراحل نصب IIS

  • IIS 6.0 با نصب URLScan 3.0 

 در بخش <system.webServer>  و سپس <security>، تگ requestFiltering را استفاده کنیم، در این تگ دستورالعمل‌های ویژه‌ی پالایشگر درخواست‌ها را مینویسیم (filteringRules) هر دستورالعمل پالایش دارای خصیصه‌های (Attributes) زیر است:

 denyUnescapedPercent   
مقدار Boolean و انتخابی
اگر برابر با true تنظیم گردد، درخواست هایی که دارای کاراکتر "درصد" (%) هستند و به وسیله escape character ها پوشش داده نشده باشند، رد می‌شوند. (جهت جلوگیری از حملات XSS و...)
مقدار پیش فرض true است.
 
 name
عنوان دستورالعمل.
مقدار پیش فرض نداشته و درج کردن آن اجباری است.

scanAllRaw
مقدار Boolean و انتخابی
اگر برابر با true تنظیم گردد، پالایشگر درخواست‌ها موظف است تا با بررسی متن header های درخواست، در صورت یافتن یکی از واژه هایی که در خصیصه denyStrings ذکر کرده اید، درخواست را رد کند.
مقدار پیش فرض false است.
 
 scanQueryString   
مقدار Boolean و انتخابی
اگر برابر با true تنظیم گردد، پالایشگر درخواست‌ها موظف است تا Query string را بررسی کند تا در صورتی که یکی از واژه‌های درج شده در خصیصه denyStrings را بیابد، درخواست را رد کند.
اگر خصیصه‌ی unescapeQueryString از تگ < requestFiltering > برابر با true باشد، query string دوبار بررسی می‌شود: یکبار متن query string برای یافتن عبارات ممنوعه و بار دیگر برای یافتن کاراکترهای بدون پوشش scaped .
مقدار پیشفرض false است.
 
 scanUrl   
مقدار Boolean و انتخابی
اگر برابر با true تنظیم گردد، پالایشگر درخواست‌ها URL را برای یافتن واژه‌های ممنوعه‌ی ذکر شده در خصیصه denyStrings بررسی می‌نماید.
مقدار پیش فرض false است.
 

چند مثال:
مثال 1: در این مثال عنوان User-Agent هایی را که در موارد متعدد برای وب سایت هایی که روی آنها کار میکردم مزاحمت ایجاد میکردند را پالایش میکنیم. (لیست این Bot‌ها آپدیت میشود)
<requestFiltering>
  <filteringRules>
    <filteringRule name="BlockSearchEngines" scanUrl="false" scanQueryString="false">
      <scanHeaders>
        <clear />
        <add requestHeader="User-Agent" />
      </scanHeaders>
      <appliesTo>
        <clear />
      </appliesTo>
      <denyStrings>
        <clear />
        <add string="Python UrlLib" />
        <add string="WGet" />
        <add string="Apache HttpClient" />
        <add string="Unknown Bot" />
        <add string="Yandex Spider" />
        <add string="libwww-perl" />
        <add string="Nutch" />
        <add string="DotBot" />
        <add string="CCBot" />
        <add string="Majestic 12 Bot" />
        <add string="Java" />
        <add string="Link Checker" />
        <add string="Baiduspider" />
        <add string="Exabot" />
        <add string="PHP" />
      </denyStrings>
    </filteringRule>
  </filteringRules>
</requestFiltering>

مثال 2: ممانعت از SQL Injection

<requestFiltering>
   <filteringRules>
      <filteringRule name="SQLInjection" scanUrl="false" scanQueryString="true">
         <appliesTo>
            <clear />
            <add fileExtension=".asp" />
            <add fileExtension=".aspx" />
            <add fileExtension=".php" />
         </appliesTo>
         <denyStrings>
            <clear />
            <add string="--" />
            <add string=";" />
            <add string="/*" />
            <add string="@" />
            <add string="char" />
            <add string="alter" />
            <add string="begin" />
            <add string="cast" />
            <add string="create" />
            <add string="cursor" />
            <add string="declare" />
            <add string="delete" />
            <add string="drop" />
            <add string="end" />
            <add string="exec" />
            <add string="fetch" />
            <add string="insert" />
            <add string="kill" />
            <add string="open" />
            <add string="select" />
            <add string="sys" />
            <add string="table" />
            <add string="update" />
         </denyStrings>
         <scanHeaders>
            <clear />
         </scanHeaders>
      </filteringRule>
   </filteringRules>
</requestFiltering>

مثال 3: ممانعت از درخواست انواع خاصی از فایل ها

<requestFiltering>
   <filteringRules>
      <filteringRule name="Block Image Leeching" scanUrl="false" scanQueryString="false" scanAllRaw="false">
         <scanHeaders>
            <add requestHeader="User-agent" />
         </scanHeaders>
         <appliesTo>
            <add fileExtension=".zip" />
            <add fileExtension=".rar" />
            <add fileExtension=".exe" />
         </appliesTo>
         <denyStrings>
            <add string="leech-bot" />
         </denyStrings>
      </filteringRule>
   </filteringRules>
</requestFiltering>

اطلاعات بیشتر در وب سایت رسمی IIS

مطالب
آموزش QUnit #1
مقدمه:
تست و آزمایش کد برنامه‌ها و وب سایت‌هایمان، بهترین راه کاهش خطا و مشکلات آنها بعد از انتشار است. از جمله روش‌های موجود، تست واحد است که ویژوال استادیو نیز از آن برای پروژه‌های دات نت پشتیبانی می‌کند. با افزایش روز افزون کتابخانه‌های جاوا اسکریپتی و جی کوئری، نیاز به تست کد‌های جاواسکریپتی نیز بیشتر به نظر می‌رسد و بهتر است تست واحد و آزمایش شوند. اما برخلاف کدهای #C و ASP.NET تست کد‌های جاوا اسکریپت، مخصوصا زمانی که به دستکاری عناصر DOM می‌پردازیم و یا رویداد‌های درون صفحه وب را با استفاده از جی کوئری می‌نویسیم، حتی اگر در فایل جداگانه‌ای نوشته شود، این بدان معنی نیست که آماده تست واحد است و ممکن است امکان نوشتن تست وجود نداشته باشد.
بنابراین چه چیزی یک تست واحد است؟ در بهترین حالت توابعی که مقداری را برمی گردادنند، بهترین حالت برای تست واحد است. اما در بیشتر موارد شما نیاز دارید تا تاثیر کد را بر روی عناصر صفحه نیز مشاهد نمایید.
ساخت تست واحد
برای تست پذیری بهتر، توابع جاوا اسکریپت و هر کد دیگری، آن را می‌بایست طوری بنویسید که مقادیر تاثیر گذار در اجرای تابع به عنوان ورودی تابع در نظر گرفته شده باشند و همیشه نتیجه به عنوان خروجی تابع برگردانده شود؛ قطعه کد زیر را در نظر بگیرید:
 function prettyDate(time){
    var date = new Date(time || ""),
      diff = (((new Date()).getTime() - date.getTime()) / 1000),
      day_diff = Math.floor(diff / 86400);
 
    if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
      return;
 
    return day_diff == 0 && (
        diff < 60 && "just now" ||
        diff < 120 && "1 minute ago" ||
        diff < 3600 && Math.floor( diff / 60 ) +
          " minutes ago" ||
        diff < 7200 && "1 hour ago" ||
        diff < 86400 && Math.floor( diff / 3600 ) +
          " hours ago") ||
      day_diff == 1 && "Yesterday" ||
      day_diff < 7 && day_diff + " days ago" ||
      day_diff < 31 && Math.ceil( day_diff / 7 ) +
        " weeks ago";
  }
تابع pertyDate اختلاف زمان حال را نسبت به زمان ورودی، بصورت یک رشته برمی گرداند. اما در اینجا مقدار زمان حال، در خط سوم، در خود تابع ایجاد شده است و در صورتی که بخواهیم برای چندین مقدار آن را تست کنیم زمان حال متفاوتی در نظر گرفته می‌شود و حداکثر، زمان 31 روز قبل را نمایش داده و در بقیه تاریخ ها undefined را بر می‌گرداند. برای تست واحد، چند تغییر می‌دهیم.
بهینه سازی، مرحله اول:
پارامتری به عنوان مقدار زمان جاری برای تابع در نظر می‌گیریم و تابع را جدا کرده و در یک فایل جداگانه قرار می‌دهیم. فایل prettydate.js بصورت زیر خواهد شد.
function prettyDate(now, time){
  var date = new Date(time || ""),
    diff = (((new Date(now)).getTime() - date.getTime()) / 1000),
    day_diff = Math.floor(diff / 86400);
 
  if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
    return;
 
  return day_diff == 0 && (
      diff < 60 && "just now" ||
      diff < 120 && "1 minute ago" ||
      diff < 3600 && Math.floor( diff / 60 ) +
        " minutes ago" ||
      diff < 7200 && "1 hour ago" ||
      diff < 86400 && Math.floor( diff / 3600 ) +
        " hours ago") ||
    day_diff == 1 && "Yesterday" ||
    day_diff < 7 && day_diff + " days ago" ||
    day_diff < 31 && Math.ceil( day_diff / 7 ) +
      " weeks ago";
}
حال یک تابع برای تست داریم، چند تست واحد واقعی می‌نویسیم
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Refactored date examples</title>
  <script src="prettydate.js"></script>
  <script>
  function test(then, expected) {
    results.total++;
    var result = prettyDate("2013/01/28 22:25:00", then);
    if (result !== expected) {
      results.bad++;
      console.log("Expected " + expected +
        ", but was " + result);
    }
  }
  var results = {
    total: 0,
    bad: 0
  };
  test("2013/01/28 22:24:30", "just now");
  test("2013/01/28 22:23:30", "1 minute ago");
  test("2013/01/28 21:23:30", "1 hour ago");
  test("2013/01/27 22:23:30", "Yesterday");
  test("2013/01/26 22:23:30", "2 days ago");
  test("2012/01/26 22:23:30", undefined);
  console.log("Of " + results.total + " tests, " +
    results.bad + " failed, " +
    (results.total - results.bad) + " passed.");
  </script>
</head>
<body>
 
</body>
</html>
در کد بالا یک تابع بدون استفاده از Qunit برای تست واحد نوشته ایم که با آن تابع prettyDate را تست می‌کند. تابع test مقدار زمان حال و رشته خروجی را گرفته  و آن را با تابع اصلی تست می‌کند در آخر تعداد تست ها، تست‌های شکست خورده و تست  های پاس شده گزارش داده می‌شود.
خروجی می‌تواند مانند زیر باشد:

Of 6 tests, 0 failed, 6 passed.
Expected 2 day ago, but was 2 days ago. 
f 6 tests, 1 failed, 5 passed.

نظرات مطالب
استفاده از pjax بجای ajax در ASP.NET MVC
برای دیباگ کار، بررسی کنید:
- آیا لینک‌هایی که بر روی آن‌ها کلیک می‌شود، ویژگی withpjax را دارند؟ آیا اسکریپت متناظر با آن به صفحه پیوست شده؟ 
آیا خطایی مشاهده نمی‌شود؟
- آیا header مربوط به X-PJAX در درخواست ارسالی به سرور مطابق تصاویر فوق وجود دارند؟
- آیا در سمت سرور بر اساس هدر
X-PJAX دریافتی، فایل layout صحیحی تنظیم می‌شود؟