اشتراک‌ها
معرفی Zen Coding یا Emmet Syntax

معرفی Zen Coding یا Emmet Syntax

برای من همیشه نوشتن کد‌های HTML با دشواری و مرارت‌های زیادی همراه بوده. نوشتن ساختار‌های تو در تو، قرار دادن id و class مناسب و باز و بسته کردن تگ‌ها و قرار دادن قرزند‌ها در داخل تگ‌ها حقیقتا کار هایی خسته کننده، تکراری و حوصله سر بر هستند.
روش Zen Coding یا Emmet Syntax برای حل همین مسئله به وجود آمده و سرعت توسعه کد‌های HTML را بسیار بیشتر خواهد کرد. پس از استفاده از این روش، به هیچ وجه قادر نخواهید بود که به روش سنتیِ گذشته کد‌های HTML بزنید.
1. مثلا با استفاده از دستور div#control.panel.panel-success و فشردن کلید tab، یک div با شناسه panel و کلاس‌های panel و panel-success ساخته خواهد شد.
2. با استفاده از Zen Codding به راحتی می‌توانیم المنت‌های داخلی هم در یک خط تعریف کنیم. مثلا ul>li*5 موجب خواهد شد یک ul همراه با پنج المنت li در درون اش تعریف شود.
سعی کنید حتما از روش Zen Coding استفاده کنید، چرا که سرعت توسعه شما را به شدت بالا خواهد برد.
Zen Coding پس از نصب افزونه Web Essentials در ویژوال استودیو افزوده خواهد شد. همچنین Visual Studio Code به طور پیش فرض از این روش پشتیبانی می‌کند.
 

معرفی Zen Coding یا Emmet Syntax
اشتراک‌ها
تازه ها در TypeScript
TypeScript continues its growth journey with more and more JavaScript developers and framework authors taking advantage of the excellent tooling and productivity boost that TypeScript provides in order to create apps that scale. In this session, we talk about the Typescript journey with a focus on some of the most exciting recent features and a preview of things to come. Apart from the language and tools enhancements, see efforts already underway to bring some of the power of TypeScript to existing JavaScript projects. 
تازه ها در TypeScript
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت اول - نصب پیشنیازها
- نکات مطلب «مبانی TypeScript؛ تنظیمات TypeScript در ویژوال استودیو» را پیگیری کنید.
+ VS 2013 دیگر پشتیبانی جدیدی از TypeScript را نخواهد داشت و آخرین نگارش‌های AngularJS 2.0 از TypeScript 2.0 استفاده می‌کنند که فقط برای VS 2015 ارائه شده‌است.
آخرین نگارشی که برای VS 2013 ارائه شده 1.8.5 است و آخرین نگارشی که برای VS 2015 ارائه شده 2.0.6 است.
مطالب
توزیع پروژه‌های ASP.NET MVC بدون ارائه فایل‌های View آن
پروژه دیگری از آقای David Ebbo (عضو تیم ASP.NET که پیشتر با پروژه T4 MVC آن در این سایت آشنا شده‌اید)، جهت کامپایل کامل فایل‌های View و ارائه پروژه نهایی ASP.NET MVC بدون نیاز به ارائه پوشه Views آن به نام Razor Generator وجود دارد که در ادامه خلاصه‌ای از نحوه استفاده از آن‌را مرور خواهیم کرد.

الف) ابتدا افزونه Razor Generator را از اینجا دریافت و نصب کنید.
ب) سپس به پروژه MVC خود بسته NuGet زیر را اضافه نمائید:
 PM> Install-Package RazorGenerator.Mvc
در این حالت باید پروژه پیش فرض، همان وب سایت MVC شما انتخاب گردد:


با اضافه کردن این بسته NuGet تغییرات زیر به پروژه جاری اعمال خواهند شد:
  - ارجاعی به اسمبلی RazorGenerator.Mvc.dll به پروژه اضافه خواهد شد.
  - در پوشه App_Start، فایلی به نام RazorGeneratorMvcStart.cs اضافه گردیده و کار تنظیم موتور View مخصوص کار با Viewهای کامپایل شده را انجام می‌دهد.

ج) پس از نصب بسته NuGet یاد شده در همان خط فرمان پاورشل نوگت دستور زیر را صادر کنید:
 PM> Enable-RazorGenerator
و ... همین!

پس از انجام اینکار، دو کار صورت خواهد گرفت:
  - برای تمام Viewهای برنامه، فایل cs متناظری تولید می‌شود که ذیل فایل‌های View قابل مشاهده است.
  - گزینه Custom tool این Viewها نیز به RazorGenerator تنظیم می‌شود.


