اشتراکها
اشتراکها
Prototypes در Javascript
اشتراکها
معرفی gif.js
اشتراکها
spacetree (ساختار چارت سازمانی زیبا)
nuget برای کارهای جاوا اسکریپتی مناسب نیست و توصیه نمیشود. از npm استفاده کنید.
نظرات مطالب
با ASP.MVC چه مزایایی را به دست خواهیم آورد
مستقیما میتونید با MVC شروع کنید. البته پیشنیازهای CSS ،HTML و جاوا اسکریپت را هم باید لحاظ کنید.
گذشته از بحث NHibernate، اینطوری که پیداست واقعاً جاوا Follower سی شارپ شده!!!
در قسمت قبلی با نحوه انتشار برنامهها آشنا شدیم. در این قسمت نحوه پیکربندی یا تغییر پیکربندی برنامه را مشخص میکنیم.
کاربر یا مدیر سیستم بهتر از هر کسی میتواند جنبههایهای مختلف اجرای برنامه را مشخص کند. به عنوان نمونه ممکن است مدیر سیستم بخواهد فایلهای یک برنامه را سمت هارد دیسک سیستم کاربر انتقال دهد یا اطلاعات مانیفست یک اسمبلی را رونویسی کند و مباحث نسخه بندی که در آینده در مورد آن صحبت میکنیم.
با ارائه یک فایل پیکربندی در شاخه برنامه میتوان به مدیر سسیتم اجازه داد تا کنترل بیشتر بر روی برنامه داشته باشد. ناشر برنامه میتواند این فایل را همراه دیگر فایلهای برنامه پکیج کند تا در شاخه برنامه نصب شود تا بعدا مدیر یا کاربر سیستم بتوانند آن را تغییر و ویرایش کنند. CLR هم محتوای این فایل را تفسیر کرده و قوانین بارگیری اسمبلیها و ... را تغییر میدهد. این فایل پیکربندی میتواند به صورت XML هم ارائه شود. مزیت قرار دادن یک فایل جداگانه نسبت به رجیستری این مزیت را دارد که هم قابل جابجایی و پشتیبانی گیری است و هم اینکه تغییر آن سادهتر است.
البته در آینده بیشتر در مورد این فایل صحبت میکنیم ولی در حال حاضر بهتر است اندکی طعم آن را بچشیم. فرض را بر این میگذاریم که ناشر میخواهد فایلهای اسمبلی MultiFileLibrary را در دایرکتوری جداگانهای قرار دهد و چیزی شبیه به ساختار زیر را در نظر دارد:
حال با تنظیم بالا به دلیل اینکه CLR انتظار دارد این اسمبلی را در دایرکتوری برنامه بیابد و با این جابجایی قادر به انجام این کار نیست، استثنای زیر را صادر میکند:
برای حل این مشکل، ناشر یک فایل XML را ایجاد کرده و در مسیر دایرکتوری
برنامه قرار میدهد. این فایل باید همنام اسمبلی اصلی برنامه با پسوند
config. باشد. به عنوان مثال، نام فایل میشود: Program.exe.config و فایل
پیکربندی هم چیزی شبیه فایل زیر میشود:
نحوه عملکرد اسکن CLR برای بارگزاری اسمبلی ها
موقعی که CLR قصد بارگزاری اسمبلیهای خنثی را دارد، شاخههای زیر را به طور خودکار اسکن خواهد نمود ( مسیرهای FirstPrivatePath و SecondPrivatePath توسط فایل پیکربندی مشخص شده است)
این نکته ضروری است که اگر اسمبلی شما در یک دایرکتوری همنام خودش (در مثال ما MultiFileLibrary) قرار بگیرد، نیازی نیست این مسیر را در فایل پیکربندی ذکر کنید؛ زیرا CLR در صورت نیافتن دایرکتوری با این نام را اسکن خواهد نمود. بعد از آن اگر به هر نحوی CLR نتواند اسمبلی را در هیچ کدام از دایرکتوریهای گفته شده بیابد، با همان قوانین گفته شده اینبار به دنبال فایلی با پسوند exe خواهد بود و اگر باز هم جست و جوی آن نتیجهای را در بر نداشته باشد، استثنای زیر را صادر میکند:
برای اسمبلیهای ماهوارهای همان قوانین بالا دنبال میشود؛ با این تفاوت که انتظار میرود اسمبلی داخل یک زیر دایرکتوری با تگهای RFC1766 مطابقت داشته باشد. به عنوان مثال اگر اسمبلی با فرهنگ و منطقه En-US مشخص شده باشد، دایرکتوریهای زیر اسکن خواهند شد:
نحوه اسکن کردن CLR میتواند به ما بگوید که عمل اسکن میتواند گاهی اوقات با زمان زیادی روبرو شود (به خصوص که قابلیت اسکن روی شبکه را هم دارد). برای محدود کردن ناحیه یا نواحی اسکن میتوانید یک یا چند المان culture را در فایل پیکربندی مشخص کنید. همچنین مایکروسافت ابزاری به نام FUSLogVw.exe را ارائه داده است که میتواند نواحی اسکن را در حین اجرای برنامه به ما گزارش دهد.
نام و محل فایل پیکربندی بسته به نوع برنامه میتواند متغیر باشد:
برای فایلهای اجرایی EXE فایل پیکربندی باید در شاخه فایل اجرایی باشد و باید نام فایل پیکربندی همانند فایل exe بوده و یک پسوند config. را به آن اضافه کرد.
برای برنامههای وب فرم، فایل web.config موجود است که در ریشه شاخه مجازی وب اپلیکیشن قرار میگیرد و هر زیر دایرکتوری هم میتواند یک web.config جداگانه داشته باشد که میتواند از web.config ریشه هم تنظمیاتش را ارث بری کند. برای نمونه آدرس زیر را در نظر بگیرد:
یک فایل کانفیگ در ریشه قرار میگیرد و یکی هم در زیر شاخه newsArchive میتواند قرار بگیرد.
فصل دوم «نحوه ساخت و توزیع اسمبلی ها» از بخش اول «اصول اولیه CLR» پایان یافت. فصل بعدی در مورد اسمبلیهای اشتراکی است که بعد از آماده شدن این فصل، قسمتهای بعدی در دسترس عزیزان قرار خواهد گرفت.
کاربر یا مدیر سیستم بهتر از هر کسی میتواند جنبههایهای مختلف اجرای برنامه را مشخص کند. به عنوان نمونه ممکن است مدیر سیستم بخواهد فایلهای یک برنامه را سمت هارد دیسک سیستم کاربر انتقال دهد یا اطلاعات مانیفست یک اسمبلی را رونویسی کند و مباحث نسخه بندی که در آینده در مورد آن صحبت میکنیم.
با ارائه یک فایل پیکربندی در شاخه برنامه میتوان به مدیر سسیتم اجازه داد تا کنترل بیشتر بر روی برنامه داشته باشد. ناشر برنامه میتواند این فایل را همراه دیگر فایلهای برنامه پکیج کند تا در شاخه برنامه نصب شود تا بعدا مدیر یا کاربر سیستم بتوانند آن را تغییر و ویرایش کنند. CLR هم محتوای این فایل را تفسیر کرده و قوانین بارگیری اسمبلیها و ... را تغییر میدهد. این فایل پیکربندی میتواند به صورت XML هم ارائه شود. مزیت قرار دادن یک فایل جداگانه نسبت به رجیستری این مزیت را دارد که هم قابل جابجایی و پشتیبانی گیری است و هم اینکه تغییر آن سادهتر است.
البته در آینده بیشتر در مورد این فایل صحبت میکنیم ولی در حال حاضر بهتر است اندکی طعم آن را بچشیم. فرض را بر این میگذاریم که ناشر میخواهد فایلهای اسمبلی MultiFileLibrary را در دایرکتوری جداگانهای قرار دهد و چیزی شبیه به ساختار زیر را در نظر دارد:
AppDir directory (contains the application’s assembly files) Program.exe Program.exe.config (discussed below) AuxFiles subdirectory (contains MultiFileLibrary’s assembly files) MultiFileLibrary.dll FUT.netmodule RUT.netmodule
حال با تنظیم بالا به دلیل اینکه CLR انتظار دارد این اسمبلی را در دایرکتوری برنامه بیابد و با این جابجایی قادر به انجام این کار نیست، استثنای زیر را صادر میکند:
System.IO.FileNotFoundException
<configuration> <runtime> <assemblyBinding xmlns="urn:schemasmicrosoftcom:asm.v1"> <probing privatePath="AuxFiles" /> </assemblyBinding> </runtime> </configuration>
حالا هر موقع CLR در جست و جوی یک اسمبلی باشد که نتواند آن را در دایرکتوری مربوطه بیابد مسیر Auxfiles را هم بررسی خواهد کرد. با قرار دادن کارکتر « , » هم میتوان برای خصوصیت PrivatePath، دایرکتوریهای زیادتری را معرفی کرد. البته مسیردهی این خصوصیت باید به طور نسبی باشد نه مطلق یا اینکه یک مسیر نسبی خارج از دایرکتوری برنامه. ایده اصلی اینکار این است که برنامه کنترل بیشتری روی دایرکتوری خود و دایرکتوریهای زیرمجموعه داشته باشد نه خارج از آن.
نحوه عملکرد اسکن CLR برای بارگزاری اسمبلی ها
موقعی که CLR قصد بارگزاری اسمبلیهای خنثی را دارد، شاخههای زیر را به طور خودکار اسکن خواهد نمود ( مسیرهای FirstPrivatePath و SecondPrivatePath توسط فایل پیکربندی مشخص شده است)
AppDir\AsmName.dll AppDir\AsmName\AsmName.dll AppDir\firstPrivatePath\AsmName.dll AppDir\firstPrivatePath\AsmName\AsmName.dll AppDir\secondPrivatePath\AsmName.dll AppDir\secondPrivatePath\AsmName\AsmName.dll ...
این نکته ضروری است که اگر اسمبلی شما در یک دایرکتوری همنام خودش (در مثال ما MultiFileLibrary) قرار بگیرد، نیازی نیست این مسیر را در فایل پیکربندی ذکر کنید؛ زیرا CLR در صورت نیافتن دایرکتوری با این نام را اسکن خواهد نمود. بعد از آن اگر به هر نحوی CLR نتواند اسمبلی را در هیچ کدام از دایرکتوریهای گفته شده بیابد، با همان قوانین گفته شده اینبار به دنبال فایلی با پسوند exe خواهد بود و اگر باز هم جست و جوی آن نتیجهای را در بر نداشته باشد، استثنای زیر را صادر میکند:
FileNotFoundException
C:\AppDir\enUS\AsmName.dll C:\AppDir\enUS\AsmName\AsmName.dll C:\AppDir\firstPrivatePath\enUS\AsmName.dll C:\AppDir\firstPrivatePath\enUS\AsmName\AsmName.dll C:\AppDir\secondPrivatePath\enUS\AsmName.dll C:\AppDir\secondPrivatePath\enUS\AsmName\AsmName.dll C:\AppDir\enUS\AsmName.exe C:\AppDir\enUS\AsmName\AsmName.exe C:\AppDir\firstPrivatePath\enUS\AsmName.exe C:\AppDir\firstPrivatePath\enUS\AsmName\AsmName.exe C:\AppDir\secondPrivatePath\enUS\AsmName.exe C:\AppDir\secondPrivatePath\enUS\AsmName\AsmName.exe C:\AppDir\en\AsmName.dll C:\AppDir\en\AsmName\AsmName.dll C:\AppDir\firstPrivatePath\en\AsmName.dll C:\AppDir\firstPrivatePath\en\AsmName\AsmName.dll C:\AppDir\secondPrivatePath\en\AsmName.dll C:\AppDir\secondPrivatePath\en\AsmName\AsmName.dll C:\AppDir\en\AsmName.exe C:\AppDir\en\AsmName\AsmName.exe C:\AppDir\firstPrivatePath\en\AsmName.exe C:\AppDir\firstPrivatePath\en\AsmName\AsmName.exe C:\AppDir\secondPrivatePath\en\AsmName.exe C:\AppDir\secondPrivatePath\en\AsmName\AsmName.exe
نحوه اسکن کردن CLR میتواند به ما بگوید که عمل اسکن میتواند گاهی اوقات با زمان زیادی روبرو شود (به خصوص که قابلیت اسکن روی شبکه را هم دارد). برای محدود کردن ناحیه یا نواحی اسکن میتوانید یک یا چند المان culture را در فایل پیکربندی مشخص کنید. همچنین مایکروسافت ابزاری به نام FUSLogVw.exe را ارائه داده است که میتواند نواحی اسکن را در حین اجرای برنامه به ما گزارش دهد.
نام و محل فایل پیکربندی بسته به نوع برنامه میتواند متغیر باشد:
برای فایلهای اجرایی EXE فایل پیکربندی باید در شاخه فایل اجرایی باشد و باید نام فایل پیکربندی همانند فایل exe بوده و یک پسوند config. را به آن اضافه کرد.
برای برنامههای وب فرم، فایل web.config موجود است که در ریشه شاخه مجازی وب اپلیکیشن قرار میگیرد و هر زیر دایرکتوری هم میتواند یک web.config جداگانه داشته باشد که میتواند از web.config ریشه هم تنظمیاتش را ارث بری کند. برای نمونه آدرس زیر را در نظر بگیرد:
https://www.dntips.ir/newsarchive
یک فایل کانفیگ در ریشه قرار میگیرد و یکی هم در زیر شاخه newsArchive میتواند قرار بگیرد.
فصل دوم «نحوه ساخت و توزیع اسمبلی ها» از بخش اول «اصول اولیه CLR» پایان یافت. فصل بعدی در مورد اسمبلیهای اشتراکی است که بعد از آماده شدن این فصل، قسمتهای بعدی در دسترس عزیزان قرار خواهد گرفت.
در مقاله پیشین، افزونه نویسی برای فایرفاکس را آغاز و مسائل مربوط به رابطهای کاربری را بررسی کردیم. در این قسمت که قسمت پایانی افزونه نویسی برای فایرفاکس است، به مباحث پردازشی و دیگر خصوصیتها میپردازیم.
اولین موردی که باید برای برنامهی ما در نظر گرفت، ذخیره و بازیابی مقادیر است که باید روی پنجرهی popup.html اعمال گردد و همچنین مقداردهی مقادیر پیش فرض برنامه بعد از نصب افزونه اعمال شود. برای ذخیرهی مقادیر، طبق نوشته موجود در راهنمای موزیلا، از روش زیر بهره برده و میتوان مقادیر زیر را به راحتی در آنها ذخیره کرد:
var ss = require("sdk/simple-storage"); ss.storage.myArray = [1, 1, 2, 3, 5, 8, 13]; ss.storage.myBoolean = true; ss.storage.myNull = null; ss.storage.myNumber = 3.1337; ss.storage.myObject = { a: "foo", b: { c: true }, d: null }; ss.storage.myString = "O frabjous day!";
delete ss.storage.value;
برای ذخیره مقادیر پیش فرض اولین، کاری که میکنیم اسم متغیرها را چک میکنیم. اگر مخالف null بود، یعنی قبلا ست شدهاند؛ ولی اگر null شد، عمل ذخیره سازی اولیه را انجام میدهیم:
if (!ss.storage.Variables) { ss.storage.Variables=[]; ss.storage.Variables.push(true); ss.storage.Variables.push(false); ss.storage.Variables.push(false); ss.storage.Variables.push(false); } if (!ss.storage.interval) ss.storage.interval=1; if (!ss.storage.DateVariables) { var now=String(new Date()); ss.storage.DateVariables=[]; ss.storage.DateVariables.push(now); ss.storage.DateVariables.push(now); ss.storage.DateVariables.push(now); ss.storage.DateVariables.push(now); }
برای ذخیره مقادیر popup.html، به طور مستقیم نمیتوانیم از کدهای بالا در جاوااسکریپت استفاده کنیم. مجبور هستیم که یک پل ارتباطی بین فایل main.js و فایل جاوااسکریپت داشته باشیم. در مقاله پیشین در مورد postmessage که ارتباطی از/به محتوا یا فایل جاوااسکریپت به/از main.js برقرار میکرد، صحبت کردیم و در این قسمت راه حل بهتری را مورد استفاده قرار میدهیم. برای ایجاد چنین ارتباطی، آن هم به صورت دو طرفه از port استفاده میکنیم. دستور پورت در اکثر اشیایی که ایجاد میکنید وجود دارد ولی باز هم همیشه قبل از استفاده، از مستندات موزیلا حتما استفاده کنید تا مطمئن شوید دسترسی به شیء پورت در همهی اشیا وجود دارد. ولی به صورت کلی تا آنجایی که من دیدم، در همهی اشیا قرار دارد. از کد port.emit برای ارسال مقادیر به سمت فایل اسکریپت یا حتی بالعکس مورد استفاده قرار میگیرد و port.on هم یک شنونده برای آن است. شکل زیر به خوبی این مبحث را نشان میدهد.
addon process در شکل بالا همان فایل main.js هست که کد اصلی addon داخل آن است و content process نیز محتوای اسکریپت است و حالا میتواند با استفاده از خاصیت contentscrip که به صورت رشته ای اعمال شده باشد یا اینکه با استفاده از خاصیت contentscriptfile، در یک یا چند فایل js استفاده نماید:
contentScriptFile: self.data.url("jquery.min.js") contentScriptFile: [self.data.url("jquery.min.js"),self.data.url("const.js"),self.data.url("popup.js")]
از شیء port به صورت عملی استفاده میکنیم. کد main.js را به صورت زیر تغییر دادیم:
function handleChange(state) { if (state.checked) { panel.show({ position: button }); var v1=[],v2; if (ss.storage.Variables) v1=ss.storage.Variables; if (ss.storage.interval) v2=ss.storage.interval; panel.port.emit("vars",v1,v2); } } panel.port.on("vars", function (vars,interval) { ss.storage.Variables=vars; ss.storage.interval=interval; });
در شماره پیشین گفتیم که رویداد handlechange وظیفه نمایش پنل را دارد، ولی الان به غیر آن چند سطر، کد دیگری را هم اضافه کردیم تا موقعی که پنل باز میشود، تنظیمات قبلی را که ذخیره کردهایم روی صفحه نمایش داده شوند. با استفاده از شیء port.emit محتواهای دریافت شده را به سمت فایل اسکریپت ارسال میکنیم تا تنظیمات ذخیره شده را برای نمایش اعمال کند. پارامتر اول رشته vars نام پیام رسان شما خواهد بود و در فایل مقصد هم تنها به پیامی با این نام گوش داده خواهد شد. این خصوصیت زمانی سودمندی خود را نشان میدهد که بخواهید در زمینههای مختلف، از چندین پیام رسان به سمت یک مقصد استفاده کنید. شیء panel.port.on هم برای گوش دادن به متغیرهایی است که از آن سمت برای ما ارسال میشود و از آن برای ذخیرهی مواردی استفاده میگردد که کاربر از آن سمت برای ما ارسال میکند. پس ما در این مرحله، یک ارتباطه کاملا دو طرفه داریم.
کد فایل popup.js که به صورت تگ script در popup.html معرفی شده است:
$(document).ready(function () { addon.port.on("vars", function(vars,interval) { if (vars) { $("#chkarticles").attr("checked", vars[0]); $("#chkarticlescomments").attr("checked", vars[1]); $("#chkshares").attr("checked", vars[2]); $("#chksharescomments").attr("checked", vars[3]); } $("#interval").val(interval); }); $("#btnsave").click(function() { var Vposts = $("#chkarticles").is(':checked'); var VpostsComments = $("#chkarticlescomments").is(':checked'); var Vshares = $("#chkshares").is(':checked'); var VsharesComments = $("#chksharescomments").is(':checked'); var Vinterval = $("#interval").val() ; var Variables=[]; Variables[0]=Vposts; Variables[1]=VpostsComments; Variables[2]=Vshares; Variables[3]=VsharesComments; interval=Vinterval; addon.port.emit("vars", Variables,Vinterval); $("#messageboard").text( Messages.SettingsSaved); }); });
نکته بسیار مهم: در کد بالا ما فایل جاوااسکریت را از طریق فایل popup.html معرفی کردیم، نه از طریق خصوصیت contentscriptfile. این نکته را همیشه به خاطر داشته باشید. فایلهای js خود را تنها در دو حالت استفاده کنید:
- از طریق دادن رشته به خصوصیت contentScript و استفاده از self به جای addon
- معرفی فایل js داخل خود فایل html با تگ script که به درد اسکریپتهای با کد زیاد میخورد.
اگر فایل شما شامل استفاده از کلمهی کلیدی addon نمیشود، میتوانید فایل js خود را از طریق contentScriptFile هم اعمال کنید.
فایل popup.html
<script src="jquery.min.js"></script> <!-- Including jQuery --> <script type="text/javascript" src="const.js"></script> <script type="text/javascript" src="popup.js"></script>
خواندن فید RSS سایت
خواندن فید سایت توسط فایل Rssreader.js انجام میشود که تمام اسکریپتهای مورد نیاز برای اجرای آن، توسط background.htm صدا زده شده است:
<script type="text/javascript" src="const.js"></script> <script type="text/javascript" src="jquery.min.js"></script> <script type="text/javascript" src="https://www.google.com/jsapi"></script> <script type="text/javascript" src="rssreader.js"></script>
pageWorker = require("sdk/page-worker"); page= pageWorker.Page({ contentScriptWhen: "ready", contentURL: self.data.url("./background.htm") }); page.port.emit("vars",ss.storage.Variables,ss.storage.DateVariables,ss.storage.interval);
فایل RSSReader.js
در اینجا هم مانند مطلبی که برای کروم گذاشتیم، خواندن فید، در یک دورهی زمانی اتفاق میافتد. در کروم ما از chrome.alarm استفاده میکردیم، ولی در فایرفاکس از همان تایمرهای جاوااسکریپتی بهره میبریم. کد زیر را به فایلی به اسم rssreader.js اضافه میکنیم:
var variables=[]; var datevariables=[]; var period_time=60000; var timer; google.load("feeds", "1"); $(document).ready(function () { addon.port.on("vars", function(vars,datevars,interval) { if (vars) { Variables=vars; } if (datevars) { datevariables=datevars; } if(interval) period_time=interval*60000; alarmManager(); }); }); function alarmManager() { timer = setInterval(Run,period_time); } function Run() { if(Variables[0]){RssReader(Links.postUrl,0, Messages.PostsUpdated);} if(Variables[1]){RssReader(Links.posts_commentsUrl,1,Messages.CommentsUpdated); } if(Variables[2]){RssReader(Links.sharesUrl,2,Messages.SharesUpdated);} if(Variables[3]){RssReader(Links.shares_CommentsUrl,3,Messages.SharesCommentsUpdated);} } function RssReader(URL,index,Message) { var feed = new google.feeds.Feed(URL); feed.setResultFormat(google.feeds.Feed.XML_FORMAT); feed.load(function (result) { if(result!=null) { var strRssUpdate = result.xmlDocument.firstChild.firstChild.childNodes[5].textContent; var RssUpdate=new Date(strRssUpdate); var lastupdate=new Date(datevariables[index]); if(RssUpdate>lastupdate) { datevariables[index]=strRssUpdate; addon.port.emit("notification",datevariables,Message); } } }); }
- چه بخشهایی از سایت باید بررسی شوند.
- آخرین تاریخ تغییر هر کدام که در زمان نصب افزونه، تاریخ نصب افزونه میشود و با اولین به روز رسانی، تاریخ جدیدی جای آن را میگیرد.
- دورهی سیکل زمانی یا همان interval بر اساس دقیقه
پس از اینکه شنونده مقادیر را دریافت کرد، تابع alarmManager اجرا شده و یک تایمر ایجاد میکند. بر خلاف کروم که برای این کار api تدارک دیده بود، اینجا شما باید از تایمرهای خود جاوااسکریپت مانند SetTimeout یا SetInterval استفاده کنید. موقع دریافت interval یا period_time ما آن را در 60000 ضرب کردیم تا دقیقه تبدیل به میلی ثانیه شود؛ چرا که تایمر، زمان را بر حسب میلی ثانیه دریافت میکند. وظیفه تایمر این هست که در هر دورهی زمانی تابع Run را اجرا کند.
Run
این تابع بررسی میکند کاربر درخواست بررسی چه قسمت هایی از سایت را دارد و به ازای هر کدام، اطلاعات آن را از طریق پارامترها به تابع rssreader داده تا هر قسمت جداگانه بررسی شود. این اطلاعات به ترتیب: لینک فید مورد نظر، اندیس آخرین تاریخ به روزرسانی آن قسمت، پیامی که باید در وقت به روزرسانی به کار نمایش داده شود.
RSSReader
این تابع را قبلا در این مقاله توضیح دادیم. تنها تغییری که کرده است، بدنهی شرط بررسی تاریخ است که در صورت موفقیت، تاریخ جدید، جایگزین تاریخ قبلی شده و یک پیام به فایل main.js ارسال میکند تا از آن درخواست ذخیرهی تاریخی جدید و هچنین ایجاد یک notification برای آگاه سازی کاربر کند. پس باز به فایل main.js رفته و شنونده آن را تعریف میکنیم:
page.port.on("notification",function(lastupdate,Message) { ss.storage.DateVariables=lastupdate; Make_a_Notification(Message); }) function Make_a_Notification(Message) { var notifications = require("sdk/notifications"); notifications.notify({ title: "سایت به روز شد", text: Message, iconURL:self.data.url("./icon-64.png"), data:"https://www.dntips.ir", onClick: function (data) { tabs.open(data); } }); }
البته این نکته قابل ذکر است که اگر کاربر طلاعات پنل را به روزرسانی کند، تا وقتی که مرورگر بسته نشده و دوباره باز نشود تغییری نمیکند؛ چرا که ما تنها در ابتدای امر مقادیر ذخیره شده را به RSSReader فرستاده و اگر کاربر آنها را به روز کند، ارسال پیام دیگری توسط page worker صورت نمیگیرد. پس کد موجود در main.js را به صورت زیر ویرایش میکنیم:
pageWorker = require("sdk/page-worker"); page= pageWorker.Page({ contentScriptWhen: "ready", contentURL: self.data.url("./background.htm") }); function SendData() { page.port.emit("vars",ss.storage.Variables,ss.storage.DateVariables,ss.storage.interval); } SendData(); panel.port.on("vars", function (vars,interval) { ss.storage.Variables=vars; ss.storage.interval=interval; SendData(); });
var timer; function alarmManager() { timer = setInterval(Run,period_time); } addon.port.on("vars", function(vars,datevars,interval) { if (vars) { Variables=vars; } if (datevars) { datevariables=datevars; } if(interval) period_time=interval*60000; if(timer!=null) { clearInterval(timer); } alarmManager(); });
افزونهی ما تکمیل شد. اجازه بدهید قبل از بستن بحث چندتا از موارد مهم موجود در sdk را نام ببریم:
Page Mod
page mod موقعی که کاربر آدرسی را مطابق با الگویی (pattern) که ما دادیم، باز کند یک اسکریپت را اجرا خواهد کرد:
var pageMod = require("sdk/page-mod"); pageMod.PageMod({ include: "*.mozilla.org", contentScript: 'window.alert("Page matches ruleset");' });
var data = require("sdk/self").data; var pageMod = require("sdk/page-mod"); pageMod.PageMod({ include: "*.mozilla.org", contentScriptFile: [data.url("jquery-1.7.min.js"), data.url("my-script.js")] });
پنل تنظیمات
موقعی که شما افزونهای را در فایرفاکس اضافه میکنید، در پنلی که مدیریت افزونهها قرار دارد میتوانید در تنظیمات هر افزونه، تغییری ایجاد کنید. برای ساخت چنین صفحهای از خصوصیت preferences در فایل package.json کمک میگیریم که مقادیر به صورت آرایه ای داخل آن قرار میگیرند. مثال زیر پنج کنترل را به بخش تنظیمات افزونه اضافه میکند که چهار کنترل اول چک باکس Checkbox هستند؛ چرا که خصوصیت type آنها به bool ست شده است و شامل یک نام و عنوان یا برچسب label و یک توضیح کوتاه است و مقدار پیش فرض آن با خصوصیت value مشخص شده است. آخرین کنترل هم یک کادر عددی است؛ چرا که خاصیت type آن با integer مقداردهی شده و مقدار پیش فرض آن 10 میباشد.
"preferences": [{ "description": "مطالب سایت", "type": "bool", "name": "post", "value": true, "title": "مطالب سایت" }, { "description": "نظرات مطالب سایت", "type": "bool", "name": "postcomments", "value": false, "title": "نظرات مطالب سایت" }, { "description": "اشتراک ها", "type": "bool", "name": "shares", "value": false, "title": "اشتراک ها" }, { "description": "نظرات اشتراک ها", "type": "bool", "name": "sharescomments", "value": false, "title": "نظرات اشتراک ها" }, { "description": "دوره زمان برای بررسی سایت", "name": "interval", "type": "integer", "value": 10, "title": "دوره زمانی" }]
از آنجا که مقادیر بالا تنها مقادیر پیش فرض خودمان هست و اگر کاربر آنها را تغییر دهد، در این صفحه هم باید اطلاعات تصحیح شوند، برای همین از کد زیر برای دسترسی به پنل تنظیمات و کنترلهای موجود آن استفاده میکنیم. همانطور که میبینید کد مورد نظر را در یک تابع به نام Perf_Default_Value قرار دادیم و آن را در بدو اجرا صدا زدیم. پس کاربر اگر به پنل تنظمیات رجوع کند، میتواند تغییراتی را که قبلا داده است، ببیند. بنابراین اگر الان تغییری را ایجاد کند، تا باز شدن مجدد مرورگر چیزی نمایش داده نمیشود. برای همین دقیقا مانند تابع SendData این تابع را هم در کد شنود پنل panel اضافه میکنیم؛ تا اگر کاربر اطلاعات را از طریق روش قبلی تغییر داد، اطلاعات هم اینک به روز شوند.
function Perf_Default_Value() { var preferences = require("sdk/simple-prefs").prefs; preferences.post = ss.storage.Variables[0]; preferences.postcomments = ss.storage.Variables[1]; preferences.shares = ss.storage.Variables[2]; preferences.sharescomments = ss.storage.Variables[3]; preferences["myinterval"] =parseInt(ss.storage.interval); } Perf_Default_Value(); panel.port.on("vars", function (vars,interval) { ss.storage.Variables=vars; ss.storage.interval=interval; SendData(); Perf_Default_Value(); });
perf=require("sdk/simple-prefs"); var preferences = perf.prefs; function onPrefChange(prefName) { switch(prefName) { case "post": ss.storage.Variables[0]=preferences[prefName]; break; case "postcomments": ss.storage.Variables[1]=preferences[prefName]; break; case "shares": ss.storage.Variables[2]=preferences[prefName]; break; case "sharescomments": ss.storage.Variables[3]=preferences[prefName]; break; case "myinterval": ss.storage.interval=preferences[prefName]; break; } } //perf.on("post", onPrefChange); //perf.on("postcomments", onPrefChange); perf.on("", onPrefChange);
متد on دو پارامتر دارد: اولی، نام کنترل مورد نظر که با خصوصیت name تعریف کردیم و دومی هم تابع callback آن میباشد و در صورتی که پارامتر اول با "" مقداردهی شود، هر تغییری که در هر کنترلی رخ بدهد، تابع callback صدا زده میشود. از آنجا که نام کنترلها به صورت string برگشت داده میشوند، برای دسترسی به مقادیر موجود در تنظیمات از همان روش داخل [""] بهره میگیریم. مقادیر را گرفته و داخل storage ذخیره میکنیم.
اشکال زدایی Debug
یکی از روشهای اشکال زدایی، استفاده از console.log هست که میتونید برای بازبینی مقادیر و وضعیتها، از آن استفاده کنید که نتیجهی آن داخل کنسول نمایش داده میشود و اگر هم دربرنامه خطایی رخ دهد، داخل کنسول به شما نمایش خواهد داد.