نظرات مطالب
ساختار پروژه های Angular
ضمن تشکر فراوان از جناب آقای پاکدل عزیز، در این مقاله به خوبی درباره lazy loading در angularjs بحث شده. نکته مهم اینکه حتما پروژه‌ی قابل اجرایی که در انتهای مقاله لینک شده را ملاحظه کنید. نکاتی در این پروژه هست از جمله اینکه برای دسترسی به providerها برای lazy loading آنها به این ترتیب به app افزوده شده اند:
app.config([
        '$stateProvider',
        '$urlRouterProvider',
        '$locationProvider',
        '$controllerProvider',
        '$compileProvider',
        '$filterProvider',
        '$provide',


        function ($stateProvider, $urlRouterProvider, $locationProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
            //برای رجیستر کردن غیر همروند اجزای انگیولاری در آینده
            app.lazy =
            {
                controller: $controllerProvider.register,
                directive:  $compileProvider.directive,
                filter:     $filterProvider.register,
                factory:    $provide.factory,
                service:    $provide.service
            };
.
.
.
])
(البته این کد از پروژه خودمان است و بعضی وابستگی‌های دیگر هم تزریق شده‌اند).
استفاده از app.lazy باعث سهولت بیشتر در استفاده و خواناتر شدن کد می‌شود. در ادامه به این ترتیب می‌توانید از app.lazy استفاده کنید:
angular.module('app').lazy.controller('myController',
        ['$scope',  function($scope){
...
}]);
به این ترتیب کد نوشته شده به دلیل نام گذاری ارجاع controllerProvider  با  controller  به حالت عادی شبیه است، و از طرفی lazy پیش از آن به فهم ماجرا کمک خواهد کرد.
این نقطه شروع یکی از پروژه‌های ماست که به عنوان نمونه بد نیست ملاحظه کنید:
<script type="text/javascript">
// --- Scriptjs ---
!function (a, b, c) { function t(a, c) { var e = b.createElement("script"), f = j; e.onload = e.onerror = e[o] = function () { e[m] && !/^c|loade/.test(e[m]) || f || (e.onload = e[o] = null, f = 1, c()) }, e.async = 1, e.src = a, d.insertBefore(e, d.firstChild) } function q(a, b) { p(a, function (a) { return !b(a) }) } var d = b.getElementsByTagName("head")[0], e = {}, f = {}, g = {}, h = {}, i = "string", j = !1, k = "push", l = "DOMContentLoaded", m = "readyState", n = "addEventListener", o = "onreadystatechange", p = function (a, b) { for (var c = 0, d = a.length; c < d; ++c) if (!b(a[c])) return j; return 1 }; !b[m] && b[n] && (b[n](l, function r() { b.removeEventListener(l, r, j), b[m] = "complete" }, j), b[m] = "loading"); var s = function (a, b, d) { function o() { if (!--m) { e[l] = 1, j && j(); for (var a in g) p(a.split("|"), n) && !q(g[a], n) && (g[a] = []) } } function n(a) { return a.call ? a() : e[a] } a = a[k] ? a : [a]; var i = b && b.call, j = i ? b : d, l = i ? a.join("") : b, m = a.length; c(function () { q(a, function (a) { h[a] ? (l && (f[l] = 1), o()) : (h[a] = 1, l && (f[l] = 1), t(s.path ? s.path + a + ".js" : a, o)) }) }, 0); return s }; s.get = t, s.ready = function (a, b, c) { a = a[k] ? a : [a]; var d = []; !q(a, function (a) { e[a] || d[k](a) }) && p(a, function (a) { return e[a] }) ? b() : !function (a) { g[a] = g[a] || [], g[a][k](b), c && c(d) }(a.join("|")); return s }; var u = a.$script; s.noConflict = function () { a.$script = u; return this }, typeof module != "undefined" && module.exports ? module.exports = s : a.$script = s }(this, document, setTimeout)

$script(['/Scripts/Lib/jquery/jquery-1.10.2.min.js'], function () {
    $script(['/Scripts/Lib/angular/angular.js'], function () {
        $script(['/Scripts/Lib/angular/angular-ui-router.min.js',
                    '/Scripts/Lib/angular/angular-resource.min.js',
                    '/Scripts/Lib/angular/angular-cache.min.js',
                    '/Scripts/Lib/angular/angular-sanitize.min.js',
                    '/Scripts/Lib/angular/angular-animate.min.js',
                    '/Scripts/Lib/angular/angular-cookie.min.js',
                    '/APP/Common/directives.js'
                ], function () {
                    $script('/app/app.js', function () {
                        angular.bootstrap(document, ['app']);
                    });
                });
            })
        });
</script>

این تگ script در صفحه شروع پروژه آمده است.
کد minify شده scriptjs در ابتدا قرار دارد، پس از آن فایل‌های js مورد نیاز با رعایت وابستگی‌های احتمالی به ترتیب بارگذاری شده‌اند.
این قسمت resolve یکی از بخش‌های مسیریابی است: 
resolve: {
                        fileDeps: ['$q', '$rootScope', function ($q, $rootScope) {
                            var deferred = $q.defer();
                            var deps = ['/app/HotStories/dataContextService.js',
                                        '/app/HotStories/hotStController.js'];
                            $script(deps, function () {
                                $rootScope.$apply(function () {
                                    deferred.resolve();
                                });
                            });

                            return deferred.promise;
                        }]
                    }
این نحوه تعریف سرویسی که فایل آن در وابستگی‌ها آمده و قرار است lazy load شود:
angular.module('app').lazy.service('dataContextService',
        ['$rootScope', '$resource', '$angularCacheFactory', '$q', function($rootScope, $resource, $cacheFactory, $q){
...
}]);
و این هم نحوه تعریف کنترلری که فایل آن در وابستگی‌ها آمده و قرار است lazy load شود: 
angular.module('app').lazy.controller('hotStController',
        ['$scope', 'ipCookie', 'dataContextService', function($scope, ipCookie, dataContextService){
...
}]);


مطالب
شروع به کار با بوت استرپ 4
روش‌های مختلف دریافت و نصب بوت استرپ 4

اولین کاری را که باید جهت شروع به کار با بوت استرپ 4 انجام داد، نصب و افزودن آن به صفحه‌ی HTML جاری است. روش‌های زیادی برای انجام اینکار وجود دارند:
الف) استفاده از نگارش SASS آن
بوت استرپ 4 در اصل مبتنی بر SASS توسعه یافته‌است و فایل‌های آن با فرمت scss. ارائه می‌شوند. مزیت کار با این روش، امکان سفارشی سازی بوت استرپ 4 و یا مشارکت در پروژه‌ی آن است و بدیهی است پس از آن باید SASS را به CSS کامپایل و مورد استفاده قرار داد.

