An exception of type 'System.Data.SqlClient.SqlException' occurred in EntityFramework.SqlServer.dll but was not handled in user code Additional information: CREATE FULLTEXT CATALOG statement cannot be used inside a user transaction.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; namespace Image2Base64Converter { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void btnOpen_Click(object sender, EventArgs e) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Multiselect = false; ofd.Filter = "Pictures |*.bmp;*.jpg;*.jpeg;*.png"; if (ofd.ShowDialog(this) == DialogResult.OK) { txtImagePath.Text = ofd.FileName; } } private void btnConvert_Click(object sender, EventArgs e) { if (!File.Exists(txtImagePath.Text)) { MessageBox.Show("File not found!"); return; } btnCopy.Enabled = false; btnConvert.Enabled = false; txtOutput.Enabled = false; BackgroundWorker bworker = new BackgroundWorker(); bworker.DoWork += (o, a) => { string header = "data:image/{0};base64,"; header = string.Format(header, txtImagePath.Text.GetExtension()); StringBuilder data = new StringBuilder(); byte[] buffer; BinaryReader head = new BinaryReader( new FileStream(txtImagePath.Text, FileMode.Open)); buffer = head.ReadBytes(6561); while (buffer.Length > 0) { data.Append(Convert.ToBase64String(buffer)); buffer = head.ReadBytes(6561); } head.Close(); head.Dispose(); this.Invoke(new Action(() => { txtOutput.ResetText(); txtOutput.AppendText(header); txtOutput.AppendText(data.ToString()); btnCopy.Enabled = true; btnConvert.Enabled = true; txtOutput.Enabled = true; })); }; bworker.RunWorkerAsync(); } private void btnCopy_Click(object sender, EventArgs e) { Clipboard.SetText(txtOutput.Text); } } public static class ExtensionMethods { public static string GetExtension(this string str) { string ext = string.Empty; for (int i = str.Length - 1; i >= 0; i--) { if (str[i] == '.') { break; } ext = str[i] + ext; } return ext; } } }
AngularJS #2
//استفاده از امکانات Razor لیست مطالب //استفاده از امکانات Angular <div ng-controller="PostController"> <ul> <li ng-repeat="item in ListOfItems"> {{item.Title}} </li> </ul> </div>
PostApp.controller('PostController', function ($scope, $http, postServices) { //... $scope.ListOfItems = postServices.GetPosts(post) .success(function (data) { $scope.ListOfItems = data; }); //... }
کار کردن با مسیریابی برای یک پروژه ساده ، نیاز به طراحی پیچیده ندارد. مسیریابی پیش فرض موجود در فایل RoutConfig.cs برای کارهای ابتدایی کافیست. اما اگر کمی کار پیچیده شود و صفحات مختلفی با منطقهای متفاوتی ایجاد کنیم، ممکن است با مشکل روبرو شویم. در MVC5 به کمک دخالت ویژگیها در مسیریابی، کار ساده شده است اما در MVC4 و قبل از آن چه باید کرد؟ پیش از بسط مساله، ابتدا این سوال را پاسخ میدهیم که چگونه صفحهی start پروژه انتخاب میشود؟
مسیریابی پیش فرض یک پروژه MVC به شکل زیر است :
routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } );
وقتی یک پروژه MVC را بررسی کنید، مشاهده میکنید که در شاخهی اصلی آن، فایل index یا default وجود ندارد و اصولا منطق کار با اکشنها و صدا زدن آنها و دیدن پاسخها توسط View، این اتفاق را توجیه میکند. پس وقتی درخواستی به سمت شاخهی اصلی ما فرستاده میشود، مسیریابی وارد عمل میشود. وقتی پروژه را اجرا میکنید، متد RegisterRoutes از شیءایی که از کلاس RoutingConfig.cs ساخته شده (به فایل global.asax نگاه کنید) فراخوانی میشود و شیء Routes از " قالب آدرس " هایی که ما تعیین کرده ایم پُر میشود. بخشی از فایل RoutConfig.cs را در تصویر زیر میبینیم.
به Url کد فوق نگاه کنید که در حقیقت آدرسی نسبی است. آدرس ما به طور کامل به شکل زیر قابل تعریف است:
http://mydomain.com/{controller}/{action}/{id}
واضح است که درخواست اولیه به سایت ما دارای بخشهای controller و action و بخش اختیاری id نیست. پس به شکل پیشفرض بر اساس آنچه در جلوی خصوصیت defaults نوشته شده است، فراخوانی خواهد شد. یعنی اکشن Index از داخل کنترلر Home صدا زده میشود و چون id نداریم، هیچ نوع id به متد (اکشن) index ارسال نخواهد شد.
یک روتینگ دیگر به شکل زیر در بالای کد فوق اضافه کنید :
routes.MapRoute( name: "Default1", url: "{controller}/{action}/{id}", defaults: new { controller = "News", action = "Index", id = UrlParameter.Optional } );
حتما متوجه شدید که اینبار با اجرای پروژه، متد(اکشن) Index از کنترلر News فراخوانی خواهد شد. در حقیقت اولین روتینگ ساخته شده همان چیزیست که Asp.NET MVC پس از دریافت ریکوئست به آن رجوع میکند. دقت کنید که مقدار name در دو MapRouting فوق متفاوت است.
اما این اسمها(name ها) به چه دردی میخورند؟
قبل از توضیح در این مورد، default1 را از پروژه حذف میکنیم و با همان MapRouting پیش فرض به کار خود ادامه میدهیم. ابتدا مروری بر عملکرد MapRouting انجام میدهیم.
همانطور که میدانید برای ایجاد یک لینک از طریق ویوی Razor از چیزی شبیه دستور زیر میتوانیم استفاده کنیم:
<a href="@Url.Action("post", "News", new { id = @item.PostName }, null)"> @item.PostTitle </a>
فرض کنید یک سایت خبری داریم و گروههای مختلف آن شامل مطالب متفاوتی هستند. کنترلری به نام News ایجاد کردهایم، که دارای یک اکشن به نام post است که نام مطلب را دریافت کرده و یک View به ما برمیگرداند. بخش هایی از صفحه اول را در تصاویر زیر میبینید. تصویر آخر، بخش پایین صفحه اخبار است، که بوسیله لینک (لینک های) موجود صفحات جابجا میشوند.
وقتی کد فوق در یک صفحه فراخوانی میشود، کالکشن Routes بررسی میشود و لینک مورد نظر، با توجه به ورودیهای Url.Action ساخته میشود. ما یک MapRouting به نام Default در کالکشن Routes داریم، پس Url.Action لینک زیر را میسازد:
http://mydomain.com/news/post/نام-مقاله
استفاده از MapRouting مثل یک جعبه است که ورودی آن مقادیر موجود در Url.Action میباشد و آنچه به ما بر میگرداند یک آدرس برای فراخوانی اکشن مورد نیاز است. (آخرین پارامتر، null قرار داده شده که برای تعیین نوع پروتکل استفاده میشود که http یا https میتواند باشد.)
برای فراخوانی صفحهی اخبار (لیست اخبار) میتوانید دستور زیر را بنویسید :
@Html.ActionLink("اخبار", "Index", "News", null, new { @class="btn btn-success"})
این دستور تگ anchor را نیز میسازد و به کمک MapRouting آدرسی شبیه آدرس زیر را به ما بر میگرداند. (به تفاوت دو دستور دقت کنید):
<a href="/News">اخبار</a>
واضح است که چون MapRouting یک حالت پیش فرض درونی دارد که دارای اکشن دیفالت index است، پس ActionLink اگر ببیند لینکش در صفحه قرار است به شکل /news/index تعریف شود خود بخود بخش index را حذف میکند.
فرض کنید بعد از کلیک روی لینک فوق، اکشن index ، یک آبجکتِ لیستی با 10 خبر آخر، ایجاد میکند. پس ما میخواهیم قابلیت صفحه بندی را برای لیست اخبار فعال کنیم و در هر صفحه 10 مطلب را نمایش دهیم. مشکل از همینجا آغاز میشود. MapRouting فعلی جوابگوی ما نخواهد بود و آدرس را به شکل زیر نمایش میدهد.
<a href="/News/index?pid=2">صفحه بعد</a>
و آنچه ما در View نوشتهایم چیزی شبیه کد زیر است :
@Html.ActionLink("صفحه بعد", "index", "News", new { pid = …. }, null)
مشکلی در ارجاع به صفحات وجود ندارد و با کلیک روی لینک "صفحه بعد" مقدار عدد 2 به اکشن index ارسال میشود و اگر کد نویسی را برای take و skip کردن لیست، درست انجام شده باشد، نتیجه مورد نظر نمایش داده خواهد شد. اما آدرس فوق آدرس زیبایی نیست. اولین فکری که به ذهن برنامه نویس میرسد، ایجاد یک مسیریابی دیگر است. فکر درستیست؛ اما اگر چند بار دیگر این اتفاق بیفتد و در بخش هایی از برنامه نیاز به روتینگ پیدا کنید و روتینگهای جدید ایجاد کنید متوجه خواهید شد که مدیریت این MapRouting ها کار خسته کننده و طاقت فرسایی خواهد شد، مخصوصا اگر بدانید که فقط مجاز به استفاده از یک پارامتر optional در هر MapRouting هستید! دست شما کاملا بسته است. لینکهای بالای سایت را اصلاح میکنید ولی لینکهای پایین سایت خراب میشوند و بالعکس.
به هر حال MapRouting زیر را به RoutConfig.cs اضافه میکنیم :
اینکار بستگی به پروژهی شما دارد، مپ روتینگ فوق این مزیت را دارد که با مپ روتینگهای دیگر به سختی قاطی میشود! یعنی حتا اگر قبل از مپ روتینگ دیفالت نوشته شود برنامه با مشکل مواجه نخواهد شد، چون اصلا شکل درخواست اولیه به سایت، چیزی شبیه این آدرس نیست. اما خاص بودن آن و همچنین نوع بهره گیری از آن با کمک Action یا ActioLink شاید شما را سردرگم خواهد کند.- اسم این MapRouting ، دیگر Default نیست.routes.MapRoute("PostPaging", "{controller}/{action}/{id}/{pid}", defaults: new { controller = "News", action = "Index", id = "all", pid = UrlParameter.Optional } );
- یک پارامتر pid اضافهتر از MapRouting اولی دارد.
- pid به عنوان یک پارامتر اختیاری تعریف شده است، پس "قالب آدرس" بسیار شبیه مپ روتینگ قبلی است.
- مقدار id اختیاری نیست، چون قرار است در آینده بتوانیم گروههای مختلف موجود در بخش اخبار را صفحه بندی کنیم و قرار نیست پشت سر هم MapRouting ایجاد کنیم و کافیست به جای id اسم گروه را بنویسیم. در حالتیکه اسمی از گروه درلینکهایمان نبرده باشیم به شکل پیشفرض all قرار داده میشود که یعنی کل اخبار مد نظر است. (در اکشن مربوطه باید این تصمیمات را لحاظ کنیم)
- حتما این MapRouting را بعد از MapRouting اولیه بنویسید، کمی پیشتر، علت این امر توضیح داده شد و گفته شد اولین چیزی که MVC پس از درخواست ما میبیند به عنوان Routing بررسی میکند (درخواست اولیه) و چون ساختار MapRouting فوق تا اندازه ای شبیه ساختار Default MapRouting است ممکن است با فراخوانی سایت مشکل ایجاد شود.
- میتوانید MapRouting را کمی خاصتر هم بنویسیم :
routes.MapRoute("NewsPaging", "News/index/{id}/{pid}", defaults: new { controller = "News", action = "Index", id = "all", pid = UrlParameter.Optional } );
اما مشکل این MapRouting ها چیست؟
درخواست به سایت آمده و قرار است سایت بارگذاری شود؛ ترتیب زیر در شیء routes ثبت شده است :
در صفحه اول ما لینکی به شکل زیر گذاشته ایم :
@Html.ActionLink("اخبار", "Index", "News", null, new { @class="btn btn-success"})
در صفحه اخبار، لینک "صفحه بعد" وجود دارد ، آیا این لینک به شکل صحیح نمایش داده میشود؟ خیر! نتیجه کار را ببینید :
<a href="/News/index?pid=2">صفحه بعد</a>
به نظر میرسد یک طرح دقیق برای آدرس دهی شاید این مسائل را حل کند ولی نه وقت داریم و نه اعصاب.
1. ساخته شدن لینکها توسط هلپرها (که از مپ روتینگهای ثبت شده ما پیروی میکند)
2. واکنش پروژه به درخواستهای دریافتی و هدایت آن به اکشنهای مورد نظر که کاملا به ترتیب ثبت مپ روتینگها در کالکشن Routes بستگی دارد .
@Html.RouteLink("صفحه بعد", "PostPaging", new { action = "cat", controller = "News", id =. .., pid = ...})
نتیجه برای لینک موجود در صفحه اخبار چیزی شبیه شکل زیر خواهد شد :
<a href="/News/cat/all/2">صفحه بعد</a>
@Html.RouteLink("اخبار", "Default", new { controller = "news" }) @Html.RouteLink("درباره ما", "pages", new RouteValueDictionary(new { controller="Page", action="Index", pagename="درباره-ما"}), new Dictionary<string, Object> { { "data-toggle", "popover" }, { "data-placement", "top" } })
همانطور که میبینید در RoutLink
اولی، اخبار را به کمک MapRouting
با نام default
بازنویسی میکنیم و نتیجه چیزی شبیه کد زیر خواهد شد :
<a href="/News">اخبار</a>
در RoutLink دومی اولا از یک RoutValueDictionary به جای یک آبجکت ساده استفاده کرده ایم و مقادیر را به شکل فوق به کنترلر و اکشن و ...نسبت داده ایم ثانیا برای بخش HTML نیز پراپرتیها را به کمک یک دیکشنری ارسال میکنیم، به خاطر وجود "-" در یکی از خواص، راه دیگری غیر از اینکار نداریم.
اما دقت کنید که از یک MapRouting جدید استفاده کردیم که نامش pages است،
این MapRoutnig را قبل از دیگر Routing ها مینویسیم؟ وسط دو MapRouting قبلی مینویسیم؟ آخر MapRouting ها مینویسیم؟ آیا فرقی میکند؟ اگر سریع بگوییم خــیر! اشتباه کرده ایم. واقعا فرق میکند.
دقت کنید موضوع MapRouting فقط ایجاد یک لینکتر و تمیز نیست؛ RoutLink یک لینک تمیز بر اساس مپ روتینگی که نامش برده شده ایجاد میکند
اما تضمین نمیکند که با کلیک بر روی لینک به هدف برسیم و به خطای 404 برخورد نکنیم! اگر روی لینک کلیک کنید آدرس شروع به تفسیر شدن
میکند و این تفسیر اصلا ربطی به نامی که به RoutLink داده ایم ندارد و ترتیب موجود در کالکشن ایجاد شده در RoutConfig تعیین کننده است.(آبجکت Routes ) اگر MapRouting فوق را در انتهای بقیه بگذاریم صفحه اول لود میشود ولی با کلیک
روی "درباره ما" صفحه پیغام خطا خواهد داد.
باید به یاد
داشته باشیم برای اجرای درخواست (کلیک روی لینک)، آنچه برای ASP.NET MVC اهمیت دارد، ترتیب قرار گیری MapRouting ها در RouteRegister است و
ما به کمک RoutLink تنها
مشکل ساخت لینکها بر اساس قالب MapRouting مورد
نظرمان را حل کردیم و این به ما تضمینی برای هدایت آن لینک به مکان درست را نخواهد
داد.
اگر ترتیب به شکل زیر باشد :
1باشد. درخواست اولیه برای بالا آمدن سایت به مشکل برخورد نمیکند چون همان مپ روتینگ 1 اجرا میشود. اما مشکل فوق به وجود خواهد آمد و خطای 404 با کلیک بر روی "درباره ما" نمایش داده خواهد شد چون با کلیک روی "درباره ما" مپ روتینگ شماره 1 وارد عمل میشود.
2
3
اگر ترتیب به شکل زیر باشد :
3آیا اصلا صفحه اول سالم لود خواهد شد؟ خیر! درخواست نسبی " / " (یا به طور کامل http://mydomain.com ) شماره 3 را به خیر پشت سر میگذارد، چون اصلا چیزی به نام page در آدرس وجود ندارد که از این MapRouting بخواهد پیروی کند. اما در شماره 2 گیر میافتد چون این فرمت را حفظ کرده است :
2
1
"{controller}/{action}/{id}/{pid}"
اگر ترتیب به شکل زیر باشد :
3این همان چیزیست که مد نظر ماست. اولا 1 قبل از 2 است و صفحه اول برای لود شدن به مشکل برخورد نمیکند.
1
2
ثانیا وقتی روی "درباره ما" کلیک میکنیم همان شماره 3 فراخوانی میشود و بقیه مپ روتینگها اعمال نمیشوند.
بارگذاری یک یوزرکنترل با استفاده از جیکوئری
برای دات نت 2 کمی کار شما بیشتر میشود چون در VS2008 خیلی از موارد را خودکار اضافه میکند.
در این حالت یا باید ASP.NET AJAX Extensions روی سرور نصب باشد یا dll های آنرا به صورت دستی به دایرکتوری bin برنامه اضافه کنید و یک ارجاع از آنها را نیز به برنامه اضافه کنید.
همچنین وب کانفیگ را نیز باید ویرایش کنید تا این تغییر در آن لحاظ شود. (در غیر اینصورت کار نمیکند)
یک نکته دیگر هم اینکه بجای msg.d شما فقط msg را در asp.net 2 دارید و این d از 3.5 به بعد اضافه شده (در قسمت success و هنگام دریافت پاسخ از سرور توسط jQuery).
گریس مانکی یکی از افزونههای فایرفاکس است که توسط آن میتوان اسکریپتهایی را بر روی صفحات وب در حال مشاهده، جهت تغییر آنها اجرا کرد. این نوع تغییرات بیشتر در جهت بالا بردن کارآیی یا خوانایی سایتها صورت میگیرد. مثلا بررسی وجود لینکهای ارائه شده در یک صفحه وب (قبل از اینکه به آنها رجوع کنیم، مشخص شود که آیا وجود دارند یا خیر) و هزاران مثال شبیه به این که در سایت اسکریپتهای آن قابل دریافت هستند.
گریس مانکی به خودی خود کار خاصی را انجام نمیدهد و فقط میزبان اجرایی اسکریپتهایی است که برای آن تهیه شده اند. به این اسکریپتها user scripts گفته میشود و جهت تهیه آنها از زبان جاوا اسکریپت استفاده میگردد.
مطابق اصول نامگذاری آن، فایل این اسکریپتها حتما باید به .user.js ختم شود تا توسط افزونه گریس مانکی قابل شناسایی باشد.
اسکریپت سلام دنیای گریس مانکی!
// ==UserScript==
// @name Hello World
// @namespace http://diveintogreasemonkey.org/download/
// @description example script to alert "Hello world!" on every page
// @include *
// @exclude http://diveintogreasemonkey.org/*
// @exclude http://www.diveintogreasemonkey.org/*
// ==/UserScript==
alert('Hello world!');
در پایان مثلا نام آنرا hello.user.js خواهیم گذاشت (همانطور که ذکر شد قسمت user.js آن باید رعایت شود). برای نصب آن فقط کافی است این فایل را به درون پنجره فایر فاکس کشیده و رها کنیم (drag & drop).
استفاده از jQuery در اسکریپتهای گریس مانکی
عمده کاربردهای اسکریپتهای گریس مانکی در جهت اعمال تغییرات بر روی document object model صفحه (DOM) هستند. کتابخانه jQuery اساسا برای این منظور تهیه و بهینه سازی شده است.
مثال:
فرض کنید قصد داریم به نتایج خروجی جستجوی گوگل، fav icon سایتهای یافت شده را اضافه کنیم (در کنار هر لینک، آیکون سایت مربوطه را نمایش دهیم). گوگل اینکار را انجام نداده است. اما ما علاقمند هستیم که این قابلیت را اضافه کنیم!
// ==UserScript==
// @name Google FavIcon
// @namespace http://userscripts.org
// @description Shows favicon for Google searches results
// @include http://*.google.*/search?*
// ==/UserScript==
loadJquery();
$(document).ready(function(){
$("h3.r > a.l").each(function(){
var $a = $(this);
var href = $a.attr("href");
var domain = href.replace(/<\S[^><]*>/g, "").split('/')[2];
var image = '<img src="http://' + domain + '/favicon.ico" style="border:0;padding-right:4px;" />';
//GM_log(">image:> " + image);
$(this).prepend(image);
});
});
دریافت سورس این مثال
در اینجا ابتدا لینکهای حاصل از جستجو پیدا میشوند. سپس نام دومین مربوطه استخراج میگردد (با استفاده از regular expressions) و در ادامه از این نام دومین یک آدرس استاندارد http://domain/favico.ico ساخته شده و از آن یک تگ img درست میشود. در آخر این تگ به قبل از لینکهای گوگل اضافه میشود.
شاید سؤال بپرسید از کجا مشخص شد که باید به دنبال h3.r > a.l گشت؟ به تصویر زیر دقت نمائید (نمایی است از یکی از تواناییهای افزونه fireBug فایرفاکس).
هنگامیکه صفحه بارگذاری شد، بر روی آیکون سوسک موجود در status bar فایرفاکس کلیک کنید تا FireBug ظاهر شود (البته من اینجا سوسک دیدم شاید موجود دیگری باشد :) )
سپس بر روی دکمه inspect در نوار ابزار آن کلیک کنید. در همین حال اشارهگر ماوس را به یکی از لینکهای نتیجه جستجوی گوگل نزدیک کنید. بلافاصله تگها و کلاسهای مورد استفاده آن به شکل زیبایی ظاهر خواهند شد. به این صورت صرفه جویی قابل ملاحظهای در وقت صورت خواهد گرفت.
نتیجه اجرای اسکریپت فوق (پس از نصب) به صورت زیر است:
نکته: نحوه دیباگ کردن اسکریپتهای گریس مانکی
اگر نیاز به مشاهده مقدار متغیرها در لحظه اجرای اسکریپت داشتید، یکی از راهحلهای موجود استفاده از تابع GM_log مربوط به API گریس مانکی است که خروجی آنرا در قسمت messages مربوط به error console فایرفاکس میتوان دید.
<div>نام و نام خانوادگی: {{name}}</div> <div>معدل: {{avg()}}<div>
scope.nums=[19,20,17,16,15,18,19]; scope.name='بهنام محمدی'; scope.avg= function(){ return scope.nums.reduce(function(previousValue, currentValue) { return previousValue + currentValue; })/scope.nums.length; }
نام و نام خانوادگی: بهنام محمدی معدل:17.71
var nums=[19,20,17,16,15,18,19]; scope.name='بهنام محمدی'; scope.avg= function(){ return nums.reduce(function(previousValue, currentValue) { return previousValue + currentValue; })/nums.length; }
<div> @(Html.Kendo().TreeView() .Name("treeview") .TemplateId("treeview-template") .HtmlAttributes(new { @class = "demo-section" }) .DragAndDrop(true) .BindTo(Model.Where(e=>e.ParentFolderID==null).OrderBy(e=>e.Order), mappings => { mappings.For<DAL.Folder>(binding => binding .ItemDataBound((item, folder) => { item.Text = folder.FolderName; item.SpriteCssClasses = "folder"; item.Expanded=true; item.Id = folder.FolderID.ToString(); }) .Children(folder => folder.Folder1)); mappings.For<DAL.Folder>(binding => binding .ItemDataBound((item, folder) => { item.Text = folder.FolderName; item.SpriteCssClasses = " folder"; item.Expanded = true; item.Id = folder.FolderID.ToString(); })); }) ) </div>
<style type="text/css" scoped> .demo-section { width: 200px; } #treeview .k-sprite ,#treeview2 .k-sprite { background-image: url("@Url.Content("/Content/kendo/images/coloricons-sprite.png")"); } .rootfolder { background-position: 0 0; } .folder { background-position: 0 -16px; } .pdf { background-position: 0 -32px; } .html { background-position: 0 -48px; } .image { background-position: 0 -64px; } .delete-link,.edit-link { width: 12px; height: 12px; overflow: hidden; display: inline-block; vertical-align: top; margin: 2px 0 0 3px; -webkit-border-radius: 5px; -mox-border-radius: 5px; border-radius: 5px; } .delete-link{ background: transparent url("@Url.Content("/Content/kendo/images/close.png")") no-repeat 50% 50%; } .edit-link{ background: transparent url("@Url.Content("/Content/kendo/images/edit.png")") no-repeat 50% 50%; } </style>
<a href="#" id="serialize">ذخیره</a>
$('#serialize').click(function () { serialized = serialize(); window.location.href = "Folder/SaveMenu?serial=" + serialized + "!"; }); function serialize() { var tree = $("#treeview").data("kendoTreeView"); var json = treeToJson(tree.dataSource.view()); return JSON.stringify(json); } function treeToJson(nodes) { return $.map(nodes, function (n, i) { var result = { id: n.id}; //var result = { text: n.text, id: n.id, expanded: n.expanded, checked: n.checked }; if (n.hasChildren) result.items = treeToJson(n.children.view()); return result; }); }
var tree = $("#treeview").data("kendoTreeView"); var json = treeToJson(tree.dataSource.view());
var result = { id: n.id}; //var result = { text: n.text, id: n.id, expanded: n.expanded, checked: n.checked }; if (n.hasChildren) result.items = treeToJson(n.children.view());
return JSON.stringify(json);
window.location.href = "Folder/SaveMenu?serial=" + serialized + "!";
"[{\"id\":\"2\"},{\"id\":\"5\",\"items\":[{\"id\":\"3\"},{\"id\":\"6\"},{\"id\":\"7\"}]}]!"
همانطور که میبینید گره دوم که "پوشه چهارم45" نام دارد شامل سه فرزند است که در رشته داده شده با عنوان item شناخته شده است. حال باید این رشته با برنامه نویسی سی شارپ جداسازی کرد :
string serialized; Dictionary<int, int> numbers = new Dictionary<int, int>();
public ActionResult SaveMenu(string serial) { var newfolders = new List<Folder>(); serialized = serial; calculte_serialized(0); return RedirectToAction("Index"); }
void calculte_serialized(int parent) { while (serialized.Length > 0) { var id_index=serialized.IndexOf("id"); if (id_index == -1) { return; } serialized = serialized.Substring(id_index + 5); var quote_index = serialized.IndexOf("\""); var id=serialized.Substring(0, quote_index); numbers.Add(int.Parse(id), parent); serialized = serialized.Substring(quote_index); var condition = serialized.Substring(0,3); switch (condition) { case "\"},": break; case "\",\"": calculte_serialized(int.Parse(id)); break; case "\"}]": return; break; default: break; } } }
calculte_serialized با گرفتن 0 کار خود را شروع میکند یعنی از گره هایی که پدر ندارند و تمام idها را همراه با پدرشان در یک دیکشنری میریزد و هرکجا که به فرزندی برخورد به صورت بازگشتی فراخوانی میشود. پس از اجرای کامل آن ما درخت را در یک دیکشنری به صورت عنصرهای مجزا در اختیار داریم که میتوانیم در پایگاه داده ذخیره کنیم.
CoffeeScript #6
Classes
Inheritance & Super
شما میتوانید به راحتی از کلاسهای دیگری که نوشتهاید، با استفاده از کلمهی کلیدی ،extends ارث بری کنید:
class Animal constructor: (@name) -> alive: -> true class Parrot extends Animal constructor: -> super("Parrot") dead: -> not @alive()
همانطوری که در مثال بالا مشاهده میکنید، در کلاس Parrot در تابع constructor، تابع super فراخوانی شده است. با استفاده از کلمهی کلیدی super میتوان تابع سازندهی کلاس پدر را فراخوانی کرد. نتیجهی کامپایل super در مثال بالا به این صورت میشود:
Parrot.__super__.constructor.call(this, "Parrot");
در صورتیکه تابع constructor را در کلاس فرزند ننوشته باشید، به طور پیش فرض CoffeeScript سازنده کلاس پدر را فراخوانی میکند.
CoffeeScript با استفاده از prototypal inheritance، به صورت خودکار تمامی خصوصیات کلاس پدر، به فرزندان انتقال پیدا میکند. این ویژگی سبب داشتن کلاسهای پویا میشود. برای درک بهتر این موضوع، فرض کنید که خصوصیتی را به کلاس پدر بعد از ارث بری کلاس فرزند اضافه میکنید. خصوصیت اضافه شده به تمامی فرزندان کلاس پدر به صورت خودکار اضافه میشود.
class Animal constructor: (@name) -> class Parrot extends Animal Animal::rip = true parrot = new Parrot("Macaw") alert("This parrot is no more") if parrot.rip
Mixins
Mixins توسط CoffeeScript پشتیبانی نمیشود و برای همین نیاز است که این قابلیت را برای خودمان پیاده سازی کنیم، به مثال زیر توجه کنید.extend = (obj, mixin) -> obj[name] = method for name, method of mixin obj include = (klass, mixin) -> extend klass.prototype, mixin # Usage include Parrot, isDeceased: true alert (new Parrot).isDeceased
var extend, include; extend = function(obj, mixin) { var method, name; for (name in mixin) { method = mixin[name]; obj[name] = method; } return obj; }; include = function(klass, mixin) { return extend(klass.prototype, mixin); }; include(Parrot, { isDeceased: true }); alert((new Parrot).isDeceased);
Extending classes
Mixins خیلی مرتب و خوب است اما خیلی شیء گرا نیست؛ در عوض امکان ادغام را در کلاسهای CoffeeScript ایجاد میکند. برای اینکه اصول شیء گرایی را بخواهیم رعایت کنیم و ویژگی ادغام را نیز داشته باشیم، کلاسی با نام Module را پیاده سازی میکنیم و تمامی کلاسهایی را که میخواهیم ویژگی ادغام را داشته باشند، از آن ارث بری میکنیم.
moduleKeywords = ['extended', 'included'] class Module @extend: (obj) -> for key, value of obj when key not in moduleKeywords @[key] = value obj.extended?.apply(@) this @include: (obj) -> for key, value of obj when key not in moduleKeywords # Assign properties to the prototype @::[key] = value obj.included?.apply(@) this
classProperties = find: (id) -> create: (attrs) -> instanceProperties = save: -> class User extends Module @extend classProperties @include instanceProperties # Usage: user = User.find(1) user = new User user.save()
همچنین برای خلاصه نویسی بیشتر میتوان از این الگو استفاده کرد (ساده و زیبا).
ORM = find: (id) -> create: (attrs) -> extended: -> @include save: -> class User extends Module @extend ORM