ml5.js is a high level JavaScript library for machine learning. The main idea of this project is to further reduce the barriers between lower level machine learning and creative outputs using JavaScript
پروژه River Trail
1- در MPage یک سری متا تگها هست + CSS + JavaScript
2- وقتی در یک webForm متصل به Mpage کد:
Head.InnerHtml = SEO.GenerateMetaTag ....
رو قرار میدهم تمام تگهای Head حتی MasterPage هم پاک میشه
اینم میدونم این کلاس این کارو میکنه
راه حل چیه که تگها پاک نشه ؟ مخصوصا آدرسهای CSS و JavaScript ؟
پیش نیازها
- شروع یک پروژهی جدید وب با پشتیبانی از Web API
- نصب دو بستهی نیوگت مرتبط با Structure map 3
PM>install-package structuremap PM>install-package structuremap.web
پیاده سازی IHttpControllerActivator توسط Structure map
using System; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using StructureMap; namespace WebApiDISample.Core { public class StructureMapHttpControllerActivator : IHttpControllerActivator { private readonly IContainer _container; public StructureMapHttpControllerActivator(IContainer container) { _container = container; } public IHttpController Create( HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { var nestedContainer = _container.GetNestedContainer(); request.RegisterForDispose(nestedContainer); return (IHttpController)nestedContainer.GetInstance(controllerType); } } }
نکتهی مهم آن استفاده از NestedContainer آن است. معرفی آن به متد request.RegisterForDispose سبب میشود تا کلیه کلاسهای IDisposable نیز در پایان کار به صورت خودکار رها سازی شده و نشتی حافظه رخ ندهد.
معرفی StructureMapHttpControllerActivator به برنامه
فایل WebApiConfig.cs را گشوده و تغییرات ذیل را در آن اعمال کنید:
using System.Web.Http; using System.Web.Http.Dispatcher; using StructureMap; using WebApiDISample.Core; using WebApiDISample.Services; namespace WebApiDISample { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // IoC Config ObjectFactory.Configure(c => c.For<IEmailsService>().Use<EmailsService>()); var container = ObjectFactory.Container; GlobalConfiguration.Configuration.Services.Replace( typeof(IHttpControllerActivator), new StructureMapHttpControllerActivator(container)); // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } }
تهیه سرویسی برای آزمایش برنامه
namespace WebApiDISample.Services { public interface IEmailsService { void SendEmail(); } } using System; namespace WebApiDISample.Services { /// <summary> /// سرویسی که دارای قسمت دیسپوز نیز هست /// </summary> public class EmailsService : IEmailsService, IDisposable { private bool _disposed; ~EmailsService() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public void SendEmail() { //todo: send email! } protected virtual void Dispose(bool disposeManagedResources) { if (_disposed) return; if (!disposeManagedResources) return; //todo: clean up resources here ... _disposed = true; } } }
نکتهی مهم آن استفاده از IDisposable در این کلاس خاص است (ضروری نیست؛ صرفا جهت بررسی بیشتر اضافه شدهاست). اگر در کدهای برنامه، یک چنین کلاسی وجود داشت، نیاز است متد Dispose آن نیز توسط IoC Container فراخوانی شود. برای آزمایش آن یک break point را در داخل متد Dispose قرار دهید.
استفاده از سرویس تعریف شده در یک Web API Controller
using System.Web.Http; using WebApiDISample.Services; namespace WebApiDISample.Controllers { public class ValuesController : ApiController { private readonly IEmailsService _emailsService; public ValuesController(IEmailsService emailsService) { _emailsService = emailsService; } // GET api/values/5 public string Get(int id) { _emailsService.SendEmail(); return "_emailsService.SendEmail(); called!"; } } }
تزریق وهلهی مورد نیاز آن، به صورت خودکار توسط StructureMapHttpControllerActivator که در ابتدای بحث معرفی شد، صورت میگیرد.
فراخوانی متد Get آنرا نیز توسط کدهای سمت کاربر ذیل انجام خواهیم داد:
<h2>Index</h2> @section scripts { <script type="text/javascript"> $(function () { $.getJSON('/api/values/1?timestamp=' + new Date().getTime(), function (data) { alert(data); }); }); </script> }
اکنون برنامه را اجرا کنید. هنگام فراخوانی متد Get، وهلهی سرویس مورد نظر، نال نیست. همچنین متد Dispose نیز به صورت خودکار فراخوانی میشود.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید
WebApiDISample.zip
//برای ذخیره مقادیر از ساختار نام و مقدار استفاده میکنیم که نامها را اینجا ثبت کرده ام var Variables={ posts:"posts", postsComments:"postsComments", shares:"shares", sharesComments:"sharesComments", } //برای ذخیره زمان آخرین تغییر سایت برای هر یک از مطالب به صورت جداگانه نیاز به یک ساختار نام و مقدار است که نامها را در اینجا ذخیره کرده ام var DateContainer={ posts:"dtposts", postsComments:"dtpostsComments", shares:"dtshares", sharesComments:"dtsharesComments", interval:"interval" } //برای نمایش پیامها به کاربر var Messages={ SettingsSaved:"تنظیمات ذخیره شد", SiteUpdated:"سایت به روز شد", PostsUpdated:"مطلب ارسالی جدید به سایت اضافه شد", CommentsUpdated:"نظری جدیدی در مورد مطالب سایت ارسال شد", SharesUpdated:"اشتراک جدید به سایت ارسال شد", SharesCommentsUpdated:"نظری برای اشتراکهای سایت اضافه شد" } //لینکهای فید سایت var Links={ postUrl:"https://www.dntips.ir/feeds/posts", posts_commentsUrl:"https://www.dntips.ir/feeds/comments", sharesUrl:"https://www.dntips.ir/feed/news", shares_CommentsUrl:"https://www.dntips.ir/feed/newscomments" } //لینک صفحات سایت var WebLinks={ Home:"https://www.dntips.ir", postUrl:"https://www.dntips.ir/postsarchive", posts_commentsUrl:"https://www.dntips.ir/commentsarchive", sharesUrl:"https://www.dntips.ir/newsarchive", shares_CommentsUrl:"https://www.dntips.ir/newsarchive/comments" }
chrome.runtime.onInstalled.addListener(function(details) { var now=String(new Date()); var params={}; params[Variables.posts]=true; params[Variables.postsComments]=false; params[Variables.shares]=false; params[Variables.sharesComments]=false; params[DateContainer.interval]=1; params[DateContainer.posts]=now; params[DateContainer.postsComments]=now; params[DateContainer.shares]=now; params[DateContainer.sharesComments]=now; chrome.storage.local.set(params, function() { if(chrome.runtime.lastError) { /* error */ console.log(chrome.runtime.lastError.message); return; } }); });
chrome.storage.local.set('mykey':myvalue,....
chrome.storage.local.set(mykey:myvalue,...
"background": { "scripts": ["const.js","init.js"] }
نکته:نمی توان در تعریف بک گراند هم فایل اسکریپت معرفی کرد و هم فایل html
"background": { "page": "background.htm" }
<html> <head> <script type="text/javascript" src="const.js"></script> <script type="text/javascript" src="https://www.google.com/jsapi"></script> <script type="text/javascript" src="init.js"></script> <script type="text/javascript" src="omnibox.js"></script> <script type="text/javascript" src="rssreader.js"></script> <script type="text/javascript" src="contextmenus.js"></script> </head> <body> </body> </html>
- کدنویسی راحتتر و خلاصهتر برای خواندن RSS
- استفاده اجباری از یک پروکسی به خاطر Content Security Policy و حتی CORS
"content_security_policy": "script-src 'self' https://*.google.com; object-src 'self'"
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
استفاده از این Api در rssreader.js
google.load("feeds", "1"); google.setOnLoadCallback(alarmManager);
function alarmManager() { chrome.storage.local.get(DateContainer.interval,function ( items) { period_time==items[DateContainer.interval]; chrome.alarms.create('RssInterval', {periodInMinutes: period_time}); }); chrome.alarms.onAlarm.addListener(function (alarm) { console.log(alarm); if (alarm.name == 'RssInterval') { var boolposts,boolpostsComments,boolshares,boolsharesComments; chrome.storage.local.get([Variables.posts,Variables.postsComments,Variables.shares,Variables.sharesComments],function ( items) { boolposts=items[Variables.posts]; boolpostsComments=items[Variables.postsComments]; boolshares=items[Variables.shares]; boolsharesComments=items[Variables.sharesComments]; chrome.storage.local.get([DateContainer.posts,DateContainer.postsComments,DateContainer.shares,DateContainer.sharesComments],function ( items) { var Vposts=new Date(items[DateContainer.posts]); var VpostsComments=new Date(items[DateContainer.postsComments]); var Vshares=new Date(items[DateContainer.shares]); var VsharesComments=new Date(items[DateContainer.sharesComments]); if(boolposts){var result=RssReader(Links.postUrl,Vposts,DateContainer.posts,Messages.PostsUpdated);} if(boolpostsComments){var result=RssReader(Links.posts_commentsUrl,VpostsComments,DateContainer.postsComments,Messages.CommentsUpdated); } if(boolshares){var result=RssReader(Links.sharesUrl,Vshares,DateContainer.shares,Messages.SharesUpdated);} if(boolsharesComments){var result=RssReader(Links.shares_CommentsUrl,VsharesComments,DateContainer.sharesComments,Messages.SharesCommentsUpdated);} }); }); } }); }
- آدرس فیدی که قرار است از روی آن بخواند
- آخرین به روزسانی که از سایت داشته متعلق به چه تاریخی است.
- نام کلید ذخیره سازی تاریخ آخرین تغییر سایت که اگر بررسی شد و مشخص شد سایت به روز شده است، تاریخ جدید را روی آن ذخیره کنیم.
- در صورتی که سایت به روز شده باشد نیاز است پیامی را برای کاربر نمایش دهیم که این پیام را در اینجا قرار میدهیم.
function RssReader(URL,lastupdate,datecontainer,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); if(RssUpdate>lastupdate) { SaveDateAndShowMessage(datecontainer,strRssUpdate,Message) } } }); }
var strRssUpdate = result.xmlDocument.firstChild.firstChild.childNodes[5].textContent;
function SaveDateAndShowMessage(DateField,DateValue,Message) { var params={ } params[DateField]=DateValue; chrome.storage.local.set( params,function(){ var options={ type: "basic", title: Messages.SiteUpdated, message: Message, iconUrl: "icon.png" } chrome.notifications.create("",options,function(){ chrome.notifications.onClicked.addListener(function(){ chrome.tabs.create({'url': WebLinks.Home}, function(tab) { }); }); }); }); }
"permissions": [ "storage", "tabs", "alarms", "notifications" ]
chrome.notifications.create("",options,function(){ chrome.notifications.onClicked.addListener(function(){ chrome.tabs.create({'url': WebLinks.Home}, function(tab) { });}); });
"web_accessible_resources": [ "icon.png" ]
خوب؛ کار افزونه تمام شده است ولی اجازه دهید این بار امکانات افزونه را بسط دهیم:
"options_page": "popup.html"
جایزگزینی صفحات یا Override Pages
"chrome_url_overrides": { "newtab": "newtab.htm" }
ایجاد یک تب اختصاصی در Developer Tools
"devtools_page": "devtools.htm"
<script src="devtools.js"></script>
chrome.devtools.panels.create( "Dotnettips Updater Tools", "icon.png", "devtoolsui.htm", function(panel) { } );
- One-Time Requests یا درخواستهای تک مرتبهای
- Long-Lived Connections یا اتصالات بلند مدت یا مصر
درخواستهای تک مرتبه ای
window.addEventListener("load", function() { chrome.extension.sendMessage({ type: "dom-loaded", data: { myProperty : "value" } }); }, true);
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) { switch(request.type) { case "dom-loaded": alert(request.data.myProperty ); break; } return true; });
var port = chrome.runtime.connect({name: "my-channel"}); port.postMessage({myProperty: "value"}); port.onMessage.addListener(function(msg) { // do some stuff here });
chrome.runtime.onConnect.addListener(function(port) { if(port.name == "my-channel"){ port.onMessage.addListener(function(msg) { // do some stuff here }); } });
نمونه کد
آپلود نهایی کار در Google web store
برای آپلود نهایی کار به google web store که در آن تمامی برنامهها و افزونههای کروم قرار دارند بروید. سمت راست آیکن تنظیمات را بزنید و گزینه developer dashboard را انتخاب کنید تا صفحهی آپلود کار برای شما باز شود. دایرکتوری محتویات اکستنشن را zip کرده و آپلود نمایید. توجه داشته باشید که محتویات و سورس خود را باید آپلود کنید نه فایل crx را. بعد از آپلود موفقیت آمیز، صفحهای ظاهر میشود که از شما آیکن افزونه را در اندازه 128 پیکسل میخواهد بعلاوه توضیحاتی در مورد افزونه، قیمت گذاری که به طور پیش فرض به صورت رایگان تنظیم شده است، لینک وب سایت مرتبط، لینک محل پرسش و پاسخ برای افزونه، اگر لینک یوتیوبی در مورد افزونه دارید، یک شات تصویری از افزونه و همینطور چند تصویر برای اسلایدشو سازی که در همان صفحه استاندارد آنها را توضیح میدهد و در نهایت گزینهی جالبتر هم اینکه اکستنشن شما برای چه مناطقی تهیه شده است که متاسفانه ایران را ندیدم که میتوان همه موارد را انتخاب کرد. به خصوص در مورد ایران که آی پیها هم صحیح نیست، انتخاب ایران چنان تاثیری ندارد و در نهایت گزینهی publish را میزنید که متاسفانه بعد از این صفحه درخواست میکند برای اولین بار باید 5 دلار آمریکا پرداخت شود که برای بسیاری از ما این گزینه ممکن نیست.
سورس پروژه را میتوانید از اینجا ببینید و خود افزونه را از اینجا دریافت کنید.
public class Startup { public void ConfigureServices(IServiceCollection services) { // ... services.AddTransient<ICustomerService, DefaultCustomerService>(); // ... } }
public class SupportController { // DefaultCustomerService will be injected here: public SupportController(ICustomerService customerService) { // ... } }
برای سفارشی سازی مراحل وهله سازی اشیاء توسط IoC Container توکار برنامه و امکان دخالت در آن، قابلیتی تحت عنوان «factory registration» نیز پیش بینی شدهاست که در ادامه آنرا بررسی میکنیم.
Factory Registration چیست؟
اگر در اسمبلی Microsoft.Extensions.DependencyInjection.Abstractions و فضای نام Microsoft.Extensions.DependencyInjection آن به کلاس ServiceCollectionServiceExtensions که متدهای الحاقی مانند AddScoped را ارائه میکند، بیشتر دقت کنیم، تک تک این متدها امضاهای دیگری را نیز دارند:
namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionServiceExtensions { public static IServiceCollection AddScoped<TService>( this IServiceCollection services) where TService : class; public static IServiceCollection AddScoped( this IServiceCollection services, Type serviceType, Type implementationType); public static IServiceCollection AddScoped( this IServiceCollection services, Type serviceType, Func<IServiceProvider, object> implementationFactory); public static IServiceCollection AddScoped<TService, TImplementation>(this IServiceCollection services) public static IServiceCollection AddScoped( this IServiceCollection services, Type serviceType); public static IServiceCollection AddScoped<TService>( this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory) where TService : class; public static IServiceCollection AddScoped<TService, TImplementation>( this IServiceCollection services, Func<IServiceProvider, TImplementation> implementationFactory) // ... } }
مثال 1 : تزریق وابستگیها در حالتیکه کلاس سرویس مدنظر دارای تعدادی پارامتر ثابت است
IoC Container توکار برنامههای NET Core.، به صورت خودکار وابستگیهای تزریق شدهی در سازندههای سرویسهای مختلف را تا هر چند سطح ممکن، به صورت خودکار وهله سازی میکند؛ به شرطیکه این وابستگیهای تزریق شده نیز خودشان سرویس بوده باشند و در تنظیمات ابتدایی آن ثبت و معرفی شده باشند. به عبارتی زمانیکه با سیستم تزریق وابستگیها کار میکنیم، مهم نیست که نگران مقدار دهی پارامترهای سازندهی تزریق شدهی در سازندههای سرویسی خاص باشیم. اما ... برای نمونه سرویس زیر را که یک رشته را در سازندهی خود دریافت میکند درنظر بگیرید:
namespace CoreIocServices { public interface IParameterizedService { string GetConstructorParameter(); } public class ParameterizedService : IParameterizedService { private readonly string _connectionString; public ParameterizedService(string connectionString) { _connectionString = connectionString; } public string GetConstructorParameter() { return _connectionString; } } }
services.AddTransient<IParameterizedService, ParameterizedService>();
services.AddTransient<IParameterizedService>(serviceProvider => { return new ParameterizedService("some value ...."); });
services.AddTransient<IParameterizedService>(serviceProvider => new ParameterizedService("some value ...."));
در اینجا چون serviceProvider نیز در اختیار ما است، حتی میتوان این مقدار را از سرویسی دیگر دریافت کرد و سپس مورد استفاده قرار داد:
services.AddTransient<IParameterizedService>(serviceProvider => { var config = serviceProvider.GetRequiredService<ITestService>().GetConfigValue(); return new ParameterizedService(config); });
نمونهی دیگری از این دست، کار با IUrlHelper توکار ASP.NET Core است. این سرویس برای اینکه پاسخ درستی را ارائه دهد، نیاز به ActionContext جاری را دارد تا بتواند از طریق آن به تمام جزئیات اکشن متد یک کنترلر و درخواست رسیده دسترسی داشته باشد. در این حالت برای ساده سازی کار با آن، بهتر است تامین وابستگیهای لحظهای این سرویس را با سفارشی سازی نحوهی وهله سازی آن، انجام دهیم، تا اینکه این قطعه کد تکراری را در هر جائیکه به IUrlHelper نیاز است، تکرار کنیم:
services.AddScoped<IUrlHelper>(serviceProvider => { var actionContext = serviceProvider.GetRequiredService<IActionContextAccessor>().ActionContext; var urlHelperFactory = serviceProvider.GetRequiredService<IUrlHelperFactory>(); return urlHelperFactory.GetUrlHelper(actionContext); });
مثال 2: وهله سازی در صورت نیاز به وابستگیهای یک سرویس، به کمک Lazy loading
فرض کنید دو سرویس را در سازندهی سرویس دیگری تزریق کردهاید:
namespace Services { public class OrderHandler : IOrderHandler { private readonly IAccounting _accounting; private readonly ISales _sales; public OrderHandler(IAccounting accounting, ISales sales) {
روش انجام یک چنین کارهایی با استفاده از کلاس Lazy اضافه شدهی به NET 4x. قابل انجام است:
public class OrderHandlerLazy : IOrderHandler { public OrderHandlerLazy(Lazy<IAccounting> accounting, Lazy<ISales> sales) {
services.AddTransient<IOrderHandler, OrderHandlerLazy>(); services.AddTransient<IAccounting, Accounting>() .AddTransient(serviceProvider => new Lazy<IAccounting>(() => serviceProvider.GetRequiredService<IAccounting>())); services.AddTransient<ISales, Sales>() .AddTransient(serviceProvider => new Lazy<ISales>(() => serviceProvider.GetRequiredService<ISales>()));
- سپس سرویسهایی که قرار است به صورت Lazy نیز واکشی شوند، بار دیگر توسط روش factory registration با وهله سازی new Lazy از نوع سرویس مدنظر و فراهم آوردن پیاده سازی آن با استفاده از serviceProvider.GetRequiredService، مجددا معرفی خواهند شد.
پس از این تنظیمات، اگر سرویس IOrderHandler را از طریق سیستم تزریق وابستگیها درخواست کنید، وابستگیهای تزریق شدهی در سازندهی آن فقط زمانی و در محلی وهله سازی میشوند که از طریق خاصیت Value شیء Lazy آنها مورد استفاده قرار گرفته شده باشند.
مثال کامل IOrderHandler را از فایل پیوستی انتهای مطلب میتوانید دریافت. اگر آنرا اجرا کنید (برنامهی کنسول آنرا)، در خروجی آن، فقط اجرا شدن سازندهی سرویسی را مشاهده میکنید که مورد استفاده قرار گرفته و نه وابستگی دومی که تزریق شده، اما استفاده نشدهاست.
مثال 3: چگونه بجای اینترفیسها، یک وهله از کلاسی مشخص را از سیستم تزریق وابستگیها درخواست کنیم؟
فرض کنید سرویسی را به صورت زیر به سیستم تزریق وابستگیها معرفی کردهاید:
services.AddTransient<IMyDisposableService, MyDisposableService>();
public class AnotherController { public AnotherController(MyDisposableService customerService) { // ... } }
An unhandled exception occurred while processing the request. InvalidOperationException: Unable to resolve service for type ‘MyDisposableService’ while attempting to activate ‘AnotherController’. Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired)
services.AddTransient<IMyDisposableService, MyDisposableService>(); services.AddTransient<MyDisposableService>(serviceProvider => serviceProvider.GetRequiredService<IMyDisposableService>() as MyDisposableService);
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: CoreDependencyInjectionSamples-06.zip
#C در بین زبانهای برتر سال 2014
How Google handles JavaScript throughout the indexing process
A research to demystify Google's rendering through empirical evidence.