ب) استفاده از CDN و یا Content delivery network
- مزیت آن بالا رفتن سرعت سایت با کش شدن آن در شبکه و یا شبکه‌های توزیع محتوا است.
- اما این روش محدودیت و الزام کار آنلاین با فایل‌های بوت استرپ را نیز به همراه دارد.
برای کار با CDN‌های بوت استرپ، مطابق راهنمای آن، تنها کافی است مدخل فایل css آن‌را به head صفحه و مداخل فایل‌های js ذیل را پیش از بسته شدن تگ body قرار دهید:
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
در اینجا شاید نام دو فایل، برای شما تازگی داشته باشند:
- jquery-3.3.1.slim : در اینجا slim یک نگارش بسیار کوچک از jQuery می‌باشد که بوت استرپ 4 بر مبنای آن کار می‌کند. البته در یک پروژه‌ی واقعی احتمالا نیاز به نگارش کامل آن‌را خواهید داشت و یا اگر قصد حذف کردن جی‌کوئری را دارید، این نگارش، کم‌حجم‌ترین آن است.
- popper.min.js : برای نمونه Bootstrap dropdown برای کارکرد صحیح آن در نگارش 4، نیاز به این وابستگی جدید را دارد.

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

د) استفاده از ابزارهای مدیریت بسته‌ها
روشی را که ما در اینجا از آن استفاده خواهیم کرد، دریافت و نصب وابستگی‌های مورد نیاز جهت کار با بوت استرپ 4، توسط npm است. به همین جهت یک فایل جدید package.json را با محتوای ذیل ایجاد کنید:
{
  "name": "bootstrap.4",
  "version": "1.0.0",
  "description": "client side resources of the project",
  "scripts": {},
  "author": "VahidN",
  "license": "ISC",
  "dependencies": {
    "bootstrap": "^4.1.3",
    "components-font-awesome": "5.0.6",
    "jquery": "^3.3.1",
    "popper.js": "^1.14.4"
  }
}
سپس از طریق خط فرمان به این پوشه وارد شده و دستور npm install را جهت دریافت این وابستگی‌ها صادر کنید. یکی از مزیت‌های مهم این روش، آگاه شدن خودکار از به روز رسانی‌های وابستگی‌های فوق، توسط افزونه‌هایی مانند Version Lens است.
در اینجا font-awesome را نیز مشاهده می‌کنید؛ از این جهت که بوت استرپ 4 برخلاف نگارش 3 آن، به همراه گلیف آیکن‌های پیش‌فرض آن نیست.


ایجاد قالب ابتدایی شروع به کار با بوت استرپ 4

پس از دریافت وابستگی‌های مورد نیاز جهت شروع به کار با بوت استرپ 4 که هم اکنون باید در پوشه‌ی node_modules واقع در ریشه‌ی پوشه‌ی جاری موجود باشند، در ادامه حداقل قالبی را که برای کار با آن نیاز است، مرور می‌کنیم:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/node_modules/components-font-awesome/css/fa-solid.min.css">
    <link rel="stylesheet" href="/node_modules/components-font-awesome/css/fontawesome.min.css">
    <title>Bootstrap</title>
</head>
<body>
    <div class="container">

    </div>
    <script src="/node_modules/jquery/dist/jquery.min.js"></script>
    <script src="/node_modules/popper.js/dist/umd/popper.min.js"></script>
    <script src="/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
</body>
</html>
- در ابتدا سازگاری با edge و الزام به استفاده‌ی از آخرین نگارش IE نصب شده مشخص شده‌است.
- سپس viewport استاندارد، جهت تعیین اینکه این صفحه با ابزارهای موبایل نیز سازگار است، تعریف شده‌است.
- در قسمت head، مدخل فایل bootstrap.min.css تعریف شده‌است. همچنین مداخل مورد نیاز جهت کار با font-awesome را نیز مشاهده می‌کنید.
- پیش از بسته شدن تگ body، تعاریف jQuery، کتابخانه‌ی popper و سپس bootstrap.min.js قید شده‌اند. کتابخانه‌ی popper از مسیر umd آن دریافت شده‌است تا همه جا کار کند.


نکته‌ی مهم!
در نگارش نهایی برنامه‌ی شما، مسیرهای فایل‌های شروع شده‌ی با /node_modules/ نباید وجود داشته باشند. این فایل‌ها را بهتر است توسط ابزارهای bundling & minification یکی و سپس به صفحه اضافه کنید.


غنی سازی ویرایشگر VSCode برای کار ساده‌تر با بوت استرپ

