نظرات اشتراک‌ها
روش دیگری برای تمیزسازی HTML و مقابله با XSS
سلام آقای نصیری بنده اینجا که توضیحی مشاهده نمیکنم.ممنون میشم برای تمیزکاری کدهای html راهی بهمون معرفی کنید بنده خودم تویه سایتم از getsafehtmlfragment استفاده کردم ولی مشکل اینجاست که تویه نسخه جدید این متد تمام تگ‌ها رو حذف میکنه از جمله تگ‌های html و اینجاست که برای گرفتن امن اطلاعات یک ادیتور با مشکل مواجه میشیم.فکر کنم باید از htmlagilitypack استفاده بشه که نحوه کار کردن باهاش رو بلد نیستم بنده یه سوال دیگه هم دارم ممنون میشم تویه این پست توضیح بدهید برای نمایش امن اطلاعات یک ادیتور باید چیکار کنیم وقتی از htmlencode استفاده میکنم تمام تگ‌ها از جمله تگ‌های html رو نشون میده باید چیکار کنم شما که زحمت کشید یه توضیح کامل بدید که همگی استفاده کنیم ممنون از شما
نظرات مطالب
نوع‌های نال نپذیر در TypeScript
یک نکته‌ی تکمیلی: اضافه شدن strictPropertyInitialization به TypeScript 2.7

در نگارش 2.7 اگر یک چنین تعریفی را داشته باشید:
export class MovieComponent {

  @Input() movie: Movie;

}
خطای زیر را دریافت خواهید کرد:
Error! Property movie has no initializer and is not assigned directly in the constructor.
یک روش برطرف کردن این خطا، نال‌پذیر تعریف کردن این خاصیت و سپس مقدار دهی اولیه‌ی به آن است:
@Input() movie: Movie | null = null;
و یا در مثال زیر می‌توان مشخص کرد که baz می‌تواند undefined هم باشد:
class C {
    baz: boolean | undefined;
}
روش دیگر، مقدار دهی اولیه، حین تعریف یک خاصیت است:
class C {
    bar = "hello";
}
و یا می‌توان این بررسی را به صورت زیر خاموش کرد:
class C {
    foo!: number;

    ngOnInit() {
        this.foo = 0;
    }
}
با استفاده از عملگر ! به کامپایلر اعلام می‌کنیم که این خاصیت حتما قرار است توسط روشی کمکی مقدار دهی اولیه شود و نال یا بدون مقدار نیست و استفاده‌ی از آن در این کلاس امن است.
مطالب
مروری بر کتابخانه ReactJS - قسمت هشتم - آخرین قسمت - چرخه حیات کامپوننت‌ها

هر کامپوننتی در React یک چرخه زندگی دارد. زمانیکه یک کامپوننت را به روش React.createClass یا React.Component تعریف میکنیم و در ReactDOM.render نمونه‌ای از کامپوننت را برای نمایش در مرورگر می‌سازیم، چرخه حیات آن شروع میشود. 


ReactDOMServer

کتابخانه ReactDOMServer جهت ساخت یا render کردن کامپوننت‌ها در سمت سرور استفاده میشود. توسط این کتابخانه میتوانیم کامپوننت‌ها را در سمت سرور ایجاد کنیم و نتیجه آن را که تگ‌های HTML هستند به مرورگر ارسال کنیم. این روش جهت داشتن صفحه‌های وب سریع‌تر و اهداف SEO مفید است. جهت اطلاعات بیشتر و روش‌های استفاده به مستندات آن رجوع کنید. در مثال زیر روش استفاده از این کتابخانه به اختصار آمده.

var persons = [
    { id: 1, personName: "Parham", personContact: "parhamda@gmail.com" },
    { id: 2, personName: "Roham", personContact: "roham@yahoo.com" },
    { id: 3, personName: "Raha", personContact: "raha@live.com" }
];

class Person extends React.Component{
    render(){
        return (
            <div>
                <p>{this.props.personName}</p>
                <p>{this.props.personContact}</p>
            </div>
        )
    }
}

let person1 = persons[0];
let personElement = <Person personName={person1.personName} personContact={person1.personContact}/>
console.log(ReactDOMServer.renderToStaticMarkup(personElement));

در کد بالا مواردی که جدید هستند، یکی ساخت یک نمونه از کامپوننت Person است و دیگری ساخت آن در سمت سرور، بدون آن که فعلا نمایشی در مرورگر داشته باشیم. در کنسول میتوانیم خروجی کتابخانه را که تگ‌های HTML هستند ببینیم. ReactDOMServer دو متد را فراهم کرده که کارکردی مشابه دارند؛ اما در جزئیات متفاوت هستند. 

  • renderStaticMarkup یک خروجی استاتیک و بدون attributeهای اضافه را تولید میکند که بیشتر برای بررسی یا استفاده در صفحه‌های وب ایستا مفید هستند.
  • renderToString یک خروجی به صورت HTML String ایجاد میکند که برای HTML DOM در سمت کاربر سازگار‌تر است و مناسب برای صفحات پویا. 

در نهایت خروجی از هر نوع که بود، برای اینکه در سمت کاربر قابل مشاهده باشد باید از همان متد ReactDOM.render استفاده کنیم. از آنجایی که این مجموعه جهت معرفی و بررسی ابزارهای اصلی React به صورت مختصر است، از آوردن مثال‌های زیاد و پیچیده پرهیز میکنم. در اینجا میتوانید یک نمونه ساده برای استفاده از ReactDOMServer به صورت استاندارد و با جزئیات را بررسی کنید.


متدهای چرخه حیات در React

React چند متد را برای زمان‌های قبل و بعد از ساخت شدن یک کامپوننت در DOM دارد که میشود رفتارهایی را برای کامپوننت، در این متدها در نظر گرفت تا در زمان مناسب اجرا شوند. در ادامه این متد‌ها معرفی و کاربرد هر یک بیان میشود. 


 componentWillMount: این متد قبل از اینکه کامپوننت، تگ‌های متد render را بسازد اجرا میشود. این متد هم در سمت کلاینت کاربرد دارد و هم در سمت سرور. به همین جهت برای گرفتن log از داده‌های کامپوننت و کار با پایگاه داده مکان مناسبی است. به عنوان مثال در قطعه کد زیر داده‌های کامپوننت، توسط Ajax ارسال شده‌اند. 

componentWillMount() {
   Ajax.post("/componentLog", {
     name: this.constructor.name,
     props: this.props
   });
}

componentDidMount: این متد بعد از اینکه بخش render اجرا شد فراخوانی میشود. همچنین فقط در سمت کلاینت و زمانیکه از ReactDOM.render استفاده میکنیم کاربرد دارد. این متد مناسب برای تعامل کامپوننت با افزونه‌ها و API‌ها است؛ مانند دریافت اطلاعات مورد نیاز کامپوننت از سایتی دیگر توسط یک API. از  این متد در قسمت چهارم مثالی آورده شده. 