بدیهی است اگر از دستور Enable-RazorGenerator استفاده نکنید، نیاز خواهید داشت تا تنظیم گزینه Custom tool به RazorGenerator کلیه Viewها را دستی انجام داده و اگر فایل cs متناظر با View تولید نشد روی فایل view کلیک راست کرده و گزینه run custom tool را انتخاب کنید. اما دستور Enable-RazorGenerator کار را بسیار ساده می‌کند.

مزایا:
  - در عمل موتور ASP.NET همین کارها را در زمان اولین بار اجرای Viewها(ی کامپایل نشده) در پشت صحنه انجام می‌دهد. بنابراین با این روش زمان آغاز برنامه سریعتر می‌شود.
  - دیگر نیازی به توزیع فایل‌های cshtml نخواهید داشت.
  - خطایابی Viewها نیز ساده‌تر می‌شود. از این جهت که کامپایل آن‌ها به زمان اجرا موکول نخواهد شد.

یک سری قابلیت‌های دیگر نیز به همراه این پروژه است مانند انتقال Viewها به یک اسمبلی دیگر و یا استفاده از MSBuild برای انجام عملیات که می‌توانید آن‌ها را در Wiki پروژه Razor Generator مطالعه کنید. انتقال Viewها به یک اسمبلی دیگر هرچند در این روش کاملا ممکن شده و کار می‌کند اما صفحه dialog افزودن یک view جدید مهیا در کلیک راست بر روی اکشن متدهای یک کنترلر را غیرفعال می‌کند که در عمل آنچنان جالب نیست.


یک نکته مهم:
اگر در آینده بسته NuGet و افزونه یاد شده را به روز کردید نیاز است دستور زیر را اجرا کنید:
 PM> Redo-RazorGenerator
به این ترتیب بر اساس ساختار و کدهای جدید RazorGenerator، کلیه فایل‌های cs تولید شده مجددا به روز و تولید خواهند شد.


فایل‌های Helper قرار گرفته در پوشه App_Code

اگر HTML Helperهای خود را توسط فایل‌های Razor قرار گرفته در پوشه App_Code تولید می‌کنید، پس از اجرای دستور Enable-RazorGenerator، برای این موارد نیز فایل‌های cs متناظری تولید می‌شود. با این تفاوت که Build Action آن‌ها بر روی Compile قرار ندارند که این مورد را باید دستی تنظیم کنید. همچنین حین استفاده از این توابع کمکی نیاز است فضای نام مرتبط را نیز در ابتدای فایل View خود ذکر کنید مثلا:
 @using MvcViewGenTest2.app_code
البته با استفاده از Razor Generaor دیگر نیازی به استفاده از پوشه App_Code نخواهد بود؛ از این جهت که کار کامپایل خودکار، به زمان اجرا موکول نخواهد شد. بنابراین اینبار در هر جایی که علاقمند بودید می‌توانید این فایل‌های کمکی را تولید و کامپایل کنید. فقط ذکر فضای نام مرتبط را در ابتدای View خود فراموش نکنید.


حذف فایل RazorGeneratorMvcStart.cs
 اگر علاقمند به استفاده از فایل پیش فرض RazorGeneratorMvcStart.cs نیستید و می‌خواهید موتورهای View اضافی را حذف کنید، ابتدا فایل RazorGeneratorMvcStart.cs را حذف کرده و سپس در فایل global.asax.cs تغییر زیر را اعمال نمائید:
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.WebPages;
using RazorGenerator.Mvc;

namespace MvcViewGenTest2
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            // Adding PrecompiledMvcEngine
            var engine = new PrecompiledMvcEngine(typeof(MvcApplication).Assembly);
            ViewEngines.Engines.Clear();
            ViewEngines.Engines.Add(engine);
            VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
        }
    }
}
مطالب
نحوه پیکربندی سرور شیرپوینت 2013 برای نصب app از Office Store
از ویژگی‌های جدید و البته جالب شیرپوینت 2013 امکان استفاده از App‌ها می‌باشد. برای شناخت بیشتر app‌ها پیشنهاد می‌کنم به MSDN  مراجعه کنید. در این پست قصد دارم مراحل استفاده از SharePoint Marketplace مایکروسافت را برای دریافت و نصب app در سرور شیرپوینت و طریقه پیکر بندی سروربیان کنم.
اگر برای بار اول بخواهید یک app را روی سرور شیرپوینت نصب کنید ممکن است این پیغام به شما نمایش داده شود :
Sorry, apps are turned off. If you know who tuns the server, tell them to enable apps.


دقت کنید که کم رنگ بودن آیکون App به معنی عدم پشتیبانی در سرور شیرپوینت شما است و در صورت تلاش برای نصب آن این پیغام را خواهید دید :