VSCode یک ویرایشگر حرفه‌ای چندسکویی است که برای ویندوز، مک و لینوکس تهیه شده‌است. این ویرایشگر را می‌توان توسط افزونه‌های زیر برای کار ساده‌تر با بوت استرپ غنی کرد:
Bootstrap 4, Font awesome 4, Font Awesome 5 Free & Pro snippets: ساده سازی تشکیل تگ‌های بوت استرپ
Path Autocomplete: کار وارد کردن مسیر فایل‌ها و تصاویر را ساده می‌کند.
HTML CSS Support: کار آن غنی سازی intellisense این ویرایشگر جهت کار با ویژگی‌ها و همچنین کلاس‌های CSS است.
IntelliSense for CSS class names in HTML: انتخاب کلاس‌های CSS بوت استرپ را ساده‌تر می‌کند.
Live Server: کار آن راه اندازی یک وب سرور آزمایشی و سپس امکان مشاهده‌ی آنی تغییرات در برنامه و فایل HTML جاری، در مرورگر می‌باشد.


برای کار با آن، در حالیکه صفحه‌ی HTML جاری در VSCode باز است، بر روی دکمه‌ی Go Live اضافه شده‌ی در status bar آن کلیک کنید. پس از آن، یک وب سرور آزمایشی را بر روی پورت 5500 آغاز کرده و صفحه‌ی جاری را در آدرس http://127.0.0.1:5500/index.html در مرورگر پیش‌فرض سیستم نمایش می‌دهد. اکنون فایل HTML خود را در VSCode ویرایش کنید. ملاحظه خواهید کرد که بلافاصله این تغییرات در مرورگر قابل مشاهده هستند.


نگارش‌های راست به چپ بوت استرپ 4

قرار است بوت استرپ 4 نگارش رسمی راست به چپ نیز داشته باشد. به همین منظور می‌توانید در اینجا رای خود را اظهار کنید.
همچنین پروژه‌های زیر نیز چنین قابلیتی را ارائه می‌دهند:
  1. MahdiMajidzadeh/bootstrap-v4-rtl
  2. DediData/Bootstrap-RTL 
  3. GhalamborM/bootstrap4-rtl



کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: Bootstrap4_01.zip
مطالب
شروع کار با Apache Cordova در ویژوال استودیو #5

همانطور که در قسمت قبل گفته شد، در این قسمت با روش کار jQuery Mobile و plugin‌های مربوط به Cordova آشنا خواهیم شد.


تگ متای زیر برای تنظیمات مربوط به viewport است و برای jQuery Mobile توصیه می‌شود.
<!DOCTYPE html> 
<html> 
<head> 
<meta charset="utf-8"> 
<title>Title</title> 
<meta name="viewport" content="width=device-width, initial-scale=1">
device-width  نشان می‌دهد که می‌خواهیم مقیاس محتوای ما به اندازه‌ی عرض دستگاه(device) مورد نظر باشد و initial-scale هم مقدار زوم را برای Web page ما مشخص می‌کند. شما می‌توانید با مقدار دهی user-scalable=no هم امکان تغییر زوم را به کاربر ندهید. این متا تگ را در تمام صفحات html خود بعد از تگ title قرار دهید.

روال کار jQuery Mobile
برای اینکه بتواند سند HTML ما را برای استفاده‌ی در موبایل بهینه کند، ابتدا آن را لود می‌کند و سپس بر  اجزایی که با ویژگیdata-role علامت گذاری شده‌اند، CSS3 بهینه شده برای موبایل را اعمال می‌کند.


از آنجایی که مستندات jQuery Mobile به قدر کافی کامل هست، نیازی نیست تا در مورد تک تک آنها مثال بزنیم و از اصل مطلب دور شویم. در هر مثالی که زده خواهد شد، در صورت استفاده از ویجتی خاص، با آن آشنا خواهیم شد.

لیست کامل اتریبیوت‌های -data به همراه مقادیری که می‌پذیرند 

دموی مربوط به ویجت‌ها  

لیست تمام رخدادها 

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

لیست کامل کلاس‌های CSS  



Cordova Plugins

از این قسمت http://plugins.cordova.io/#/viewAll و این قسمت  http://plugreg.com/plugins می‌توانید سراغ پلاگین‌های مورد نیاز خود بگردید. برای مثال وارد بخش کانفیگ پروژه شده و از قسمت plugins  و تب Core یکسری از پلاگین‌هایی را که در Cordova گنجانده شده است، مشاهده می‌کنید. با کلیک بر روی دکمه‌ی Add می‌توانید آن را دانلود کرده و از API‌های آن استفاده کنید.



برای مثال پلاگین Notification را به پروژه اضافه می‌کنم. سپس یک فایل js را با نام custom.js به فولدر scripts در ریشه پروژه اضافه کرده و  محتوای فایل‌های index.html , custome.js را به شکل زیر در نظر می‌گیرم:


$(function() {
    $("#alert").on('tap', function(event) {
        navigator.notification.alert("اطلاعات ذخیره شد",null, "alert", "تایید");
    });

    $("#prompt").on('tap', function(event) {
        navigator.notification.prompt("برای تائید نام خود را وارد کنید", onPrompt, "prompt", "تایید", "لغو"],"نام خود"]);
    });

    function onPrompt(results) {
        navigator.notification.alert(results.buttonIndex + "\n" + results.input1, null);
    }
    $("#confirm").on('tap', function(event) {
        navigator.notification.confirm("حذف انجام شود؟", onConfirm, "confirm", ["بله", "خیر", "نمیدانم"]);
    });

    function onConfirm(buttonIndex) {
        navigator.notification.alert(buttonIndex , null);
    }
    $("#beep").on('tap', function(event) {
        navigator.notification.beep(1);
    });

});

رخداد tap زمانی صادر می‌شود که کاربر، دکمه‌ی مورد نظر را لمس کند و یکی از رخداد‌های jQuery Mobile می‌باشد. بعد از نصب پلاگین Notification، با استفاده از navigator.notification می‌توانید به متد‌های مورد نظر که در بالا مشخص است، دسترسی پیدا کنید.

