اشتراک‌ها
کتابخانه نام دستگاه‌های اندرویدی

این کتابخانه برای اندروید نوشته شده ولی در پوشه json آن می‌توان لیست تمامی دستگاه‌های اندرویدی را دریافت کرد.

یک نمونه از داده‌ها:

{
    "manufacturer": "Samsung",
    "market_name": "Galaxy Note10+ 5G",
    "codename": "d2x",
    "model": "SM-N976B"
  }


کتابخانه نام دستگاه‌های اندرویدی
نظرات مطالب
ساخت ربات تلگرامی با #C
با تشکر از این مطلب مفید. بنده هم چند روز پیش سری به مستندات تلگرام زدم و یه ربات با تمام قابلیت هاش طراحی کردم. در این مورد نکات کلی هست که انشاله مورد استفاده قرار بگیره.
برای ایجاد ربات بهتره با کتابخانه پیشنهادی خود تلگرام کار کنید.(اینجا ) و یا اون رو از طریق Nuget دریافت کنید.
pm> Install-Package Telegram.Bot
درمورد دو روش کار با ربات باید بدونید که روش getUpdate فقط برای تست کردن پاسخگویی ربات بهتره استفاده بشه و اگراصرار به استفاده از این حالت دارید ربات شما نمیتونه به چندین کاربر پاسخ بده چون طبق این روش در هر لحظه برای  مثال 10 آپدیت دریافت میشه این آپدیت‌ها همون درخواست‌های کاربرا هستن پس درخواست یازدهم باید صبر کنه تا 10 درخواست اول پاسخ داده بشن. حل شد؟
روش اصلی که شما برای ربات باید استفاده کنید همون Webhook هست اما این هم نکاتی داره. طبق قواعد تلگرام برای استفاده از این روش باید حتما ssl روی دامنه شما فعال باشه و شما هم باید یه وب سرویس برای پاسخگویی به درخواست‌ها پیاده کنید.
برای دریافت ssl رایگان میتونید از CloudFlare استفاده کنید که اگر بگردید آموزش هاش هست و کار راحتیه
برای پیاده سازی وب سرویس اون هم با Web Api  میتونید از این مثال استفاده کنید.
حالا بعد از اینکه ربات رو توی حالت getupdate طراحی و تست کردید میتونید اون رو به حالت webhook منتقل کنید.
نکته ای هم که وجود داره اینه که شما نمیتونید به طور همزمان برای یک ربات هم از webhook و هم از getupdate استفاده کنید !
پس برای زمانی که ربات رو در حالت webhook منتشر کردید ولی دوباره نیاز به تست و دیباگ دارید و میخواید از getupdate استفاده کنید باید حتما حالت webhook  رو با استفاده از فراخوانی api زیر غیر فعال کنید (داخل آدرس بار مرورگر).
https://api.telegram.org/bot[bot-token]/setwebhook
به جای [bot-token]  باید توکن ربات خودتون رو بذارید.
بعد دوباره برای فعال کردن webhook میتونید فراخوانی زیر رو داشته باشید.
https://api.telegram.org/bot[bot-token]/setwebhook?url=https://yourdomain.example/api/webhook  
مسیرراه‌ها
ASP.NET MVC
              نظرات مطالب
              استخراج متن از فایل‌های PDF توسط iTextSharp
              استخراج متن PDF، به همراه ساختار متن آن نیست. فقط text است که در متن جاری توضیح داده شده‌است. اگر نیاز به بررسی ساختاری سطح پایین آن‌را دارید، نیاز است با نرم افزاری به نام itextrups آشنا شوید. در دو مطلب زیر، از این نرم افزار برای تحلیل ساختار فایل PDF و سپس دستکاری آن استفاده شده‌است:
              مطالب
              Cookie - قسمت دوم

              کوکی در جاوا اسکریپت 

              همانطور که در قسمت قبل اشاره کوتاهی شد، مدیریت کوکی‌های در دسترس در وضعیت جاری، در جاوا اسکریپت ازطریق پراپرتی cookie از شی document امکان‌پذیر است. این پراپرتی کاری همانند هدرهای Set-Cookie و Cookie (که در قسمت قبل درباره آن‌ها بحث شد) انجام می‌دهد. این پراپرتی یک مورد کاملا استثنایی و نسبتا عجیب در زبان جاوا اسکریپت است. در نگاه اول ظاهرا document.cookie از نوع رشته است، اما قضیه کاملا فرق می‌کند. برای روشن شدن مطلب به ادامه بحث توجه کنید.

              افزودن کوکی
              - برای افزودن یا ویرایش یک کوکی باید از ساختاری مانند ساختار هدر Set-Cookie که چیزی شبیه به عبارت زیر است، پیروی کرد:
              document.cookie = "name=value; expires=date; domain=theDomain; path=thePath; secure";
               
              نکته: با توجه به توضیحاتی که در قسمت قبل ارائه شد، بدیهی است که امکان ثبت یک کوکی با فلگ HttpOnly در جاوا اسکریپت وجود ندارد!
               
              اجرای دستوری شبیه با ساختار نشان داده شده در بالا، موجب حذف کوکی‌های قبلی نمی‌شود. از این دستور برای ایجاد یک کوکی و یا ویرایش یک کوکی موجود استفاده می‌شود. کوکی‌های ایجادشده با این روش تفاوتی با کوکی‌های ایجادشده توسط هدر Set-Cookie ندارند و همانند آنها در درخواست‌های بعدی با توجه به خواص تنظیم شده، به سمت سرور ارسال خواهند شد.
              همانطور که مشاهده می‌کنید خاصیت‌های کوکی به صورت جفت‌های نام-مقدار درون یک رشته به document.cookie نسبت داده می‌شوند. این خاصیت‌ها توسط یک کاراکتر ; از یکدیگر جدا می‌شوند. شرح ساختار فوق در  زیر آورده شده است:
              1. همیشه اولین جفتِ نام-مقدار همانند مثال بالا باید «عنوان و مقدار» کوکی را مشخص سازد. این قسمت تنها عضو اجباری ساختار فوق است.
              2. سپس یک سمی‌کالن و یک فاصله
              3. تاریخ انقضا (expires) یا حداکثر طول عمر کوکی (max-age)
              4. سپس یک سمی‌کالن و یک فاصله
              5. دمین و یا مسیر مربوط به کوکی
              6. سپس یک سمی‌کالن و یک فاصله
              7. سایر خواص چون Secure
              نکته: این ساختار عجیب معرفی شده را عینا رعایت کنید. بقیه کار توسط مرورگر انجام خواهد شد.
              نکته: قسمت‌های مختلف این ساختار case-sensitive نیست، البته به‌جز نام کوکی که کاملا case-sensitive است.
              مثلا برای ثبت یک کوکی با عنوان myCookie و مقدار myValue و دمین d.com و مسیر test و طول عمر 5 روزه باید از دستور زیر استفاده کرد:
              document.cookie = 'myCookie=myValue; max-age=432000; domain=d.com; path=/test';
               
              خواندن کوکی
              - برای خواندن کوکی‌ها تنها کافی است مقدار پراپرتی document.cookie بررسی شود. با اینکه از دستور نشان داده شده در بالا اینگونه برمی آید که پراپرتی document.cookie به رشته معرفی شده مقداردهی شده است، اما به محض خواندن این پراپرتی چیزی شبیه به عبارت زیر برگردانده میشود:
              myCookie=myValue 
              از بقیه خواص اثری نیست! این رفتار به دلیل حفط امنیت کوکی‌ها در تمام مرورگرها رعایت می‌شود.
              - برای ثبت کوکی دیگری در وضعیت جاری کافی است یکبار دیگر دستور بالا را برای کوکی جدید به کار ببریم. مثلا به صورت زیر:
              document.cookie = 'mySecondCookie=mySecondValue; path=/'
              اینار یک کوکی سشنی بدون دمین و با مقدار / برای مسیر کوکی ثبت می‌شود! در این حالت کوکی قبلی دوباره نویسی و یا حذف نمی‌شود و تنها یک کوکی جدید به لیست کوکیهای مرورگر اضافه می‌شود! این رفتار عجیب از ویژگی‌های جالب document.cookie است.
              - اگر مقدار document.cookie در این حالت خوانده شود مقدار زیر برگشت داده می‌شود:
              myCookie=myValue; mySecondCookie=mySecondValue
              باز هم خبری از سایر خاصیت‌ها نیست. ولی همانطور که می‌بینید کوکی دوم به لیست کوکی‌های مرورگر اضافه شده است.

              نکته: عبارت برگشت داده شده از پراپرتی document.cookie همانند مقداری است که در هدر Cookie هر درخواست توسط مرورگر گنجانده می‌شود، یعنی جفت نام-مقدار کوکی‌ها به همراه یک ; و یک فاصله بین مقادیر هر کوکی. بنابراین برای بدست آوردن مقدار یک کوکی یکسری عملیات جهت Parse کردن داده‌های آن نیاز است!

              متدها
              امروزه کتابخانه‌های متعددی با استفاده از زبان جاوا اسکریپت برای برنامه نویسی سمت کلاینت وجود دارد که بیشتر آنها قابلیت‌هایی برای کار با کوکی‌ها نیز دارند. ازجمله می‌توان به jQuery و YUI اشاره کرد. پلاگین مخصوص کوکی‌ها در jquery در اینجا بحث شده است. برای کسب اطلاعات بیشتر درباره قابلیت‌های کار با کوکی در YUI نیز به اینجا مراجعه کنید. مطالب زیر صرفا برای روشن شدن بحث ارائه می‌شوند. بدیهی است که برای کارهای عملی بهتر است از کتابخانه‌های موجود استفاده شود.
              با توجه به اطلاعات بالا از متدهای زیر می‌توان برای خواندن، افزودن و حذف کوکی‌ها استفاده کرد.

              نکته: متدهای زیر از ترکیب چندین ریفرنس مختلف بدست آمده است. هرچند برای موارد خاص‌تر می‌توانند بیشتر سفارشی شوند.

              افزودن و یا ویرایش کوکی
              function setCookie(data, value) {
                if (typeof data === "string") {
                  data = { name: data, value: value };
                };
                if (!data.name) throw "Cookie's name can not be null.";
              
                var cookie = escape(data.name) + "=" + escape(data.value);
              
                var expDate = null;
                if (data.expDays) {
                  expDate = new Date();
                  expDate.setDate(expDate.getDate() + data.expDays);
                }
                else if (data.expYear && data.expMonth && data.expDay) {
                  expDate = new Date(data.expYear, data.expMonth, data.expDay);
                }
                else if (data.expires) {
                  expDate = data.expires;
                }
                else if (data.maxAge) {
                  expDate = new Date();
                  expDate.setSeconds(expDate.getSeconds() + data.maxAge);
                }
                if (expDate != null) cookie += "; expires=" + expDate.toGMTString();
              
                if (data.domain)
                  cookie += "; domain=" + escape(data.domain);
              
                if (data.path)
                  cookie += "; path=" + escape(data.path);
              
                if (data.secure)
                  cookie += "; secure";
              
                document.cookie = cookie;
                return document.cookie;
              }
              در کد فوق برای انکد کردن رشته‌های مورد استفاده از متد escape استفاده شده است. برای آشنایی با این متد به اینجا مراجعه کنید.
              هم‌چنین کار کردن با نوع داده تاریخ در جاوا اسکریپت کمی متفاوت است. بنابراین برای آشنایی بیشتر با این نوع داده به اینجا رجوع کنید.
               
              نکته: در متد بالا بدلیل عدم پشتیبانی از خاصیت max-age در نسخه‌های قدیمی اینترنت اکسپلورر (نسخه 8 و قبل از آن) تنها از خاصیت expires استفاده شده است.
               
              نحوه استفاده از متد بالا به صورت زیر است:
              setCookie('cookie1', 'Value1');
              setCookie({name:'cookie1', value:'Value1'});
              setCookie({name:'cookie2', value:'Value2', expDays:10});
              setCookie({name:'cookie3', value:'Value3', expires:new Date()});
              setCookie({name:'cookie4', value:'Value4', expYear:2013, expMonth:0, expDay:13});
              setCookie({name:'cookie3', value:'Value3', maxAge:365*24*60*60});
              setCookie({name:'cookie5', value:'Value5', domain:'d.net', path:'/'});
              setCookie({name:'cookie6', value:'Value6', secure:true});
              setCookie({name:'cookie7', value:'Value7', expDays:100, domain:'dd.com', path:'/employee', secure:true});
               
              حذف کوکی
              همانطور که در قسمت قبل هم اشاره شد، برای حذف یک کوکی، کافی است تا تاریخ انقضای آن به تاریخی در گذشته مقداردهی شود. بنابراین برای اینکار می‌توان از متد زیر استفاده کرد:
              function delCookie(data) {
                if (typeof data === "string") {
                  data = { name: data };
                };
                data.expDays = -1;
                return setCookie(data);
              }
              در متد فوق از متد setCookie که در بالا معرفی شد، استفاده شده است. نحوه استفاده از این متد هم به صورت زیر است:
              delCookie('myCookie');
              delCookie({ name: 'myCookie', domain: 'd.com', path: '/test' });
               
              خواندن کوکی
              برای خواندن مقدار یک کوکی می‌توان از متد زیر استفاده کرد:
              function getCookie(name) {
                var cookies = document.cookie.split(";");
                for (var i = 0; i < cookies.length; i++) {
                  var cookie = cookies[i].split("=");
                  if (cookie[0].trim() == escape(name)) {
                    return unescape(cookie[1].trim());
                  }
                }
                return null;
              }
              برای آشنایی با متد unescape که در بالا از آن استفاده شده است به اینجا مراجعه کنید. در متد فوق از متد trim زیر استفاده شده است:
              String.prototype.trim = function () {
                return this.replace(/^\s+|\s+$/g, "");
              };
              String.prototype.trimStart = function () {
                return this.replace(/^\s+/, "");
              };
              String.prototype.trimEnd = function () {
                return this.replace(/\s+$/, "");
              };
              این متدها از اینجا گرفته شده است.
              روش استفاده شده برای خواندن مقادیر کوکی‌ها در متد بالا بسیار ساده و ابتدایی است و صرفا برای آشنایی با نحوه Parse کردن رشته برگشت داده شده توسط document.cookie ارائه شده است. روش‌های مناسب‌تر و مطمئن‌تر با یک جستجوی ساده در دسترس هستند. البته همانطور که قبلا هم اشاره شد، استفاه از کتابخانه‌های موجود راه‌حل بهتری است.
              هم‌چنین ازآنجاکه مقدار یک کوکی می‌تواند شامل کاراکتر = نیز باشد، بنابراین قسمت return متد فوق را می‌توان به صورت زیر تغییر داد:
              cookie.shift(1);
              return unescape(cookie.join('=').trim());
               
              نکته: با توجه به مطالب ارائه شده در قسمت قبل  بدست آوردن مقادیر کوکی‌ها کمی پیچیده‌تر از دیگر عملیات‌هاست. ازآنجاکه راه مستقیمی با استفاده از جاوا اسکریپت برای یافتن سایر خواص کوکی وجود ندارد، بنابراین بدست آوردن مقدار دقیق کوکی موردنظر ممکن است غیرممکن باشد! (با توجه به اینکه کوکی‌های متفاوت می‌توانند نام‌های یکسانی داشته باشند).
               
              با توجه به نکته بالا، حال اگر با یک نام بخصوص، چندین کوکی ثبت شده باشد (با خواص متفاوت)، یکی از راه‌حل‌ها این است که آرایه‌ای از مقادیر این کوکی‌های همنام برگشت داده شود. بنابراین متد فوق را می‌توان به صورت زیر تکمیل کرد:
              function getCookie(name) {
                var foundCookies = [];
                var cookies = document.cookie.split(";");
                for (var i = 0; i < cookies.length; i++) {
                  var cookie = cookies[i].split("=");
                  if (cookie[0].trim() == escape(name) && cookie.length >= 2) {
                    cookie.shift(1);
                    foundCookies.push(unescape(cookie.join('=').trim()));
                  }
                }
                return foundCookies.length > 1 
                          ? foundCookies
                          : foundCookies.length == 1
                              ? foundCookies[0]
                              : null;
              }

              خلاصه‌ای از نحوه استفاده از متدهای بالا در IE8 (برای نمایش اجرای درست در مرورگری قدیمی!) در تصویر زیر  نشان داده شده است:

               
              نکته: کار توسعه این متدها را میتوان برای پشتیبانی از SubCookieها نیز ادامه داد، اما به دلیل دورشدن از مبحث اصلی، این موضوع در این مطلب ارائه نمیشود (درباره این نوع از کوکی‌ها در قسمت قبل شرح کوتاهی داده شده است). اگر علاقه‌مند و نیازمند به این نوع کوکی‌ها هستید، کتابخانه YUI پشتیبانی کاملی از آنها ارائه میکند.
               
              در قسمت بعدی به نکات کار با کوکی در ASP.NET میپردازیم.

              منابع:
              مطالب
              آموزش زبان Rust - قسمت 13 - Enum
              Enums، مخفف enumerations و یک ساختار داده قدرتمند در زبان برنامه نویسی Rust است. Enum‌های Rust، بسیار متنوع‌تر از آنهایی هستند که در بسیاری از زبان‌های برنامه نویسی دیگر یافت می‌شوند و به شما این امکان را می‌دهند که داده‌های پیچیده را با تطبیق الگو و مدیریت خطا، مدل سازی کنید. در این مقاله به اصول اولیه‌ی enums در Rust، موارد استفاده‌ی از آنها و چند مثال کاربردی خواهیم پرداخت.

              تعریف Enums در Rust

              Enum‌ها در Rust، با استفاده از کلمه‌ی کلیدی enum و به دنبال آن، نام enumeration تعریف می‌شوند. هر enum می‌تواند صفر یا بیشتر، مقادیر مرتبط با انواع داده‌های مختلف را داشته باشد. در اینجا یک مثال ساده آورده شده‌است:
              enum Direction {
                  North,
                  East,
                  South,
                  West,
              }
              Rust به enum‌ها اجازه می‌دهد تا داده‌های مرتبطی را داشته باشند و آنها را به ابزاری قدرتمند، برای مدل سازی ساختارهای داده پیچیده، تبدیل می‌کند. در اینجا یک مثال از یک enum که اشکال مختلفی را نشان می‌دهد، مشاهده می‌کنید:
              enum Shape {
                  Circle(f64),
                  Rectangle(f64, f64),
                  Square(f64),
              }
              در این مثال، نوع Circle دارای یک مقدار ورودی برای شعاع است؛ در حالیکه نوع Rectangle دارای دو مقدار ورودی است که نشان دهنده‌ی عرض و ارتفاع است. نوع Square دارای یک مقدار ورودی برای طول ضلع خود است.


              Pattern Matching در Enums

              ** Pattern Matching  ** در ادامه مطالب به صورت کامل صحبت خواهد شد .

              قابلیت Pattern Matching به ویژه هنگام کار با enums مفید است. می‌توانید از کلمه‌ی کلیدی match برای destructure  و مطابقت با انواع enum استفاده کنید که به شما امکان می‌دهد، کد مختصر و خوانایی را بنویسید. در اینجا یک مثال، با استفاده از Shape enum آورده شده‌است:
              fn area(shape: Shape) -> f64 {
                  match shape {
                      Shape::Circle(radius) => std::f64::consts::PI * radius * radius,
                      Shape::Rectangle(width, height) => width * height,
                      Shape::Square(side) => side * side,
                  }
              }

               
              استفاده از Enum در Error Handling

              Enum‌ها معمولاً در Rust، برای رسیدگی به خطا استفاده می‌شوند؛ به لطف Result ارائه شده توسط کتابخانه‌ی استاندارد. Enum مربوط به Result، دو نوع را دارد: Ok برای نتایج موفق و Err برای خطاها. در اینجا نمونه‌ای از استفاده‌ی از Result را در تابعی که داده‌ها را از یک فایل می‌خواند، مشاهده می‌کنید:
              **Result ** در ادامه‌ی مطالب به صورت کامل صحبت خواهد شد. در اینجا هدف فقط نشان دادن کاربردهای Enum است:
              use std::io::Read;
              use std::fs::File;
              use std::io;
              
              fn read_file_contents(file_path: &str) -> Result<String, io::Error> {
                  let mut file = File::open(file_path)?;
                  let mut contents = String::new();
                  file.read_to_string(&mut contents)?;
                  Ok(contents)
              }


              Enums و Optionality

              یکی دیگر از موارد استفاده‌ی رایج برای enums در Rust، نمایش مقادیر اختیاری است که توسط Option enum ارائه شده‌ی توسط کتابخانه‌ی استاندارد، تسهیل می‌شود. Option enum دو نوع دارد: برخی برای مقادیر فعلی و None برای مقادیر غایب. در اینجا مثالی از استفاده‌ی از Option enum، برای پیاده سازی یک پشته‌ی ساده آورده شده‌است:
              **Option ** در ادامه مطالب به صورت کامل صحبت خواهد شد. در اینجا هدف فقط نشان دادن کاربردهای Enum است:
              struct Stack<T> {
                  elements: Vec<T>,
              }
              
              impl<T> Stack<T> {
                  fn new() -> Self {
                      Stack { elements: Vec::new() }
                  }
              
                  fn push(&mut self, item: T) {
                      self.elements.push(item);
                  }
              
                  fn pop(&mut self) -> Option<T> {
                      self.elements.pop()
                  }
              }
              Enums در Rust، یک ویژگی قدرتمند است که به شما امکان می‌دهد ساختارهای داده‌ی پیچیده را مدل سازی کنید و به راحتی خطاها را مدیریت کنید. با استفاده از قدرت enums،  Pattern Matching  ، و فهرست‌های داخلی کتابخانه‌ی استاندارد مانند Result و Option میتوانید از قدرت Enum‌ها استفاده کنید. 
              مطالب
              دریافت و نمایش فایل‌های PDF در برنامه‌های Blazor WASM
              زمانیکه قرار است با فایل‌های باینری واقع در سمت سرور کار کنیم، اگر اکشن متدهای ارائه دهنده‌ی آن‌ها محافظت شده نباشند، برای نمایش و یا دریافت آن‌ها تنها کافی است از آدرس مستقیم این منابع استفاده کرد و در این حالت نیازی به رعایت هیچ نکته‌ی خاصی نیست. اما اگر اکشن متدی در سمت سرور توسط فیلتر Authorize محافظت شده باشد و روش محافظت نیز مبتنی بر کوکی‌ها نباشد، یعنی این کوکی‌ها در طی درخواست‌های مختلف، به صورت خودکار توسط مرورگر به سمت سرور ارسال نشوند، آنگاه نیاز است با استفاده از HttpClient برنامه‌های Blazor WASM، درخواست دسترسی به منبعی را به همراه برای مثال JSON Web Tokens کاربر به سمت سرور ارسال کرد و سپس فایل باینری نهایی را به صورت آرایه‌ای از بایت‌ها دریافت نمود. در این حالت با توجه به ماهیت Ajax ای این این عملیات، برای نمایش و یا دریافت این فایل‌های محافظت شده در مرورگر، نیاز به دانستن نکات ویژه‌ای است که در این مطلب به آن‌ها خواهیم پرداخت.



              کدهای سمت سرور دریافت فایل PDF

              در اینجا کدهای سمت سرور برنامه، نکته‌ی خاصی را به همراه نداشته و صرفا یک فایل PDF ساده (محتوای باینری) را بازگشت می‌دهد:
              using Microsoft.AspNetCore.Mvc;
              
              namespace BlazorWasmShowBinaryFiles.Server.Controllers
              {
                  [ApiController]
                  [Route("api/[controller]")]
                  public class ReportsController : ControllerBase
                  {
                      [HttpGet("[action]")]
                      public IActionResult GetPdfReport()
                      {
                          //TODO: create the `sample.pdf` report file on the server
              
                          return File(virtualPath: "~/app_data/sample.pdf",
                                      contentType: "application/pdf",
                                      fileDownloadName: "sample.pdf");
                      }
                  }
              }
              که در نهایت با آدرس api/Reports/GetPdfReport در سمت کلاینت قابل دسترسی خواهد بود.

              یک نکته: استفاده مستقیم از کتابخانه‌های تولید PDF در برنامه‌های سمت کاربر Blazor منطقی نیست؛ چون به ازای هر کاربر، گاهی از اوقات مجبور به ارسال بیش از 8 مگابایت اضافی مختص به فایل‌های dll. آن کتابخانه‌ی تولید PDF خواهیم شد. بنابراین بهتر است تولید PDF را در سمت سرور و در اکشن متدهای Web API انجام داد و سپس فایل نهایی تولیدی را در برنامه‌ی سمت کلاینت، دریافت و یا نمایش داد. به همین جهت در این مثال خروجی نهایی یک چنین عملیات فرضی را توسط یک اکشن متد Web API ارائه داده‌ایم که در بسیاری از موارد حتی می‌تواند توسط فیلتر Authorize نیز محافظت شده باشد.


              ساخت URL برای دسترسی به اطلاعات باینری

              تمام مرورگرهای جدید از ایجاد URL برای اشیاء Blob دریافتی از سمت سرور، توسط متد توکار URL.createObjectURL پشتیبانی می‌کنند. این متد، شیء URL را از شیء window جاری دریافت می‌کند و سپس اطلاعات باینری را دریافت کرده و آدرسی را جهت دسترسی موقت به آن تولید می‌کند. حاصل آن، یک URL ویژه‌است مانند blob:https://localhost:5001/03edcadf-89fd-48b9-8a4a-e9acf09afd67 که گشودن آن در مرورگر، یا سبب نمایش آن تصویر و یا دریافت مستقیم فایل خواهد شد.
              در برنامه‌های Blazor نیاز است اینکار را توسط JS Interop آن انجام داد؛ از این جهت که API تولید یک Blob URL، صرفا توسط کدهای جاوا اسکریپتی قابل دسترسی است. به همین جهت فایل جدید Client\wwwroot\site.js را با محتوای زیر ایجاد کرده و همچنین مدخل آن‌را در به انتهای فایل Client\wwwroot\index.html، پیش از بسته شدن تگ body، اضافه می‌کنیم:
              window.JsBinaryFilesUtils = {
                createBlobUrl: function (byteArray, contentType) {
                  // The byte array in .NET is encoded to base64 string when it passes to JavaScript.
                  const numArray = atob(byteArray)
                    .split("")
                    .map((c) => c.charCodeAt(0));
                  const uint8Array = new Uint8Array(numArray);
                  const blob = new Blob([uint8Array], { type: contentType });
                  return URL.createObjectURL(blob);
                },
                downloadFromUrl: function (fileName, url) {
                  const anchor = document.createElement("a");
                  anchor.style.display = "none";
                  anchor.href = url;
                  anchor.download = fileName;
                  document.body.appendChild(anchor);
                  anchor.click();
                  document.body.removeChild(anchor);
                },
                downloadBlazorByteArray: function (fileName, byteArray, contentType) {
                  const blobUrl = this.createBlobUrl(byteArray, contentType);
                  this.downloadFromUrl(fileName, blobUrl);
                  URL.revokeObjectURL(blobUrl);
                },
                printFromUrl: function (url) {
                  const iframe = document.createElement("iframe");
                  iframe.style.display = "none";
                  iframe.src = url;
                  document.body.appendChild(iframe);
                  if (iframe.contentWindow) {
                    iframe.contentWindow.print();
                  }
                },
                printBlazorByteArray: function (byteArray, contentType) {
                  const blobUrl = this.createBlobUrl(byteArray, contentType);
                  this.printFromUrl(blobUrl);
                  URL.revokeObjectURL(blobUrl);
                },
                showUrlInNewTab: function (url) {
                  window.open(url);
                },
                showBlazorByteArrayInNewTab: function (byteArray, contentType) {
                  const blobUrl = this.createBlobUrl(byteArray, contentType);
                  this.showUrlInNewTab(blobUrl);
                  URL.revokeObjectURL(blobUrl);
                },
              };
              توضیحات:
              - زمانیکه در برنامه‌های Blazor با استفاده از متد ()HttpClient.GetByteArrayAsync آرایه‌ای از بایت‌های یک فایل باینری را دریافت می‌کنیم، ارسال آن به کدهای جاوااسکریپتی به صورت یک رشته‌ی base64 شده صورت می‌گیرد (JS Interop اینکار را به صورت خودکار انجام می‌دهد). به همین جهت در متد createBlobUrl روش تبدیل این رشته‌ی base64 دریافتی را به آرایه‌ای از بایت‌ها، سپس به یک Blob و در آخر به یک Blob URL، مشاهده می‌کنید.  این Blob Url اکنون آدرس موقتی دسترسی به آرایه‌ای از بایت‌های دریافتی توسط مرورگر است. به همین جهت می‌توان از آن به عنوان src بسیاری از اشیاء HTML استفاده کرد.
              - متد downloadFromUrl، کار دریافت یک Url و سپس دانلود خودکار آن‌را انجام می‌دهد. اگر به یک anchor استاندارد HTML، ویژگی download را نیز اضافه کنیم، با کلیک بر روی آن، بجای گشوده شدن این Url، مرورگر آن‌را دریافت خواهد کرد. متد downloadFromUrl کار ساخت لینک و تنظیم ویژگی‌های آن و سپس کلیک بر روی آن‌را به صورت خودکار انجام می‌دهد. از متد downloadFromUrl زمانی استفاده کنید که منبع مدنظر، محافظت شده نباشد و Url آن به سادگی در مرورگر قابل گشودن باشد.
              - متد downloadBlazorByteArray همان کار متد downloadFromUrl را انجام می‌دهد؛ با این تفاوت که Url مورد نیاز توسط متد downloadFromUrl را از طریق یک Blob Url تامین می‌کند.
              - متد printFromUrl که جهت دسترسی به منابع محافظت نشده طراحی شده‌است، Url یک منبع را دریافت کرده، آن‌را به یک iframe اضافه می‌کند و سپس متد print را بر روی این iframe به صورت خودکار فراخوانی خواهد کرد تا سبب ظاهر شدن صفحه‌ی پیش‌نمایش چاپ شود.
              - printBlazorByteArray همان کار متد printFromUrl را انجام می‌دهد؛  با این تفاوت که Url مورد نیاز توسط متد printFromUrl را از طریق یک Blob Url تامین می‌کند.


              تهیه‌ی متدهایی الحاقی جهت کار ساده‌تر با JsBinaryFilesUtils

              پس از تهیه‌ی JsBinaryFilesUtils فوق، می‌توان با استفاده از کلاس زیر که به همراه متدهایی الحاقی جهت دسترسی به امکانات آن است، کار با متدهای دریافت، نمایش و چاپ فایل‌های باینری را ساده‌تر کرد و از تکرار کدها جلوگیری نمود:
              using System.Threading.Tasks;
              using Microsoft.JSInterop;
              
              namespace BlazorWasmShowBinaryFiles.Client.Utils
              {
                  public static class JsBinaryFilesUtils
                  {
                      public static ValueTask<string> CreateBlobUrlAsync(
                          this IJSRuntime JSRuntime,
                          byte[] byteArray, string contentType)
                      {
                          return JSRuntime.InvokeAsync<string>("JsBinaryFilesUtils.createBlobUrl", byteArray, contentType);
                      }
              
                      public static ValueTask DownloadFromUrlAsync(this IJSRuntime JSRuntime, string fileName, string url)
                      {
                          return JSRuntime.InvokeVoidAsync("JsBinaryFilesUtils.downloadFromUrl", fileName, url);
                      }
              
                      public static ValueTask DownloadBlazorByteArrayAsync(
                          this IJSRuntime JSRuntime,
                          string fileName, byte[] byteArray, string contentType)
                      {
                          return JSRuntime.InvokeVoidAsync("JsBinaryFilesUtils.downloadBlazorByteArray",
                                  fileName, byteArray, contentType);
                      }
              
                      public static ValueTask PrintFromUrlAsync(this IJSRuntime JSRuntime, string url)
                      {
                          return JSRuntime.InvokeVoidAsync("JsBinaryFilesUtils.printFromUrl", url);
                      }
              
                      public static ValueTask PrintBlazorByteArrayAsync(
                          this IJSRuntime JSRuntime,
                          byte[] byteArray, string contentType)
                      {
                          return JSRuntime.InvokeVoidAsync("JsBinaryFilesUtils.printBlazorByteArray", byteArray, contentType);
                      }
              
                      public static ValueTask ShowUrlInNewTabAsync(this IJSRuntime JSRuntime, string url)
                      {
                          return JSRuntime.InvokeVoidAsync("JsBinaryFilesUtils.showUrlInNewTab", url);
                      }
              
                      public static ValueTask ShowBlazorByteArrayInNewTabAsync(
                          this IJSRuntime JSRuntime,
                          byte[] byteArray, string contentType)
                      {
                          return JSRuntime.InvokeVoidAsync("JsBinaryFilesUtils.showBlazorByteArrayInNewTab", byteArray, contentType);
                      }
                  }
              }


              اصلاح Content Security Policy سمت سرور جهت ارائه‌ی محتوای blob

              پس از دریافت فایل PDF به صورت یک blob، با استفاده از متد URL.createObjectURL می‌توان آدرس موقت محلی را برای دسترسی به آن تولید کرد و یک چنین آدرس‌هایی به صورت blob:http تولید می‌شوند. در این حالت در Content Security Policy سمت سرور، نیاز است امکان دسترسی به تصاویر و همچنین اشیاء از نوع blob را نیز آزاد معرفی کنید:
              img-src 'self' data: blob:
              default-src 'self' blob:
              object-src 'self' blob:
              در غیراینصورت مرورگر، نمایش یک چنین تصاویر و یا اشیایی را سد خواهد کرد.


              نمایش فایل PDF دریافتی از سرور، به همراه دکمه‌های دریافت، چاپ و نمایش آن در صفحه‌ی جاری

              در ادامه کدهای کامل مرتبط با تصویری را که در ابتدای بحث مشاهده کردید، ملاحظه می‌کنید:
              @page "/"
              
              @using BlazorWasmShowBinaryFiles.Client.Utils
              
              @inject IJSRuntime JSRuntime
              @inject HttpClient HttpClient
              
              <h1>Display PDF Files</h1>
              <button class="btn btn-info" @onclick="handlePrintPdf">Print PDF</button>
              <button class="btn btn-primary ml-2" @onclick="handleShowPdf">Show PDF</button>
              <button class="btn btn-success ml-2" @onclick="handleDownloadPdf">Download PDF</button>
              
              @if(!string.IsNullOrWhiteSpace(PdfBlobUrl))
              {
                  <section class="card mb-5 mt-3">
                      <div class="card-header">
                          <h4>using iframe</h4>
                      </div>
                      <div class="card-body">
                          <iframe title="PDF Report" width="100%" height="600" src="@PdfBlobUrl" type="@PdfContentType"></iframe>
                      </div>
                  </section>
              
                  <section class="card mb-5">
                      <div class="card-header">
                          <h4>using object</h4>
                      </div>
                      <div class="card-body">
                          <object data="@PdfBlobUrl" aria-label="PDF Report" type="@PdfContentType" width="100%" height="100%"></object>
                      </div>
                  </section>
              
                  <section class="card mb-5">
                      <div class="card-header">
                          <h4>using embed</h4>
                      </div>
                      <div class="card-body">
                          <embed aria-label="PDF Report" src="@PdfBlobUrl" type="@PdfContentType" width="100%" height="100%">
                      </div>
                  </section>
              }
              
              @code
              {
                  private const string ReportUrl = "/api/Reports/GetPdfReport";
                  private const string PdfContentType = "application/pdf";
              
                  private string PdfBlobUrl;
              
                  private async Task handlePrintPdf()
                  {
                      // Note: Using the `HttpClient` is useful for accessing the protected API's by JWT's (non cookie-based authorization).
                      // Otherwise just use the `PrintFromUrlAsync` method.
                      var byteArray = await HttpClient.GetByteArrayAsync(ReportUrl);
                      await JSRuntime.PrintBlazorByteArrayAsync(byteArray, PdfContentType);
                  }
              
                  private async Task handleDownloadPdf()
                  {
                      // Note: Using the `HttpClient` is useful for accessing the protected API's by JWT's (non cookie-based authorization).
                      // Otherwise just use the `DownloadFromUrlAsync` method.
                      var byteArray = await HttpClient.GetByteArrayAsync(ReportUrl);
                      await JSRuntime.DownloadBlazorByteArrayAsync("report.pdf", byteArray, PdfContentType);
                  }
              
                  private async Task handleShowPdf()
                  {
                      // Note: Using the `HttpClient` is useful for accessing the protected API's by JWT's (non cookie-based authorization).
                      // Otherwise just use the `ReportUrl` as the `src` of the `iframe` directly.
                      var byteArray = await HttpClient.GetByteArrayAsync(ReportUrl);
                      PdfBlobUrl = await JSRuntime.CreateBlobUrlAsync(byteArray, PdfContentType);
                  }
              
                  // Tips:
                  // 1- How do I enable/disable the built-in pdf viewer of FireFox
                  // https://support.mozilla.org/en-US/kb/disable-built-pdf-viewer-and-use-another-viewer
                  // 2- How to configure browsers to use the Adobe PDF plug-in to open PDF files
                  // https://helpx.adobe.com/acrobat/kb/pdf-browser-plugin-configuration.html
                  // https://helpx.adobe.com/acrobat/using/display-pdf-in-browser.html
                  // 3- Microsoft Edge is gaining new PDF reader features within the Windows 10 Fall Creator’s Update (version 1709).
              }
              توضیحات:
              - پس از تهیه‌ی JsBinaryFilesUtils و متدهای الحاقی متناظر با آن، اکنون تنها کافی است با  استفاده از متد ()HttpClient.GetByteArrayAsync، فایل PDF ارائه شده‌ی توسط یک اکشن متد را به صورت آرایه‌ای از بایت‌ها دریافت و سپس به متدهای چاپ (PrintBlazorByteArrayAsync) و دریافت (DownloadBlazorByteArrayAsync) آن ارسال کنیم.
              - در مورد نمایش آرایه‌ای از بایت‌های دریافتی، وضعیت کمی متفاوت است. ابتدا باید توسط متد CreateBlobUrlAsync، آدرس موقتی این آرایه را در مرورگر تولید کرد و سپس این آدرس را برای مثال به src یک iframe انتساب دهیم تا PDF را با استفاده از امکانات توکار مرورگر، نمایش دهد.


              کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: BlazorWasmShowBinaryFiles.zip
              نظرات مطالب
              صفحه بندی اطلاعات در ASP.NET MVC به روش HashChange
              از افزونه‌ی path.js در مطلب «پیاده سازی دکمه «بیشتر» یا «اسکرول نامحدود» به کمک jQuery در ASP.NET MVC» برای بهبود کاربری صفحه بندی ای‌جکسی هم استفاده شده‌است. این کتابخانه قابلیت افزودن صفحات مرور شده را به history مرورگر دارد؛ به همراه امکان ذخیره و بازیابی شماره صفحه‌ی ای‌جکسی (یک نمونه‌ی سبک وزن مباحث مسیریابی کتابخانه‌های SPA).
              نظرات مطالب
              آپلود فایل ها با استفاده از PlUpload در Asp.Net Mvc
              باسلام. من این کارهایی که گفتید کردم. روی vs مشکلی نداشتم. ولی وقتی نرم افزارمو پابلیش کردم و روی سرور گذاشتم متأسفانه هرکار می‌کنم خطا عدم پشتیبانی مرورگر میده. با فایرباگ چک کردم دیدم به فایلهای کتابخانه اش عدم دسترسی می‌ده. تمام تنظیمات iis رو هم چک کردم. نتونستم درستش کنم.
              خطایی که میده اینطوریه:
              NetworkError: 404 Not Found - http://~/Scripts/plupload/js/jquery.ui.plupload/jquery.ui.plupload.js" 
              مطالب
              آموزش (jQuery) جی کوئری 3#
              در ادامه مطلب قبلی آموزش (jQuery) جی کوئری 2# به ادامه بحث می‌پردازیم.

              انتخاب عناصر صفحه
              در پستهای قبل (^ و ^) با بسیاری از توانایی‌ها و کارکردهای jQuery شامل توانایی‌های آن برای انتخاب عناصر موجود در صفحه تا تعریف توابع جدید و استفاده از آنها به محض آماده شدن صفحه آشنا شدیم.
              در این پست و پست بعدی توضیحات تکمیلی در خصوص دو مورد از توانایی‌های jQuery و البته تابع ()$ خواهیم داشت که مورد اول، انتخاب عناصر صفحه با استفاده از انتخاب کننده‌ها و مورد دوم ایجاد عناصر جدید می‌باشد.
              در بسیاری از مواقع برای تعامل با صفحه اینترنتی نیاز به تغییر دادن بخشی از یکی از اشیا موجود در صفحه داریم. اما پیش از آنکه قادر باشیم آنها را تغییر دهیم، ابتدا باید با استفاده از مکانیزمی شی مورد نظر را مشخص و سپس آن را انتخاب کنیم تا پس از آن قادر به اعمال تغییری در آن باشیم. بنابراین اجازه دهید تا به یک بررسی عمیق از راه‌های مختلف انتخاب عناصر صفحه و ایجاد تغییر در آنها بپردازیم.

              1-انتخاب عناصر صفحه برای ایجاد تغییر

              اولین قدم برای استفاده از هر گونه تابع jQuery، مشخص کردن و انتخاب عناصری است که می‌خواهیم تابع روی آن عناصر اعمال شود. گاهی اوقات انتخاب این مجموعه عناصر با یک توضیح ساده مشخص می‌شود، برای مثال "تمام عناصر پاراگراف موجود در صفحه". اما گاهی اوقات مشخص کردن این مجموعه نیاز به توضیح پیچیده‌تری دارد، برای مثال "تمام عناصر لیست در صفحه که دارای کلاس listElement هستند و لینکی دارند که اولین عضو آن لیست می‌باشد".
              خوشبختانه jQuery یک مکانیزم بسیار قوی و قدرتمند ارایه کرده است که انتخاب هر عنصری از صفحه را به سادگی امکان پذیر می‌سازد. انتخاب کننده‌های jQuery از ساختار مربوط به CSS استفاده می‌کنند، بنابراین ممکن است شما هم اکنون با تعداد زیادی از آنها آشنا باشید . در ادامه شمار بیشتر و قدرتمندتری خواهید آموخت.
              برای درک بهتر شما از مطالب مربوط به بخش انتخاب کننده ها، یک مثال آماده مختص به این مبحث، در قالب یک صفحه اینترنتی، را در فایل صفحه کارگاهی قرار داده ایم، این فایل در ادرس chapter2/lab.selector.htm قابل دسترسی می‌باشد. این مثال از پیش آماده و کامل (نوشته شده توسط نویسنده کتاب)، این امکان را به شما می‌دهد تا با وارد کردن یک رشته، به عنوان پارامتر انتخاب کننده، در همان زمان عنصر انتخاب کننده در صفحه را رویت کنید. زمانی که این صفحه را اجرا می‌کنید تصویری مانند زیر ظاهر خواهد شد.


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


              1-1- استفاده از انتخاب کننده‌های ابتدایی CSS
              برنامه نویسان وب برای اعمال فرمت‌های ظاهری گوناگون به بخش‌ها و عناصر مختلف یک صفحه اینترنتی، از ایک راه بسیار ساده، در عین حال قدرتمند و کارا استفاده می‌کنند که در تمام مرورگرهای مختلف نیز جوابگو باشد. این انتخاب کننده‌ها عناصر را بر اساس نام شناسه آنها، نام کلاس و یا ساختار سلسله مراتبی موجود در صفحه انتخاب می‌کنند.
              در زیر به معرفی چند نمونه از این انتخاب کننده‌های ساده CSS می‌پردازیم:
              • a   : تمام عناصر <a> را انتخاب می‌کند.
              • specialID# : عنصری را که دارای ID با عنوان specialID باشد انتخاب می‌کند.
              • specialClass.               : عناصری را که دارای کلاس specialClass هستند انتخاب می‌کند.
              • a#specialID.specialClass : این عبارت عنصری را انخاب می‌کند که شناسه آن specialID باشد، به شرط آنکه این عنصر <a> باشد و دارای کلاس specialClass نیز باشد را انتخاب می‌کند.
              • p a.specialClass: تمام عناصر لینک (<a>) را که دارای کلاس specialClass باشند و درون یک عنصر پاراگراف (<p>) قرار گرفته باشند را انتخاب می‌کند.

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

              نکته مثبت در مورد انتخاب کننده‌های CSS این است که از همین انتخاب کننده‌ها می‌توانیم در jQuery نیز استفاده کنیم. برای این کار تنها کافیست انتخاب کننده مورد نظر را به تابع ()$ ارسال کنیم. در زیر یک نمونه را مشاهده می‌کنید:

              $("p a.specialClass")
              به جز چند مورد خاص که استثنا وجود دارد، CSS3 و jQuery کاملا با هم سازگاری دارند. بنابراین انتخاب عناصر به این شکل طبیعی خواهد بود. به عبارتی دیگر هر عنصر که از این طریق توسط CSS انتخاب شود، همان انتخاب حاصل انتخاب کننده jQuery نیز خواهد بود. اما باید به این نکته توجه داشت که jQuery وابسته به CSS نیست و اگر مرورگری پیاده سازی استانداردی برای CSS نداشته باشد، انتخاب کننده jQuery به مشکل بر نمی‌خورد، بلکه jQuery انتخاب خود را به درستی انجام می‌دهد، چرا که jQuery از قوانین استاندارد W3C تبعیت می‌کند.

              2-1- استفاده از انتخاب کننده‌های فرزند (Child) ، نگهدارنده (Container) و صفت (Attribute)

              برای انتخاب کننده‌های پیشرفته تر، jQuery از جدیدترین مرورگرهایی که CSS را پشتیبانی می‌کنند، استفاده می‌کند که می‌توان به Mozilla Firefox, Internet Explorer 7, Safariو سایر مرورگرهای پیشرفته (مدرن) اشاره کرد. این انتخاب کننده‌های پیشرفته شما را قادر می‌سازند تا مستقیما فرزند یک عنصر را انتخاب کنید و یا از ساختار سلسله مراتبی عناصر صفحه، مستقیما به عنصر مورد نظر دسترسی داشته باشید و یا حتی تمام عناصری که یک صفت خاص را شامل می‌شوند، انتخاب کنید. گاهی اوقات انتخاب فرزندی از یک شی برای ما مطلوب است. برای مثال ممکن است ما به چند مورد از یک لیست احتیاج داشته باشیم، نه یک زیر مجموعه ای از آن لیست. به قطعه کد زیر که از صفحه کارگاهی این پست گرفته شده است دقت نمایید:

              <ul>
                 <li><a href="http://jquery.com">jQuery supports</a>
                    <ul>
                          <li><a href="css1">CSS1</a></li>
                          <li><a href="css2">CSS2</a></li>
                          <li><a href="css3">CSS3</a></li>
                          <li>Basic XPath</li>
                     </ul>
                  </li>
                  <li>jQuery also supports
                      <ul>
                           <li>Custom selectors</li>
                           <li>Form selectors</li>
                       </ul>
                    </li>
              </ul>
              حال فرض کنید از این ساختار، لینک وب سایت jQuery مد نظر ماست و این کار بدون انتخاب سایر لینک‌های مربوط به CSS مطلوب است. اگر بخواهیم از دستور‌های انتخاب کننده CSS استفاده کینم، دستوری به شکل ul.myList li a خواهیم داشت. اما متاسفانه این دستور تمام لینک‌های این ساختار را انتخاب میکند، زیرا همه آنها لینک هایی در عنصر li می‌باشند. با نوشتن این دستور در صفحه کارگاهی خروجی به شکل زیر خواهد بود:
              راه حل مناسب برای انتخاب چنین حالتی استفاده از انتخاب فرزند می باشد که به این منظور Parent (والد) و Child (فرزند)، به وسیله یک کاراکتر < از یکدیگر جدا می‌شوند:
              p > a
              این دستور تنها لینک (<a>) هایی را بر می‌گرداند که فرزند مستقیم یک عنصر <p> می‌باشند. بنابراین اگر در یک <p> لینکی در عنصر <span> معرفی شده باشد، این لینک انتخاب نمی‌شود، چرا که فرزند مستقیم <p> به حساب نمی‌آید. در مورد مثال لینک‌های موجود در لیست، می‌توانیم دستور زیر را به منظور انتخاب لینک مورد نظرمان استفاده کنیم:
              ul.myList > li > a
               دستور انتخاب فوق از میان عناصر <ul>، عنصری را که دارای کلاس myList می‌باشد، انتخاب می‌کند و پس از آن لینکهایی (<a>) که فرزند مستقیم گزینه‌های آن هستند، برگردانده می‌شوند. همانگونه که در شکل زیر مشاهده می‌کنید لینک‌های زیرمجموعه عنصر <ul> انتخاب نمی‌شوند، زیرا فرزند مستقیم این عنصر محصوب نمی‌شوند.

              انتخاب کننده‌های صفت نیز بسیار قدرتمند می‌باشند و ما را تواناتر می‌سازند، فرض کنید برای منظوری خاص قصد دارید به تمام لینک‌های موجود در صفحه که به مکانی خارج از این وب سایت اشاره دارند، رفتاری را اضافه کنید (مثلا مانند همین سایت به کنار آنها یک آیکن اضافه نمایید) . فرض کنید این کد (کد موجود در مثال کارگاهی) را در صفحه خود دارید:
              <li><a href="http://jquery.com">jQuery supports</a>
                  <ul>
                        <li><a href="css1">CSS1</a></li>
                        <li><a href="css2">CSS2</a></li>
                        <li><a href="css3">CSS3</a></li>
                        <li>Basic XPath</li>
                   </ul>
              </li>
              موردی که یک لینک با اشاره به وب سایت خارجی را از سایر لینک‌ها متمایز می‌سازد، شروع شدن مقدار صفت href آن با //:http می‌باشد. انتخاب لینک هایی که مقدار href آنها با //:http آغاز می‌شود، به سهولت و از طریق دستور زیر صورت می‌پذیرد:
              a[href^=http://]
              این دستور باعث انتخاب تمام لینک هایی که مقدار صفت href آنها دقیقا با //:http آغاز می‌شود، می‌گردد. علامت ^ موجب می‌شود تابررسی، لزوما از ابتدای مقادیر صورت پذیرد و از آنجا که استفاده از این کاراکتر در سایر عبارات منظم به همین منظور صورت می‌پذیرد، به خاطر سپردن آن دشوار نخواهد بود.
              می توانید این کد را در صفحه کار گاهی تست کنید.
              را‌های دیگری برای استفاده از انتخاب کننده‌های صفت وجود دارد.

              form[method]
              این دستور تمام عناصر <form> را که یک صفت method دارند را انتخاب می‌کند.

              input[type=text]
              این انتخاب کننده تمام عناصر input را که type آنها برابر text با شد انتخاب می‌کند.
              دستور زیر مثالی دیگر برای بررسی یک مقدار بر اساس کاراکترهای نخست آن می‌باشد:
              div[title^=my]
              همانطور که از دستور فوق بر میآید، عناصر div که مقدار title آنها با رشته my اغاز می‌شود، هدف این انتخاب کننده خواهد بود.
              اما اگر بخواهیم تنها بر اساس کاراکتر‌های انتهایی انتخابی انجام دهیم، دستور مناسب چه خواهد بود؟ برای چنین منظوری مانند زیر عمل می‌کنیم:
              a[href$=.pdf]
              این دستور کاربرد زیادی برای شناسایی لنک‌های اشاره کننده به فایل‌های pdf دارد. ساختار زیر نیز زمانی استفاده می‌شود که یک عبارت منظم در جایی از یک صفت قرار گرفته باشد، خواه این عبارت از کاراکتر دوم آغاز شده باشد و یا از هرجای دیگر.
              a[href*=jquery.com]
              همانگونه که انتظار میرود این انتخاب کننده ، تمام لینک هایی که به وب سایت jQuery اشاره دارند را برمی گرداند.
              فراتر از خصوصیات، بعضی مواقع ما می‌خواهیم بررسی کنیم که آیا یک عنصر شامل عنصر دیگری هست یا خیر. در مثال‌های قبلی فرض کنید ما می‌خواهیم بدانیم که آیا یک li شامل a هست یا خیر، jQuery با استفاده از انتخاب کننده‌های Container‌ها این را پشتیبانی می‌کند:
              li:has(a)
              این انتخاب کننده همه li هایی را برمی گرداند که شامل لینک (<a>) هستند. دقت کنید که این انتخاب گر مانند li a نیست، انتخاب گر دوم تمامی لینک هایی را که در li هستند بر میگرداند اما دستور بالا li هایی را بر میگرداند که دارای لینک (<a>) هستند.

              تصویر زیر انتخاب گرهایی را نشان میدهد که ما می‌توانیم در jQuery استفاده نماییم.

              انشالله در پست‌های بعدی ادامه مباحث را بررسی خواهد شد.