دلیل این پیغام ( apps are turned off) تنظیم نبودن سرور شیرپوینت (Front-End) برای پشتیبانی و میزبانی از App‌ها می‌باشد . برای استفاده از app‌ها در شیرپوینت نیازمند یک sub-domain و دیگر تنظیمات هستید تا بتوانید از app‌ها استفاده کنید . برای این منظور مراحل زیر را پی بگیرید :
وارد سایت Office Store مایکروسافت شده و app مورد نظر خود را بیابید . در اینجا من از app‌های رایگان1 مورد را انتخاب می‌کنم و با آن شروع می‌کنم : نمایش وضعیت آب و هوا در شیرپوینت .

روی Add کلیک کنید تا جزییات app و شناسه آن نمایش داده شود . سپس آن شناسه را کپی کنید : ( شناسه app مذکور WA103062091 است )

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

در پنجره باز شده شناسه app را paste کنید و جستجو را آغاز کنید :
  

باید در نتیجه جستجو نمایش داده شود که app در SharePoint Store یافت شد
  

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

ودر این مرحله خطای مذکور که گفته شد نمایش داده می‌شود :  

حال به بیان راه حل می‌پردازیم :
برای استفاده از App‌ها در شیرپوینت باید سرویس‌های مرتبط و زیر دامنه (CNAME) سرور مرتبط برای آن تنظیم شده باشد .
برای این منظور ابتدا تنظیمات DNS را انجام می‌دهیم :

 
برای دامنه جاری یک CNAME تعریف کنید : 

Alias Name پنجره فوق به این معنا است که تمام app‌ها در مسیری با فرمت زیر مدیریت می‌شوند :
AppID.app.vm-seifollahi.iri
اگر به جای *.app فقط * قرار دهید ، هر شناسه app به عنوان زیر دامنه آدرس دهی می‌شود که در کل تفاوتی ندارد و برای مشخص شدن بهتر این کار را انجام دادم .
برای چک کردن صحت تنظیمات خود روی مسیری مانند Apps-12345678ABCDEF.app.vm-seifollahi.iri دستور ping را اجرا نمایید.
  
پس از تایید این تنظیمات باید وارد CA شوید و سرویس‌ها را تنظیم کنید : باید دو سرویس App Management Service و Subscription Setting Service در وضعیت Started باشند .

  پس از چک کردن سرویس‌ها باید تنظیمات مربوط به App Pool‌های IIS و دیتابیس برای App Managemetn Service و Subscription Service تنظیم شود . برای این منظور از Power Shell کمک می‌گیریم و دستورات زیر را در آن اجرا می‌کنیم (توضیحات در کامنت‌ها وجود دارند ) :


$account = Get-SPManagedAccount "vmseifollahi\administrator
# Gets the name of the managed account and sets it to the variable $account for later use.

$appPoolSubSvc = New-SPServiceApplicationPool -Name SettingsServiceAppPool -Account $account
# Creates an application pool for the Subscription Settings service application. 
# Uses a managed account as the security account for the application pool.
# Stores the application pool as a variable for later use.

 
$appPoolAppSvc = New-SPServiceApplicationPool -Name AppServiceAppPool -Account $account
# Creates an application pool for the Application Management service application. 
# Uses a managed account as the security account for the application pool.
# Stores the application pool as a variable for later use.

 
$appSubSvc = New-SPSubscriptionSettingsServiceApplication –ApplicationPool $appPoolSubSvc –Name SettingsServiceApp –DatabaseName MBS_SettingsServiceDB
# Creates the Subscription Settings service application, using the variable to associate it with the application pool that was created earlier.
# Stores the new service application as a variable for later use.
$proxySubSvc = New-SPSubscriptionSettingsServiceApplicationProxy –ServiceApplication $appSubSvc
# Creates a proxy for the Subscription Settings service application.

 
$appAppSvc = New-SPAppManagementServiceApplication -ApplicationPool $appPoolAppSvc -Name AppServiceApp -DatabaseName MBS_AppServiceDB
# Creates the Application Management service application, using the variable to associate it with the application pool that was created earlier.
# Stores the new service application as a variable for later use.

 
$proxyAppSvc = New-SPAppManagementServiceApplicationProxy -ServiceApplication $appAppSvc
# Creates a proxy for the Application Management service application.
 

پس از نصب مشاهده میکنید که دیتابیس‌ها با موفقیت نصب شدند :
  

حال به CA رفته ( DOMAIN/_admin/ServiceApplications.aspx ) و از Start بودن سرویس‌های تنظیم شده اطمینان پیدا کنید : (از همین صفحه نیز می‌توانید تنظیماتی که قبلا در power shell انجام شد را انجام دهید)
 

حال در CA به صفحه Apps می‌رویم :

و روی Configure App URL کلیک کنید :

در صورتی که پیغام زیر را مشاهده کردید ، IIS را باز کنید :
 

در قسمت Application Pools به دنبال SharePoint Web Service Root بگردید و آن را Start نمایید :

حال صفحه تنظیمات باز می‌شود . مقادیر domain و prefix را تنظیم کنید :