برای آشنایی با این پلاگین می‌توانید داکیومنت آن را مطالعه کنید.

در کد بالا با استفاده از متد‌های callback توانسته‌ایم اطلاعاتی در مورد نوع عملکرد کاربر با notification ما بدست آوریم.


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>CordovaApp01</title>
   <meta name="viewport" content="width=device-width, initial-scale=1"/> 
    <!-- CordovaApp01 references -->
    <link href="css/index.css" rel="stylesheet" />
    <link href="jquery.mobile.rtl/css/themes/default/rtl.jquery.mobile-1.4.0.css" rel="stylesheet" />
</head>
<body>
<div data-role="page" id="page1">
    <div data-role="header">
        <h2>
            تست پلاگین Notification
        </h2>
    </div>
    <div data-role="content">
        <a href="#page2" data-transition="pop" data-rel="dialog" data-role="button" data-inline="true" data-icon="back">page 2</a>
       
        <button data-role="button" id="alert" data-inline="true" >alert</button>
        <button data-role="button" id="confirm" data-inline="true">confirm</button>
        <button data-role="button" id="beep" data-inline="true" >beep</button>
        <button data-role="button" id="prompt" data-inline="true" >prompt</button>

    </div>
    <div data-role="footer">
        <h2>من فوتر هستم</h2>
    </div>
</div>
    <div data-role="page" id="page2">
        <div data-role="header">
            <h1>Header</h1>
        </div>
        <div data-role="content">
            Content
        </div>
        <div data-role="footer">
            <h1>Footer</h1>
        </div>
    </div>
<!-- Cordova reference, this is added to your app when it's built. -->
    <script src="scripts/jquery-2.1.3.min.js"></script>
    <script src="cordova.js"></script>
    <script src="scripts/platformOverrides.js"></script>
    <script src="scripts/index.js"></script>
    <script src="jquery.mobile.rtl/js/rtl.jquery.mobile-1.4.0.js"></script>
    <script src="scripts/custom.js"></script>
</body>
</html>

در کد بالا 4 تا button دیده می‌شود که ویژگی data-role آنها مقدار button در نظر گرفته شده‌است تا توسط jQuery Mobile به عنوان button شناخته شوند و استایل‌های لازم بر روی آن‌ها اعمال گردد. قرار است طبق کد js ایی که نوشته‌ایم، با لمس کردن هر کدام از دکمه‌ها، notification هایی نمایش داده شوند.


برای اینکار شبیه ساز YouWave را دانلود کرده و نصب کنید. سپس در قسمت toolbar ویژوال، گزینه‌ی Device را به جای شبیه ساز Ripple انتخاب کنید. نرم افزار youwave را اجرا کنید حال اگر برنامه را اجرا کنید با خطای زیر مواجه خواهید شد:

Error447C:\Users\Administrator\Documents\Visual Studio 2013\Projects\CordovaApp-01\CordovaApp-01\bld\Debug\platforms\android\cordova\node_modules\q\q.js:126CordovaApp-01
Error448throw e;CordovaApp-01
Error449^CordovaApp-01
Error450Error : DEP10201 : Failed to deploy to device, no devices found.CordovaApp-01
مشخصا خطا، مبنی بر پیدا نشدن دستگاه خارجی است. برای رفع این مشکل می‌بایست شبیه ساز youwave را به ویژوال استودیو وصل کنیم. برای این منظور دستور زیر را در cmd اجرا کنید.
adb connect localhost:5558

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


علاوه بر این ما در سند HTML خود در بالا، یک page و یک تگ a قرار داده‌ایم. 
 <a href="#page2" data-transition="pop" data-rel="dialog" data-role="button" data-inline="true" data-icon="back">page 2</a>
data-role: با مقدار button در نظر گرفته شده است؛ لذا به شکل 4 دکمه دیگر رندر خواهد شد.
data-transition: با مقدار pop در نظر گرفته شده است که مشخص کننده‌ی افکت ظاهر شدن صفحه‌ای است که قرار است بار گذاری شود.
data-rel: مشخص می‌کند که صفحه‌ی مورد نظر من به صورت دیالوگ باز شود.
data-icon: با استفاده از این ویژگی می‌توان icon مورد نظر خود را برای المنت در نظر گرفت.
data-inline: برای به خط کردن دکمه‌ها کنار هم استفاده می‌شود.
با لمس کردن این دکمه، نتیجه به شکل زیر خواهد بود:

در مقاله‌ی بعد، به مباحث Database در Cordova خواهیم پرداخت.

ادامه دارد...

مطالب
شروع کار با Apache Cordova در ویژوال استودیو #4
در قسمت قبل یک مثال ساده را کار کردیم. در این قسمت با jQuery Mobile آشنا شده و در پروژه‌ی خود استفاده خواهیم کرد.

توضیح تکمیلی در مورد ساختار فایل‌های پروژه
همان طور که در قسمتها قبل گفته شد، تگ اسکریپت زیر 
<script src="cordova.js"></script>
از استاندارد‌های Cordova است؛ وجود خارجی ندارد و بخشی از فرآیند ساخت برنامه است.
اگر توجه کنید فایلی با نام platformOverrides.js در فولدر scripts موجود در ریشه، خالی است اما در فولدر merges موجود در ریشه‌ی پروژه مربوط به هر پلتفرم و همنام آن پلتفرم قرار دارد. برای مثال برای android، یک چنین دایرکتوری merges/android/scripts وجود دارد که درون آن فایلی به‌نام  platformOverrides.js دیده می‌شود و اگر دقت کنید، همنام فایل موجود در فولدر scripts موجود در ریشه پروژه است که درون خود فایلی بنام  android2.3-jscompat.js را فراخوانی می‌کند. (برای کمک به سازگاری کتابخانه‌های ثالث)
در زمان build ، تمام فایل‌های موجود در "merges/"platformname ، در فولدر‌های هم نامی در شاخه‌ی ریشه‌ی پروژه کپی شده و جایگزین فایل‌های قبلی خواهند شد.

 مثال برای اندروید 
