داستانی از Unicode
<html> <body> <style type="text/css">p {float: left; padding: 0 15px; margin: 0;}</style> <script type="text/javascript"> for (var i=0; i<128; i++) document.writeln ((i%32?'':'<p>') + i + ': ' + String.fromCharCode (i) + '<br>'); </script> </body> </html>
private String ISO = "ISO-8859-"; protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { for (int i = 1; i < 16; i++) { ListItem item = new ListItem(); item.Text = ISO + i.ToString(); item.Value = i.ToString(); DropDownList1.Items.Add(item); } ShowCodes(1); } } protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e) { if (DropDownList1.SelectedItem != null) { int value = int.Parse(DropDownList1.SelectedValue); ShowCodes(value); } } private void ShowCodes(int value) { Response.Charset = ISO + value; string s = ""; for (int i = 0; i < 256; i++) { char ch = (char)i; s += i + "-" + ch; s += "<br/>";//br tag } Label1.Text = s; }
کد زیر در جاوااسکریپت کاراکترهای یونیکد را در مرز معینی که برایش مشخص کردهایم نشان میدهد:
<html> <body> <style type="text/css">p {float: left; padding: 0 15px; margin: 0;}</style> <script type="text/javascript"> for (var i=0; i<2096; i++) document.writeln ((i%256?'':'<p>') + i + ': ' + String.fromCharCode (i) + '<br>'); </script> </body> </html>
CSS & Unicode
/* cyrillic */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/mErvLBYg_cXG3rLvUsKT_fesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/-2n2p-_Y08sg57CNWQfKNvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+1F00-1FFF; } /* greek */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/u0TOpm082MNkS5K0Q4rhqvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0370-03FF; } /* vietnamese */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/NdF9MtnOpLzo-noMoG0miPesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB; } /* latin-ext */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/Fcx7Wwv8OzT71A3E1XOAjvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; }
UTF-8 نجات بخش میشود
- بسیاری از نرم افزارها و پروتکلها هنوز 8 بیتی کار میکنند.
- اگر یک متن انگلیسی ارسال کنید، 8 بیت هم کافی است ولی در این حالت 32 بیت جابجا میشود؛ یعنی 4 برابر و در ارسال و دریافت و پهنای باند برایمان مشکل ایجاد میکند.
مقایسهای بین نسخههای مختلف :
همانطور که میبینید UTF-8 برای کاراکترهای اسکی، از یک بایت و برای دیگر حروف از دوبایت و برای بقیه BMPها از سه بایت استفاده میکند و در صورتی که کاراکتری در ناحیه مکمل supplementary باشد، از چهار بایت استفاده خواهد کرد. UTF-16 از دو بایت برای نمایش کاراکترهای BMP و از 4 بایت برای نمایش کاراکترهای مکمل استفاده میکند و در UTF-32 از 4 بایت برای همه کاراکترها یا کد پوینتها استفاده میشود.
مراحل نحوه اجرای برنامه:
نصب کتابخانههای زیر:
//client Install-Package angularjs Install-Package angular-strap Install-Package Microsoft.AspNet.SignalR.JS install-package AngularJs.SignalR.Hub Install-Package jQuery.TimeAgo Install-Package FontAwesome Install-Package toastr Install-Package Twitter.Bootstrap.RTL bower install angular-smilies //server Install-Package Newtonsoft.Json Install-Package Microsoft.AspNet.SignalR Install-Package EntityFramework
گامهای برنامه:
public partial class Message { public int Id { get; set; } public string Sender { get; set; } public string Receiver { get; set; } public string Body { get; set; } public DateTimeOffset? CreationTime { get; set; } public int? SessionId { get; set; } public virtual Session Session { get; set; } } public partial class Session { public Session() { Messages = new List<Message>(); Sessions = new List<Session>(); } public int Id { get; set; } public string AgentName { get; set; } public string CustomerName { get; set; } public DateTime CreatedDateTime { get; set; } public int? ParentId { get; set; } public virtual Session Parent { get; set; } public virtual ICollection<Message> Messages { get; set; } public virtual ICollection<Session> Sessions { get; set; } }
2- ایجاد ویو مدلهای زیر
public class UserInformation { public string ConnectionId { get; set; } public bool IsOnline { get; set; } public string UserName { get; set; } } public class ChatSessionVm { public string Key { get; set; } public List<string> Value { get; set; } } public class AgentViewModel { public int Id { get; set; } public string CustomerName { get; set; } public int Lenght { get; set; } public DateTimeOffset? Date { get; set; } }
3- ایجاد Hub در سرور
[HubName("chatHub")] public class ChatHub : Microsoft.AspNet.SignalR.Hub { }
listeners متدهای سمت کلاینت
$scope.myHub = new hub("chatHub", { listeners: {}, methods: [] })
$scope.myHub.promise.done(function () { $scope.myHub.init(); $scope.myHub.promise.done(function () { }); });
public void Init() { _chatSessions = _chatSessions ?? (_chatSessions = new List<ChatSessionVm>()); _agents = _agents ?? (_agents = new ConcurrentDictionary<string, UserInformation>()); Clients.Caller.onlineStatus(_agents.Count(x => x.Value.IsOnline) > 0); }
5-وضعیت کارشناسان :
$scope.requestChat = function (msg) { if (!defaultCustomerUserName) { //گرفتن کاربر لاگین شده //ما از آرایه تصادفی استفاده میکنیم var nameDefaultArray = [ 'حسین', 'حسن', 'علی', 'عباس', 'زهرا', 'سمیه' ]; defaultCustomerUserName=nameDefaultArray[Math.floor(Math.random() * nameDefaultArray.length)]; } var userName = defaultCustomerUserName; if (!$scope.chatId) { $scope.chatId = sessionStorage.getItem(chatKey); $http.get("http://ipinfo.io") .success(function (response) { $scope.myHub.logVisit(response.city, response.country, msg, userName); }).error(function (e, status, headers, config) { $scope.myHub.logVisit("Tehran", "Ir", msg, userName) }); $scope.myHub.requestChat(msg); $scope.chatTitle = $scope.options.waitingForOperator; $scope.pendingRequestChat = true; } else { $scope.myHub.clientSendMessage(msg, userName); }; $scope.message = ""; };
6-مشاهده تقاضای مکالمه کاربران توسط کارشناسان:
public void AcceptRequestChat(string customerConnectionId, string body, string userName) { var agent = FindAgent(Context.ConnectionId); var session = _chatSessions.FirstOrDefault(item => item.Key.Equals(agent.Key)); if (session == null) { _chatSessions.Add(new ChatSessionVm { Key = agent.Key, Value = new List<string> { customerConnectionId } }); } else { session.Value.Add(customerConnectionId); } Clients.Client(Context.ConnectionId).agentChat(customerConnectionId, body, userName); Clients.Client(customerConnectionId).clientChat(customerConnectionId, agent.Value.UserName); foreach (var item in _agents.Where(item => item.Value.IsOnline)) { Clients.Client(item.Value.ConnectionId).refreshChatWith(agent.Value.UserName, customerConnectionId); } var session = _db.Sessions.Add(new Session { AgentName = agent.Key, CustomerName = userName, CreatedDateTime = DateTime.Now }); _db.SaveChanges(); var message = new Message { CreationTime = DateTime.Now, Sender = agent.Key, Receiver = userName, body=body, Session = session }; _db.Messages.Add(message); _db.SaveChanges(); }
public void CloseChat(string id) { var findAgent = FindAgent(Context.ConnectionId); var session = _chatSessions.FirstOrDefault(item => item.Value.Contains(id)); if (session == null) return; Clients.Client(id).clientAddMessage(findAgent.Key, "مکالمه شما با کارشناس مربوطه به اتمام رسیده است"); foreach (var agent in _agents) { Clients.Client(agent.Value.ConnectionId).refreshLeaveChat(agent.Value.UserName, id); } _chatSessions.Remove(session); }
8-انتقال مکالمه مشتری به کارشناسی دیگر
public void EngageVisitor(string newAgentId, string cumtomerId, string customerName,string clientSessionId) { #region remove session of current agent var currentAgent = FindAgent(Context.ConnectionId); var currentSession = _chatSessions.FirstOrDefault(item => item.Value.Contains(cumtomerId)); if (currentSession != null) { _chatSessions.Remove(currentSession); } #endregion #region add session to new agent var newAgent = FindAgent(newAgentId); var newSession = _chatSessions.FirstOrDefault(item => item.Key.Equals(newAgent.Key)); if (newSession == null) { _chatSessions.Add(new ChatSessionVm { Key = newAgent.Key, Value = new List<string> { cumtomerId } }); } else { newSession.Value.Add(cumtomerId); } #endregion Clients.Client(currentAgent.Value.ConnectionId).addMessage(cumtomerId, newAgent.Key, "ادامه مکالمه به کارشناس " + newAgent.Key + "مقابل منتقل شد"); Clients.Client(newAgentId).addMessage(cumtomerId, currentAgent.Key, "لطفا مکالمه را ادامه دهید.با تشکر"); Clients.Client(cumtomerId).clientAddMessage(newAgent.Value.UserName, "مکالمه شما با کارشناس زیر برقرار گردید" + newAgent.Key); var session = _db.Sessions.FirstOrDefault (item => item.AgentName.Equals(currentAgent.Value.UserName) && item.CustomerName.Equals(customerName)); if (session != null) { var sessionId = session.Id; var messages = _db.Messages.Where(item => item.Session.Id.Equals(sessionId)); var result = JsonConvert.SerializeObject(messages, new Formatting(), _settings); Clients.Client(newAgentId).visitorSwitchConversation (Context.ConnectionId, customerName, result, clientSessionId); } foreach (var item in _agents.Where(item => item.Value.IsOnline)) { Clients.Client(item.Value.ConnectionId).refreshChatWith(newAgent.Value.UserName, cumtomerId); } _db.Sessions.Add(new Session { AgentName = newAgent.Key, CustomerName = customerName, CreatedDateTime = DateTime.Now, Parent = _db.Sessions.Where(item => item.AgentName.Equals(currentAgent.Key) && item.CustomerName.Equals(customerName)).OrderByDescending(item => item.Id).FirstOrDefault() }); _db.SaveChanges(); }
var app = angular.module("app", ["SignalR", 'ngRoute', 'ngAnimate', 'ngSanitize', 'mgcrea.ngStrap', 'angular-smilies']); app.config(["$routeProvider", "$provide", "$httpProvider", "$locationProvider", function ($routeProvider, $provide, $httpProvider, $locationProvider) { $routeProvider. when('/', { templateUrl: 'app/views/home.html', controller: "HomeCtrl" }). when('/agent', { templateUrl: 'app/views/agent.html', controller: "ChatCtrl" }) .otherwise({ redirectTo: "/" });; }]); app.controller("HomeCtrl", ["$scope", function ($scope) { $scope.title = "home"; }]) app.controller("ChatCtrl", ["$scope", "Hub", "$location", "$http", "$rootScope", function ($scope, hub, $location, $http, $rootScope) { if (!$scope.myHub) { var chatKey = "angular-signalr"; var defaultCustomerUserName = null; function getid(id) { var find = false; var position = null; angular.forEach($scope.chatConversation, function (index, i) { if (index.id === id && !find) { find = true; position = i; return; } }); return position; } function apply() { $scope.$apply(); } $scope.boxheader = function () { var height = 0; $("#chat-box").slideToggle('slow', function () { if ($("#chat-box-header").css("bottom") === "0px") { height = $("#chat-box").height() + 20; } else { height = 0; } $("#chat-box-header").css("bottom", height); }); }; var init = function () { $scope.agent = { id: "", name: "", isOnline: false }; $rootScope.msg = ""; $scope.alarmStatus = false; $scope.options = { offlineTitle: "آفلاین", onlineTitle: "آنلاین", waitingForOperator: "لطفا منتظر بمانید تا به اپراتور وصل شوید", emailSent: "ایمیل ارسال گردید", emailFailed: "متاسفانه ایمیل ارسال نگردید", logOut: "خروج", setting: "تنظیمات", conversion: "آرشیو", edit: "ویرایش", alarm: "قطع/وصل کردن صدا", complete: "تکمیل", pending: "منتظر ماندن", reject: "عدم پذیرش", lock: "آنلاین شدن", unlock: "آفلاین شدن", alarmOn: "روشن", alarmOff: "خاموش", upload: "آپلود" }; $scope.chatConversation = []; $scope.chatSessions = []; $scope.customerVisit = []; $scope.agentClientMsgs = []; $scope.clientAgentMsg = []; }(); //تعریف هاب به همراه متدهای آن $scope.myHub = new hub("chatHub", { listeners: { "clientChat": function (id, agentName) { $scope.clientAgentMsg.push({ name: agentName, msg: "با سلام در خدمت میباشم" }); $scope.chatTitle = "کارشناس: " + agentName; $scope.pendingRequestChat = false; sessionStorage.setItem(chatKey, id); }, "agentChat": function (id, firstComment, customerName) { var date = new Date(); var position = getid(id); if (position > 0) { $scope.chatSessions[position].length = $scope.chatConversation[position].length + 1; $scope.chatSessions[position].date = date.toISOString(); return; } else { $scope.chatConversation.push({ id: id, sessions: [{ name: customerName, msg: firstComment, date: date }], agentName: $scope.agent.name, customerName: customerName, dateStartChat: date.getHours() + ":" + date.getMinutes(), }); $scope.chatSessions.push({ id: id, length: 1, userName: customerName, date: date.toISOString() }); } sessionStorage.setItem(chatKey, id); apply(); }, //برروز رسانی لیست برای کارشناسان "refreshChatWith": function (agentName, customerConnectionId) { angular.forEach($scope.customerVisit, function (index, i) { if (index.connectionId === customerConnectionId) { $scope.customerVisit[i].chatWith = agentName; } }); apply(); }, //برروز رسانی لیست برای کارشناسان "refreshLeaveChat": function (agentName, customerConnectionId) { angular.forEach($scope.customerVisit, function (index, i) { if (index.connectionId === customerConnectionId) { $scope.customerVisit[i].chatWith =agentName + "---" + " به مکالمه خاتمه داده است "; } }); apply(); } //وضعیت آنلاین بودن کارشناسان , "onlineStatus": function (state) { if (state) { $scope.chatTitle = $scope.options.onlineTitle; $scope.hasOnline = true; $scope.hasOffline = false; } else { $scope.chatTitle = $scope.options.offlineTitle; $scope.hasOffline = true; $scope.hasOnline = false; } $scope.$apply() }, "loginResult": function (status, id, name) { if (status) { $scope.agent.id = id; $scope.agent.name = name; $scope.agent.isOnline = true; $scope.userIsLogin = $scope.agent; $scope.$apply(function () { $location.path("/agent"); }); } else { $scope.agent = null; toastr.error("کارشناسی با این مشخصات وجود ندارد"); return; } }, "newVisit": function (userName, city, country, chatWith, connectionId, firstComment) { var exist = false; angular.forEach($scope.customerVisit, function (index) { if (index.connectionId === connectionId) { exist = true; return; } }); if (!exist) { var date = new Date(); $scope.customerVisit.unshift({ userName: userName, date: date, city: city, country: country, chatWith: chatWith, connectionId: connectionId, firstComment: firstComment }); if ($scope.alarmStatus) { var snd = new Audio("/App/assets/sounds/Sedna.ogg"); snd.play(); } toastr.success("تقاضای جدید دریافت گردید"); apply(); } }, "addMessage": function (id, from, value) { if ($scope.alarmStatus) { var snd = new Audio("/App/assets/sounds/newmsg.mp3"); snd.play(); } $scope.agentUserMsgs = []; var date = new Date(); var position = getid(id); if ($scope.chatConversation.length > 0 && position != null) { $scope.chatConversation[position].sessions.push({ name: from, msg: value, date: date }); } var item = $scope.chatConversation[position]; if (item) { angular.forEach(item.sessions, function (index) { $scope.agentUserMsgs.push({ name: index.name, msg: index.msg, date: date }); }); $scope.chatSessions[position].length = $scope.chatSessions[position].length + 1; } apply(); }, "clientAddMessage": function (id, from) { if ($scope.alarmStatus) { var snd = new Audio("/App/assets/sounds/newmsg.mp3"); snd.play(); } $scope.clientAgentMsg.push({ name: id, msg: from }); apply(); }, "visitorSwitchConversation": function (id, customerName, sessions, sessionId) { sessions = JSON.parse(sessions); var date = new Date(); var sessionList = []; angular.forEach(sessions, function (index) { sessionList.push({ name: index.sender, msg: index.body, date: index.creationTime }); }); $scope.chatConversation.push({ id: sessionId, sessions: sessionList, customerName: customerName, dateStartChat: date.getHours() + ":" + date.getMinutes(), agentName: $scope.agent.name }); $scope.chatSessions.push({ id: sessionId, length: sessions.length, date: date }); }, "receiveTicket": function (items) { angular.forEach(JSON.parse(items), function (index) { $scope.ticketList = []; $scope.ticketList.push(index); }); }, //آرشیو گفته گوهای کارشناس "receiveHistory": function (items) { $scope.agentHistory = []; angular.forEach(JSON.parse(items), function (index) { $scope.agentHistory.push(index); }); apply(); }, //جزییات آرشیو گفتگوها "detailsHistory": function (items) { $scope.historyMsg = []; angular.forEach(JSON.parse(items), function (index) { $scope.historyMsg.push({ name: index.sender, msg: index.body, date: index.creationTime }); }); $("#detailsAgentHistory").modal(); apply(); }, //لیست کارشناسان آنلاین "agentList": function (items) { $scope.agentList = []; angular.forEach(items, function (index) { if ($scope.agent.name != index.Key) { $scope.agentList.push({ name: index.Key, id: index.Value.ConnectionId }); } }); $("#agentList").modal(); apply(); } }, methods: ["agentConnect", "sendTicket", "requestChat", "clientSendMessage", "closeChat", "init", "logVisit", "agentChangeStatus", "engageVisitor", "agentSendMessage", "transfer", "leaveChat", "acceptRequestChat", "leaveChat", "detailsSessoinMessage", "showAgentList", "getAgentHistoryChat" ], errorHandler: function (error) { console.error(error); } }); $scope.myHub.promise.done(function () { $scope.myHub.init(); $scope.myHub.promise.done(function () { }); }); $scope.LeaveChat = function () { $scope.myHub.LeaveChat(); }; $scope.loginAgent = function (userName) { // username :security user username from agent role if (userName == "hossein" || userName == "ali") { $scope.myHub.promise.done(function () { $scope.myHub.agentConnect(userName).then(function (result) { $scope.agent.name = userName; $scope.agent.isOnline = true; }); }); } }; $scope.requestChat = function (msg) { if (!defaultCustomerUserName) { //گرفتن کاربر لاگین شده //ما از آرایه تصادفی استفاده میکنیم var nameDefaultArray = [ 'حسین', 'حسن', 'علی', 'عباس', 'زهرا', 'سمیه' ]; defaultCustomerUserName=nameDefaultArray[Math.floor(Math.random() * nameDefaultArray.length)]; } var userName = defaultCustomerUserName; if (!$scope.chatId) { $scope.chatId = sessionStorage.getItem(chatKey); $http.get("http://ipinfo.io") .success(function (response) { $scope.myHub.logVisit(response.city, response.country, msg, userName); }).error(function (e, status, headers, config) { $scope.myHub.logVisit("Tehran", "Ir", msg, userName) }); $scope.myHub.requestChat(msg); $scope.chatTitle = $scope.options.waitingForOperator; $scope.pendingRequestChat = true; } else { $scope.myHub.clientSendMessage(msg, userName); }; $scope.message = ""; }; $scope.acceptRequestChat = function (customerConnectionId, firstComment, customerName) { $scope.myHub.acceptRequestChat(customerConnectionId, firstComment, customerName); }; $scope.changeAgentStatus = function () { $scope.agent.isOnline = !$scope.agent.isOnline; $scope.myHub.agentChangeStatus($scope.agent.isOnline); }; $scope.detailsChat = function (chatId, userName) { $scope.agentUserMsgs = []; angular.forEach($scope.chatConversation, function (index) { if (index.id === chatId) { $scope.dateStartChat = index.dateStartChat; angular.forEach(index.sessions, function (value) { $scope.agentUserMsgs.push({ name: value.name, msg: value.msg, date: value.date }); }); }; }); $scope.agentChatWithUser = chatId; $scope.customerName = userName; $("#agentUserChat").modal(); }; $scope.ticket = { submit: function () { var name = $scope.ticket.name; var email = $scope.ticket.email; var comment = $scope.ticket.comment; $scope.myHub.sendTicket(name, email, comment); } }; $scope.showHistory = function () { $scope.myHub.getAgentHistoryChat($scope.agent.name); }; $scope.detailsChatHistory = function (id) { $scope.myHub.detailsSessoinMessage(id, $scope.agent.id); }; $scope.agentMsgToUser = function (msg) { var chatId = $scope.agentChatWithUser; var customerName = $scope.customerName; if (!customerName) { angular.forEach($scope.customerVisit, function (index) { if (index.connectionId == chatId) { customerName = index.userName; } }); } if (chatId !== "" && msg !== "") { $scope.myHub.agentSendMessage(chatId, msg, customerName); } //not bind to scope.msg! not correctly work $scope.msg = ""; $("#post-msg").val(""); }; $scope.closeChat = function (chatId) { var item = $scope.chatConversation[getid(chatId)]; $scope.myHub.closeChat(chatId); }; $scope.engageVisitor = function (newAgentId) { var customerId = $scope.customerId; var customerName = $scope.customerName; var clientSessionId = $scope.clientSessionId; $scope.myHub.engageVisitor(newAgentId, customerId, customerName, clientSessionId); $("[data-dismiss=modal]").trigger({ type: "click" }); }; $scope.selectVisitor = function (customerId, customerName, clientSessionId) { $scope.customerId = customerId; $scope.customerName = customerName; $scope.clientSessionId = clientSessionId; $scope.myHub.showAgentList(); }; $scope.setClass = function (item) { if (item === "من") return "question"; else return "response"; }; $scope.setdirectionClass = function (item) { if (item === $scope.agent.name) return { "float": "left" }; else return { "float": "right" }; }; $scope.setArrowClass = function (item) { if (item === $scope.agent.name) return "left-arrow"; else return "right-arrow"; }; $scope.setAlarm = function () { $scope.alarmStatus = !$scope.alarmStatus; }; } }]); app.directive("showtab", function () { return { link: function (scope, element, attrs) { element.click(function (e) { e.preventDefault(); $(element).addClass("active"); $(element).tab("show"); }); } }; }); //زمان ارسال پیام app.directive("timeAgo", function ($q) { return { restrict: "AE", scope: false, link: function (scope, element, attrs) { jQuery.timeago.settings.strings = { prefixAgo: null, prefixFromNow: null, suffixAgo: "پیش", suffixFromNow: "از حالا", seconds: "کمتر از یک دقیقه", minute: "در حدود یک دقیقه", minutes: "%d دقیقه", hour: "حدود یگ ساعت", hours: "حدود %d ساعت ", day: "یک روز", days: "%d روز", month: "حدود یک ماه", months: "%d ماه", year: "حدود یک سال", years: "%d سال", wordSeparator: " ", numbers: [] } var parsedDate = $q.defer(); parsedDate.promise.then(function () { jQuery(element).timeago(); }); attrs.$observe("title", function (newValue) { parsedDate.resolve(newValue); }); } }; });
[HubName("chatHub")] public class ChatHub : Microsoft.AspNet.SignalR.Hub { private readonly ApplicationDbContext _db = new ApplicationDbContext(); private static ConcurrentDictionary<string, UserInformation> _agents; private static List<ChatSessionVm> _chatSessions; private readonly JsonSerializerSettings _settings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver(), ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; public void Init() { _chatSessions = _chatSessions ?? (_chatSessions = new List<ChatSessionVm>()); _agents = _agents ?? (_agents = new ConcurrentDictionary<string, UserInformation>()); Clients.Caller.onlineStatus(_agents.Count(x => x.Value.IsOnline) > 0); } public void AgentConnect(string userName) { //ما برای ساده کردن مقایسه ساده ای انجام دادیم فقط کاربر حسین یا علی میتواند کارشناس باشد if (userName == "hossein" || userName == "ali") { var agent = new UserInformation(); if (_agents.Any(item => item.Key == userName)) { agent = _agents[userName]; agent.ConnectionId = Context.ConnectionId; } else { agent.ConnectionId = Context.ConnectionId; agent.UserName = userName; agent.IsOnline = true; _agents.TryAdd(userName, agent); } Clients.Caller.loginResult(true, agent.ConnectionId, agent.UserName); Clients.All.onlineStatus(_agents.Count(x => x.Value.IsOnline) > 0); } else { Clients.Caller.loginResult(false, null, null); } } public void AgentChangeStatus(bool status) { var agent = _agents.FirstOrDefault(x => x.Value.ConnectionId == Context.ConnectionId).Value; if (agent == null) return; agent.IsOnline = status; Clients.All.onlineStatus(_agents.Count(x => x.Value.IsOnline) > 0); } public void LogVisit(string city, string country, string firstComment, string userName) { foreach (var agent in _agents) { Clients.Client(agent.Value.ConnectionId).newVisit(userName, city, country, null, Context.ConnectionId, firstComment); } } public void AcceptRequestChat(string customerConnectionId, string body, string userName) { var agent = FindAgent(Context.ConnectionId); var session = _chatSessions.FirstOrDefault(item => item.Key.Equals(agent.Key)); if (session == null) { _chatSessions.Add(new ChatSessionVm { Key = agent.Key, Value = new List<string> { customerConnectionId } }); } else { session.Value.Add(customerConnectionId); } Clients.Client(Context.ConnectionId).agentChat(customerConnectionId, body, userName); Clients.Client(customerConnectionId).clientChat(customerConnectionId, agent.Value.UserName); foreach (var item in _agents.Where(item => item.Value.IsOnline)) { Clients.Client(item.Value.ConnectionId).refreshChatWith(agent.Value.UserName, customerConnectionId); } _db.Sessions.Add(new Session { AgentName = agent.Key, CustomerName = userName, CreatedDateTime = DateTime.Now }); _db.SaveChanges(); var message = new Message { CreationTime = DateTime.Now, Sender = agent.Key, Receiver = userName, Body = body, //ConnectionId = _agents.FirstOrDefault(item => item.Value.UserName == userName).Key, Session = _db.Sessions.OrderByDescending(item => item.Id) .FirstOrDefault(item => item.AgentName.Equals(agent.Key) && item.CustomerName.Equals(userName)) }; _db.Messages.Add(message); _db.SaveChanges(); } public void GetAgentHistoryChat(string userName) { var dic = new Dictionary<int, int>(); var lenght = 0; var chats = _db.Sessions.OrderBy(item => item.Id).Include(item => item.Parent) .Where(item => item.AgentName.Equals(userName)).ToList(); foreach (var session in chats) { Result(session, ref lenght); dic.Add(session.Id, lenght); lenght = 0; } if (!chats.Any()) return; var historyResult = chats.Select(item => new AgentViewModel { Id = item.Id, CustomerName = item.CustomerName, Date = item.CreatedDateTime, Lenght = dic.Any(di => di.Key.Equals(item.Id)) ? dic.FirstOrDefault(di => di.Key.Equals(item.Id)).Value : 0, }).OrderByDescending(item => item.Id).ToList(); Clients.Caller.receiveHistory(JsonConvert.SerializeObject(historyResult, new Formatting(), _settings)); } public void DetailsSessoinMessage(int sessionId, string agentId) { var session = _db.Sessions.FirstOrDefault(item => item.Id.Equals(sessionId)); if (session == null) return; var list = new List<Message>(); GetAllMessages(session, list); var result = JsonConvert.SerializeObject(list.OrderBy(item => item.Id), new Formatting(), _settings); Clients.Client(Context.ConnectionId).detailsHistory(result); } public void ClientSendMessage(string body, string userName) { var session = _chatSessions.FirstOrDefault(item => item.Value.Contains(Context.ConnectionId)); if (session == null || session.Key == null) return; var agentId = _agents.FirstOrDefault(item => item.Key.Equals(session.Key)).Value.ConnectionId; Clients.Caller.clientAddMessage("من", body); Clients.Client(agentId).addMessage(Context.ConnectionId, userName, body); var message = new Message { Sender = FindAgent(agentId).Key, Receiver = userName, Body = body, CreationTime = DateTime.Now, Session = FindSession(userName, FindAgent(agentId).Key) }; _db.Messages.Add(message); _db.SaveChanges(); } public void AgentSendMessage(string id, string body, string userName) { var agent = FindAgent(Context.ConnectionId); Clients.Caller.addMessage(id, agent.Value.UserName, body); Clients.Client(id).clientAddMessage(agent.Value.UserName, body); var message = new Message { Sender = agent.Key, Receiver = userName, Body = body, Session = FindSession(agent.Key, userName), CreationTime = DateTime.Now }; _db.Messages.Add(message); _db.SaveChanges(); } public void CloseChat(string id) { var findAgent = FindAgent(Context.ConnectionId); var session = _chatSessions.FirstOrDefault(item => item.Value.Contains(id)); if (session == null) return; Clients.Client(id).clientAddMessage(findAgent.Key, "مکالمه شما با کارشناس مربوطه به اتمام رسیده است"); foreach (var agent in _agents) { Clients.Client(agent.Value.ConnectionId).refreshLeaveChat(agent.Value.UserName, id); } _chatSessions.Remove(session); } public void RequestChat(string message) { Clients.Caller.clientAddMessage("من", message); } public void EngageVisitor(string newAgentId, string cumtomerId, string customerName,string clientSessionId) { #region remove session of current agent var currentAgent = FindAgent(Context.ConnectionId); var currentSession = _chatSessions.FirstOrDefault(item => item.Value.Contains(cumtomerId)); if (currentSession != null) { _chatSessions.Remove(currentSession); } #endregion #region add session to new agent var newAgent = FindAgent(newAgentId); var newSession = _chatSessions.FirstOrDefault(item => item.Key.Equals(newAgent.Key)); if (newSession == null) { _chatSessions.Add(new ChatSessionVm { Key = newAgent.Key, Value = new List<string> { cumtomerId } }); } else { newSession.Value.Add(cumtomerId); } #endregion Clients.Client(currentAgent.Value.ConnectionId).addMessage(cumtomerId, newAgent.Key, "ادامه مکالمه به کارشناس " + newAgent.Key + "مقابل منتقل شد"); Clients.Client(newAgentId).addMessage(cumtomerId, currentAgent.Key, "لطفا مکالمه را ادامه دهید.با تشکر"); Clients.Client(cumtomerId).clientAddMessage(newAgent.Value.UserName, "مکالمه شما با کارشناس زیر برقرار گردید" + newAgent.Key); var session = _db.Sessions.FirstOrDefault (item => item.AgentName.Equals(currentAgent.Value.UserName) && item.CustomerName.Equals(customerName)); if (session != null) { var sessionId = session.Id; var messages = _db.Messages.Where(item => item.Session.Id.Equals(sessionId)); var result = JsonConvert.SerializeObject(messages, new Formatting(), _settings); Clients.Client(newAgentId).visitorSwitchConversation (Context.ConnectionId, customerName, result, clientSessionId); } foreach (var item in _agents.Where(item => item.Value.IsOnline)) { Clients.Client(item.Value.ConnectionId).refreshChatWith(newAgent.Value.UserName, cumtomerId); } _db.Sessions.Add(new Session { AgentName = newAgent.Key, CustomerName = customerName, CreatedDateTime = DateTime.Now, Parent = _db.Sessions.Where(item => item.AgentName.Equals(currentAgent.Key) && item.CustomerName.Equals(customerName)).OrderByDescending(item => item.Id).FirstOrDefault() }); _db.SaveChanges(); } public void ShowAgentList() { Clients.Caller.agentList(_agents.ToList()); } public override Task OnDisconnected(bool stopCalled) { var id = Context.ConnectionId; var isAgent = _agents != null && _agents.Any(item => item.Value.ConnectionId.Equals(id)); if (isAgent) { UserInformation agent; var currentAgentConnectionId = FindAgent(id).Key; if (currentAgentConnectionId == null) return base.OnDisconnected(stopCalled); if (_chatSessions.Any()) { var sessions = _chatSessions.FirstOrDefault(item => item.Key.Equals(currentAgentConnectionId)); //اطلاع دادن به تمام کاربرانی که در حال مکالمه با کارشناس هستند if (sessions != null) { var result = sessions.Value.ToList(); for (var i = 0; i < result.Count(); i++) { var localId = result[i]; Clients.Client(localId).clientAddMessage(currentAgentConnectionId, "ارتباط شما با مشاور مورد نظر قطع شده است"); } } } _agents.TryRemove(currentAgentConnectionId, out agent); Clients.All.onlineStatus(_agents.Count(x => x.Value.IsOnline) > 0); Clients.Client(id).loginResult(false, null, null); } else { if (_chatSessions == null || !_chatSessions.Any(item => item.Value.Contains(id) && _agents == null)) return base.OnDisconnected(stopCalled); var session = _chatSessions.FirstOrDefault(item => item.Value.Contains(id)); if (session == null) return base.OnDisconnected(stopCalled); var agentName = session.Key; var agent = _agents.FirstOrDefault(item => item.Key.Equals(agentName)); if (agent.Key != null) { Clients.Client(agent.Value.ConnectionId).addMessage(id, "کاربر", "اتصال با کاربر قطع شده است"); } } return base.OnDisconnected(stopCalled); } private KeyValuePair<string, UserInformation> FindAgent(string connectionId) { return _agents.FirstOrDefault(item => item.Value.ConnectionId.Equals(connectionId)); } private Session FindSession(string key, string userName) { return _db.Sessions.Where(item => item.AgentName.Equals(key) && item.CustomerName.Equals(userName)) .OrderByDescending(item => item.Id).FirstOrDefault(); } private static void Result(Session parent, ref int lenght) { while (true) { if (parent == null) return; lenght += parent.Messages.Count(); parent = parent.Parent; } } private static List<Message> GetAllMessages(Session node, List<Message> list) { if (node == null) return null; list.AddRange(node.Messages); if (node.Parent != null) { GetAllMessages(node.Parent, list); } return null; } }
<div> <div> <h2> خوش آمدید <span ng-bind="agent.name"> </span> <a ng-click="changeAgentStatus()"> <i ng-if="changeStatus==null" data-placement="bottom" data-trigger="hover " bs-tooltip="options.lock"></i> <i ng-if="changeStatus==true" data-placement="bottom" data-trigger="hover" bs-tooltip="options.unlock"></i> </a> </h2> <div style="float: left"> <a ng-click="setAlarm()"> <i ng-show="alarmStatus" data-placement="bottom" data-trigger="hover " bs-tooltip="options.alarmOn"></i> <i ng-show="!alarmStatus" data-placement="bottom" data-trigger="hover " bs-tooltip="options.alarmOff"></i> </a> <!--<a data-placement="bottom" data-trigger="hover " bs-tooltip="options.conversion" ng-click="showHistory()"><i></i></a>--> <a data-placement="bottom" data-trigger="hover " bs-tooltip="options.edit"><i></i><span></span></a> <a data-placement="bottom" data-trigger="hover " bs-tooltip="options.setting"><i></i></a> <a data-placement="bottom" data-trigger="hover " bs-tooltip="options.signOut" ng-click="LeaveChat()"><i></i><span></span></a> </div> </div> <div> <div> <div id="chat-content"> <div> <ul> <li> <a showtab href="#online-list">آنلاین</a> </li> <li> <a ng-click="showHistory()" showtab href="#conversation">آرشیو گفتگوها</a> </li> </ul> <div> <div id="online-list"> <div> <h2> <i></i><span></span> <span>نمایش آنلاین مراجعه ها</span> </h2> </div> <div> <div id="agent-chat"> <div id="real-time-visits"> <table id="current-visits"> <thead> <tr> <th>نام کاربر</th> <th>زمان اولین تقاضا</th> <th>منطقه</th> <th>پاسخ</th> </tr> </thead> <tbody> <tr id="{{item.connectionId}}" ng-animate="animate" ng-repeat="item in customerVisit "> <td ng-bind="item.userName"></td> <td> <span time-ago title="{{item.date}}"></span> </td> <td> <span ng-bind="item.country"></span> /<span ng-bind="item.city"> </span> </td> <td> <a style="cursor: pointer" ng-if="item.chatWith== null" ng-click="acceptRequestChat(item.connectionId,item.firstComment,item.userName)"> شروع مکالمه </a> <span ng-if="item.chatWith "> وضعیت: <span>در حال مکالمه با</span> <span ng-bind="item.chatWith"></span> <a ng-show="item.chatWith==agent.name" ng-click="selectVisitor(item.connectionId,item.userName,item.connectionId)"> انتقال مکالمه </a> </span> <ul ng-repeat="session in chatSessions track by $index" style="padding:0px;"> <li ng-if="session.id==item.connectionId" id="{{session.id}}"> <div> <p> تاریخ شروع مکالمه: <span time-ago title="{{session.date}}"></span> </p> <p> تعداد پیام ها: <span ng-bind="session.length"></span> </p> </div> <p> <a ng-click="detailsChat(session.id,session.userName)">جزییات </a> <a ng-click="closeChat(session.id)"> خاتمه عملیات </a> </p> </li> </ul> </td> </tr> </tbody> </table> </div> </div> </div> </div> <div id="conversation"> <div> <h2> <i></i><span></span> <span>آرشیو گفتگوهای </span> {{agent.name}} </h2> </div> <div> <div> <table id="current-visits"> <thead> <tr> <th>شناسه مشتری</th> <th>نام مشتری</th> <th>تعداد محاوره ها</th> <th>تاریخ</th> <th>جزئیات</th> </tr> </thead> <tbody> <tr ng-repeat="item in agentHistory track by $index"> <td ng-bind="item.id"></td> <td ng-bind="item.customerName"></td> <th ng-bind="item.lenght"></th> <td><span time-ago title="{{item.date}}"></span></td> <th> <ang-click="detailsChatHistory(item.id)" >مشاهده جزییات گفتگو</a> </th> </tr> </tbody> </table> </div> </div> </div> </div> </div> </div> </div> </div> <div id="detailsAgentHistory" tabindex="-1" role="dialog" aria-labelledby="cmdLabel" aria-hidden="true"> <div> <div> <div> <div> <button type="button" data-dismiss="modal" aria-hidden="true">×</button> </div> <h2> <span></span>تاریخچه گفتگو </h2> </div> <div> <div style="display: block"> <ul ng-repeat="item in historyMsg"> <li> <span ng-bind="item.name" ng-style="setdirectionClass(item.name)"> </span> <span ng-style="setdirectionClass(item.name)"> <span ng-class="setArrowClass(item.name)"></span> <span time-ago title="{{item.date}}"></span> <span> <p ng-bind-html="item.msg | smilies"></p> </span> </span> </li> </ul> </div> </div> </div> </div> </div> <div id="agentList" tabindex="-1" role="dialog" aria-labelledby="cmdLabel" aria-hidden="true"> <div> <div> <div> <div> <button type="button" data-dismiss="modal" aria-hidden="true">×</button> </div> <h2> <span></span>لیست تمام کارشناسان </h2> </div> <div> <div style="display: block;"> <div ng-show="agentList.length==0"> کارشناس آنلاینی وجود ندارد </div> <ul ng-repeat="item in agentList"> <li> <span> <a ng-click="engageVisitor(item.id)">{{item.name}}</a> </span> </li> </ul> </div> </div> </div> </div> </div> <div id="agentUserChat" tabindex="-1" role="dialog" aria-labelledby="cmdLabel" aria-hidden="true"> <div> <div> <div> <div> <button type="button" data-dismiss="modal" aria-hidden="true">×</button> </div> <h2> <span></span>گفتگو </h2> </div> <div> <div> <div> <div style="display: block;"> <label>شروع چت در </label>: <span ng-bind="dateStartChat"></span> <ul> <li ng-repeat="item in agentUserMsgs"> <span ng-bind="item.name" ng-style="setdirectionClass(item.name)"> </span> <span ng-style="setdirectionClass(item.name)"> <span ng-class="setArrowClass(item.name)"></span> <span time-ago title="{{item.date}}"></span> <span> <p ng-bind-html="item.msg | smilies"></p> </span> </span> </li> </ul> <div> <div> <textarea id="post-msg" ng-model="msg" placeholder="متن خود را وارد نمایید" style="overflow: hidden; word-wrap: break-word; resize: horizontal; height: 80px; max-width: 100%"></textarea> <span smilies-selector="msg" smilies-placement="right" smilies-title="Smilies"></span> </div> <div style="text-align: center; margin-top: 5px"> <button ng-click="agentMsgToUser(msg)">ارسال</button> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div>
<html ng-app="app"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Live Support</title> <link href="~/Content/bootstrap-rtl.css" rel="stylesheet" /> <link href="~/Scripts/smilies/angular-smilies-embed.css" rel="stylesheet" /> <link href="~/Content/font-awesome.css" rel="stylesheet" /> <link href="~/Content/toastr.css" rel="stylesheet" /> <link href="~/Content/liveSupport.css" rel="stylesheet" /> <script src="~/Scripts/jquery-1.10.2.js"></script> <script src="~/Scripts/toastr.js"></script> <script src="~/Scripts/jquery.timeago.js"></script> <script src="~/Scripts/angular.js"></script> <script src="~/Scripts/angular-animate.js"></script> <script src="~/Scripts/angular-sanitize.js"></script> <script src="~/Scripts/angular-route.js"></script> <script src="~/Scripts/angular-strap.js"></script> <script src="~/Scripts/angular-strap.tpl.js"></script> <script src="~/Scripts/smilies/angular-smilies.js"></script> <script src="~/Scripts/jquery.signalR-2.2.0.js"></script> <script src="~/Scripts/angular-signalr-hub.js"></script> <script src="~/app/app.js"></script> @Scripts.Render("~/bundles/bootstrap") </head> <body ng-controller="ChatCtrl"> <div ng-view> </div> <div id="chat-box-header" ng-click="boxheader()"> {{chatTitle}} </div> <div id="chat-box"> <div ng-show="hasOnline"> <div id="style-1" style="min-height:100px;"> <div ng-repeat="item in clientAgentMsg track by $index"> <span ng-class="setClass(item.name)"> {{item.name}} </span> <br /> <p ng-bind-html="item.msg | smilies"></p> </div> </div> <div> <label>پیام</label> <div style="text-align: left; clear: both"> <a data-placement="top" data-trigger="hover " bs-tooltip="options.alarm" ng-click="alarm()"><i></i></a> <a data-placement="top" data-trigger="hover " bs-tooltip="options.signOut" href="signOut()"><i></i><span></span></a> <a data-placement="top" data-trigger="hover " bs-tooltip="options.upload" href="fileupload()"> <span><i></i></span> </a> </div> <div> <textarea style="height: 150px; max-height: 160px;" ng-model="message" placeholder=" متن خود را وارد نمایید"></textarea> <span smilies-selector="message" smilies-placement="right" smilies-title="Smilies"></span> </div> </div> <div style="text-align: center"> <button type="button" ng-disabled="pendingRequestChat" ng-click="requestChat(message)">ارسال </button> </div> </div> <div ng-show="hasOffline"> <div> <form name="Ticket" id="form1"> <fieldset> <div> <label>نام</label> <input name="email" ng-model="ticket.name" > </div> <div> <label>ایمیل</label> <input name="email" ng-model="ticket.email" > </div> <div> <label>پیام</label> </div> <div> <textarea ng-model="ticket.comment" placeholder="متن خود را وارد نمایید"></textarea> <span smilies-selector="ticket.comment" smilies-placement="right" smilies-title="Smilies"></span> </div> </fieldset> <div style="text-align: center"> <button type="button" ng-click="ticket.submit(ticket)"> ارسال </button> </div> </form> </div> </div> </div> </body> </html>
نکات تکمیلی :
app.MapSignalR();
Here's a couple of final thoughts.
- 3 major front end frameworks (Angular, React, and Vue)
- React is the highest growing and highest paying front end framework
- Express is still the dominant Node framework by far
- TypeScript is becoming more and more popular (and it's going to take over...just my thought)
- Tooling is making JavaScript a more evolved and appealing language
- Options to reach almost any platform (desktop, mobile, server, web, hybrid, etc.)
- Graphql is on the rise, especially with the rise of Gatsby.js
his release updates Windows Terminal Preview to version 1.8 and moves Windows Terminal to version 1.
IIS 10.0 Express منتشر شد
Internet Information Services (IIS) 10.0 Express is a free, simple and
self-contained version of IIS that is optimized for developers. IIS 10.0
Express makes it easy to use the most current version of IIS to develop
and test websites. IIS 10.0 Express has all the core capabilities of
IIS 10.0 and additional features to ease website development.
The benefits of using IIS 10.0 Express include:
- The same web server that runs on your production server is now available on your development computer.
- Most tasks can be done without the need for administrative privileges.
- IIS Express runs on Windows 7 Service Pack 1 and all later versions of Windows.
- Many users can work independently on the same computer.
UI-Router ابزاری برای مسیریابی در AngularJS است که این امکان را برایتان فراهم میکند تا بخشهای برنامه رابط کاربریتان را به شکل یک ماشین حالت ساماندهی کنید. برخلاف سرویس route$ که بر اساس مسیریابی URLها ساماندهی شده و کار میکند، UI-Router بر اساس حالتها کار میکند، که این حالتها میتوانند در صورت لزوم مسیریابی هم داشته باشند.
UI-Router یکی از افزونههای مجموعه Angular-ui، و پاراگراف بالا معرفی آن در صفحه خانگیش است (تقریبا!). این افزونه جزئیات مفصلی دارد و در این مطلب تنها به معرفی آن خواهم پرداخت (بر اساس مطالب صفحه خانگیش). پیش از ادامه پیشنهاد میکنم اگر مطالب زیر را نخواندهاید ابتدا آنها را مرور کنید:
برای استفاده از UI-Router باید:
- فایل جاوا اسکریپت آن را دانلود کنید (released یا minified).
- در صفحه اصلی برنامهتان پس از include کردن فایل اصلی AngularJS فایل angular-ui-router.js (یا angular-ui-router.min.js) را include کنید.
- 'ui.router' را به لیست وابستگیهای ماژول اصلی اضافه کنید.
<!doctype html> <html ng-app="myApp"> <head> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script> <script src="js/angular-ui-router.min.js"></script> <script> var myApp = angular.module('myApp', ['ui.router']); // For Component users, it should look like this: // var myApp = angular.module('myApp', [require('angular-ui-router')]); </script> ... </head> <body> ... </body> </html>
حالتها و viewهای تو در تو
قابلیت اصلی UI-Router امکان تعریف حالتها و vieweهای تو در تو است. در مطلب مسیریابی در AngularJs #بخش اول دایرکتیو ng-view معرفی شده است. هنگام استفاده از سرویس route$ با این دایرکتیو میتوان محل مورد نظر برای بارگذاری محتویات مربوط به مسیرها را مشخص کرد. دایرکتیو ui-view در UI-Router همین نقش را دارد. فرض کنید این کد فایل index.html باشد:<!-- index.html --> <body> <div ui-view></div> <!-- We'll also add some navigation: --> <a ui-sref="state1">State 1</a> <a ui-sref="state2">State 2</a> </body>
در ادامه برای هر کدام از حالتها یک template اضافه میکنیم:
فایل state1.html:
<!-- partials/state1.html --> <h1>State 1</h1> <hr/> <a ui-sref="state1.list">Show List</a> <div ui-view></div>
<!-- partials/state2.html --> <h1>State 2</h1> <hr /> <a ui-sref="state2.list">Show List</a> <div ui-view></div>
دو نکته قابل توجه در این templateها وجود دارد. اول اینکه همانطور که میبینید templateها خود شامل تگی با دایرکتیو ui-view هستند. و دوم مقدار دایرکتیو ui-sref است که به صورت state1.list و state2.list آمده است. این جدا سازی با نقطه نشان دهنده سلسله مراتب حالتهاست. یعنی حالتهای state1 و state2 هرکدام حالت فرزندی به نام list دارند. در ادامه وقتی حالتها و مسیریابی را در ()app.config تعریف کنیم این مسائل از هالهای از ابهام که در آن هستند خارج میشوند! فعلا بیاید با راهنمای UI-Router پیش برویم و فایلهای template حالتهای فرزند را تعریف کنیم. templateهایی که قرار است در ui-view پدرانشان بارگذاری شوند:
<!-- partials/state1.list.html --> <h3>List of State 1 Items</h3> <ul> <li ng-repeat="item in items">{{ item }}</li> </ul>
<!-- partials/state2.list.html --> <h3>List of State 2 Things</h3> <ul> <li ng-repeat="thing in things">{{ thing }}</li> </ul>
myApp.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) { // // For any unmatched url, redirect to /state1 $urlRouterProvider.otherwise("/state1"); // // Now set up the states $stateProvider .state('state1', { url: "/state1", templateUrl: "partials/state1.html" }) .state('state1.list', { url: "/list", templateUrl: "partials/state1.list.html", controller: function($scope) { $scope.items = ["A", "List", "Of", "Items"]; } }) .state('state2', { url: "/state2", templateUrl: "partials/state2.html" }) .state('state2.list', { url: "/list", templateUrl: "partials/state2.list.html", controller: function($scope) { $scope.things = ["A", "Set", "Of", "Things"]; } }) }]);
خصوصیت url مشخص کننده مسیر حالت است. این خصوصیت همان مقداریست که به عنوان پارامتر اول به ()routeProvider.when$ پاس میشد. در این پارامتر میشود متغیرهای url را هم به همان ترتیب تعریف کرد. مثلا اگر حالت state1 در آدرسش یک پارامتر id داشته باشد میشود آن را به این ترتیب تعریف کرد:
.state('state1', { url: "/state1/:id", templateUrl: "partials/state1.html" })
$stateParams.id
.state('list', { parent: "state1", url: "/list", templateUrl: "partials/state1.list.html", controller: function($scope) { $scope.items = ["A", "List", "Of", "Items"]; } }) .state('list', { parent: "state2", url: "/list", templateUrl: "partials/state2.list.html", controller: function($scope) { $scope.items = ["A", "List", "Of", "Items"]; } })
- وابستگیهای فراهم شده در حالت پدر به وسیله "resolve"
- دادههای سفارشی مشخص شده در خصوصیت data حالت پدر
.state('state1', { url: "/state1", templateUrl: "partials/state1.html", data:{ foodata: 'addorder' } })
$state.current.data.foodata
Viewهای نامگذاری شده و چندگانه
<!-- index.html --> <body> <div ui-view="viewA"></div> <div ui-view="viewB"></div> <!-- Also a way to navigate --> <a ui-sref="route1">Route 1</a> <a ui-sref="route2">Route 2</a> </body>
myApp.config(function ($stateProvider) { $stateProvider .state('index', { url: "", views: { "viewA": { template: "index.viewA" }, "viewB": { template: "index.viewB" } } }) .state('route1', { url: "/route1", views: { "viewA": { template: "route1.viewA" }, "viewB": { template: "route1.viewB" } } }) .state('route2', { url: "/route2", views: { "viewA": { template: "route2.viewA" }, "viewB": { template: "route2.viewB" } } }) });
چند نکته
.state('list', { parent: "state1", url: "/list", templateUrl: "partials/state1.list.html", controller: "state1ListController as listCtrl1" } }) .state('list', { parent: "state2", url: "/list", templateUrl: "partials/state2.list.html", controller: "state2ListController as listCtrl2" } })
حالتهای انتزاعی
حساسیت به حروف بزرگ و کوچک
- در فایل "angular-ui-router.js" عبارت "new RegExp(compiled)" را پیدا کرده و آن را به "RegExp(compiled, 'i')" تبدیل کنید. و یا در "angular-ui-router.min.js" (هرکدام از فایلها که استفاده میکنید) عبارت new RegExp(o) را پیدا کرده و آن را به new RegExp(o, "i") تبدیل کنید. همین؛ صدایش را هم در نیاورید!
سازماندهی برنامههای Angular توسط ماژولها
ویژگی جدیدی به Angular 6 به نام tree-shakable providers اضافه شدهاست که نمونهای از کاربرد آن به صورت پیشفرض در حین «ایجاد پروژهی «کتابخانه» توسط Angular CLI 6.0» ارائه میشود. به این معنا که با استفادهی از آن دیگر نیازی نیست تا سرویسهای سراسری برنامه را در قسمت providers مربوط به CoreModule ثبت کرد. همینقدر که یک سرویس سراسری را به صورت ذیل تعریف کنید:
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class MyCoreService { }
در این حالت تزریق وابستگیهایی که در مطلب «تزریق وابستگیها فراتر از کلاسها در برنامههای Angular» نیز بحث شدند، پشتیبانی میشود:
@Injectable({ providedIn: 'root', useClass: LazyFlightCancellingService, deps: [NgModuleFactoryLoader, Injector] }) export class FlightCancellingService { }
به عنوان مثال کدهای مخزن کد «اعتبارسنجی کاربران در برنامههای Angular» جهت استفادهی از این قابلیت به روز شد.
4.Visual Studio 2017 15.7 منتشر شد
These are the customer-reported issues addressed in 15.7.4:
- Green squiggles and light bulb with "Macro in skipped region" message.
- F7 does not switch to code.
- F7 does not build anymore.
- F7 build does not work.
- LINK : error : Telemetry event upload failed: 'Failed to open connection to VCTIP'.
- AXML file doesn't open after update.
- F7 no longer toggles between the designer and the code.
- XAML Editor Error: Window is not supported in WPF project.
- WinForms View.ToggleDesigner does not work as it used to.
- Toggle designer key binding lost.
- F7 does not start a new build.
- F7 no longer toggles between Designer and Code views.
- There is a problem with structure definition in C language.
- Provisioning a new SQL Server and new SQL DB in a different region than the App Service plan fails.
Git Security Vulnerability
We also fixed a security vulnerability in Git that was disclosed by the Git community. The vulnerability can lead to arbitrary code execution when a user clones a malicious repository. This blog post has more information.
هدف: استفاده از کتابخانهی jsSHA
میخواهیم در یک برنامهی AngularJS 2.0، از کتابخانهی jsSHA استفاده کرده و هش SHA512 یک رشته را محاسبه کنیم.
تامین پیشنیازهای اولیه
میتوان فایلهای این کتابخانه را مستقیما از GitHub دریافت و به پروژه اضافه کرد. اما بهتر است اینکار را توسط npm مدیریت کنیم. به همین جهت فایل package.json آنرا گشوده و سپس مدخل متناظری را به آن اضافه کنید:
"dependencies": { // ... "jssha": "^2.1.0", // ... },
بارگذاری فایلهای کتابخانه به صورت پویا
یک روش استفاده از این کتابخانه یا هر کتابخانهی جاوا اسکریپتی، افزودن مدخل تعریف آن به صفحهی index.html است:
<script src="node_modules/jssha/src/sha512.js"></script>
// map tells the System loader where to look for things var map = { // ... 'jssha': 'node_modules/jssha/src' }; // packages tells the System loader how to load when no filename and/or no extension var packages = { // ... 'jssha': { main: 'sha512.js', defaultExtension: 'js' } };
به این ترتیب هر زمانیکه کار import این کتابخانه صورت گیرد، بارگذاری پویای آن انجام خواهد شد. به علاوه ابزارهای بسته بندی و deploy پروژه هم این فایل را پردازش کرده و به صورت خودکار، کار bundling، فشرده سازی و یکی سازی اسکریپتها را انجام میدهند.
استفاده از jsSHA به صورت untyped
پس از دریافت بستههای این کتابخانه و مشخص سازی نحوهی بارگذاری پویای آن، اکنون نوبت به استفادهی از آن است. در اینجا منظور از untyped این است که فرض کنیم برای این کتابخانه، فایلهای typings مخصوص TypeScript وجود ندارند و پس از جستجوی در مخزن کد https://github.com/DefinitelyTyped/DefinitelyTyped نتوانستهایم معادلی را برای آن پیدا کنیم. بنابراین فایل جدید untyped-sha.component.ts را با محتوای ذیل به پروژه اضافه کنید:
import { Component, OnInit } from '@angular/core'; var jsSHA = require("jssha"); // ==> loads `sha512.js` file dynamically using `systemjs.config.js` file definitions //declare var jsSHA: any; // ==> this requires adding <script src="node_modules/jssha/src/sha512.js"></script> to the first page manually. @Component({ templateUrl: 'app/using-third-party-libraries/untyped-sha.component.html' }) export class UnTypedShaComponent implements OnInit { hash: String; ngOnInit(): void { let shaObj = new jsSHA("SHA-512", "TEXT"); shaObj.update("This is a test"); this.hash = shaObj.getHash("HEX"); } }
<h1>SHA-512 Hash / UnTyped</h1> <p>String: This is a test</p> <p>HEX: {{hash}}</p>
هر زمانیکه فایلهای typing یک کتابخانهی جاوا اسکریپتی در دسترس نبودند، فقط کافی است از روش declare var jsSHA: any استفاده کنید. در اینجا any به همان حالت استاندارد و بینوع جاوا اسکریپت اشاره میکند. در این حالت برنامه بدون مشکل کامپایل خواهد شد؛ اما از تمام مزایای TypeScript مانند بررسی نوعها و همچنین intellisense محروم میشویم.
در این مثال در hook ویژهای به نام OnInit، کار ساخت شیء SHA را انجام داده و سپس هش عبارت This is a test محاسبه شده و به خاصیت عمومی hash انتساب داده میشود. سپس این خاصیت عمومی، در قالب این کامپوننت از طریق روش interpolation نمایش داده شدهاست.
دو نکتهی مهم
الف) اگر از روش declare var jsSHA: any استفاده کردید، کار بارگذاری فایل sha512.js به صورت خودکار رخ نخواهد داد؛ چون ماژولی را import نمیکند. بنابراین تعاریف systemjs.config.js ندید گرفته خواهد شد. در این حالت باید از همان روش متداول افزودن تگ script این کتابخانه به فایل index.html استفاده کرد.
ب) برای بارگذاری پویای کتابخانهی jsSHA بر اساس تعاریف فایل systemjs.config.js از متد require کمک بگیرید:
var jsSHA = require("jssha");
استفاده از jsSHA به صورت typed
کتابخانهی jsSHA در مخزن کد https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/jssha دارای فایل d.ts. مخصوص خود است. برای نصب آن از یکی از دو روش ذیل استفاده کنید:
الف) نصب دستی فایلهای typings
npm install -g typings typings install jssha --save --ambient
ب) تکمیل فایل typings.ts
{ "ambientDependencies": { // ... "jssha": "registry:dt/jssha#2.1.0+20160317120654" } }
پس از نصب فایلهای typings این پروژه، به فایل main.ts مراجعه کرده و مدخل ذیل را به ابتدای آن اضافه کنید:
/// <reference path="../typings/browser/ambient/jssha/index.d.ts" />
در ادامه فایل جدید typed-sha.component.ts را با محتوای ذیل به پروژه اضافه کنید:
import { Component, OnInit } from '@angular/core'; //import { jsSHA } from "jssha"; import * as jsSHA from "jssha"; // ===> var jsSHA = require("jssha"); // ===> loads `sha512.js` file dynamically using `systemjs.config.js` file definitions @Component({ templateUrl: 'app/using-third-party-libraries/typed-sha.component.html' }) export class TypedShaComponent implements OnInit{ hash: String; ngOnInit(): void { let shaObj = new jsSHA("SHA-512", "TEXT"); shaObj.update("This is a test"); this.hash = shaObj.getHash("HEX"); } }
در اینجا تنها نکتهی مهم و جدید نسبت به روش قبل (استفاده از jsSHA به صورت untyped)، روش import این کتابخانه است. روش "import * as jsSHA from "jssha به عبارت var jsSHA = require("jssha") ترجمه میشود که در نهایت سبب بارگذاری خودکار فایلهای jssha بر اساس تعاریف مداخل آن در فایل systemjs.config.js میگردد.
کدهای کامل این پروژه را از اینجا میتوانید دریافت کنید.