سپس روی OK کلیک کنید در این مرحله تنظیمات سرور شیرپوینت تمام شد و باید به ترتیب زیر آنها را restart کنید :

ابتدا SharePoint Timer service را Stop کنید.
سپس سرویس IIS را Restart کنید
حال SharePoint Timer service را Start کنید .

اکنون مراحل را مجدد از سر بگیرید یعنی روی منوی تنظیمات سایت و روی add App کلیک کنید و app را جستجو کنید و مراحل نصب را اجرا کنید تا به مرحله Add کردن app برسید . حال مشاهده می‌کنید که دکمه فعال بوده و می‌توانید آن را نصب کنید :
  
 

پس از کلیک روی add به store preview منتقل خواهید شد : (این تصویر مربوط به محصولی دیگر است)

ممکن است پس از زدن دکمه continue خطایی مانند تصویر زیر را مشاهده کنید :

در این صورت احتمالا با کاربر System Account وارد سیستم شده اید که باید از آن خارح شده و با نام کاربری دیکری که دسترسی لازم را دارد وارد شوید .

با کلیک روی continue به marketplace مایکروسافت منتقل خواهید شد که نیازمند یک حساب کاربری در مایکروسافت می‌باشد :

حال پنجره زیر نمایش داده می‌شود و به شما اجازه‌ی دانلود app داده می‌شود :

 
روی return to site کلیک کنید تا پنجره بعدی برای گرفتن اعتماد شما برای نصب نمایش داده شود :



روی trust it کلیک کنید تا به صفحه site Content منتقل شوید :


همانطور که مشاهده می‌کنید app در حال دانلود شدن است :

 
حال در سمت چپ سایت روی نام App کلیک کنید (ترجیحا از مرور گر IE و ورژن 9 یا 10 استفاده کنید )

حال وارد تنظمیات app می‌شوید (در صورت درخواست نام کاربری و کلمه عبور آن را وارد کنید)

و نتیجه این هفت خوان رستم :

 
مطالب
منسوخ شده‌ها در نگارش‌های جدید SQL server

با تکامل SQL server و بهبودهای حاصل شده، یک سری از ویژگی‌های موجود صرفا جهت حفظ سازگاری با نگارش‌های قبلی ارائه می‌شوند. لیست کامل آنها را در آدرس زیر می‌توان مشاهده نمود:

Deprecated Database Engine Features in SQL Server 2008

لیست بلند بالایی است. اما در یک محیط کاری، نوع‌های زیر از سایر موارد ذکر شده بیشتر مورد استفاده قرار می‌گیرند:
منسوخ شده‌ها: text ، ntext و image . جایگزین‌ها : varchar ، nvarchar و varbinary از نوع max دار

عموما علت استفاده از نوع‌های text یا ntext (نمونه یونیکد text) ، مشخص نبودن تعداد کاراکتری است که کاربر قرار است وارد کند. برای مثال یک سایت خبری ایجاد کرده‌اید و طول محتوای خبر ثبت شده در بانک اطلاعاتی از یک خبر به خبر دیگر کاملا متفاوت است. در اینجا برای حل این مشکل از نوع‌های text یا ntext استفاده می‌شد (این مورد تا اس‌کیوال سرور 2000 توصیه می‌شود).
varchar max تا 2,147,483,648 کاراکتر را می‌تواند ذخیره کند، یعنی تا 2 GB و nvarchar max تا نصف این مقدار را. در اس کیوال سرور 2000 محدودیت 8000 کاراکتر برای نوع vrachar وجود داشت (و نوع nvrachar تا 4000 کاراکتر).

مزایای استفاده از نوع‌های max دار (از اس کیوال سرور 2005 به بعد) :
  • بهبود کارآیی کوئری‌های جستجو نسبت به نوع‌های Text‌
  • اگر مطلب تشخیص کمبود ایندکس‌ها را دنبال کرده باشید، در آنجا ذکر شد که در قسمت included columns نمی‌توان از text و ntext‌ استفاده کرد اما نوع‌های max دار متنی مجازند.
  • امکان استفاده از فیلدهای max دار برای مرتب سازی کوئری مجاز است. (به شخصه با این مورد زیاد برخورد داشتم. برای مثال امکان سورت کردن یک گرید را در ASP.Net فراهم کرده‌اید و کاربر با کلیک بر روی سر ستون فیلدی از نوع ntext با یک خطا متوقف خواهد شد)
  • امکان استفاده از نوع‌های Text به‌عنوان متغیر در رویه‌های ذخیره شده یا توابع T-SQL مهیا نیست اما این محدودیت در نوع‌های max دار برطرف شده است.
  • نوع‌های text را در توابع REPLACE ، CHARINDEX و SUBSTRINGنمی‌توان بکار برد (برخلاف نوع‌های متنی max دار).