در زمان ساخت (build) فایل scripts/platformOverrides.js با فایل merges/windows/scripts/platformoverrides.js جایگزین خواهد شد. این امکان برای فلدر‌های css, images و بقیه‌ی آنها نیز امکان پذیر است.
توجه داشته باشید این ادغام در سطح فایل‌ها و نه در سطح محتوای فایل‌ها انجام می‌شود.

نکته 
برای محتوای موجود در فولدر res، قضیه فرق می‌کند. زیرا محتوای این resource‌ها برای اپلیکیشن پکیچ ضروریست؛ پیش از آن که کد‌های ما درون WebView یا host رندر شوند. باید توجه کرد که این فولدر به جهت اینکه منابع اصلی را (با توجه به پلتفرم باید از فایل‌های مشخص آن برای تشخیص ساختار فولدر‌های اپلیکیشن پکیچ استفاده کند) در بر دارد و این منابع باید در زمان ساخت پروژه تشخیص داده شوند.


رویداد‌های بومی
در زیر تعدادی از رخدادهایی که در Cordova گنجانده شده‌اند تا اپلیکیشن ما از رخداد‌های دستگاه با خبر شوند، نشان داده شده است. برای تست آنها به راحتی بعد از اجرای برنامه توسط شبیه ساز Ripple می‌توانید از قسمت Events، رخداد مورد نظر را شبیه سازی کنید:
(function () {
    "use strict";

    document.addEventListener( 'deviceready', onDeviceReady.bind( this ), false );

    function onDeviceReady() {
        // Handle the Cordova pause and resume events
        document.addEventListener( 'pause', onPause.bind( this ), false );
        document.addEventListener('resume', onResume.bind(this), false);
        document.addEventListener('menubutton', onMenuButton.bind(this), false);
        document.addEventListener('backbutton', onBackButton.bind(this), false);
        //document.addEventListener('searchbutton', onResume.bind(this), false);
        //document.addEventListener('endcallbutton', onResume.bind(this), false);
        //document.addEventListener('offline', onResume.bind(this), false);
        //document.addEventListener('online', onResume.bind(this), false);
        //document.addEventListener('startcallbutton', onResume.bind(this), false);
        //document.addEventListener('volumedownbutton', onResume.bind(this), false);
        //document.addEventListener('volumeupbutton', onResume.bind(this), false);
        
        // TODO: Cordova has been loaded. Perform any initialization that requires Cordova here.
    };

    function onPause() {
        // TODO: This application has been suspended. Save application state here.
        alert("paused");
    };

    function onResume() {
        alert("resume");
    };
    function onMenuButton() {
        alert("menu");
    };

    function onBackButton() {
        alert("back button");
    };
   

} )();

.در مقالات آینده از افزونه‌های موجود، برای مدیریت رخداد‌های باتری سیستم استفاده خواهیم کرد



jQuery Mobile
جی کوئری موبایل، یک فریمورک (UI Framework) جدید با قابلیت استفاده‌ی آسان برای ساخت اپلیکیشن‌های چند سکویی موبایل است. با استفاده از این فریمورک شما قادر خواهید بود اپلیکیشن‌های موبایل بهینه شده برای اجرا بر روی تمام تلفن‌ها، دسکتاپ و تبلت‌ها را بسازید. علاوه بر این، جی کوئری موبایل می‌تواند یک فریمورک ایده آل برای توسعه دهند گان و طراحان وب که قصد ساخت اپلیکیشن‌های غنی وب برای موبایل را دارند، باشد.

 Supported Devices

  Phones/Tablets 
  Android 1.6+ 
  BlackBerry 5+ 
  iOS 3+ 
  Windows Phone 7 
  WebOS 1.4+ 
  Symbian (Nokia S60) 
  Firefox Mobile Opera Mobile 11+ 
  Opera Mini 5+ 
  Desktop browsers 
  Chrome 11+ 
  Firefox 3.6+ 
  Internet Explorer 7+ 
  Safari   


برای نصب jQuery Mobile کافی است دستورات  زیر را در package manager console ویژوال استودیو استفاده کنید:

PM>install-package jquery

PM>install-package jquery.mobile.rtl

بعد از دانلود فایل‌های مورد نظر خود، فولدری بنام jquery.mobile.rtl در ریشه پروژه ایجاد خواهد شد. به ترتیب فایل های rtl.jquery.mobile-1.4.0.css و rtl.jquery.mobile-1.4.0.js موجود در زیر شاخه‌های فلدر مذکور را به head و آخر body فایل index.html اضافه کنید.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>CordovaApp01</title>

    <!-- CordovaApp01 references -->
    <link href="css/index.css" rel="stylesheet" />
    <link href="jquery.mobile.rtl/css/themes/default/rtl.jquery.mobile-1.4.0.css" rel="stylesheet" />
</head>
<body>
    <div data-role="page" id="page1">
        <div data-role="header">
            <h1>اولین برنامه</h1>
        </div>
        <div data-role="content">
            <p>سلام من محتوای اولین برنامه هستم</p>
        </div>
        <div data-role="footer">
            <h1>من فوتر هستم</h1>
        </div>
    </div>
<!-- Cordova reference, this is added to your app when it's built. -->
<script src="scripts/jquery-2.1.3.min.js"></script>
    <script src="cordova.js"></script>
    <script src="scripts/platformOverrides.js"></script>
    <script src="scripts/index.js"></script>

    <script src="jquery.mobile.rtl/js/rtl.jquery.mobile-1.4.0.js"></script>