(componentWillReciveProps(nextProps: این متد زمانی اجرا میشود که داده‌های ورودی کامپوننت با مقادیری جدید تغییر کنند.

componentWillReceiveProps(nextProps) {
    // Do something with new received data and change the state. 
}

ReactDOM.render(
    <TestComponent someData={newDataEveryFiveSecond()}/>,
    document.getElementById("divTest")
);

در مثال بالا یک کامپوننت داریم که داده‌های ورودی خود را از یک تابع میگیرد. این تابع هر پنج ثانیه یک بار یک داده تازه ایجاد میکند و به کامپوننت ارسال میکند. میتوانیم داخل کامپوننت، از متد componentWillReceiveProps جهت دستکاری داده‌های رسیده و تغییر وضعیت کامپوننت توسط setState استفاده کنیم. 


(shouldComponentUpdate(nextProps, nextState: این متد شبیه به متد componentWillReceiveProps است، البته با تفاوت‌هایی. این متد هم مقدار ورودی جدید برای پارامتر‌های کامپوننت میگیرد و هم مقداری برای وضعیتی که کامپوننت دارد. این متد باید یک مقدار بازگشتی false یا  true داشته باشد. با این مقدار بازگشتی میتوان کنترل کرد که آیا کامپوننت بر اساس داده‌های جدید بروز بشود یا نه. 

class ComponentExample extends React.Component {
    shouldComponentUpdate(nextProps, nextState) {
        return notEqual(this.props, nextProps) ||
            notEqual(this.state, nextState);
    }
}

در مثال بالا پارامترها و وضعیت جاری کامپوننت، با مقدارهای تازه تغییر یافته و وضعیت جدید مقایسه میشوند. اگر مقادیر مقایسه شده برابر نباشند (یعنی داده تکراری وارد نشده) مقدار بازگشتی true خواهد بود و React کامپوننت را بر اساس وضعیت جدید و داده‌های تازه دوباره میسازد.


(componentWillUpdate(nextProps, nextState: این متد زمانیکه کامپوننت ساخته شده، داده‌های جدیدی را دریافت کند و یا وضعیت آن تغییر کند و دقیقا قبل از اجرای render فراخوانی میشود. اگر از متد shouldComponentUpdate مقدار false بازگشت داده شود، این متد دیگر اجرا نخواهد شد. باید توجه داشته باشیم که setState را نمیشود در این متد پیاده‌سازی کرده. به این علت که، زمانیکه وضعیت کامپوننت تغییر میکند، React متد componentWillUpdate و بلافاصله بعد از آن render را اجرا میکند و برای تغییر وضعیت دیگر دیر شده! تفاوت componentWillUpdate با componentWillMount  این است که Will Mount در اولین وهله سازی از کامپوننت اجرا میشود، ولی Will Update بعد از هر دوباره سازی (rerender). 


(componentDidUpdate(prevProps, prevStat: احتمالا میشود به راحتی حدس زد که این متد دقیقا بعد از دوباره سازی کامپوننتی که ساخته شده فراخوانی میشود.


componentWillUnmount: این متد زمانی اجرا میشود که یک کامپوننت از DOM پاک شود. برای پاک کردن نمونه‌ای از یک کامپوننت که در DOM در حال نمایش است میتوانیم از دستور زیر استفاده کنیم. 

ReactDOM.unmountComponentAtNode(document.getElementById("react"));
مطالب
WebStorage
webstorage تقریبا فناوری جدیدی است که برای نگهداری ثابت داده‌ها بر روی سیستم کاربر استفاده می‌شود. webstorage مزایای زیادی برای برنامه‌های تحت وب دارد. برای مثال با استفاده از آن میتوان فعالیت‌های کاربر را رصد کرد، بدون اینکه کد و دیتابیس سمت سرور را دخالت دهیم. حتی اگر سیستم کاربر آفلاین هم بشود برنامه می‌تواند همچنان به فعالیتش ادامه دهد. در این مقاله به مزایای این روش می‌پردازیم.

WebStorage در برابر کوکی ها
یکی از روش‌های سنتی ذخیره اطلاعات در سیستم کاربر، کوکی‌ها در بستر Http هستند. تفاوت‌های زیادی بین این دو وجود دارد که تعدادی از آن‌ها را در زیر بررسی می‌کنیم:

  • مکانیزم ذخیره سازی:
کوکی‌ها داده‌های ساخت یافته‌ای هستند که از وب سرور به سمت مرورگر کاربر به عنوان پاسخی در ازای درخواستی ارسال می‌شوند. درخواست و پاسخ کوکی‌ها شامل بخش هدر بوده که اطلاعات آن باعث شناسایی کوکی برای مدیریت و شناسایی آن‌ها می‌گردد تا هر موقع درخواستی صورت بگیرد، به سمت سرور برگشت خواهد خورد.
به طور خلاصه اینکه کوکی‌ها توسط درخواست‌ها و پاسخ‌ها ایجاد یا به روز می‌شوند. در نتیجه داده‌ها چه تغییر کرده باشند چه تغییر نکرده باشند، همیشه بخشی از هدر Http هستند. در سوی دیگر webstorage به طور کامل به صورت کلاینتی پیاده سازی گشته است و وب سرور را درگیر کار خودشان نمی‌کنند و کارایی بهتری را ارائه می‌دهند. از آنجا که همه چیز در خود سیستم کاربر اتفاق می‌افتد، در صورت از دست دادن کانکشن شبکه، کاربر می‌تواند همچنان به فعالیت‌های به روزرسانی و تغییر ادامه دهد.

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

  • محدودیت حجمی
محدودیت کوکی‌ها و webstorage از نظر حجم ذخیره سازی بین مروگرهای مختلف، متفاوت است. ولی در حالت کلی در بیشتر مرورگرها محدودیت حجم 4 کیلوبایت برای کوکی‌ها موجود است. (این ابزار می تواند نهایت حجمی را که که مرورگر شما از کوکی پشتیبانی می‌کند، نشان دهد.)
در مورد webstorage استاندارد W3C محدودیتی اعلام نکرده است و تصمیم گیری بر سر این موضوع را به سازندگان مرورگرها واگذار کرده است. ولی در حالت کلی حجم بیشتری از کوکی را ذخیره میکند و عموما تا 5 مگابایت توانایی ذخیره سازی وجود دارد. بدین ترتیب حجم آن 124,527% بیشتر از کوکی‌ها است. (این ابزار می‌تواند نهایت حجمی را که مرورگر شما از webstorage پشتیبانی می‌کند، ببینید).

انواع Webstorage 
دو نوع متد ذخیره سازی در webstorage داریم:
  • session storage
  • local storage

  • SessionStorage
داده‌هایی که بدین صورت ذخیره می‌شوند تنها تا زمانی دوام آورده و پایدار هستند که session مرورگر فعال است؛ یعنی تا زمانیکه کاربر در سایت فعلی حضور دارد.
استفاده از این روش برای ذخیره سازی‌های موقت عالی است. برای نمونه مقادیر ورودی فرمی که کاربر در حال کار با آن است، میتواند به طور موقت ذخیره شوند تا زمانی که کاربر بتواند تمامی مراحل را طی کرده، بدون اینکه ارجاعی به دیتابیس سمت سرور داشته باشد. همچنین ذخیره موقت داده‌ها می‌تواند به کاربر کمک کند تا در صورت reresh‌های ناگهانی یا بسته شدن‌های ناگهانی مرورگرها، نیازی به ورود مجدد داده‌ها نداشته باشد.
  • LocalStorage
در این روش داده‌ها با از دست رفتن session مرورگر جاری از بین نمی‌رود و برای بازدیدهای آتی کاربر از سایت، داده‌ها همچنان در دسترس هستند.
پشتیبانی مرورگرها
وب سایت caniuse گزارش می‌دهد که اکثر مرورگرها پشتیبانی خوبی از webstorage دارند.

با اینکه توصیه نامه W3C از پایان کار پیاده سازی این قابلیت خبر میدهد ولی در حال حاضر که این مقاله تدوین شده است هنوز نهایی اعلام نشده است. برای پشتیبانی مرورگرهای قدیمی از webstorage می‌توان از فایل جاوااسکریپتی Store.js کمک گرفت.


مفاهیم امنیتی و محافظت از داده ها

محدودیت‌های حمایتی و حفاظتی webstorage دقیقا همانند کوکی هاست. به این معنی که وب سایت‌های دیگر توانایی اتصال به webstorage سایت دیگری را ندارند. البته این مورد ممکن است برای وب سایت هایی که بر ساب دومین تکیه کرده‌اند ایجاد مشکل کند. برای حل این مسائل می‌توانید از کتابخانه‌های سورس بازی چون Cross Storage که توسط Zendesk ارائه شده است، استفاده کرد.

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

Data Integrity یا یکپارچگی داده‌ها نیز در نظر گرفته شده است. باید حفاظتی در برابر عدم موفقیت ذخیره سازی داده‌ها نیز وجود داشته باشد. این عدم موفقیت‌ها میتواند به دلایل زیر رخ دهد:

  • اگر کاربر قابلیت webstorage را غیرفعال کرده باشد.
  • اگر فضایی برای کاربر باقی نمانده باشد.
  • با محدودیت حجمی webstorage مواجه شده است.
  • با مواجه شدن با خطاها یک استثنا صادر می‌شود که می‌توانید آن را دریافت و کنترلی را روی برنامه تحت وب داشته باشید. یک نمونه استثنا  QuotaExceededError  


IndexedDB

یکی از فرایندهای ذخیره سازی داده‌ها که همان مزایای webstorage را ارائه میدهد indexed Database API است. این قابلیت از HTML 5 اضافه شده است و قسمتی از مشخصات webstorage شناخته نمی‌شود. برای همین مستنداتی در حوزه‌ی webstorage برای آن پیدا نخواهید کرد ولی قابلیت‌هایی فراتر از webstorage  دارد.

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

در قسمت آتی نحوه کدنویسی آن را فرا خواهیم گرفت.

مطالب
مفاهیم پایه سیستم های کنترل نسخه؛ قسمت دوم : SVN
در قسمت قبلی، اهمیت استفاده از سیستم‌های کنترل نسخه را بیان کردیم و مفاهیم پایه‌ای گیت را مورد بررسی قرار دادیم. در این قسمت مفاهیم پایه‌ای SVN را مورد بررسی قرار می‌دهیم.
SVN مخفف عبارت SubVersion هست و یک سیستم کنترل نسخه‌ی رایگان و متن باز است که توسط شرکت  کلاب نت حمایت می‌شود. به تعدادی از این سیستم‌ها، سیستم‌های «مدیریت پیکربندی نرم افزار»   (Software Configuration Manager (SCM هم اطلاق می‌شود.
در این سیستم فایل‌ها در یک مخزن Repository مرکزی ذخیره می‌شوند و با هر تغییری که در فایل‌ها و دایرکتوری‌ها ایجاد می‌شود، آن‌ها را ثبت می‌کند. این امکان به ما این اجازه را می‌دهد که نسخه‌ی قدیمی فایل‌ها را بازیابی کرده و تاریخچه‌ی اینکه فایل‌ها چگونه و چه موقع و توسط چه کسی تغییر کرده‌اند، به ما نشان دهد. تصویر سلسله مراتبی زیر به خوبی نحوه ارتباط کلاینت‌ها را به این مخزن نشان می‌دهد.
SVN برای مدیریت چندین نسخه از فایل ها، از مدل «کپی، ویرایش، ادغام» Copy-Modify-Merge استفاده می‌کند. در این مدل که هر کاربری در مخزن عمل خواندن را انجام می‌دهد، یک کپی جداگانه و کاملا شخصی برای او گرفته شده و سپس کپی‌های شخصی خودش را ویرایش می‌کند. بعد از اینکه ویرایش تکمیل شد، کپی شخصی خودش را با یک فایل جدید و نهایی ادغام می‌کند.
این روش به شدت از روش «قفل کردن، ویرایش، آزادسازی» »Lock-Modify-Unlock کارآمدتر است و دیگر نیازی نیست که یک کاربر در یک زمان به این ساختار دسترسی داشته باشد و آن را ویرایش کند.

در تصویر بالا هری و سالی، یک کپی از مخزن موجود را گرفته و سپس هر کدام جداگانه بر روی کپی‌های خودشان مشغول به کار می‌شوند. سپس سالی کارش را زودتر به اتمام رسانده و مخزن را به روز می‌کند. بعد از آن، هری هم کارش به پایان می‌رسد و قصد به روز کردن مخزن را دارد ولی سیستم به او اجازه این کار را نمی‌دهد؛ چون این مخزن آن مخزن نیست که هری قبلا از آن کپی گرفته است. آن مخزن بعد از به روزرسانی سالی تغییر یافته است. پس او مجبور است تا تغییرات جدید مخزن را دریافت کرده و کپی خودش را به روز کند. پس از آن می‌تواند کپی خودش را بر روی مخزن اعمال کند (با فرض اینکه تغییرات جدید هیچ تصادمی با تغییراتی که روی کپی خودش اعمال کرده است ندارند).


سناریو بالا با احتساب وجود تصادم

اگر همین سناریوی بالا را فرض کنیم که تغییراتی که هری روی فایل‌ها داده است همان تغییراتی است که سالی قبلا روی مخزن اصلی روی همان فایل‌ها  اعمال کرده است، آیا در این حالت دریافت به روزرسانی‌های جدید باعث ایجاد تصادم می‌شود؟

هری درخواست ادغام آخرین تغییرات مخزن را با کپی خودش می‌کند. از آنجا که فایل A تصادم دارد یک فلگ flag از این وضعیت می‌گیرد. حال هری میتواند تفاوت‌های ایجاد شده را ببیند و بین آن‌ها یکی را انتخاب کند. در این وضعیت هری همپوشانی‌های کدها را برطرف می‌کند و شاید هم بحثی در مورد این تصادم با سالی داشته باشد تا بهترین تغییر کد انتخاب گردد و نهایتا به روشی کاملا امن و مطمئن، با مخزن اصلی ادغام می‌شود.

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

مطالب
جایگزین کردن jQuery با JavaScript خالص - قسمت ششم - رخدادهای مرورگرها
واکنش نشان دادن به تغییرات صفحات وب، قسمت مهم و عمده‌ای از کار توسعه‌ی برنامه‌های وب را تشکیل می‌دهد. مفاهیم مرتبط با DOM events از زمان IE 4.0 و Netscape Navigator version 2 به مرورگرها اضافه شدند و به مرور تکامل یافتند. پیش از ظهور مرورگرهای مدرن (به IE 9.0 و مرورگرهای پیش از آن، مرورگرهای «باستانی» گفته می‌شود) به علت عدم هماهنگی آن‌ها با استانداردهای وب و تفاوت روش‌های رخدادگردانی، jQuery نقش مهمی را در زمینه‌ی یکدست سازی کدهای مدیریت رخدادها در بین مرورگرهای مختلف ارائه کرد. اما با پیش‌رفت‌های صورت گرفته و هماهنگی بیشتر مرورگرها با استانداردهای وب، دیگر نیازی به jQuery برای ارائه‌ی کدهای یکدست رخدادگردانی نیست و کار مستقیم با API وب مرورگرها برای این منظور کافی است.


انواع رخدادها: بومی و سفارشی

دو رده بندی عمومی رخدادها در مرورگرها وجود دارند: بومی و سفارشی.
بومی‌ها همان‌هایی هستند که در مستندات رسمی استانداردهای وب ذکر شده‌اند؛ مانند click که توسط ماوس و یا صفحه کلید فعال می‌شود و یا load که در زمان بارگذاری کامل صفحه، تصاویر و یا یک iframe رخ می‌دهد.
رخدادهای سفارشی مواردی هستند که توسط یک کتابخانه‌ی خاص و یا جهت یک برنامه‌ی خاص تهیه شده‌اند. مانند یک رخداد سفارشی که زمان شروع آپلود یک فایل را اعلام می‌کند.
رخدادهای سفارشی که بدون jQuery ایجاد و رخ‌می‌دهند، توسط jQuery نیز قابل بررسی و مدیریت هستند و نه برعکس. به عبارتی رخدادهای سفارشی ایجاد شده‌ی توسط jQuery غیراستاندارد بوده و صرفا مختص به API آن هستند.
در این بین، شیء استاندارد Event کار اتصال رخدادهای سفارشی و استاندارد را انجام می‌دهد. هر نوع رخداد DOM (سفارشی و یا بومی)، توسط یک شیء Event بیان می‌شود که آن نیز به همراه تعدادی خاصیت و متد، جهت مدیریت این رخداد است. برای مثال رخداد click دارای خاصیت type ایی به نام click است که در شیء Event متناظر با آن تعریف شده‌است.


انتشار رخدادها در صفحه

در روزهای آغازین وب، Netscape روش event capturing را برای انتشار رخدادها در صفحه ارائه داد و در مقابل آن IE روش event bubbling را معرفی کرد که متضاد یکدیگر بودند. در سال 2000 با ارائه استاندارد DOM Level 2 Events Specification، این وضعیت تغییر کرد و شامل هر دو مورد event capturing و event bubbling است و در حال حاضر تمام مرورگرهای مدرن این استاندارد را پیاده سازی کرده‌اند. بر اساس این استاندارد، زمانیکه رویدادی خلق می‌شود، فاز capturing آغاز می‌گردد که از شیء window شروع، سپس به شیء document منتشر می‌شود و این روند تا رسیدن به المانی که سبب بروز رخداد شده‌است ادامه پیدا می‌کند. پس از پایان فاز capturing، فاز جدید bubbling شروع می‌شود. در این فاز، رخداد از تمام والدین شیء هدف عبور می‌کند تا به شیء window برسد.
برای مثال اگر سند HTML ما چنین تعریفی را داشته باشد و بر روی المان «child of child of one» کلیک شده باشد:
   <!DOCTYPE html> 
   <html>
   <head>
     <title>event propagation demo</title>
   </head>
   <body>
     <section>
       <h1>nested divs</h1>
       <div>one
        <div>child of one
          <div>child of child of one</div>
        </div>
      </div>
    </section>
  </body>
  </html>
این رخداد در فاز capturing از این المان‌ها عبور می‌کند:
1.window
2.document
3.<html>
4.<body>
5.<section>
6.<div>one
7.<div>child of one
8.<div>child of child of one
و در فاز Bubbling از این المان‌ها:
9.<div>child of child of one
10.<div>child of one
11.<div>one
12.<section>
13.<body>
14.<html>
15.document
16.window
هرچند به دلایل تاریخی و همچنین عدم پشتیبانی jQuery از فاز capturing، بیشتر از فاز Bubbling به صورت پیش‌فرض در رخدادگردانی استفاده می‌شود. همچنین صدور رخداد از المانی که آن‌را ایجاد کرده‌است، بیشتر منطقی به نظر می‌رسد تا عکس آن.
البته باید درنظر داشت که jQuery از روش ارائه شده‌ی توسط مرورگر برای فاز Bubbling استفاده نمی‌کند و این مسیر را خودش مجددا محاسبه و رخدادگردان‌های این مسیر را به صورت دستی اجرا می‌کند. به همین جهت کارآیی آن نسبت به روش توکار و بومی مرورگرها کمتر است.


ایجاد رخدادهای DOM و صدور آن‌ها در jQuery

برای نمایش ایجاد و صدور رخدادهای DOM با و بدون jQuery، از قطعه کد HTML زیر استفاده می‌کنیم:
   <div>
     <button type="button">do something</button>
   </div>
 
  <form method="POST" action="/user">
     <label>Enter user name:
       <input name="user">
     </label>
     <button type="submit">submit</button>
  </form>
jQuery به همراه دو متد trigger و triggerHandler برای ایجاد و انتشار رخدادها در طول DOM است. متد trigger سبب ایجاد، صدور و انتشار یک رخداد به تمام والدهای شیء صادر کننده‌ی رخداد می‌شود. نوع این انتشار نیز bubbling است. متد triggerHandler، فقط بر روی المانی که فراخوانی می‌شود، عمل کرده و سبب انتشار این رخداد از طریق bubbling نمی‌شود:
   // submits the form 
   $('FORM').trigger('submit');
 
   // submits the form by clicking the button 
   $('BUTTON[type="submit"]').trigger('click');
 
   // focuses the text input 
   $('INPUT').trigger('focus');
 
  // removes focus from the text input 
  $('INPUT').trigger('blur');
در این مثال‌ها توسط متد trigger، به دو روش سبب submit یک فرم و همچنین در ابتدا سبب focus یک تکست باکس و سپس رفع آن شده‌ایم.
هرچند روش دومی نیز در jQuery API برای انجام همینکارها نیز پیش بینی شده‌است:
   // submits the form 
   $('FORM').submit();
 
   // submits the form by clicking the button 
   $('BUTTON[type="submit"]').click();
 
   // focuses the text input 
   $('INPUT').focus();
 
  // removes focus from the text input 
  $('INPUT').blur();
در اینجا به ازای هر رخداد، یک نام مستعار در jQuery API تدارک دیده شده‌است.

در ادامه فرض کنید یک دکمه داخل یک div قرار گرفته‌است و آن div نیز به همراه یک مدیریت کننده‌ی رخداد کلیک است. در این حالت اگر بخواهیم با کلیک بر روی دکمه سبب اجرای رویدادگردان div والد نشویم، می‌توان از متد triggerHandler استفاده کرد:
  // clicks the first button - the click event does not bubble 
  $('BUTTON[type="button"]').triggerHandler('click');


ایجاد رخدادهای DOM و صدور آن‌ها در جاوا اسکریپت (بدون استفاده از jQuery)

در web API مرورگرها، برای انجام بروز رخدادهای معادل مثالی که با jQuery مطرح شد، می‌توان متدهای بومی متناظر با این رخدادها را بر روی المان‌ها فراخوانی کرد:
   // submits the form 
   document.querySelector('FORM').submit();
 
   // submits the form by clicking the button 
   document.querySelector('BUTTON[type="submit"]').click();
 
   // focuses the text input 
   document.querySelector('INPUT').focus();
 
  // removes focus from the text input 
  document.querySelector('INPUT').blur();
قطعه کد فوق به علت استفاده‌ی از querySelector، با IE 8.0 و تمام مرورگرهای پس از آن سازگار است.
متدهای توکار و بومی click ،focus و blur بر روی تمام عناصر DOM که از اینترفیس HTMLElement مشتق شده باشند، وجود دارند. متد submit فقط بر روی المان‌هایی از نوع <form> وجود دارد و قابل فراخوانی است.
باید دقت داشت که فراخوانی متدهای click و submit از نوع bubbling است؛ اما متدهای focus و blur خیر. از این جهت که این دو رخداد فاز capturing را سبب می‌شوند.

متدهای یاد شده را توسط سازنده‌ی شیء Event و یا متد createEvent شیء document نیز می‌توان ایجاد کرد. یکی از کاربردهای آن، ارائه‌ی رفتاری سفارشی مانند triggerHandler جی‌کوئری است:
   var clickEvent;

   if (typeof Event === 'function') {
     clickEvent = new Event('click', {bubbles: false});
   }
   else {
       clickEvent = document.createEvent('Event');
      clickEvent.initEvent('click', false, true);
  }

  document.querySelector('BUTTON[type="button"]').dispatchEvent(clickEvent);
کار با سازنده‌ی شیء Event در تمام مرورگرهای جدید، منهای IE (تمام نگارش‌های آن) پشتیبانی می‌شود. در اینجا اگر این پشتیبانی وجود داشت، از خاصیت bubbles: false شیء Event استفاده می‌شود و اگر مرورگری قدیمی بود، از متد document.createEvent برای این منظور کمک گرفته می‌شود. در این حالت دومین پارامتر متد initEvent، همان bubbles است.


ایجاد و صدور رخدادهای سفارشی

فرض کنید در حال تهیه‌ی کتابخانه‌ای هستیم که افزودن و حذف آیتم‌ها را به یک گالری عکس ارائه می‌دهد. می‌خواهیم روشی را در اختیار مصرف کننده قرار دهیم تا بتواند به این رخدادهای سفارشی (غیر استانداردی که جزو W3C نیستند) گوش فرا دهد.
در جی‌کوئری برای ایجاد رخدادهای سفارشی به صورت زیر عمل می‌شود:
  // Triggers a custom "image-removed" element, 
  // which bubbles up to ancestor elements.
$libraryElement.trigger('image-removed', {id: 1});
در اینجا نیز صدور رخدادها همانند قبل و توسط همان متد trigger است. اما مشکلی که با آن وجود دارد این است که گوش فرا دهنده‌ی به این رخداد نیز باید توسط جی‌کوئری ارائه شود و خارج از این کتابخانه قابل دریافت و پیگیری نیست.
در خارج از جی‌کوئری و توسط web API استاندارد مرورگرها ایجاد و صدور رخدادهای سفارشی به همراه bubbling آن به صورت زیر است:
  var event = new CustomEvent('image-removed', {
    bubbles: true,
    detail: {id: 1}
  });
  libraryElement.dispatchEvent(event);
البته باید به‌خاطر داشته باشید این روش صرفا با مرورگرهای جدید (منهای تمام نگارش‌های IE) کار می‌کند. در اینجا اگر نیاز به ارائه‌ی راه حلی سازگار با IE نیز وجود داشت می‌توان از document.createEvent استفاده کرد:
  var event = document.createEvent('CustomEvent');
  event.initCustomEvent('image-removed', false, true, {id: 1});
  libraryElement.dispatchEvent(event);
و اگر بخواهیم بررسی وجود IE و یا پشتیبانی از CustomEvent را نیز قید کنیم، به قطعه کد زیر خواهیم رسید که با تمام مرورگرهای موجود کار می‌کند:
  var event;
 
   // If the `CustomEvent` constructor function is not supported, 
   // fall back to `createEvent` method. 
   if (typeof CustomEvent === 'function') {
     event = new CustomEvent('image-removed', {
       bubbles: true,
       detail: {id: 1}
     });
  }
  else {
      event = document.createEvent('CustomEvent');
      event.initCustomEvent('image-removed', false, true, {
        id: 1
      });
  }

  libraryElement.dispatchEvent(event);


گوش فرادادن به رخدادهای صادر شده، توسط jQuery

در جی‌کوئری با استفاده از متد on آن می‌توان به تمام رخدادهای استاندارد و همچنین سفارشی گوش فرا داد:
  $(window).on('resize', function() {
     // react to new window size 
  });
در ادامه برای حذف تمام گوش فرا دهنده‌های به رخداد resize می‌توان از متد off آن استفاده کرد:
  // remove all resize listeners - usually a bad idea 
  $(window).off('resize');
اما مشکلی را که این روش به همراه دارد، از کار انداختن تمام قسمت‌های دیگری است که هم اکنون به صدور این رخداد گوش فرا می‌دهند.
روش بهتر انجام اینکار، ذخیره‌ی ارجاعی به متدی است که قرار است این رویداد گردانی را انجام دهد:
  var resizeHandler = function() {
      // react to new window size 
  };

  $(window).on('resize', resizeHandler);

  // ...later 
  // remove only our resize handler 
  $(window).off('resize', resizeHandler);
و در آخر تنها این گوش فرا دهنده‌ی خاص را در صورت نیاز غیرفعال می‌کنیم و نه تمام گوش فرادهنده‌های سراسر برنامه را.

همچنین اگر یک گوش فراهنده‌ی به رخدادی تنها قرار است یکبار در طول عمر برنامه اجرا شود، می‌توان از متد one استفاده کرد:
$(someElement).one('click', function() {
    // handle click event 
});
پس از یکبار اجرای رخدادگردان کلیک در اینجا، از کلیک‌های بعدی صرفنظر خواهد شد.


گوش فرادادن به رخدادهای صادر شده، توسط جاوا اسکریپت خالص (یا همان web API مرورگرها)

ابتدایی‌ترین روش گوش فرادادن به رخدادها که از زمان آغاز معرفی آن‌ها در دسترس بوده‌است، روش تعریف inline آن‌ها است:
  <button onclick="handleButtonClick()">click me</button>
در اینجا متد رویدادگردان به یکی از ویژگی المان انتساب داده می‌شود. مشکل این روش، نیاز به سراسری تعریف کردن متد handleButtonClick است و دیگر نمی‌توان آن‌را در فضای نامی خاص و یا سفارشی قرار داد.
روش دیگر ثبت رویدادگردان click، انتساب متد آن به خاصیت رخداد متناظری در آن المان ویژه است:
  buttonEl.onclick = function() {
    // handle button click 
  };
مزیت این روش، عدم نیاز به استفاده‌ی از متدهای سراسری است.
البته باید دقت داشت که یکی از دو روش یاد شده را می‌توانید استفاده کنید. در اینجا آخرین رویدادگردان متصل شده‌ی به المان، همواره تمام نمونه‌های موجود دیگر را بازنویسی می‌کند.
اگر نیاز به معرفی رویدادگردان‌های متعددی برای یک المان در ماژول‌های مختلف برنامه وجود داشت، از زمان IE 9.0 به بعد، متد addEventListener برای این منظور تدارک دیده شده‌است و syntax آن بسیار شبیه به متد on جی‌کوئری است:
  buttonEl.addEventListener('click', function() {
    // handle button click 
  });
در این حالت دیگر مشکل نیاز به متدهای سراسری و یا عدم امکان تعریف بیش از یک رویدادگردان خاص برای المانی مشخص، دیگر وجود ندارد.
برای نمونه معادل قطعه کد جی‌کوئری که پیشتر با متد on نوشتیم، با جاوا اسکریپت خالص به صورت زیر است:
  window.addEventListener('resize', function() {
    // react to new window size 
  });
در اینجا برای حذف یک رویدادگردان می‌توان از متد removeEventListener استفاده کرد. تفاوت مهم آن با متد off جی‌کوئری این است که در اینجا حتما باید مشخص باشد کدام رویدادگردان را می‌خواهید حذف کنید:
  var resizeHandler = function() {
      // react to new window size 
  };

  window.addEventListener('resize', resizeHandler);

  // ...later 
  // remove only our resize handler 
  window.removeEventListener('resize', resizeHandler);
یعنی روش حذف رویدادگردان‌ها در اینجا شبیه به مثال دومی است که برای متد off جی‌کوئری ارائه کردیم. ابتدا باید ارجاعی را به متد رویدادگردان تهیه کنیم و سپس بر اساس این ارجاع، امکان حذف آن وجود خواهد داشت.
در اینجا حتی امکان تعریف متد one جی‌کوئری نیز پیش بینی شده‌است (البته جزو استانداردهای جدید وب از سال 2016 است):
  someElement.addEventListener('click', function(event) {
     // handle click event 
  }, { once: true });
اگر بخواهیم متد one سازگار با مرورگرهای قدیمی‌تر را نیز ارائه کنیم، چنین شکلی را پیدا می‌کند:
  var clickHandler = function() {
    // handle click event 
    // ...then unregister handler 
    someElement.removeEventListener('click', clickHandler);
  };
  someElement.addEventListener('click', clickHandler);
در اینجا پس از بروز رخداد، کار removeEventListener آن به صورت خودکار صورت می‌گیرد.


کنترل انتشار رخدادها

فرض کنید می‌خواهیم جلوی انتخاب المان‌های صفحه مانند تصاویر و متن را توسط ماوس بگیریم. روش انجام اینکار با jQuery به صورت زیر است:
$(window).on('mousedown', function(event) {
    event.preventDefault();
});
و یا توسط web API مرورگرها به این صورت:
  window.addEventListener('mousedown', function(event) {
    event.preventDefault();
  });
مطابق «W3C DOM Level 3 Events specification» عملکرد پیش‌فرض رخداد mousedown با انتخاب متون و یا کشیدن و رها کردن المان‌ها آغاز می‌شود. متد preventDefault یکی از متدهای شیء event است که به رویدادگردان‌های تعریف شده ارسال می‌شود و توسط آن عملکرد پیش‌فرض آن رخداد لغو می‌شود.

برای جلوگیری کردن از انتشار رخدادی مانند click جهت رسیدن به سایر رویدادگردان‌های ثبت شده‌ی در بین راه فاز bubbling، می‌توان از متد stopPropagation استفاده کرد. روش انجام اینکار در جی‌کوئری:
  $someElement.on('click', function(event) {
      event.stopPropagation();
  });
البته jQuery صرفا فاز انتشار از نوع bubbling را پشتیبانی می‌کند.
و با web Api جهت جلوگیری از انتشار رخدادها در فاز capturing (این تنها راه مدیریت فاز capturing است):
  // stop propagation during capturing phase 
  someElement.addEventListener('click', function(event) {
      event.stopPropagation();
  }, true);
و یا استفاده از web API برای جلوگیری از انتشار رخدادها در فاز bubbling:
  // stop propagation during bubbling phase 
  someElement.addEventListener('click', function(event) {
      event.stopPropagation();
  });
البته باید درنظر داشت که متد stopPropagation از انتشار رخدادها به سایر گوش فرا دهنده‌های همان المان صادر کننده‌ی رخداد جلوگیری نمی‌کند. برای این منظور باید از متد stopImmediatePropagation استفاده کرد؛ در جی‌کوئری:
  $someElement.on('click', function(event) {
      event.stopImmediatePropagation();
  });
و توسط web API مرورگرها:
  someElement.addEventListener('click', function(event) {
      event.stopImmediatePropagation();
  });

یک نکته: در این حالت اگر متد رویدادگردانی مقدار false را برگرداند، به معنای فراخوانی هر دوی متد preventDefault و stopPropagation است.


ارسال اطلاعات به رویدادگردان‌ها

روش ارسال اطلاعات اضافی به رویداد گردان‌ها در جی‌کوئری به صورت زیر است:
  $uploaderElement.trigger('uploadError', {
    filename: 'picture.jpeg'
  });
و رویدادگردان گوش فرا دهنده‌ی به آن، به این نحو می‌تواند به filename دسترسی پیدا کند:
  $uploaderParent.on('uploadError', function(event, data) {
     showAlert('Failed to upload ' + data.filename);
  });
در اینجا دومین پارامتر تعریف شده، امکان دسترسی به تمام خواص سفارشی ارسالی را میسر می‌کند.

روش انجام اینکار با web API مرورگرها به صورت زیر است:
   // send the failed filename w/ an error event
  var event = new CustomEvent('uploadError', {
     bubbles: true,
     detail: {filename: 'picture.jpeg'}
   });
   uploaderElement.dispatchEvent(event);
 
   // ...and this is a listener for the event 
   uploaderParent.addEventListener('uploadError', function(event) {
      showAlert('Failed to upload ' + event.detail.filename);
  });
این روش با تمام مرورگرهای مدرن (منهای تمام نگارش‌های IE) کار می‌کند و روش دسترسی به خاصیت detail سفارشی تعریف و ارسال شده، از طریق همان خاصیت event ارسالی به رویدادگردان است.
و اگر می‌خواهید از IE هم پشتیبانی کنید، روش جایگزین کردن شیء CustomEvent با createEvent به صورت زیر است:
  // send the failed filename w/ an error event 
   var event = document.createEvent('CustomEvent');
   event.initCustomEvent('uploadError', true, true, {
     filename: 'picture.jpeg'
   });
   uploaderElement.dispatchEvent(event);
 
   // ...and this is a listener for the event 
   uploaderParent.addEventListener('uploadError', function(event) {
    showAlert('Failed to upload ' + event.detail.filename);
  });


متوجه شدن زمان بارگذاری یک شیء در صفحه

در حین توسعه‌ی برنامه‌های وب، با این نوع سؤالات زیاد مواجه خواهید شد: چه زمانی تمام و یا بعضی از المان‌های صفحه کاملا بارگذاری و رندر شده‌اند؟
پاسخ به این نوع سؤالات در W3C UI Events specification توسط رویداد استاندارد load داده شده‌است.

- چه زمانی تمام المان‌های موجود در صفحه کاملا بارگذاری و رندر شده و همچنین شیوه‌نامه‌های تعریف شده نیز به آن‌ها اعمال گردیده‌اند؟
روش انجام اینکار با jQuery:
  $(window).on('load', function() {
    // page is fully rendered 
  });
و توسط web API بومی مرورگرها که بسیار مشابه نمونه‌ی jQuery است:
  window.addEventListener('load', function() {
    // page is fully rendered 
  });

- چه زمانی markup استاتیک صفحه‌ی جاری در جای خود قرار گرفته‌اند؟
اهمیت این موضوع، به دسترسی به زمان مناسب و امن ایجاد تغییرات در DOM بر می‌گردد. برای این منظور رویداد استاندارد DOMContentLoaded پیش‌بینی شده‌است که زودتر از رویداد load، در دسترس برنامه نویس قرار می‌گیرد. در جی‌کوئری توسط یکی از دو روش معروف زیر به رویداد یاد شده دسترسی خواهید داشت:
  $(document).ready(function() {
    // markup is on the page 
  });

  //or
  $(function() {
    // markup is on the page 
  });
و معادل web API آن در تمام مرورگرهای جدید، همان تعریف رویدادگردانی برای DOMContentLoaded استاندارد است:
  document.addEventListener('DOMContentLoaded', function() {
    // markup is on the page 
  });

یک نکته: بهتر است این تعریف web API را پیش از تگ‌های <link> قرار دهید. زیرا بارگذاری آن‌ها، اجرای هر نوع اسکریپتی را تا زمان پایان عملیات، سد می‌کند.

- چه زمانی المانی خاص در صفحه بارگذاری شده‌است و چه زمانی بارگذاری یک المان با شکست مواجه شده‌است؟
در جی‌کوئری توسط بررسی رویدادهای load و error می‌توان به وضعیت نهایی بارگذاری المان‌هایی خاص دسترسی یافت:
   $('IMG').on('load', function() {
     // image has successfully loaded 
   });
 
   $('IMG').on('error', function() {
     // image has failed to load 
   });
روش انجام اینکار با web API مرورگرها نیز یکی است:
  document.querySelector('IMG').addEventListener('load', function() {
    // image has successfully loaded 
  });

  document.querySelector('IMG').addEventListener('error', function() {
    // image has failed to load 
  });

- جلوگیری از ترک اتفاقی صفحه‌ی جاری
گاهی از اوقات نیاز است برای از جلوگیری از تخریب صفحه‌ی جاری و از دست رفتن اطلاعات ذخیره نشده‌ی کاربر، اگر بر روی دکمه‌ی close بالای صفحه کلیک کرد و یا کاربر به اشتباه به صفحه‌ی دیگری هدایت شد، جلوی اینکار را بگیریم. برای این منظور رخداد استاندارد beforeunload درنظر گرفته شده‌است. روش استفاده‌ی از این رویداد در جی‌کوئری:
  $(window).on('beforeunload', function() {
    return 'Are you sure you want to unload the page?';
  });
و در web API مرورگرها:
  window.addEventListener('beforeunload', function(event) {
    var message = 'Are you sure you want to unload the page?';
    event.returnValue = message;
    return message;
  });
در حالت web API بعضی از مرورگرها از نتیجه‌ی متد استفاده می‌کنند و بعضی دیگر از مقدار خاصیت event.returnValue. به همین جهت هر دو مورد در اینجا مقدار دهی شده‌اند.
نظرات مطالب
جلوگیری از ارسال Spam در ASP.NET MVC
با تشکر از شما. چند نکته از لاگ‌های استخراج شده‌ی سایت جاری:
- این برنامه‌ها، user agent‌های متفاوتی را به ازای هر درخواست ارسال می‌کنند (عموما). بنابراین مورد دوم هم علاوه بر مورد سوم نباید بکار گرفته شود.
- دو نوع هش در حالت کلی وجود دارند. هش‌های سریع و هش‌های امن. هش‌های امن سعی می‌کنند این تضمین را ارائه دهند که به ازای یک ورودی مشخص، خروجی منحصربفردی را تولید کنند؛ اما ... با قیمت کندتر بودن عملیات هش. هش‌های سریع، مانند xxHash، برای یک چنین مواردی که نیاز هست کلید کش تولید شود بکار گرفته می‌شوند. الزاما مانند هش‌های امن سعی در تولید خروجی‌های منحصربفردی نمی‌کنند، اما تا این اندازه دقیق هستند که در بانک‌های اطلاعاتی key-value store فوق سریعی مانند Redis از آن‌ها استفاده می‌شود. بنابراین در یک چنین مواردی مانند سناریوی جاری بهتر است از هش‌های سریع استفاده شود. البته اگر آدرس صفحه و همچنین UA را حذف کنیم، نیازی به هش کردن نخواهد بود؛ چون IP را می‌توان بعنوان کلید درنظر گرفت.
- بررسی UA از دیدگاه دیگری به صورت جداگانه می‌تواند مفید باشد. تشخیص بات‌های شناخته شده و بستن دسترسی آن‌ها.
مطالب
استفاده از Kendo UI templates
در مطلب «صفحه بندی، مرتب سازی و جستجوی پویای اطلاعات به کمک Kendo UI Grid» در انتهای بحث، ستون IsAvailable به صورت زیر تعریف شد:
columns: [
               {
                   field: "IsAvailable", title: "موجود است",
                   template: '<input type="checkbox" #= IsAvailable ? checked="checked" : "" # disabled="disabled" ></input>'
                }
]
Templates، جزو یکی از پایه‌های Kendo UI Framework هستند و توسط آن‌ها می‌توان قطعات با استفاده‌ی مجدد HTML ایی را طراحی کرد که قابلیت یکی شدن با اطلاعات جاوا اسکریپتی را دارند.
همانطور که در این مثال نیز مشاهده می‌کنید، قالب‌های Kendo UI از Hash (#) syntax استفاده می‌کنند. در اینجا قسمت‌هایی از قالب که با علامت # محصور می‌شوند، در حین اجرا، با اطلاعات فراهم شده جایگزین خواهند شد.
برای رندر مقادیر ساده می‌توان از # =# استفاده کرد. از # :# برای رندر اطلاعات HTML-encoded کمک گرفته می‌شود و #  # برای رندر کدهای جاوا اسکریپتی کاربرد دارد. از حالت HTML-encoded برای نمایش امن اطلاعات دریافتی از کاربران و جلوگیری از حملات XSS استفاده می‌شود.
اگر در این بین نیاز است # به صورت معمولی رندر شود، در حالت کدهای جاوا اسکریپتی به صورت #\\ و در HTML ساده به صورت #\ باید مشخص گردد.


مثالی از نحوه‌ی تعریف یک قالب Kendo UI

    <!--دریافت اطلاعات از منبع محلی-->
    <script id="javascriptTemplate" type="text/x-kendo-template">
        <ul>
            # for (var i = 0; i < data.length; i++) { #
            <li>#= data[i] #</li>
            # } #
        </ul>
    </script>

    <div id="container1"></div>
    <script type="text/javascript">
        $(function () {
            var data = ['User 1', 'User 2', 'User 3'];
            var template = kendo.template($("#javascriptTemplate").html());
            var result = template(data); //Execute the template
            $("#container1").html(result); //Append the result
        });
    </script>
این قالب ابتدا در تگ script محصور می‌شود و سپس نوع آن مساوی text/x-kendo-template قرار می‌گیرد. در ادامه توسط یک حلقه‌ی جاوا اسکریپتی، عناصر آرایه‌ی فرضی data خوانده شده و با کمک Hash syntax در محل‌های مشخص شده قرار می‌گیرند.
در ادامه باید این قالب را رندر کرد. برای این منظور یک div با id مساوی container1 را جهت تعیین محل رندر نهایی اطلاعات مشخص می‌کنیم. سپس متد kendo.template بر اساس id قالب اسکریپتی تعریف شده، یک شیء قالب را تهیه کرده و سپس با ارسال آرایه‌ای به آن، سبب اجرای آن می‌شود. خروجی نهایی، یک قطعه کد HTML است که در محل container1 درج خواهد شد.
همانطور که ملاحظه می‌کنید، متد kendo.template، نهایتا یک رشته را دریافت می‌کند. بنابراین همینجا و به صورت inline نیز می‌توان یک قالب را تعریف کرد.


کار با منابع داده راه دور

فرض کنید مدل برنامه به صورت ذیل تعریف شده‌است:
namespace KendoUI04.Models
{
    public class Product
    {
        public int Id { set; get; }
        public string Name { set; get; }
        public decimal Price { set; get; }
        public bool IsAvailable { set; get; }
    }
}
و لیستی از آن توسط یک ASP.NET Web API کنترلر، به سمت کاربر ارسال می‌شود:
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using KendoUI04.Models;

namespace KendoUI04.Controllers
{
    public class ProductsController : ApiController
    {
        public IEnumerable<Product> Get()
        {
            return ProductDataSource.LatestProducts.Take(10);
        }
    }
}
در سمت کاربر و در View برنامه خواهیم داشت:
    <!--دریافت اطلاعات از سرور-->
    <div>
        <div id="container2"><ul></ul></div>
    </div>

    <script id="template1" type="text/x-kendo-template">
        <li> #=Id# - #:Name# - #=kendo.toString(Price, "c")#</li>
    </script>

    <script type="text/javascript">
        $(function () {
            var producatsTemplate1 = kendo.template($("#template1").html());

            var productsDataSource = new kendo.data.DataSource({
                transport: {
                    read: {
                        url: "api/products",
                        dataType: "json",
                        contentType: 'application/json; charset=utf-8',
                        type: 'GET'
                    }
                },
                error: function (e) {
                    alert(e.errorThrown);
                },
                change: function () {
                    $("#container2 > ul").html(kendo.render(producatsTemplate1, this.view()));
                }
            });
            productsDataSource.read();
        });
    </script>
ابتدا یک div با id مساوی container2 جهت تعیین محل نهایی رندر قالب template1 در صفحه تعریف می‌شود.
هرچند خروجی دریافتی از سرور نهایتا یک آرایه از اشیاء Product است، اما در template1 اثری از حلقه‌ی جاوا اسکریپتی مشاهده نمی‌شود. در اینجا چون از متد kendo.render استفاده می‌شود، نیازی به ذکر حلقه نیست و به صورت خودکار، به تعداد عناصر آرایه دریافتی از سرور، قطعه HTML قالب را تکرار می‌کند.
در ادامه برای کار با سرور از یک Kendo UI DataSource استفاده شده‌است. قسمت transport/read آن، کار تعریف محل دریافت اطلاعات را از سرور مشخص می‌کند. رویدادگران change آن اطلاعات نهایی دریافتی را توسط متد view در اختیار متد kendo.render قرار می‌دهد. در نهایت، قطعه‌ی HTML رندر شده‌ی نهایی حاصل از اجرای قالب، در بین تگ‌های ul مربوط به container2 درج خواهد شد.
رویدادگران change زمانیکه data source، از اطلاعات راه دور و یا یک آرایه‌ی جاوا اسکریپتی پر می‌شود، فراخوانی خواهد شد. همچنین مباحث مرتب سازی اطلاعات، صفحه بندی و تغییر صفحه، افزودن، ویرایش و یا حذف اطلاعات نیز سبب فراخوانی آن می‌گردند. متد view ایی که در این مثال فراخوانی شد، صرفا در روال رویدادگردان change دارای اعتبار است و آخرین تغییرات اطلاعات و آیتم‌های موجود در data source را باز می‌گرداند.


یک نکته‌ی تکمیلی: فعال سازی intellisense کدهای جاوا اسکریپتی Kendo UI

اگر به پوشه‌ی اصلی مجموعه‌ی Kendo UI مراجعه کنید، یکی از آن‌ها vsdoc نام دارد که داخل آن فایل‌های min.intellisense.js و vsdoc.js مشهود هستند.
اگر از ویژوال استودیوهای قبل از 2012 استفاده می‌کنید، نیاز است فایل‌های vsdoc.js متناظری را به پروژه اضافه نمائید؛ دقیقا در کنار فایل‌های اصلی js موجود. اگر از ویژوال استودیوی 2012 و یا بالاتر استفاده می‌کنید باید از فایل‌های intellisense.js متناظر استفاده کنید. برای مثال اگر از kendo.all.min.js کمک می‌گیرید، فایل متناظر با آن kendo.all.min.intellisense.js خواهد بود.
بعد از اینکار نیاز است فایلی به نام references.js_ را به پوشه‌ی اسکریپت‌های خود با این محتوا اضافه کنید (برای VS 2012 به بعد):
/// <reference path="jquery.min.js" />
/// <reference path="kendo.all.min.js" />
نکته‌ی مهم اینجا است که این فایل به صورت پیش فرض از مسیر Scripts/_references.js/~ خوانده می‌شود. برای اضافه کردن مسیر دیگری مانند js/_references.js/~ باید آن‌را به تنظیمات ذیل اضافه کنید:
 Tools menu –> Options -> Text Editor –> JavaScript –> Intellisense –> References
گزینه‌ی Reference Group را به (Implicit (Web تغییر داده و سپس مسیر جدیدی را اضافه نمائید.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید:
KendoUI04.zip
نظرات مطالب
کار با کوکی‌ها در ASP.NET Core

یک نکته‌ی تکمیلی: در تنظیمات کوکی‌های سایت جدید، SameSite به Strict تنظیم شده بود:

options.Cookie.SameSite = SameSiteMode.Strict;

این موضوع سبب شده بود که اگر کاربری در سایت لاگین کرده بود و ایمیلی را دریافت می‌کرد، پس از کلیک بر روی ایمیل دریافتی و ورود به سایت، اعتبارسنجی نشده تلقی می‌شد و برای مثال امکان ارسال پاسخی را نداشت؛ چون حالت Strict به این معنا است که کوکی‌های سایت (منجمله کوکی‌ اعتبارسنجی کاربر)، فقط به درخواست‌های رسیده‌ی از طریق خود سایت، به صورت خودکار توسط مرورگرها ارسال می‌شوند و نه در حالتیکه یک redirect از سایت دیگری وجود داشته باشد. برای تعدیل این تنظیم می‌توان از حالت Lax استفاده کرد:

options.Cookie.SameSite = SameSiteMode.Lax;

در اصل، حالت Lax با حالت Strict، تفاوت آنچنانی ندارد و هنوز هم کوکی‌ها را فقط به درخواست‌های رسیده‌ی از طریق دومین جاری ارسال می‌کند. اما در این حالت اگر redirect ای نیز به دومین برنامه، از نوع GET وجود داشته باشد، آن‌را امن حساب کرده و کوکی‌های برنامه را در اختیار آن درخواست قرار می‌دهد.

پ.ن.

برای دریافت کوکی‌های جدید از نوع Lax، باید یکبار از برنامه خارج شوید و مجددا به آن وارد شوید تا کوکی‌های جدید را دریافت کنید.

مطالب
بارگذاری یک یوزرکنترل با استفاده از جی‌کوئری

مزیت استفاده از یوزر کنترل‌ها، ماژولار کردن برنامه است. برای مثال اگر صفحه جاری شما قرار است از چهار قسمت اخبار، منوی پویا ، سخن روز و آمار کاربران تشکیل شود، می‌توان هر کدام را توسط یک یوزر کنترل پیاده سازی کرده و سپس صفحه اصلی را از کنار هم قرار دادن این یوزر کنترل‌ها تهیه نمود.
با این توضیحات اکنون می‌خواهیم یک یوزکنترل ASP.Net را توسط jQuery Ajax بارگذاری کرده و نمایش دهیم. حداقل دو مورد کاربرد را می‌توان برای آن متصور شد:
الف) در اولین باری که یک صفحه در حال بارگذاری است، قسمت‌های مختلف آن‌را بتوان از یوزر کنترل‌های مختلف خواند و تا زمان بارگذاری کامل هر کدام، یک عبارت لطفا منتظر بمانید را نمایش داد. نمونه‌ی آن‌را شاید در بعضی از CMS های جدید دیده باشید. صفحه به سرعت بارگذاری می‌شود. در حالیکه مشغول مرور صفحه جاری هستید، قسمت‌های مختلف صفحه پدیدار می‌شوند.
ب) بارگذاری یک قسمت دلخواه صفحه بر اساس درخواست کاربر. مثلا کلیک بر روی یک دکمه و امثال آن.

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

پیاده سازی:
قسمت تبدیل یک یوزر کنترل به رشته را قبلا در مقاله "تهیه قالب برای ایمیل‌های ارسالی یک برنامه ASP.Net" مشاهده کرده‌اید. در این‌جا برای استفاده از این متد در یک وب سرویس نیاز به کمی تغییر وجود داشت (KeyValuePair ها درست سریالایز نمی‌شوند) که نتیجه نهایی به صورت زیر است. یک فایل Ajax.asmx را به برنامه اضافه کرده و سپس در صفحه Ajax.asmx.cs کد آن به صورت زیر می‌تواند باشد:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Script.Services;
using System.Web.Services;
using System.Web.UI;
using System.Web.UI.HtmlControls;

namespace AjaxTest
{
public class KeyVal
{
public string Key { set; get; }
public object Value { set; get; }
}

/// <summary>
/// Summary description for Ajax
/// </summary>
[ScriptService]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class Ajax : WebService
{
/// <summary>
/// Removes Form tags using Regular Expression
/// </summary>
private static string cleanHtml(string html)
{
return Regex.Replace(html, @"<[/]?(form)[^>]*?>", string.Empty, RegexOptions.IgnoreCase);
}

/// <summary>
/// تبدیل یک یوزر کنترل به معادل اچ تی ام ال آن
/// </summary>
/// <param name="path">مسیر یوزر کنترل</param>
/// <param name="properties">لیست خواص به همراه مقادیر مورد نظر</param>
/// <returns></returns>
/// <exception cref="NotImplementedException"><c>NotImplementedException</c>.</exception>
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string RenderUserControl(string path,
List<KeyVal> properties)
{
Page pageHolder = new Page();

UserControl viewControl =
(UserControl)pageHolder.LoadControl(path);

viewControl.EnableViewState = false;

Type viewControlType = viewControl.GetType();

if (properties != null)
foreach (var pair in properties)
{
if (pair.Key != null)
{
PropertyInfo property =
viewControlType.GetProperty(pair.Key);

if (property != null)
{
if (pair.Value != null) property.SetValue(viewControl, pair.Value, null);
}
else
{
throw new NotImplementedException(string.Format(
"UserControl: {0} does not have a public {1} property.",
path, pair.Key));
}
}
}

//Form control is mandatory on page control to process User Controls
HtmlForm form = new HtmlForm();

//Add user control to the form
form.Controls.Add(viewControl);

//Add form to the page
pageHolder.Controls.Add(form);

//Write the control Html to text writer
StringWriter textWriter = new StringWriter();

//execute page on server
HttpContext.Current.Server.Execute(pageHolder, textWriter, false);

// Clean up code and return html
return cleanHtml(textWriter.ToString());
}
}
}
تا این‌جا متد وب سرویسی را داریم که می‌تواند مسیر یک یوزر کنترل را به همراه خواص عمومی آن‌را دریافت کرده و سپس یوزر کنترل را رندر نموده و حاصل را به صورت HTML به شما تحویل دهد. با استفاده از reflection خواص عمومی یوزر کنترل یافت شده و مقادیر لازم به آن‌ها پاس می‌شوند.

چند نکته:
الف) وب کانفیگ برنامه ASP.Net شما اگر با VS 2008 ایجاد شده باشد مداخل لازم را برای استفاده از این وب سرویس توسط jQuery Ajax دارد در غیر اینصورت موفق به استفاده از آن نخواهید شد.
ب) هنگام بازگرداندن این اطلاعات با فرمت json = ResponseFormat.Json جهت استفاده در jQuery Ajax ، گاهی از اوقات بسته به حجم بازگردانده شده ممکن است خطایی حاصل شده و عملیات متوقف شد. این طول پیش فرض را (maxJsonLength) در وب کانفیگ به صورت زیر تنظیم کنید تا مشکل حل شود:

<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="10000000"></jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>

برای پیاده سازی قسمت Ajax آن برای اینکه کار کمی تمیزتر و با قابلیت استفاده مجدد شود یک پلاگین تهیه شده (فایلی با نام jquery.advloaduc.js) که سورس آن به صورت زیر است:

$.fn.advloaduc = function(options) {
var defaults = {
webServiceName: 'Ajax.asmx', //نام فایل وب سرویس ما
renderUCMethod: 'RenderUserControl', //متد وب سرویس
ucMethodJsonParams: '{path:\'\'}',//پارامترهایی که قرار است پاس شوند
completeHandler: null //پس از پایان کار وب سرویس این متد جاوا اسکریپتی فراخوانی می‌شود
};
var options = $.extend(defaults, options);

return this.each(function() {
var obj = $(this);
obj.prepend("<div align='center'> لطفا اندکی تامل بفرمائید... <img src=\"images/loading.gif\"/></div>");

$.ajax({
type: "POST",
url: options.webServiceName + "/" + options.renderUCMethod,
data: options.ucMethodJsonParams,
contentType: "application/json; charset=utf-8",
dataType: "json",
success:
function(msg) {
obj.html(msg.d);

// if specified make callback and pass element
if (options.completeHandler)
options.completeHandler(this);
},
error:
function(XMLHttpRequest, textStatus, errorThrown) {
obj.html("امکان اتصال به سرور در این لحظه مقدور نیست. لطفا مجددا سعی کنید.");
}
});
});
};
برای اینکه با کلیات این روش آشنا شوید می‌توان به مقاله "بررسی وجود نام کاربر با استفاده از jQuery Ajax در ASP.Net" مراجعه نمود که از ذکر مجدد آن‌ها خودداری می‌شود. همچنین در مورد نوشتن یک پلاگین جی‌کوئری در مقاله "افزونه جملات قصار jQuery" توضیحاتی داده شده است.
عمده کاری که در این پلاگین صورت می‌گیرد فراخوانی متد Ajax جی‌کوئری است. سپس به متد وب سرویس ما (که در اینجا نام آن به صورت پارامتر نیز قابل دریافت است)، پارامترهای لازم پاس شده و سپس نتیجه حاصل به یک شیء در صفحه اضافه می‌شود.
completeHandler آن اختیاری است و پس از پایان کار متد اجکس فراخوانی می‌شود. در صورتیکه به آن نیازی نداشتید یا مقدار آن را null قرار دهید یا اصلا آن‌را ذکر نکنید.

مثالی در مورد استفاده از این وب سرویس و همچنین پلاگین جی‌کوئری نوشته شده:

الف) یوزر کنترل ساده زیر را به پروژه اضافه کنید:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="part1.ascx.cs" Inherits="TestJQueryAjax.part1" %>
<asp:Label runat="server" ID="lblData" ></asp:Label>
بدیهی است یک یوزر کنترل می‌تواند به اندازه یک صفحه کامل پیچیده باشد به همراه انواع و اقسام ارتباطات با دیتابیس و غیره.

سپس کد آن‌را به صورت زیر تغییر دهید:

using System;
using System.Threading;

namespace TestJQueryAjax
{
public partial class part1 : System.Web.UI.UserControl
{
public string Text1 { set; get; }
public string Text2 { set; get; }

protected void Page_Load(object sender, EventArgs e)
{
Thread.Sleep(3000);
if (!string.IsNullOrEmpty(Text1) && !string.IsNullOrEmpty(Text2))
lblData.Text = Text1 + "<br/>" + Text2;
}
}
}
این یوزر کنترل دو خاصیت عمومی دارد که توسط وب سرویس مقدار دهی خواهد شد و نهایتا حاصل نهایی را در یک لیبل در دو سطر نمایش می‌دهد.
عمدا یک sleep سه ثانیه‌ای در اینجا در نظر گرفته شده تا اثر آن‌را بهتر بتوان مشاهده کرد.

ب) اکنون کد مربوط به صفحه‌ای که قرار است این یوزر کنترل را به صورت غیرهمزمان بارگذاری کند به صورت زیر خواهد بود (مهم‌ترین قسمت آن نحوه تشکیل پارامترها و مقدار دهی خواص یوزر کنترل است):

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestJQueryAjax._Default" %>

<!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="js/jquery.js" type="text/javascript"></script>
<script src="js/jquery.advloaduc.js" type="text/javascript"></script>
<script src="js/json2.js" type="text/javascript"></script>

<script type="text/javascript">
function showAlert() {
alert('finished!');
}

//تشکیل پارامترهای متد وب سرویس جهت ارسال به آن
var fileName = 'part1.ascx';
var props = [{ 'Key': 'Text1', 'Value': 'سطر یک' }, { 'Key': 'Text2', 'Value': 'سطر 2'}];
var jsonText = JSON.stringify({ path: fileName, properties: props });

$(document).ready(function() {
$("#loadMyUc").advloaduc({
webServiceName: 'Ajax.asmx',
renderUCMethod: 'RenderUserControl',
ucMethodJsonParams: jsonText,
completeHandler: showAlert
});
});

</script>

</head>
<body>
<form id="form1" runat="server">
<div id="loadMyUc">
</div>
</form>
</body>

</html>
نکته:
برای ارسال صحیح و امن اطلاعات json به سرور، از اسکریپت استاندارد json2.js استفاده شد.

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


دریافت مثال فوق