بعد از این توضیحات شاید علاقمند شده باشید که نوع‌های فیلدهای قدیمی را به نوع‌های جدید تبدیل کنید (مراجعه به management studio ، تغییر نوع فیلد و کلیک بر روی دکمه ذخیره در نوار ابزار آن). اگر تعداد رکوردها بالا باشد، بدون شک با خطای زیر متوقف خواهید شد:

Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.

برای حل این مشکل می‌توان مقدار پیش فرض timeout را مطابق تصویر زیر تغییر داد (منوی tools گزینه options) :



بعد از این تغییر به سادگی می‌توان عملیات ارتقاء را بدون نگرانی از بروز خطای فوق انجام داد.

مطالب
افزونه نویسی برای مرورگرها : فایرفاکس : قسمت اول
در دو مقاله پیشین ^ ، ^ به بررسی نوشتن افزونه در مرورگر کروم پرداختیم و اینبار قصد داریم همان پروژه را برای فایرفاکس پیاده کنیم. پس در مورد کدهای تکراری توضیحی داده نخواهد شد و برای فهم آن می‌توانید به دو مقاله قبلی رجوع کنید. همه‌ی ما فایرفاکس را به خوبی می‌شناسیم. اولین باری که این مرورگر آمد سرو صدای زیادی به پا کرد و بازار وسیعی از مرورگر‌ها را که در چنگ IE بود، به دست آورد . این سر و صدا بیشتر به خاطر امنیت و کارآیی بالای این مرورگر، استفاده از آخرین فناوری‌های تحت وب و دوست داشتنی برای طراحان وب بود. همچنین یکی دیگر از مهمترین ویژگی‌های آن، امکان سفارشی سازی آن با افزونه‌ها extensions یا addon بود که این ویژگی در طول این سال‌ها تغییرات زیادی به خود دیده است. در مورد افزونه نویسی برای فایرفاکس در سطح نت مطالب زیادی وجود دارند که همین پیشرفت‌های اخیر در مورد افزونه‌ها باعث شده خیلی از این مطالب به روز نباشند. اگر در مقاله پیشین فکر می‌کنید که کروم چقدر در نوشتن افزونه جذابیت دارد و امکانات خوبی را در اختیار شما می‌گذارد، الان دیگر وقت آن است که نظر خودتان را عوض کنید و فایرفاکس را نه تنها یک سرو گردن بلکه بیشتر از این حرف‌ها بالاتر بدانید.
شرکت موزیالا برای قدرتمندی و راحتی کار طراحان یک sdk طراحی کرده است است و شما با استفاده از کدهای موجود در این sdk قادرید کارهای زیادی را انجام دهید. برای نصب این sdk باید پیش نیازهایی بر روی سیستم شما نصب باشد:
  • نصب پایتون  2.5 یا 2.6 یا 2.7 که فعلا در سایت آن، نسخه‌ی 2.7 در دسترس هست. توجه داشته باشید که هنوز برای نسخه‌ی 3 پایتون پشتیبانی صورت نگرفته است. 
  • آخرین نسخه‌ی sdk را هم می‌توانید از این آدرس  به صورت zip و یا از این آدرس به صورت tar دانلود کنید و در صورتیکه دوست دارید به سورس آن دسترسی داشته باشید یا اینکه از سورس‌های مشارکت شده یا غیر رسمی استفاده کنید، از این صفحه آن را دریافت کنید.
بعد از دانلود sdk به شاخه‌ی bin رفته و فایل activate.bat را اجرا کنید. موقعی که فایل activate اجرا شود، باید چنین چیزی دیده شود:
(C:\Users\aym\Downloads\addon-sdk-1.17) C:\Users\aym\Downloads\addon-sdk-1.17\bin>
برای سیستم‌های عامل Linux,FreeBSD,OS X دستورات زیر را وارد کنید:
اگر یک کاربر پوسته‌ی bash هستید کلمه زیر را در کنسول برای اجرای activate بزنید:
source bin/activate
اگر کاربر پوسته‌ی بش نیستید:
bash bin/activate
نهایتا باید کنسول به شکل زیر در آید یا شبیه آن:
(addon-sdk)~/mozilla/addon-sdk >
بعد از اینکه به کنسول آن وارد شدید، کلمه cfx را در آن تایپ کنید تا راهنمای دستورات و سوییچ‌های آن‌ها نمایش داده شوند. از این ابزار میتوان برای راه اندازی فایرفاکس و اجرای افزونه بر روی آن، پکیج کردن افزونه، دیدن مستندات و آزمون‌های واحد استفاده کرد.