</body>
</html>
در تکه کد بالا ما یکی از ویجت‌های jQuery Mobile را استفاده کردیم و با استفاده از ویژگی data-role که برای div اصلی با page مقدار دهی شده است، یک  کانتینر (page container) برای ویجت page جی کوئری موبایل تعریف شده‌است.

نتیجه‌ی نهایی به شکل زیر خواهد بود:

در مقاله‌ی بعد به استفاده از plugin‌ها خواهیم پرداخت.

ادامه دارد...

مطالب
فرمت کردن اطلاعات نمایش داده شده به کمک Kendo UI Grid
پیشنیازهای بحث:
- «صفحه بندی، مرتب سازی و جستجوی پویای اطلاعات به کمک Kendo UI Grid »
- «استفاده از Kendo UI templates»

صورت مساله
می‌خواهیم به یک چنین تصویری برسیم؛ که دارای گروه بندی اطلاعات است، فرمت شرطی روی ستون قیمت آن اعمال شده و تاریخ نمایش داده شده در آن نیز شمسی است. همچنین برای مثال ستون قیمت آن دارای ته جمع صفحه بوده و به علاوه یک دکمه‌ی سفارشی به نوار ابزار آن اضافه شده‌است.


مباحث قسمت سمت سرور این مثال با مطلب «صفحه بندی، مرتب سازی و جستجوی پویای اطلاعات به کمک Kendo UI Grid» دقیقا یکی است. فقط یک خاصیت AddDate نیز در اینجا اضافه شده‌است.


تغییر نحوه‌ی نمایش pager

