Husky.Net 0.6 منتشر شد 🚀
جشن گرفتن 100 میلیونی شدن گیت هاب
ویژگی ها
- امکان ایجاد progress bar و همچنین قابلیت لغو آپلود.
- Drag & Drop
- امکان paste کردن از Clipboard و قابلیت drag & drop از browser
- امکان resize کردن تصویر قبل از آپلود شدن
- امکان تغییر دادن orientation تصویر پیش از ارسال
- قابلیت resume و pause/play در آپلود
- امکان فیلتر کردن type، size و طول و عرض تصاویر
- قابلیت تبدیل به Flash shim در صورت عدم پشتیبانی html5 توسط مرورگر
نصب و پیاده سازی
bower install ng-file-upload-shim --save bower install ng-file-upload --save
<input type="file" ngf-select ng-model="picFile" name="file" accept="image/*" ngf-max-size="2MB" required>
<div ngf-drop ng-model="picFile" ngf-pattern="image/*"> ... </div>
<i ng-show="myForm.file.$error.required">*required</i><br> <i ng-show="myForm.file.$error.maxSize"> File too large: max is 2M </i>
app.controller('MyCtrl', ['$scope', 'Upload', '$timeout', function ($scope, Upload, $timeout) { // the method uploadPic called from Angular View $scope.uploadPic = function(file) { file.upload = Upload.upload({ url: 'https://angular-file-upload-cors-srv.appspot.com/upload', data: {file: file, otherData: $scope.otherData}, }); file.upload.then(function (response) { // waiting for response $timeout(function () { file.result = response.data; }); }, function (response) { //error if (response.status > 0) $scope.errorMsg = response.status + ': ' + response.data; }, function (evt) { //progress file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total)); console.log(file.progress); }); } }]);
توابع سودمند
فراخوانی و استفاده از این توابع در ابتدا ممکن است کمی عجیب به نطر برسد. به مثال زیر دقت کنید که تابع سودمند () trim را فراخوانی کرده ایم.
$.trim(someString);
در صورتی که نوشتن علامت $ برای شما عجیب به نطر میرسد میتوانید شناسه دیگر با نام jQuery به کار ببرید. کد زیر دقیقا مانند بالا عمل میکند شاید درک آن راحتتر هم باشد.
jQuery.trim(someString);
بدیهی است که از jQuery یا $ تنها به عنوان فضای نامی که تابع ()trim در آن تعریف شده اند، استفاده شده باشد.
نکته: اگر چه در نوشتههای آنلاین jQuery، این عناصر به عنوان توابع سودمند در معرفی شده اند اما در حقیقت آنها متدهایی برای تابع ()$ میباشند.
عملکرد صفحه آماده (The document ready handler)
هنگامی که از Unobtrusive JavaScriptاستفاده میکنیم، رفتار از ساختار جدا میشود، بنابراین برای انجام عملیات روی عناصر صفحه باید منتظر بمانیم تا انها ایجاد شوند. برای رسیدن به این هدف، ما نیاز به راهی داریم که تا زمان ایجاد عناصر DOM روی صفحه منتظر بماند قبل از آن عملیات را اجرا کند.
به طور معمول از onload برای نمونههای window استفاده میشود، که پس از لود شدن کامل صفحه ، دستورها قابل اجرا میباشند. بنابراین ساختار کلی آن کدی مانند زیر خواهد بود:
window.onload = function() { $("table tr:nth-child(even)").addClass("even"); };
نوشتن کد به صورت بالا سبب میشود که کد پس از بارگذاری کامل صفحه اجرا شود. متاسفانه، مرورگرها تا بعد از ساخته شدن عناصر صفحه صبر نمیکنند، بلکه پس از ساخت درخت عناصر صفحه منتظر بارگذاری کامل منابع خارجی صفحه مانند تصاویر نیز میمانند و سپس آنها را در پنجره مرورگر نمایش میدهند. در نتیجه بازدید کننده زمان زیادی منتظر میماند تا رویداد onload تکمیل شود.
حتی بدتر از آن، زمانی است که اگر به طور مثال یکی از تصاویر با مشکل مواجه شود که زمان قابل توجهی صرف بارگذاری آن شود، کاربر باید تمام این مدت را صبر کند تا پس از آن بتواند با این صفحه کار کند. این نکته میتواند دلیلی برای استفاده نکردن از Unobtrusive JavaScriptبرای شروع کار باشد.
اما راه بهتری نیز وجود دارد، میتوانیم تنها زمانی که قسمت ساختار عناصر صفحه ترجمه شده و HTML به درخت عناصر تبدیل میشود، صبر کنیم . پس از آن کد مربوط به رفتارها را اجرا کنیم. رسیدن به این روش برای استفاده از Cross-Browser کمی مشکل است، اما به لطف jQuery و قدرت آن، این امر به سادگی امکان پذیر است و دیگر نیازی به منتظر ماندن برای بارگذاری منابع صفحه مانند تصاویر و ویدیوها نمیباشد. Syntax زیر نمونه ای از چنین حالتی است:
$(document).ready(function() { $("table tr:nth-child(even)").addClass("even"); });
ابتدا صفحه مورد نظر را به تابع ()$ ارسال کرده ایم، سپس هر زمان که آن صفحه آماده شد (Ready) ، تابع ارسال شده به آن اجرا خواهد شد. البته میتوان کد نوشته شده بالا را به شکل مختصرتری هم نوشت:
$(function() { $("table tr:nth-child(even)").addClass("even"); });
با ارسال تابع به ()$، ما مرورگر را مجبور میکنیم که برای اجرای کد تا زمانی که DOM کامل لود شود (فقط DOM لود شود) منتظر بماند. حتی بهتر از آن ما میتوانیم از این تکنیک چندین با در همان سند HTML استفاده کرده و مرورگر تمامی تابعهای مشخص شده توسط ما را به ترتیب اجرا خواهد کرد. (یعنی من در دیک صفحه میتوانم چنین بار تابع ()ready را فراخوانی کنم). در مقابل روش OnLoad پنجره فقط اجازه اجرای یکبار تابع را به ما میدهد.
این هم یکی دیگر از کارکردهای دیگر تابع ()$ میباشد. حال به یکی دیگر از امکاناتی که این تابع برای ما فراهم میکند دقت کنید.
ساختن اجزای DOM (ساختن عناصر صفحه)
یکی دیگر از کارهایی که تابع ()$ می تواند برای ما انجام دهد ایجاد کردن عناصر صفحه است. به این منظور ورودی تابع ()$ را یک رشته که حاوی دستور HTML مربوط به ساخت یک عنصر میباشد، قرار میدهیم. برای مثال دستور زیر یک تگ p ایجاد میکند:
$("<p>Hi there!</p>")
اما ایجاد یک عنصر DOM یا (سلسله مراتب عناصر DOM) برای ما به تنهایی سودمند نیست، و هدف ما چیز دیگری است. ایجاد اشیا صفحه توسط ()$ زمانی برای ما مفید خواهد بود که بخواهیم به هنگام ساخت، تابعی بروی آن اعمال کنیم یا به محض ساخت آن را به تابعی ارسال کنیم به کد زیر دقت کنید:
<html> <head> <title>Follow me!</title> <script type="text/javascript" src="../scripts/jquery-1.2.js"></script> <script type="text/javascript"> // در زمان Reday بودن صفحه عنصر مورد نظر ایجاد میشود $(function(){ $("<p>Hi there!</p>").insertAfter("#followMe"); }); </script> </head> <body> <p id="followMe">Follow me!</p> </body> </html>
در کد بالا زمانی که صفحه مورد نظر Ready شد تابع مورد نظر ما اجرا شده و در عناصر صفحه بعد از عنصری که id آن followMe میباشد یک عنصر p را ایجاد میکند. که خروجی آن شبیه تصویر زیر خواهد بود.
مزیت دیگر jQuery این است که در صورتی که امکانی را ندارد شما به آسانی میتوانید آن را توسعه داده و برای آن پلاگین طراحی کنید.
برای پایان دادن به این پست همانطور که دیدیم jQuery قادر به انجام کارهای زیر است:
- انتخاب عناصر و ایجاد مجموعه ای از آنها که آماده اعمال متدهای مختلف میباشند.
- استفاده به عنوان یک فضای نام برای توابع سودمند.
- ایجاد اشیا مختلف HTML بروی صفحه.
- اجرای کد به محض آماده شدن اشیای صفحه.
موفق وموید باشید
برای خیلی ممکن است سوال پیش آمده باشد چطور یک برنامه نویس از پروژه ای که به صورت اوپن سورس منتشر میکند محافظت کرده و از سوء استفاده جلوگیری میکند ؟ بر اساس همین سوال شخص لینوس توروالدز Git را ایجاد کرد برای ذخیره پروژههای متن باز و حفظ حقوق برنامه نویس پروژه
سایت گیت هاب (github.com) بر پایه Git تشکیل شده و به همین منظور استفاده میشود. البته برنامه نویس میتواند پروژه را بصورت خصوصی ذخیره کند و از انتشار عمومی پروژه خودداری کند. با استفاده از این سیستم برنامه نویسان پروژههای متن باز را با خیال راحت و با حفظ حقوق منتشر کنند و به این ترتیب پروژه به نام آن برنامه نویس ثبت خواهد شد. در این سیکل برنامه نویس یک اکانت در این سایت ایجاد و برای هر پروژه متن باز که منتشر میکند یک صفحه (مخزن) ساخته و پروژه را در آن ذخیره میکند.
یکی دیگر از مواردی که ممکن است برای برنامههای متن باز پیش بیاید این است که اگر برنامه نویسی یک پروژه متن باز را از گیت هاب توسعه داد ، موارد اضافه شده بر عهده برنامه نویس اول گذاشته نشه و حق برنامه نویس اصلی رعایت شود ؛ برای این منظور سیکلی در سایت گیت هاب ایجاد شده با عنوان Forking که یک برنامه نویس میتواند پروژه را داشته باشد و پس از توسعه پروژه ، تغییرات ایجاد شده در برنامه را به برنامه نویس اصلی ارسال کند و پس از تایید ، تغییرات ایجاد شده در مخزن اصلی پروژه اعمال شود.
گیت هاب امکانات بیشتری را در خود پیاده کرده که این سایت را تبدیل به شبکه اجتماعی برای برنامه نویسان کرده است. موارد از قبیل انجمن برای پرسش و مشکلات ، ارسال پیغام خصوصی برای سایر اعضا و ….
فرقی نمیکند، پروژه عمومی همیشه نمایش داده میشود، این دسترسیها مربوط به شاخه بندی پروژه است که چه کسانی بتوانند تا چه حدی روی هر شاخه تغییرات را اعمال کنند ولی بحث خصوصی سازی نیاز به پرداخت هزینه دارد. هنگامی که در گیت هاب پروژه خودتون رو به صورت عمومی انتخاب کنید هیچ گزینه اضافی ندارد ولی وقتی روی خصوصی تنظیم کنید با مجموعهای از آیکنهای کارتهای اعتباری روبرو میشوید.
همینطور که دوست عزیزمان "محسن خان" گفتند شما میتوانید از طریق یک سیستم متن باز و رایگان به ایجاد یک سیستم گیت جداگانه (شخصی)اقدام کنید و تنها لازم است هزینه هاستی که خریدید را به سرویس دهنده هاست پرداخت کنید.
آغاز کار با الکترون
فرآیندها (Processes) در الکترون به دو بخش تقسیم میشوند:
یک. فرآیند اصلی (Main Process) که همان فایل جاوااسکریپتی است و توسط main، در فایل package.json مشخص شدهاست .فرآیند اصلی تنها فرآیندی است که قابلیت دسترسی به امکانات گرافیکی سیستم عامل را از قبیل نوتیفیکشن ها، دیالوگها ،Tray و ... دارد. فرآیند اصلی میتواند با استفاده از شیء BrowserWindow که در قسمت قبلی کاربرد آن را مشاهده کردیم، render process را ایجاد کند. با هر بار ایجاد یک نمونه از این شیء، یک Render Process ایجاد میشود.
دو. فرآیند رندر (Render Process): از آنجا که الکترون از کرومیوم استفاده میکند و کرومیوم شامل معماری چند پردازشی است، هر صفحهی وب میتواند پردازش خود را داشته باشد که به آن Render Process میگویند. به طور معمول در مرورگرها، صفحات وب در محیطی به نام SandBox اجرا میشوندکه اجازه دسترسی به منابع بومی را ندارند. ولی از آنجا که الکترون میتواند از Node.js استفاده کند، قابلیت دسترسی به تعاملات سطح پایین سیستم عامل را نیز داراست.
در فرآیند اصلی، پنجرهها توسط BrowserWindow ایجاد میشوند و هر پنجرهای که صفحه وبی را برای خودش باز میکند، شامل Render Process خودش است و هر پنجرهای که کارش خاتمه یابد، فرآیند مربوط به خودش به اتمام میرسد. فرآیند اصلی، همه صفحات وب به همراه Render Process مربوط به خودشان را مدیریت میکند و هر فرآیند رندر، از دیگری مجزا و محافظت شده است و تنها تمرکزش بر روی صفحه وبی است که متعلق به خودش است.
در ابتدا قصد داریم یک منو برای برنامهی خود درست کنیم. برای ساخت منو، راههای متفاوتی وجود دارند که فعلا ما راه استفاده از template را بر میگزینیم که به صورت یک آرایه نوشته میشود. کدهای زیر را در فایل index.js یا هر اسمی که برای آن انتخاب کردهاید بنویسید:
const electron = require('electron'); const {app,dialog,BrowserWindow,Menu,shell} = electron; let win; app.on('ready', function () { win = new BrowserWindow({width: 800, height: 600}); win.loadURL(`file://${__dirname}/index.html`); var app_menu=[ { label:'پرونده', submenu:[ { label:'باز کردن', accelerator:'CmdOrCtrl+O', click:()=>{ } }, { label:'ذخیره', accelerator:'CmdOrCtrl+S', click:()=>{ } } ] }, { label:'سیستم', submenu:[ { label:'درباره ما', click:()=> { shell.openExternal('https://www.dntips.ir'); } }, { label:'خروج', accelerator:'CmdOrCtrl+X', click:()=> { win=null; app.quit(); } } ] } ];
در خطوط بعدی، یک کار اضافهتر را جهت آشنایی بیشتر انجام میدهیم. قصد داریم اگر سیستم عامل مکینتاش بود، نام برنامه هم در ابتدای نوار منو نمایش داده شود. به همین جهت در ادامه خطوط زیر را اضافه میکنیم:
if(process.platform=="darwin") { const app_name=app.getName(); app_menu.unshift({ label:app_name }) }
ویندوز | win32 حتی اگر 64 بیتی باشد. |
لینوکس | linux |
مک | darwin |
فری بی اس دی | freebsd |
سولاریس | sunos |
دستو shell در بالا به شما اجازه میدهد با محیط دسکتاپ، یکپارچگی خود
را حفظ کنید و دستوراتی از قبیل باز کردن url، باز کردن یک مسیر دایرکتوری،
باز کردن یک فایل، انتقال فایل به سطل آشغال یا بازیافت و صدای بوق سیستم
(بیپ) را به شما میدهد. مستندات این شیء را در اینجا مطالعه فرمایید.
{ label:'خروج', accelerator:'CmdOrCtrl+X', role:'close' }
در انتها با اجرای دو دستور زیر، منو ساخته میشود:
var menu=Menu.buildFromTemplate(app_menu); Menu.setApplicationMenu(menu);
حال قصد داریم برای زیرمنوی «باز کردن فایل» یک دیالوگ open درخواست کنیم. برای این کار از شیء dialog استفاده میکنیم. پس خطوط زیر را به رویداد کلیک این زیرمنو اضافه میکنیم:
dialog.showOpenDialog({ title:'باز کردن فایل متنی', properties: [ 'openFile']//[ 'openFile', 'openDirectory', 'multiSelections' ] ,filters:[ {name:'فایلهای نوشتاری' , extensions:['txt','text']}, {name:'جهت تست' , extensions:['doc','docx']} ] }, (filename)=>{ if(filename===undefined) return; dialog.showMessageBox({title:'پیام اطلاعاتی',type:"info",buttons:['تایید'],message:`the name of file is [${filename}]`}); });
برای قسمت ذخیره هم کد زیر را مینویسیم:
dialog.showSaveDialog({ title:'باز کردن فایل متنی', properties: [ 'openFile']//[ 'openFile', 'openDirectory', 'multiSelections' ] ,filters:[ {name:'فایلهای نوشتاری' , extensions:['txt','text']} ] }, (filename)=>{ if(filename===undefined) return; });
حال بهتر است این دیالوگهای جاری را هدفمند کنیم و بتوانیم فایلهای متنی را به کاربر نمایش دهیم، یا آنها را ذخیره کنیم. به همین علت فایل html زیر را نوشته و طبق دستوری که در مقاله «آشنایی با الکترون» فرا گرفتیم، آن را نمایش میدهیم:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> Fie Content:<br/> <textarea id="TextFile" cols="100" rows="50"></textarea> </body> </html>
کاری که میخواهیم انجام دهیم این است که فایل متنی را باز کرده و محتوای آن را در کادر متنی نشان دهیم و موقع ذخیره نیز محتوای نوشته شده در کادر متنی را در فایلی ذخیره کنیم. از آنجا که main Process به المانهای DOM یا Render Process دسترسی ندارد، باید از طریقی، ارتباط آن را برقرار کنیم. یکی از راههای برقراری این ارتباط، IPC است. IPC در واقع یک فرستنده و یک شنونده است که هر کدام در یک سمت قرار گرفته اند. فرستنده پیام را تحت یک عنوان ارسال میکند و شنونده منتظر دریافت پیامی تحت همان عنوان میماند و پیام دریافتی را پاسخ میدهد. در این مقاله، ما فقط قسمتی از این نوع ارتباطات را بررسی میکنیم.
در نتیجه محتوای callback کدهای دیالوگ open و save را به شکل زیر تغییر میدهیم:
Open
dialog.showOpenDialog({ title:'باز کردن فایل متنی', properties: [ 'openFile']//[ 'openFile', 'openDirectory', 'multiSelections' ] ,filters:[ {name:'فایلهای نوشتاری' , extensions:['txt','text']}, {name:'جهت تست' , extensions:['doc','docx']} ] }, (filename)=>{ if(filename===undefined) return; win.webContents.send('openFile',filename); // dialog.showMessageBox({title:'پیام اطلاعاتی',type:"info",buttons:['تایید'],message:`the name of file is [${filename}]`}); });
dialog.showSaveDialog({ title:'باز کردن فایل متنی', properties: [ 'openFile']//[ 'openFile', 'openDirectory', 'multiSelections' ] ,filters:[ {name:'فایلهای نوشتاری' , extensions:['txt','text']} ] }, (filename)=>{ if(filename===undefined) return; win.webContents.send('saveFile',filename); });
برای ایجاد شنونده هم کد زیر را به فایل index.html اضافه میکنیم:
<script> const {ipcRenderer} = require('electron'); var fs=require('fs'); ipcRenderer.on('openFile', (event, arg) => { var content= fs.readFileSync(String(arg),'utf8'); document.getElementById("TextFile").value=content; }); ipcRenderer.on('saveFile', (event, arg) =>{ var content=document.getElementById("TextFile").value; fs.writeFileSync(String(arg),content,'utf8'); alert('ذخیره شد'); }); </script>
در اینجا شوندههایی را از نوع ipcRenderer ایجاد میکنیم و با استفاده از متد on، به پیامهایی تحت عنوانهای مشخص شده گوش فرا میدهیم. پیامهای ارسالی را که حاوی آدرس فایل میباشند، به شیءای که از نوع fs میباشد، میدهند و آنها را میخوانند یا مینویسند. خواندن و نوشتن فایل، به صورت همزمان صورت میگیرد. ولی اگر دوست دارید که به صورت غیر همزمان پیامی را بخوانید یا بنویسید، باید عبارت Sync را از نام متدها حذف کنید و یک callback را به عنوان پارامتر دوم قرار دهید و محتوای آن را از طریق نوشتن یک پارامتر در سازنده دریافت کنید.
فایلهای پروژه
Lazy loading در تزریق وابستگیها به کمک StructureMap
(البته طبق فایل معرفی شده در گیت هاب ، گویا در بخش ابتدایی کلاس SmObjectFactory تغییراتی داریم)
سوالی که پیش میاد اینه که اگر نیاز باشه در یک کلاس خود کلاس کانتکس رو Lazy کنیم ، آیا کدنویسی بصورت زیر درون کلاس سرویس درست است :
private readonly Lazy<IUnitOfWork> _uow; private readonly IDbSet<JobCategory> _jobCategories; public JobCategoryService(Lazy<IUnitOfWork> uow) { _uow = uow; _jobCategories = _uow.Value.Set<JobCategory>(); }
یا اینکه کد زیر را باید در متدی که مورد نیاز است بنویسیم ؟
_jobCategories = _uow.Value.Set<JobCategory>();
طبق فرمایشات شما به نظرم روش اول نادرست باشه ؛ درسته ؟