آغاز به کار
برای شروع، فایل‌های زیادی باید ساخته شوند، ولی نگران نباشید cfx این کار را برای شما خواهد کرد. دستورات زیر را جهت ساخت یک پروژه خالی اجرا کنید:
mkdir fxaddon
cd fxaddon
cfx init
یک پوشه را در مسیری که کنسول بالا اشاره میکرد، ساختم و وارد آن شدم و با دستور cfx init دستور ساخت یک پروژه‌ی خالی را دادم و باید بعد از این دستور، یک خروجی مشابه زیر نشان بدهد:
* lib directory created
* data directory created
* test directory created
* doc directory created
* README.md written
* package.json written
* test/test-main.js written
* lib/main.js written
* doc/main.md written
Your sample add-on is now ready for testing:
try "cfx test" and then "cfx run". Have fun!"
در این پوشه یک فایل به اسم package.json هم وجود دارد که اطلاعات زیر داخلش هست:
{
  "name": "fxaddon",
  "title": "fxaddon",
  "id": "jid1-QfyqpNby9lTlcQ",
  "description": "a basic add-on",
  "author": "",
  "license": "MPL 2.0",
  "version": "0.1"
}
این اطلاعات شامل نام و عنوان افزونه، توضیحی کوتاه در مورد آن، نویسنده‌ی افزونه، ورژن افزونه و ... است. این فایل دقیقا معادل manifest.json در کروم است. در افزونه نویسی‌های قدیم این فایل install.rdf نام داشت و بر پایه‌ی فرمت rdf بود. ولی در حال حاضر با تغییرات زیادی که افزونه نویسی در فایرفاکس کرده‌است، الان این فایل بر پایه یا فرمت json است. اطلاعات package را به شرح زیر تغییر می‌دهیم:
{
  "name": "dotnettips",
  "title": ".net Tips Updater",
  "id": "jid1-QfyqpNby9lTlcQ",
  "description": "This extension keeps you updated on current activities on dotnettips.info",
  "author": "yeganehaym@gmail.com",
  "license": "MPL 2.0",
  "version": "0.1"
}

رابط‌های کاربری
Action Button و Toggle Button
فایل main.js را در دایرکتوری lib باز کنید:
موقعی که در کروم افزونه می‌نوشتیم امکانی به اسم browser action داشتیم که در اینجا با نام action button شناخته می‌شود. در اینجا باید کدها را require کرد، همان کاری در خیلی از زبان‌ها مثلا مثل سی برای صدا  زدن سرآیندها می‌کنید. مثلا برای action button اینگونه است:
var button= require('sdk/ui/button/action');
نحوه‌ی استفاده هم بدین صورت است:
buttons.ActionButton({...});
که در بین {} خصوصیات دکمه‌ی مورد نظر نوشته می‌شود. ولی من بیشتر دوست دارم از شیء دیگری استفاده کنم. به همین جهت ما از یک مدل دیگر button که به اسم toggle button شناخته می‌شود، استفاده می‌کنیم. از آن جا که این button دارای دو حالت انتخاب (حالت فشرده شده) و غیر انتخاب (معمولی و آماده فشرده شدن توسط کلیک کاربر) است، بهترین انتخاب هست.

کد زیر یک toggle button را برای فایرفاکس می‌سازد که با کلیک بر روی آن، صفحه‌ی popup.htm  به عنوان یک پنل روی آن رندر می‌شود:
var tgbutton = require('sdk/ui/button/toggle');
var panels = require("sdk/panel");
var self = require("sdk/self");

var button = tgbutton.ToggleButton({
  id: "updaterui",
  label: ".Net Updater",
  icon: {
    "16": "./icon-16.png",
    "32": "./icon-32.png",
    "64": "./icon-64.png"
  },
  onChange: handleChange
});

var panel = panels.Panel({
  contentURL: self.data.url("./popup.html"),
  onHide: handleHide
});

function handleChange(state) {
  if (state.checked) {
    panel.show({
      position: button
    });
  }
}

function handleHide() {
  button.state('window', {checked: false});
}
در سه خط اول، فایل‌هایی را که نیاز است Required شوند، می‌نویسیم و در یک متغیر ذخیره می‌کنیم. اگر در متغیر نریزیم مجبور هستیم همیشه هر کدی را به جای نوشتن عبارت زیر:
 tgbutton.ToggleButton