اگر به قسمت pager تصویر فوق دقت کنید، یک دکمه‌ی refresh، تعداد موارد هر صفحه و امکان وارد کردن دستی شماره صفحه، در آن پیش بینی شده‌است. این موارد را با تنظیمات ذیل می‌توان فعال کرد:
            $("#report-grid").kendoGrid({
                // ...
                pageable: {
                    previousNext: true, // default true
                    numeric: true, // default true
                    buttonCount: 5, // default 10
                    refresh: true, // default false
                    input: true, // default false
                    pageSizes: true // default false
                },


بومی سازی پیغام‌های گرید

پیغام‌های فارسی را که در تصویر فوق مشاهده می‌کنید، حاصل پیوست فایل kendo.fa-IR.js هستند:
 <!--https://github.com/loudenvier/kendo-global/blob/master/lang/kendo.fa-IR.js-->
<script src="js/messages/kendo.fa-IR.js" type="text/javascript"></script>


گروه بندی اطلاعات

برای گروه بندی اطلاعات در Kendo UI Grid دو قسمت باید تغییر کنند.
ابتدا باید فیلد پیش فرض گروه بندی در قسمت data source گرید تعریف شود:
            var productsDataSource = new kendo.data.DataSource({
                // ...
                group: { field: "IsAvailable" },
                // ...
            });
همین تنظیم، گروه بندی را فعال خواهد کرد. اگر علاقمند باشید که به کاربران امکان تغییر دستی گروه بندی را بدهید، خاصیت groupable را نیز true کنید.
$("#report-grid").kendoGrid({
// ...
groupable: true, // allows the user to alter what field the grid is grouped by
// ...
در این حالت با کشیدن و رها کردن یک سرستون، به نوار ابزار مرتبط با گروه بندی، گروه بندی گرید بر اساس این فیلد انتخابی به صورت خودکار انجام می‌شود.


اضافه کردن ته جمع‌های ستون‌ها

این ته جمع‌ها که aggregate نام دارند باید در دو قسمت فعال شوند:
            var productsDataSource = new kendo.data.DataSource({
                //...
                aggregate: [
                    { field: "Name", aggregate: "count" },
                    { field: "Price", aggregate: "sum" }
                ]
                //...
            });
ابتدا در قسمت data source مشخص می‌کنیم که چه تابع تجمعی قرار است به ازای یک فیلد خاص استفاده شود.
سپس این متدها را می‌توان مطابق فرمت hash syntax قالب‌های Kendo UI در قسمت footerTemplate هر ستون تعریف کرد:
            $("#report-grid").kendoGrid({
                // ...
                columns: [
                    {
                        field: "Name", title: "نام محصول",
                        footerTemplate: "تعداد: #=count#"
                    },
                    {
                        field: "Price", title: "قیمت",
                        footerTemplate: "جمع: #=kendo.toString(sum,'c0')#"
                    }
                ]
                // ...
            });


فرمت شرطی اطلاعات

در ستون قیمت، می‌خواهیم اگر قیمتی بیش از 2490 بود، با پس زمینه‌ی قهوه‌ای و رنگ زرد نمایش داده شود. برای این منظور می‌توان یک قالب Kendo UI سفارشی را طراحی کرد:
    <script type="text/x-kendo-template" id="priceTemplate">
        #if( Price > 2490 ) {#
        <span style="background:brown; color:yellow;">#=kendo.toString(Price,'c0')#</span>
        #} else {#
        #= kendo.toString(Price,'c0')#
        #}#
    </script>
سپس نحوه‌ی استفاده‌ی از آن به صورت ذیل خواهد بود:
            $("#report-grid").kendoGrid({
                //...
                columns: [
                    {
                        field: "Price", title: "قیمت",
                        template: kendo.template($("#priceTemplate").html()),
                        footerTemplate: "جمع: #=kendo.toString(sum,'c0')#"
                    }
                ]
                //...
            });
توسط متد kendo.template امکان انتساب یک قالب سفارشی به خاصیت template یک ستون وجود دارد.


فرمت تاریخ میلادی به شمسی در حین نمایش

برای تبدیل سمت کلاینت تاریخ میلادی به شمسی از کتابخانه‌ی moment-jalaali.js کمک گرفته شده‌است:
 <!--https://github.com/moment/moment/-->
<script src="js/cultures/moment.min.js" type="text/javascript"></script>
<!--https://github.com/jalaali/moment-jalaali-->
<script src="js/cultures/moment-jalaali.js" type="text/javascript"></script>
پس از آن تنها کافی است متد فرمت این کتابخانه را در قسمت template ستون تاریخ و توسط hash syntax قالب‌های Kendo UI بکار برد:
            $("#report-grid").kendoGrid({
                //...
                columns: [
                    {
                        field: "AddDate", title: "تاریخ ثبت",
                        template: "#=moment(AddDate).format('jYYYY/jMM/jDD')#"
                    }
                ]
                //...
            });


اضافه کردن یک دکمه به نوار ابزار گرید

نوار ابزار Kendo UI Grid را نیز می‌توان توسط یک قالب سفارشی آن مقدار دهی کرد:
            $("#report-grid").kendoGrid({
                // ...
                toolbar: [
                    { template: kendo.template($("#toolbarTemplate").html()) }
                ]
                // ...
            });
برای نمونه toolbarTemplate فوق را به نحو ذیل تعریف کرده‌ایم:
    <script>
        // این اطلاعات برای تهیه خروجی سمت سرور مناسب هستند
        function getCurrentGridFilters() {
            var dataSource = $("#report-grid").data("kendoGrid").dataSource;
            var gridState = {
                page: dataSource.page(),
                pageSize: dataSource.pageSize(),
                sort: dataSource.sort(),
                group: dataSource.group(),
                filter: dataSource.filter()
            };
            return kendo.stringify(gridState);
        }
    </script>

    <script id="toolbarTemplate" type="text/x-kendo-template">
        <a class="k-button" href="\#" onclick="alert('gridState: ' + getCurrentGridFilters());">نوار ابزار سفارشی</a>
    </script>
دکمه‌ی اضافه شده، وضعیت فیلتر data source متصل به گرید را بازگشت می‌دهد. برای مثال مشخص می‌کند که در چه صفحه‌ای با چه تعداد رکورد قرار داریم و همچنین وضعیت مرتب سازی، فیلتر و غیره چیست. از این اطلاعات می‌توان در سمت سرور برای تهیه‌ی خروجی‌های PDF یا اکسل استفاده کرد. وضعیت فیلتر اطلاعات مشخص است. بر همین مبنا کوئری گرفته و سپس می‌توان نتیجه‌ی آن‌را تبدیل به منبع داده تهیه خروجی مورد نظر کرد.



کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید:
KendoUI05.zip
مطالب
آپلود فایل ها با استفاده از PlUpload در Asp.Net Mvc
امروزه بازار برنامه‌های تماما ajax و بدون Postback  شدن صفحه بسیار داغ میباشد که از این موارد میتوان به برنامه‌های تحت وب گوگل اشاره کرد. (gmail  ، googlePlus  ، Google Reader)
در این میان یکی از دغدغه‌های توسعه دهندگان وب ، آپلود فایل‌ها به صورت آنی (مثل attach files گوگل) میباشد. برای حل این مسئله ، ابزارها و پلاگین‌های متعددی وجود دارد که در اینجا به 10 تا از پلاگین‌های Jquery  اشاره شده است.
به شخصه با پلاگین Uploadify کار کرده ام و از استفاده از آن راضی هستم ولی همین دیشب برای قسمتی از یک پروژه نیاز
به ابزاری جهت آپلود فایل‌ها با امکانات مورد نظرم داشتم که به PlUpload برخورد کردم. 

از امکاناتی که این ابزار در اختیار شما قرار میدهد :
- یک اینترفیس زیبا جهت آپلود و افزودن فایل ها
- پشتیبانی از زبان‌های مختلف و همین طور زبان فارسی
- امکان استفاده از قالب Jquery UI
- Drag&Drop  برای مرورگرهایی که از Html5  پشتیبانی میکنند

حال که با امکانات این ابزار بیشتر آشنا شدید بریم سراغ استفاده از این ابزار در asp.net mvc  :)
ابتدا پروژه را از اینجا دانلود کنید. سپس یک پروژه‌ی جدید  mvc 3  بسازید (از نوع Internet Application و با نام دلخواه). سپس پوشه‌ی plupload  را در قسمت سلوشن برنامه کپی کنید.
حال در فایل Views->Shared->_Layout.cshtml  ، تگ head  را جهت افزودن امکانات پلاگین این گونه تغییر دهید :

    <title>@ViewBag.Title</title>

    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <link href="../../plupload/js/jquery.plupload.queue/css/jquery.plupload.queue.css" rel="stylesheet" />
    <script src="@Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
    <script type="text/javascript" src="http://bp.yahooapis.com/2.4.21/browserplus-min.js"></script>

    <script src="../../plupload/js/plupload.full.js"></script>
    <script src="../../plupload/js/jquery.plupload.queue/jquery.plupload.queue.js"></script>
    <script src="../../plupload/js/i18n/fa.js"></script>

نکته : فایل fa.js  که جهت استفاده از زبان فارسی در اینترفیس آپلود فایل‌ها میباشد، که وجود آن در آدرس واضح میباشد.
سپس به فایل Views->Home->Index.cshtml بروید و آن را این گونه دوباره نویسی کنید :
 @{
    ViewBag.Title = "Uploading Files using PlUpload";
}
<h2>@ViewBag.Message</h2>

@using (Html.BeginForm("Post", "home", FormMethod.Post,
    new { enctype = "multipart/form-data" }))
{
    <div id="uploader">
        <p>You browser doesn't have Flash, Silverlight, Gears, BrowserPlus or HTML5 support.</p>
    </div>
}

<script>
    $(function () {

        $("#uploader").pluploadQueue({
            // General settings
            runtimes: 'html5,gears,flash,silverlight,browserplus,html4',
            url: '@Url.Action("Upload" , "Home")',
            max_file_size: '10mb',
            chunk_size: '1mb',
            unique_names: true,

            // Resize images on clientside if we can
            resize: { width: 320, height: 240, quality: 90 },

            // Specify what files to browse for
            filters: [
                { title: "Image files", extensions: "jpg,gif,png" },
                { title: "Zip files", extensions: "zip" }
            ],

            // Flash settings
            flash_swf_url: '/plupload/js/plupload.flash.swf',

            // Silverlight settings
            silverlight_xap_url: '/plupload/js/plupload.silverlight.xap'
        });
    });
</script>
توضیحات و نکات :
- جهت آپلود فایل‌ها تگ enctype = "multipart/form-data" را فراموش نکنید.
- در قسمت مقداردهی به ویژگی‌های Plupload  ، قسمت runtime  به صورت ترتیبی کار میکند لذا اگر اولی پشتیبانی نشود سراغ دومی میرود و اگر دومی نشود سومی و ... در صفحه‌ی اول سایت PlUpload ، موارد پشتیبانی شده توسط تکنولوژی‌ها آورده شده است لذا این ترتیب را ترتیب مناسبی میبینم و اگر اولین مورد html5 باشد امکان Drag&Drop وجود خواهد داشت.
خود سایت PlUpload  داکیومنت خیلی خوبی جهت توضیح موارد مختلف دارد لذا توضیح دوباره لازم نیست.
همان طور که در ویژگی url  مشاهده میکنید به کنترلر Home  و اکشن متود Upload اشاره شده است که طرز کار به این گونه است که هر بار که یک فایل آپلود میشود درخواستی به این آدرس و محتوای فایل در قسمت Request.Files ارسال میشود و همین طور نام فایل که unique ارسال میشود و chunk که تیکه‌های فایل است(پست میشود).
پس اکشنی با نام Upload  در کنترلر HomeController بسازید :
        [HttpPost]
        public ActionResult Upload(int? chunk, string name)
        {
            var fileUpload = Request.Files[0];
            var uploadPath = Server.MapPath("~/App_Data");
            chunk = chunk ?? 0;
            using (var fs = new FileStream(Path.Combine(uploadPath, name), chunk == 0 ? FileMode.Create : FileMode.Append))
            {
                if (fileUpload != null)
                {
                    var buffer = new byte[fileUpload.InputStream.Length];
                    fileUpload.InputStream.Read(buffer, 0, buffer.Length);
                    fs.Write(buffer, 0, buffer.Length);
                }
            }
            return Content("chunk uploaded", "text/plain");
        } 
توضیحات : ابتدا فایل مورد نظر از قسمت Request.Files واکشی میشود و سپس فایل را در پوشه App_Data ذخیره میکند. (یکی از چندین روش ذخیره سازی که مطالعه در این قسمت به خواننده واگذار میشود.)

حال برنامه را اجرا کنید و از این ابزار لذت ببرید:) 
نکته : قسمت فارسی ساز اونو تغییر دادم چون که ترجمه‌ی فارسی خودش یه سری نقایصی داشت که گویا از کار با google translate به وجود اومده بود!
اشتراک‌ها
jQuery 3.5.0 منتشر شد

The main change in this release is a security fix, and it’s possible you will need to change your own code to adapt. Here’s why: jQuery used a regex in its jQuery.htmlPrefilter method to ensure that all closing tags were XHTML-compliant when passed to methods. For example, this prefilter ensured that a call like jQuery("<div class='hot' />") is actually converted to jQuery("<div class='hot'></div>"). Recently, an issue was reported that demonstrated the regex could introduce a cross-site scripting (XSS) vulnerability. 

jQuery 3.5.0 منتشر شد
اشتراک‌ها
Chrome 80 به همراه تغییرات پردازش کوکی‌ها منتشر شد

Third Party Site Cookies

In August 2020, Google announced their 'Privacy Sandbox' initiative, which aims to preserve and protect user's privacy. The cookie processing change is part of this initiative. An official  blog post sheds some light on it.

Google will drop support for third-party cookies in the Chrome browser in two years. Also, the company will start limiting cross-site tracking by enforcing its new SameSite rules . This has already happened in Chrome 80.

The SameSite-by-default and SameSite=None-requires-Secure behaviors will begin rolling out to Chrome 80 Stable for an initial limited population starting the week of February 17, 2020. 

Chrome 80 به همراه تغییرات پردازش کوکی‌ها منتشر شد
اشتراک‌ها
WebWindow؛ جایگزین الکترون برای برنامه‌های NET Core.

My last post investigated ways to build a .NET Core desktop/console app with a web-rendered UI without bringing in the full weight of Electron. This seems to have interested a lot of people, so I decided to upgrade it to newer technologies and add cross-platform support.

The result is a little NuGet package called WebWindow that you can add to any .NET Core console app. It can open a native OS window (Windows/Mac/Linux) containing web-based UI, without your app having to bundle either Node or Chromium. 

WebWindow؛ جایگزین الکترون برای برنامه‌های NET Core.
اشتراک‌ها
نگاهی به بهبودهای کارآیی در NET Core. و ASP.NET Core 3.0

What’s new for performance in .NET Core and ASP.NET Core 3.0 – Ben Adams
One of the biggest advantages of using .NET Core (besides cross-platform support) is the drastic improvements in performance. Because the .NET Core team was able to make minor breaking changes in the runtime and Base Class Library (BCL), lots of stuff was implemented much more efficiently. In this session Ben will dive into the performance improvements in .NET Core in the 3.0 release: runtime changes, JIT changes, intrinsics and a deep dive into some of the improvements making it the best release yet!

نگاهی به بهبودهای کارآیی در NET Core. و ASP.NET Core 3.0