به صورت زیر بنویسیم:
require('sdk/ui/button/toggle').ToggleButton
که اصلا کار جالبی نیست. اگر مسیرهای نوشته شده را از مبدا فایل zip که اکسترکت کرده‌اید، در دایرکتوری sdk در شاخه lib بررسی کنید، با دیگر موجودیت‌های sdk آشنا خواهید شد.
در خط بعدی به تعریف یک شیء از نوع toggle button به اسم button میپردازیم و خصوصیاتی که به این دکمه داده ایم، مانند یک کد شناسایی، یک برچسب که به عنوان tooltip نمایش داده خواهد شد و آیکن‌هایی در اندازه‌های مختلف که در هرجایی کاربر آن دکمه را قرار داد، در اندازه‌ی مناسب باشد و نهایتا به تعریف یک رویداد می‌پردازیم. تابع handlechange زمانی صدا زده می‌شود که در وضعیت دکمه‌ی ایجاد شده تغییری حاصل شود. در خط بعدی شیء panel را به صورت global میسازیم. شیء self دسترسی ما را به اجزا یا فایل‌های افزونه خودمان فراهم می‌کند که در اینجا دسترسی ما به فایل html در شاخه‌ی data میسر شده است و مقدار مورد نظر را در contentURL قرار می‌دهد. نهایتا هم برای رویداد onhide تابعی را در نظر می‌گیریم تا موقعی که پنجره بسته شد بتوانیم وضعیت toggle button را به حالت قبلی بازگردانیم و حالت فشرده نباشد. چرا که این دکمه تنها با کلیک ماوس به حالت فشرده و حالت معمولی سوییچ میکند. پس اگر کاربر با کلیک بر روی صفحه‌ی مرورگر پنجره را ببندد، دکمه در همان وضعیت فشرده باقی می‌ماند.
همانطور که گفتیم تابع handlechnage موقعی رخ میدهد که در وضعیت دکمه، تغییری رخ دهد و نمیدانیم که این وضعیت فشرده شدن دکمه هست یا از حالت فشرده خارج شده است. پس با استفاده از ویژگی checked بررسی میکنم که آیا دکمه‌ای فشرده شده یا خیر؛ اگر برابر true بود یعنی کاربر روی دکمه، کلیک کرده و دکمه به حالت فشرده رفته، پس ما هم پنل را به آن نشان می‌دهیم و خصوصیات دلخواهی را برای مشخص کردن وضعیت پنل نمایشی به آن پاس می‌کنیم. خصوصیت یا پارامترهای زیادی را می‌توان در حین ساخت پنل برای آن ارسال کرد. با استفاده از خصوصیت position محل نمایش پنجره را مشخص می‌کنیم. در صورتی که ذکر نشود پنجره در وسط مرورگر ظاهر خواهد شد.
تابع onhide زمانی رخ میدهد که به هر دلیلی پنجره بسته شده باشد که در بالا یک نمونه‌ی آن را عرض کردیم. ولی اتفاقی که می‌افتد، وضعیت تابع را با متد state تغییر می‌دهیم و خصوصیت checked آن را false می‌کنیم. بجای پارامتر اولی، دو گزینه را میتوان نوشت؛ یکی window و دیگری tab است. اگر شما گزینه tab را جایگزین کنید، اگر در یک تب دکمه به حالت فشرده برود و به تب دیگر بروید و باعث بسته شدن پنجره بشوید، دکمه تنها در تبی که فعال است به حالت قبلی باز می‌گردد و تب اولی همچنان حالت خود را حفظ خواهد کرد پس می‌نویسیم window تا این عمل در کل پنجره اعمال شود.

Context Menus
برای ساخت منوی کانتکست از کد زیر استفاده می‌کنیم:
var contextMenu = require("sdk/context-menu");

var home = contextMenu.Item({
  label: "صفحه اصلی",
  data: "https://www.dntips.ir/"
});
var postsarchive = contextMenu.Item({
  label: "مطالب سایت",
  data: "https://www.dntips.ir/postsarchive"
});

var menuItem = contextMenu.Menu({
  label: "Open .Net Tips",
  context: contextMenu.PageContext(),
   items: [home, postsarchive],
  image: self.data.url("icon-16.png"),
  contentScript: 'self.on("click", function (node, data) {' +
                 '  window.location.href = data;' +
                 '});'
});
این منو هم مثل کروم دو زیر منو دارد که یکی برای باز کردن صفحه‌ی اصلی و دیگر‌ی برای باز کردن صفحه‌ی مطالب است. هر کدام یک برچسب برای نمایش متن دارند و یکی هم دیتا که برای نگهداری آدرس است. در خط بعدی منوی پدر یا والد ساخته می‌شود که با خصوصیت items، زیر منوهایش را اضافه می‌کنیم و با خصوصیت image، تصویری را در پوشه‌ی دیتا به آن معرفی می‌کنیم که اندازه‌ی آن 16 پیکسل است و دومی هم خصوصیت context است که مشخص می‌کند این گزینه در چه مواردی بر روی context menu نمایش داده شود. الان روی همه چیزی نمایش داده می‌شود. اگر گزینه، SelectionContext باشد، موقعی که متنی انتخاب شده باشد، نمایش می‌یابد. اگر SelectorContext باشد، خود شما مشخص می‌کنید بر روی چه مواردی نمایش یابد؛ مثلا عکس یا تگ p  یا هر چیز دیگری، کد زیر باعث می‌شود فقط روی عکس نمایش یابد:
SelectorContext("img")
کد زیر هم روی عکس و هم روی لینکی که href داشته باشد:
SelectorContext("img,a[href]")
موارد دیگری هم وجود دارند که میتوانید مطالب بیشتری را در مورد آن‌ها در اینجا مطالعه کنید. آخرین خصوصیت باقی مانده، content script است که می‌توانید با استفاده از جاوااسکریپت برای آن کد بنویسید.  موقعی که برای آن رویداد کلیک رخ داد، مشخص شود تابعی را صدا میزند با دو آرگومان؛ گره ای که انتخاب شده و داده‌ای که به همراه دارد که آدرس سایت است و آن را در نوار آدرس درج می‌کند.
آن منوهایی که با متد item ایجاد شده‌اند منوهایی هستند که با کلیک کاربر اجرا می‌شوند؛ ولی والدی که با متد menu ایجاد شده است، برای منویی است که زیر منو دارد و خودش لزومی به اجرای کد ندارد. پس اگر منویی میسازید که زیرمنو ندارد و خودش قرار است کاری را انجام دهد، به صورت همان item بنویسید که پایین‌تر نمونه‌ی آن را خواهید دید.
الان مشکلی که ایجاد می‌شود این است که موقعی که سایت را باز می‌کند، در همان تبی رخ می‌دهد که فعال است و اگر کاربر بر روی صفحه‌ی خاصی باشد، آن صفحه به سمت سایت مقصد رفته و سایت فعلی از دست میرود. روش صحیح‌تر اینست که تبی جدید بار شود و آدرس مقصد در آن نمایش یابد. پس باید از روشی استفاده کنیم که رویداد کلیک توسط کد خود افزونه مدیریت شود، تا با استفاده از شیء tab، یک تب جدید با آدرسی جدید ایجاد کنیم. پس کد را با کمی تغییر می‌نویسیم:
var tabs = require("sdk/tabs");
var menuItem = contextMenu.Menu({
  label: "Open .Net Tips",
  context: contextMenu.PageContext(),
   items: [home, postsarchive],
  image: self.data.url("icon-16.png"),
  contentScript: 'self.on("click", function (node, data) {' +
                 '  self.postMessage(data);' +
                 '});',
 onMessage: function (data) {
     tabs.open(data);
  }
});
با استفاده از postmessage، هر پارامتری را که بخواهیم ارسال می‌کنیم و بعد با استفاده از رویداد onMessage، داده‌ها را خوانده و کد خود را روی آن‌ها اجرا می‌کنیم.
بگذارید کد زیر را هم جهت سرچ مطالب بر روی سایت پیاده کنیم: 
var Url="https://www.dntips.ir/search?term=";
var searchMenu = contextMenu.Item({
  label: "search for",
  context: [contextMenu.PredicateContext(checkText),contextMenu.SelectionContext()],
    image: self.data.url("icon-16.png"),
  contentScript: 'self.on("click", function () {' +
  '  var text = window.getSelection().toString();' +
                 '  if (text.length > 20)' +
                 '   text = text.substr(0, 20);' +
                 '  self.postMessage(text);'+
                '})',
onMessage: function (data) {
     tabs.open(Url+data);
  }
 
});

function checkText(data) {

       if(data.selectionText === null)
           return false;

       console.log('selectionText: ' + data.selectionText);

       //handle showing or hiding of menu items based on the text content.
       menuItemToggle(data.selectionText);

       return true;
};

function menuItemToggle(text){
var searchText="جست و جو برای ";
    searchMenu.label=searchText+text;

};
در ساخت این منو، ما از ContextSelection استفاده کرده‌ایم. بدین معنی که موقعی که چیزی روی صفحه انتخاب شد، این منو ظاهر شود و گزینه‌ی دیگری که در کنارش هست، گزینه contextMenu.PredicateContext وظیفه دارد تابعی که به عنوان آرگومان به آن دادیم را موقعی که منو کانتکست ایجاد شد، صدا بزند و اینگونه میتوانیم بر حسب اطلاعات کانتکست، منوی خود را ویرایش کنیم. مثلا من دوست دارم موقعی که متنی انتخاب می‌شود و راست کلیک می‌کنم گزینه‌ی "جست و جو برای..." نمایش داده شود و به جای ... کلمه‌ی انتخاب شده نمایش یابد. به شکل زیر دقت کنید. این چیزی است که ما قرار است ایجاد کنیم:
در کل موقع ایجاد منو تابع checkText اجرا شده و متن انتخابی را خوانده به عنوان یک آرگومان برای تابع menuItemToggle ارسال می‌کند و به رشته "جست و جو برای" می‌چسباند. در خود پارامترهای آیتم اصلی، گزینه content scrip، با استفاده از جاوااسکریپت، متن انتخاب شده را دریافت کرده و با استفاده از متد postmessage برای تابع  onMessage ارسال کرده و با ساخت یک تب و چسباندن عبارت به آدرس جست و جو سایت، کاربر را به صفحه مورد نظر هدایت کرده و عمل جست و جو در سایت انجام می‌گیرد.

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