1/** 2 * @license global.js 3 * 4 * Copyright 2015 Avaya Inc. All Rights Reserved. 5 * 6 * Usage of this source is bound to the terms described in 7 * licences/License.txt 8 * 9 * Avaya - Confidential & Proprietary. Use pursuant to your signed agreement or 10 * Avaya Policy 11 */ 12 13/* 14 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 15 * Holds functions and variables that are accessible across the entire site. 16 * Import this before any other script, if not using a concatenated file. 17 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 18 */ 19 20// Maximum customers per agent 21var customerSoakValue = 2; 22var browserWarning = 'Your browser does not support required features for this website. The earliest supported versions are Internet Explorer 10, Firefox 11, Chrome 31, Safari 7.1 and Opera 12.1'; 23 24// If using a popup for chat, set this to true 25var usePopupForChat = false; 26 27// the timeout for the currentqueue REST call 28var queueTimeout = 60000; 29 30// the timeout for the callback REST calls 31var callbackTimeout = 60000; 32 33// Detect browser support when ready 34document.onreadystatechange = function() { 35 'use strict'; 36 37 // IE 10 does not appear to recognise 'interactive' - however, IE 11, 38 // Firefox and Chrome do 39 if (document.readyState === 'complete' || document.readyState === 'interactive') { 40 detectBrowserSupport(); 41 gatherElements(); 42 } 43}; 44 45/** 46 * Logs a specified message to the browser console, if it has been defined for a 47 * particular browser. 48 * @param messageToLog 49 */ 50function logToConsole(messageToLog) { 51 'use strict'; 52 if (console !== undefined) { 53 console.log(messageToLog); 54 } 55} 56 57/** 58 * Returns a specific element 59 * @param el 60 * @returns the element with the specific Id 61 */ 62function getEl(el) { 63 'use strict'; 64 return document.getElementById(el); 65} 66 67/** 68 * Checks if the user's browser supports the required features. For the chat to 69 * work, the browser <b>MUST</b> support JSON parsing, WebSockets and 70 * XMLHttpRequests. If it does not support any of these, alert the user. 71 */ 72function detectBrowserSupport() { 73 'use strict'; 74 if (!WebSocket || !XMLHttpRequest || !JSON) { 75 window.alert(browserWarning); 76 } 77 78} 79 80/** 81 * Adds the specified key-value pair to sessionStorage. 82 * @param key 83 * @param value 84 */ 85function setSessionStorage(key, value) { 86 'use strict'; 87 sessionStorage.setItem(key, value); 88} 89 90/** 91 * Get the specified item from sessionStorage 92 * @param key 93 * @returns 94 */ 95function getSessionStorage(key) { 96 'use strict'; 97 return sessionStorage.getItem(key); 98} 99 100/** 101 * Gathers all elements on page load. 102 */ 103function gatherElements() { 104 'use strict'; 105 logToConsole('Gathering elements'); 106 sendButton = getEl('sendbutton-chat'); 107 callbackRequestButton = getEl('callbackPanel'); 108 closeButton = getEl('closebutton-chat'); 109 messages = getEl('messages'); 110 outMessage = getEl('outmessage'); 111 participants = getEl('participants'); 112 pagePushDiv = getEl('pagePushDiv'); 113} 114 115/** 116 * Clear the user details from session storage. 117 */ 118function clearSessionStorage() { 119 'use strict'; 120 setSessionStorage('user', ''); 121 setSessionStorage('phone', ''); 122 setSessionStorage('email', ''); 123 setSessionStorage('account', ''); 124 setSessionStorage('skillset', ''); 125 setSessionStorage('subject', ''); 126} 127 128/** 129 * webChatConfig.js 130 * 131 * Copyright 2015 Avaya Inc. All Rights Reserved. 132 * 133 * Usage of this source is bound to the terms described in 134 * licences/License.txt 135 * 136 * Avaya - Confidential & Proprietary. Use pursuant to your signed agreement or 137 * Avaya Policy 138 */ 139 140/* 141 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 142 * 143 * This section configures the Web Chat. 144 * 145 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 146 */ 147 148var webSocket; 149 150// Variables for reconnection timeout. 151// Try every 3 seconds for a total of 10 minutes 152var totalNumberOfRetrys = 0; 153var maxNumberOfRetrys = 200; 154var retryInterval = 3000; 155var dontRetryConnection = false; 156var reconnectionTimeout; 157var previouslyConnected = false; 158 159//queue Status delay timeout 160var queueStatusDelayTimeout = 3500; 161 162// Message types 163var messageTypeNewChatAck = 'newChatAcknowledgement'; 164var messageTypeAck = 'acknowledgement'; 165var messageTypeError = 'error';
166var messageTypeNotification = 'notification'; 167 168// Json method names for notification messages 169var jsonMethodRequestChat = 'requestChat'; 170var jsonMethodRequestCallBack = 'callBack'; 171var jsonMethodRequestNewParticipant = 'newParticipant'; 172var jsonMethodRequestisTyping = 'isTyping'; 173var jsonMethodRequestNewMessage = 'newMessage'; 174var jsonMethodRequestCloseConversation = 'closeConversation'; 175var jsonMethodRequestParticipantLeave = 'participantLeave'; 176var jsonMethodRequestNewPushMessage = 'newPushPageMessage'; 177var jsonMethodRequestRequestTranscript = 'requestTranscript'; 178var jsonMethodRequestQueueMetrics = 'queueStatus'; 179var jsonMethodPing = 'ping'; 180 181// run the Web On Hold messages every 3 seconds 182var webOnHoldTimer = 3000; 183 184// typingTimeouts 185var typingTimeout = 10000; 186var agentTypingTimeout = 3000; 187 188// the ping timer affects how often PING messages are sent 189var pingTimer = 5000; 190 191// Some of these variables will be moved around 192var participants; 193var users = {}; 194 195// Write response classes - defined in the sample style.css file 196var writeResponseClassResponse = 'response'; 197var writeResponseClassSent = 'sent'; 198 199// Images for the users panel 200var agentImage = 'images/agent.png'; 201var agentTypingImage = 'images/agent_typing.png'; 202var supervisorImage = 'images/supervisor.png'; 203var supervisorTypingImage = 'images/supervisor_typing.png'; 204 205/** 206 * webChat.js 207 * 208 * Copyright 2015 Avaya Inc. All Rights Reserved. 209 * 210 * Usage of this source is bound to the terms described in 211 * licences/License.txt 212 * 213 * Avaya - Confidential & Proprietary. Use pursuant to your signed agreement or 214 * Avaya Policy 215 */ 216 217/* 218 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 219 * This section defines the client end of the WebSocket chat. 220 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 221 */ 222 223// particular elements with particular names 224// These rely on elements in the page, or need to be defined first 225var sendButton; 226var callbackRequestButton; 227var closeButton; 228var messages; 229var outMessage; 230var guid = null; 231var webOnHoldComfortGroups; 232var webOnHoldURLs; 233 234// the authentication key 235var ak = null; 236 237// run the WebOnHold check every 3 seconds 238var webOnHoldInterval; 239 240// PING messages are sent at regular intervals 241var pingInterval; 242 243// these handle the isTyping notifications. By default, allows 244// a 10 second gap between keypresses 245var isTyping = false; 246var typeOut; 247var activeAgentTypeOut; 248var passiveAgentTypeOut; 249var supervisorTypeOut; 250var usersTable; 251 252// the queue status check is sent at a delay 253var queueStatusDelay; 254 255// the following variables require getSessionStorage, 256// but are initialised on chat begin 257var g_email, g_user, g_skillset, g_account, g_phone, g_country, g_subject; 258 259var initCalled = false; 260 261// should page pushes from agent open automatically? 262var autoOpen = false; 263 264// an array of timeouts to clear 265var timeouts = []; 266 267var pagePushDiv; 268 269/** 270 * Toggles the controls. Ignores the close button so the user can leave a chat 271 * at any time. 272 * @param isDisabled is true if controls are disabled 273 */ 274function disableControls(isDisabled) { 275 'use strict'; 276 277 logToConsole('Disabling controls: ' + isDisabled); 278 sendButton.disabled = isDisabled; 279 outMessage.disabled = isDisabled; 280 281 if (isDisabled === false) { 282 pagePushDiv.style.display = 'block'; 283 outMessage.addEventListener('keydown', onType); 284 } else { 285 pagePushDiv.style.display = 'none'; 286 outMessage.removeEventListener('keydown', onType); 287 288 } 289} 290 291/** 292 * Called when the customer types 293 * @param e 294 */ 295function onType(e) { 296 'use strict'; 297 if (e.keyCode === 13) { 298 sendChatMessage(); 299 } else { 300 startTypingTimer(); 301 } 302} 303 304/** 305 * Creates a new paragraph with a specific class, adds the specified text to it, 306 * and then appends it to the messages div. Also includes an autoscroll 307 * mechanism. 308 * @param text 309 * @param msgClass defined inside the CSS file 310 */ 311function writeResponse(text, msgClass) { 312 'use strict'; 313 var paragraph = document.createElement('p'); 314 paragraph.className = msgClass; 315 paragraph.textContent = text; 316 messages.appendChild(paragraph); 317 // Scroll to bottom of the messages div to show new messages 318 messages.scrollTop = messages.scrollHeight; 319} 320 321/** 322 * Updates the users section. 323 */ 324function updateUsers(agents) { 325 'use strict'; 326 327 users = {}; 328 329 // hide the children immediately, and then reshow those 330 for (var x = 0; x < participants.children.length; x++) { 331 var child = participants.children[x], image = child.getElementsByTagName('img')[0]; 332 child.className = 'hidden'; 333 image.className = 'hidden'; 334 image.src = ''; 335 // clear the TextNode after this - i.e. removes the text after the image 336 image.nextSibling.textContent = ''; 337 } 338 339 if (agents !== undefined) { 340 for (var i = 0; i < agents.length; i++) { 341 var agent = agents[i]; 342 logToConsole('Adding agent with id ' + agent.id + ' and name ' + agent.name); 343 344 var div = participants.children[i]; 345 div.className = 'user'; 346 // find the first image in the div 347 var typingImage = div.getElementsByTagName('img')[0]; 348 typingImage.src = (agent.type === 'supervisor') ? supervisorImage : agentImage; 349 typingImage.className = ''; 350 351 // should be the first TextNode in the div after the image 352 // which enters it into the div just after the image 353 typingImage.nextSibling.textContent = agent.name; 354
355 users[agent.id] = { 356 name : agent.name, 357 isTyping : false, 358 agentType : agent.type, 359 image : typingImage 360 }; 361 } 362 } 363} 364 365/** 366 * Adds a timer to the specified array. 367 * @param array 368 */ 369function addTimerToArray(array) { 370 'use strict'; 371 if (array === undefined) { 372 return; 373 } 374 375 for (var p = 0; p < array.length; p++) { 376 var obj = array[p]; 377 obj.lastCalled = new Date().valueOf(); 378 obj.currentSequence = 0; 379 } 380} 381 382/** 383 * Plays the WebOnHold messages. 384 * @param array 385 */ 386function playOnHoldMessages(array) { 387 'use strict'; 388 var currentDate = new Date(), currentTime = currentDate.valueOf(); 389 for (var i = 0; i < array.length; i++) { 390 var group = array[i]; 391 var deltaTime = (currentTime - group.lastCalled) / 1000; 392 if (deltaTime >= group.delay || deltaTime >= group.holdTime) { 393 var currentMsg; 394 395 // if this has a urls array, it's a WebOnHold URL 396 // otherwise, it's a comfort message 397 if (group.urls !== undefined) { 398 currentMsg = group.urls[group.currentSequence]; 399 400 writeResponse('Please visit this link: ', writeResponseClassResponse); 401 appendLink(currentMsg.url, '_blank', false); 402 403 } else { 404 currentMsg = group.messages[group.currentSequence]; 405 writeResponse(currentMsg.message, writeResponseClassResponse); 406 } 407 group.lastCalled = currentTime; 408 group.currentSequence++; 409 if ((group.numberOfMessages !== undefined && group.currentSequence >= group.numberOfMessages) || 410 (group.urls !== undefined && group.currentSequence >= group.urls.length)) { 411 group.currentSequence = 0; 412 } 413 } 414 } 415} 416 417/** 418 * Append a link to the transcript box. 419 * @param url 420 * @param urlDestination destination for the URL 421 * @param openAutomatically if true, should open automatically 422 */ 423function appendLink(url, urlDestination, openAutomatically) { 424 'use strict'; 425 426 var p = document.createElement('p'); 427 var link = document.createElement('a'); 428 link.href = url; 429 link.text = url; 430 link.target = '_' + urlDestination; 431 p.appendChild(link); 432 messages.appendChild(link); 433 434 // Scroll to bottom of the messages div to show new messages 435 messages.scrollTop = messages.scrollHeight; 436 437 if (openAutomatically) { 438 window.open(link); 439 } 440} 441 442/** 443 * Logs the user into the chat. 444 * @param user 445 * @param email 446 * @param account 447 * @param phone 448 */ 449function chatLogin(user, email, account, skillset, phone) { 450 'use strict'; 451 var wantsEmail = getEl('transcript-chat').checked; 452 var custAddress = getEl('address-chat'); 453 454 // if the user didn't specify a valid email address, they can't receive a transcript. 455 if (isStringEmpty(email)) { 456 wantsEmail = false; 457 } 458 459// logToConsole('country is ' + phone.country + ', area is ' + phone.area + ', phone is ' + phone.phone); 460 var msg = { 461 'apiVersion' : '1.0', 462 'type' : 'request', 463 'body' : { 464 'method' : 'requestChat', 465 'guid' : guid, 466 'authenticationKey' : ak, 467 'deviceType' : navigator.userAgent, 468 'requestTranscript' : wantsEmail, 469 'intrinsics' : { 470 'email' : email, 471 'name' : user, 472 //'country' : phone.country, 473 //'area' : phone.area, 474 //'phoneNumber' : phone.phone, 475 'skillset' : g_skillset //, 476 //'customFields' : [ { 477 // 'title' : 'address', 478 // 'value' : custAddress.value 479 //} ] 480 } 481 } 482 }; 483 writeResponse('Sende Anmeldung zum Server', writeResponseClassSent); 484 sendMessage(msg); 485} 486 487/** 488 * Sends the chatMessage. 489 */ 490function sendChatMessage() { 491 'use strict'; 492 var text = outMessage.value; 493 494 if (text !== '') { 495 writeResponse(g_user + ': ' + text, writeResponseClassSent); 496 var message = { 497 'apiVersion' : '1.0', 498 'type' : 'request', 499 'body' : { 500 'method' : 'newMessage', 501 'message' : text 502 } 503 }; 504 sendMessage(message); 505 outMessage.value = ''; 506 507 // set isTyping to false 508 isTyping = false;
509 } 510 511} 512 513/** 514 * Sends a close conversation request to the server. 515 */ 516function quitChat() { 517 'use strict'; 518 if (webSocket !== null && webSocket.readyState === webSocket.OPEN) { 519 var closeRequest = { 520 'apiVersion' : '1.0', 521 'type' : 'request', 522 'body' : { 523 'method' : 'closeConversation' 524 } 525 }; 526 writeResponse('Close request sent', writeResponseClassSent); 527 sendMessage(closeRequest); 528 } 529} 530 531/** 532 * Sends an IsTyping message to the server 533 * @param isUserTyping 534 */ 535function sendIsTyping(isUserTyping) { 536 'use strict'; 537 var isTypingMessage = { 538 'apiVersion' : '1.0', 539 'type' : 'request', 540 'body' : { 541 'method' : 'isTyping', 542 'isTyping' : isUserTyping 543 } 544 }; 545 sendMessage(isTypingMessage); 546} 547 548/** 549 * Sends a page push request to the agent. Remove if not required. 550 */ 551function sendPagePushRequest() { 552 'use strict'; 553 554 // get the URL, and then trim whitespace from it 555 var url = getEl('urlInput').value.trim(); 556 557 if (url === '') { 558 writeResponse('Please enter a link to send it', writeResponseClassResponse); 559 return; 560 } 561 562 var ppr = { 563 'apiVersion' : '1.0', 564 'type' : 'request', 565 'body' : { 566 'method' : 'newPushPageMessage', 567 'pagePushURL' : url, 568 'pagePushDestination' : 'newTab' 569 } 570 }; 571 572 writeResponse('Your link has been sent', writeResponseClassSent); 573 getEl('urlInput').value = ''; 574 sendMessage(ppr); 575 576} 577 578/** 579 * Sends a queueStatusRequest upon login. 580 */ 581function sendQueueStatusRequest() { 582 'use strict'; 583 var statusRequest = { 584 'apiVersion' : 1.0, 585 'type' : 'request', 586 'body' : { 587 'method' : 'queueStatus' 588 } 589 }; 590 591 sendMessage(statusRequest); 592 593} 594 595/** 596 * When a message of type requestChat comes in, use it to initialise some 597 * variables and the queue status check. 598 * @param body 599 */ 600function notifyRequestChat(body) { 601 'use strict'; 602 603 logToConsole(body); 604 guid = body.guid; 605 g_user = body.intrinsics.name; 606 ak = body.authenticationKey; 607 writeResponse('Der nächste freie Mitarbeiter ist gleich für Sie da.', writeResponseClassResponse); 608 609 if (totalNumberOfRetrys > 0) { 610 resetConnectionAttempts(); 611 } 612 613 // if the customer has already been connected, don't play the on hold 614 // messages 615 if (!previouslyConnected) { 616 previouslyConnected = true; 617 webOnHoldComfortGroups = body.webOnHoldComfortGroups; 618 webOnHoldURLs = body.webOnHoldURLs; 619 addTimerToArray(webOnHoldComfortGroups); 620 addTimerToArray(webOnHoldURLs); 621 startOnHoldMessages(); 622 queueStatusDelay = setTimeout(sendQueueStatusRequest, queueStatusDelayTimeout); 623 } 624 625} 626 627/** 628 * Notifies the user of the new participant. 629 * @param body of the message 630 */ 631function notifyNewParticipant(body) { 632 'use strict'; 633 634 writeResponse('Ein Mitarbeiter hat den Chat betreten', writeResponseClassResponse); 635 var agents = body.participants; 636 updateUsers(agents); 637 638 // enable the controls and clear the intervals 639 clearInterval(webOnHoldInterval); 640 clearTimeout(queueStatusDelay); 641 disableControls(false); 642} 643 644/** 645 * Notifies the user that another user is typing. 646 * @param body of the message 647 */ 648function notifyIsTyping(body) { 649 'use strict'; 650 var isAgentTyping = body.isTyping; 651 652 if (isAgentTyping === true) { 653 var agent = users[body.agentId]; 654 agent.isTyping = isAgentTyping; 655 updateTypingCell(agent, true); 656 657 var agentTypeOut; 658 if (agent.type === 'active_participant') { 659 agentTypeOut = activeAgentTypeOut; 660 } else if (agent.type === 'passive_participant') { 661 agentTypeOut = passiveAgentTypeOut; 662 } else { 663 agentTypeOut = supervisorTypeOut; 664 } 665 666 if (agentTypeOut !== undefined) { 667 clearTypingTimer(agentTypeOut); 668 } 669 670 agentTypeOut = setTimeout(function() { 671 if (users !== undefined) { 672 agent.isTyping = false;
673 updateTypingCell(agent, false); 674 } 675 }, agentTypingTimeout); 676 timeouts.push(agentTypeOut); 677 } 678} 679 680/** 681 * Updates the typing icon for a specific agent. 682 * @param agent 683 * @param isTyping 684 */ 685function updateTypingCell(agent, isTyping) { 686 'use strict'; 687 var image = agent.image; 688 689 if (agent.agentType === 'active_participant' || agent.agentType === 'passive_participant') { 690 image.src = isTyping === true ? agentTypingImage : agentImage; 691 692 } else { 693 image.src = isTyping === true ? supervisorTypingImage : supervisorImage; 694 } 695 696 image.nextSibling.textContent = isTyping === true ? agent.name.concat(' schreibt') : agent.name; 697 698} 699 700/** 701 * Notifies the user that an actual message has arrived. 702 * @param body of the message 703 */ 704function notifyNewMessage(body) { 705 'use strict'; 706 // parse the date first, then write the message out to the div 707 var date = new Date(body.timestamp); 708 writeResponse(body.displayName + ': ' + body.message + ' - gesendet um ' + date.toLocaleTimeString(), 709 writeResponseClassResponse); 710} 711 712/** 713 * Notifies the user of the status of a close request. Mainly used when the 714 * customer decides to leave. 715 * @param body of the message 716 */ 717function notifyCloseConversation(body) { 718 'use strict'; 719 logToConsole('Soll der Chat geschlossen werden? ' + body.result); 720 721 dontRetryConnection = body.result; 722 if (body.result === true) { 723 webSocket.close(1000, 'Chat erfolgreich geschlossen'); 724 725 // clear the user's details from session storage 726 clearSessionStorage(); 727 } 728} 729 730/** 731 * Notifies the user that another user has left the chat. If the endChatFlag 732 * variable of the message is set to true, close the webSocket. 733 * @param body of the message 734 */ 735function notifyParticipantLeave(body) { 736 'use strict'; 737 738 writeResponse('Der Mitarbeiter hat den Chat verlassen', writeResponseClassResponse); 739 740 if (body.endChatFlag === true) { 741 dontRetryConnection = true; 742 webSocket.close(1000, 'Gespräch beenden'); 743 } else { 744 // if there is only one user left (i.e. the customer), 745 // play the webOnHold messages 746 if (Object.keys(body.participants).length === 0) { 747 logToConsole('Nur der Kunde ist noch im Chat.'); 748 disableControls(true); 749 startOnHoldMessages(); 750 participants.textString = ''; 751 } 752 } 753 754 var agents = body.participants; 755 updateUsers(agents); 756} 757 758/** 759 * Notifies the user of a new page being pushed. 760 * @param body of the message 761 */ 762function notifyNewPagePushMessage(body) { 763 'use strict'; 764 var sentDate = new Date(body.timestamp), url = body.pagePushURL, destination = body.pagePushDestination; 765 writeResponse(body.displayName + ' pushed the following URL at ' + sentDate.toLocaleTimeString() + ':', 766 writeResponseClassResponse); 767 768 appendLink(url, destination, autoOpen); 769 770} 771 772/** 773 * Notifies the user of the current queue status. 774 * @param body 775 */ 776function notifyQueueStatus(body) { 777 'use strict'; 778 var position = body.positionInQueue, wait = body.estimatedWaitTime; 779 logToConsole('Kunde ist in position ' + position + ' und wartet bereits seit ' + wait + ' Sekunden'); 780 var waitMinutes = Math.round(wait / 60); 781 782 // if the customer's expected wait time or position in the queue is greater than 0, let them know. 783 if (waitMinutes > 0 && position > 0) { 784 writeResponse('Sie sind auf Position ' + position + ' in der Warteschlange. Die vorraussichtliche Wartezeit beträgt ' + waitMinutes + 785 ' Minuten', writeResponseClassResponse); 786 } 787 788} 789 790/** 791 * Handles notification messages to reduce complexity of handleMessage. This in 792 * turn is broken into subfunctions to further reduce it. 793 * @param message 794 */ 795function handleNotification(message) { // NOSONAR Reason: too complex, but cannot be reduced further 796 'use strict'; 797 var body = message.body, method = body.method; 798 799 if (method === jsonMethodRequestChat) { 800 notifyRequestChat(body); 801 } else if (method === jsonMethodRequestNewParticipant) { 802 notifyNewParticipant(body); 803 } else if (method === jsonMethodRequestisTyping) { 804 notifyIsTyping(body); 805 } else if (method === jsonMethodRequestNewMessage) { 806 notifyNewMessage(body); 807 } else if (method === jsonMethodRequestCloseConversation) { 808 notifyCloseConversation(body); 809 } else if (method === jsonMethodRequestParticipantLeave) { 810 notifyParticipantLeave(body); 811 } else if (method === jsonMethodRequestNewPushMessage) { 812 notifyNewPagePushMessage(body); 813 } else if (method === jsonMethodRequestQueueMetrics) { 814 notifyQueueStatus(body); 815 } else if (method === jsonMethodPing) { 816 // do nothing with pings. They just confirm that the websocket is open. 817 } else { 818 throw new TypeError('Received notification with unknown method: ' + method); 819 } 820 821} 822 823/** 824 * Opens the chat. 825 */ 826function openChat() { 827 'use strict';
828 initChat(); 829 830} 831 832/** 833 * Performs the actual initialisation. 834 */ 835function initChat() { 836 'use strict'; 837 838 // if the chat has already opened, don't bother opening it 839 if (initCalled) { 840 return; 841 } 842 843 g_user = getSessionStorage('user'); 844 g_email = getSessionStorage('email'); 845 g_skillset = getSessionStorage('skillset'); 846 g_account = getSessionStorage('account'); 847 g_phone = ''; //JSON.parse(getSessionStorage('phone')); 848 g_country = getSessionStorage('country'); 849 g_subject = getSessionStorage('subject'); 850 logToConsole('Subject is ' + g_subject); 851 852 // this is sometimes undefined at this point. 853 if (outMessage === undefined) { 854 outMessage = getEl('outmessage'); 855 } 856 857 disableControls(true); 858 openSocket(); 859 initCalled = true; 860} 861 862/** 863 * Start playing the Web on hold messages. 864 */ 865function startOnHoldMessages() { 866 'use strict'; 867 868 logToConsole('Starting the On Hold messages'); 869 webOnHoldInterval = setInterval(function() { 870 if (webOnHoldURLs !== undefined) { 871 playOnHoldMessages(webOnHoldURLs); 872 } else { 873 logToConsole('On Hold URLS not defined'); 874 } 875 876 if (webOnHoldComfortGroups !== undefined) { 877 playOnHoldMessages(webOnHoldComfortGroups); 878 } else { 879 logToConsole('On Hold Messages not defined'); 880 } 881 882 }, webOnHoldTimer); 883 884 timeouts.push(webOnHoldInterval); 885} 886 887/** 888 * Clear the keypress timer. 889 * @param obj 890 */ 891function clearTypingTimer(obj) { 892 'use strict'; 893 if (obj) { 894 clearTimeout(obj); 895 } 896} 897 898/** 899 * Start the keypress timer. 900 * @param obj 901 */ 902function startTypingTimer() { 903 'use strict'; 904 clearTypingTimer(typeOut); 905 906 if (isTyping === false) { 907 isTyping = true; 908 sendIsTyping(isTyping); 909 } 910 911 typeOut = setTimeout(function() { 912 isTyping = false; 913 914 }, typingTimeout); 915 timeouts.push(typeOut); 916} 917 918/** 919 * Clear all timeouts in the array. 920 * @param timeouts 921 */ 922function clearAllTimeouts() { 923 'use strict'; 924 925 for (var i = 0; i < timeouts.length; i++) { 926 clearTimeout(timeouts[i]); 927 } 928} 929 930/** 931 * Sends a PING message to check the server. There is no API in JavaScript to do 932 * this, so this is a workaround. Send this every 10 seconds or so. 933 */ 934function sendPing() { 935 'use strict'; 936 937 var ping = { 938 'apiVersion' : '1.0', 939 'type' : 'request', 940 'body' : { 941 'method' : 'ping' 942 } 943 }; 944 945 webSocket.send(JSON.stringify(ping)); 946} 947 948/** 949 * webChatSocket.js 950 * 951 * Copyright 2015 Avaya Inc. All Rights Reserved. 952 * 953 * Usage of this source is bound to the terms described in 954 * licences/License.txt 955 * 956 * Avaya - Confidential & Proprietary. Use pursuant to your signed agreement or 957 * Avaya Policy 958 */ 959 960/* 961 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 962 * 963 * The following section defines the WebSocket and it's interactions 964 * 965 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 966 */ 967 968/** 969 * Opens the WebSocket. 970 */ 971function openSocket() { 972 'use strict'; 973 participants = getEl('participants'); 974 logToConsole('Opening the WebSocket'); 975 976 // Ensures only one connection is open at a time 977 if (webSocket !== undefined && (webSocket.readyState !== WebSocket.CLOSED)) { 978 logToConsole('WebSocket is already opened'); 979 return; 980 } 981 982 clearTimeout(reconnectionTimeout); 983 984 // Create a new instance of the WebSocket using the specified url 985 webSocket = new WebSocket(webChatUrl); 986 987 // Binds functions to the listeners for the websocket. 988 webSocket.onopen = handleOpen; 989 webSocket.onmessage = handleMessage; 990 webSocket.onclose = function(event) { 991 992 // disable the controls upon close 993 disableControls(true); 994 995 // If it is an expected/graceful close, do not attempt to reconnect. 996 if (event.code === 1000 || dontRetryConnection) { 997 handleClose(event); 998 } else { 999 reconnect(); 1000 } 1001 }; 1002 webSocket.onerror = handleError; 1003} 1004 1005/** 1006 * Opens the WebSocket. 1007 * @param event 1008 */ 1009function handleOpen(event) { 1010 'use strict'; 1011 1012 // if there are agents in the chat, enable the controls 1013 if (Object.keys(users).length <= 0) { 1014 logToConsole('No users in room, disabling controls'); 1015 disableControls(true); 1016 } else { 1017 logToConsole('Agents already in chat, enabling controls'); 1018 disableControls(false); 1019 } 1020 1021 pingInterval = setInterval(function() { 1022 sendPing(); 1023 }, pingTimer); 1024 timeouts.push(pingInterval); 1025 1026 chatLogin(g_user, g_email, g_account, g_skillset, g_phone); 1027} 1028 1029/** 1030 * Handles incoming messages. The message is parsed as a JSON object, and then 1031 * handled according to the message type. 1032 * @param event 1033 */ 1034function handleMessage(event) { 1035 'use strict'; 1036 var msg = JSON.parse(event.data), body = msg.body, method = body.method; 1037 1038 // Handle the message according to the type and method. 1039 // Notifications are in their own method to reduce complexity. 1040 if (msg.type === messageTypeNotification) { 1041 handleNotification(msg); 1042 } else if (msg.type === messageTypeError) { 1043 writeResponse('An error occurred ' + body.code + ' (' + body.errorMessage + ')', writeResponseClassResponse); 1044 } else if (msg.type === messageTypeAck) { 1045 // Nothing to do for acks 1046 } else if (msg.type === messageTypeNewChatAck) { 1047 // if a newChatAcknowledgement has been received, 1048 // then use it to initialise the guid, webOnHold variables, etc. 1049 guid = body.guid; 1050 // enable the controls and let the user know that their request 1051 // has been approved 1052 disableControls(false); 1053 resetConnectionRetrys(); 1054 writeResponse('Chat request approved', writeResponseClassResponse); 1055 } else { 1056 throw new TypeError('Unknown message type:\n' + msg); 1057 } 1058} 1059/** 1060 * Upon closing, inform the user and disable the controls. 1061 * @param event 1062 */ 1063function handleClose() { 1064 'use strict'; 1065 logToConsole('Definitely closing the WebSocket.'); 1066 writeResponse('Verbindung geschlossen', writeResponseClassResponse); 1067 disableControls(true); 1068 outMessage.textContent = ''; 1069 clearAllTimeouts(); 1070 1071 updateUsers(); 1072} 1073 1074/** 1075 * Handles errors by printing them to the browser console and alerting the user. 1076 * @param event 1077 */ 1078function handleError(event) { 1079 'use strict'; 1080 logToConsole(event); 1081 writeResponse('A connection error has occurred', writeResponseClassResponse); 1082} 1083 1084/**
1085 * Upon disconnection, this will attempt to reconnect to a WebSocket every 1086 * @param retryInterval 1087 * @param totalNumberOfRetrys 1088 */ 1089function reconnect() { 1090 'use strict'; 1091 if (webSocket.readyState !== webSocket.OPEN) { 1092 reconnectionTimeout = setTimeout(function() { 1093 if (totalNumberOfRetrys <= maxNumberOfRetrys) { 1094 logToConsole('Connection failed. Reconnecting (attempt ' + totalNumberOfRetrys + ' of ' + 1095 maxNumberOfRetrys + ')'); 1096 openSocket(); 1097 1098 if (!dontRetryConnection) { 1099 clearTimeout(reconnectionTimeout); 1100 totalNumberOfRetrys++; 1101 reconnect(); 1102 } 1103 1104 } else { 1105 dontRetryConnection = true; 1106 writeResponse('Connection failed', writeResponseClassResponse); 1107 } 1108 }, retryInterval); 1109 } 1110 1111} 1112 1113/** 1114 * Called after a successful connection to reset the counter for number of 1115 * socketConnection retries 1116 */ 1117function resetConnectionAttempts() { 1118 'use strict'; 1119 totalNumberOfRetrys = 0; 1120 dontRetryConnection = false; 1121 clearTimeout(reconnectionTimeout); 1122 1123} 1124 1125/** 1126 * Stringifies a JSON object and sends it over the WebSocket. 1127 * @param outMsg 1128 */ 1129function sendMessage(outMsg) { 1130 'use strict'; 1131 if (webSocket !== null && webSocket.readyState === WebSocket.OPEN) { 1132 webSocket.send(JSON.stringify(outMsg)); 1133 } 1134} 1135 1136/** 1137 * webcheckQueue.js 1138 * 1139 * Copyright 2015 Avaya Inc. All Rights Reserved. 1140 * 1141 * Usage of this source is bound to the terms described in 1142 * licences/License.txt 1143 * 1144 * Avaya - Confidential & Proprietary. Use pursuant to your signed agreement or 1145 * Avaya Policy 1146 */ 1147 1148/* 1149 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 1150 * 1151 * This section performs a REST call to check the status of a queue 1152 * on the main pages. 1153 * 1154 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 1155 */ 1156 1157var pageSkillset; 1158 1159/** 1160 * Checks if a particular string is empty 1161 * @param str 1162 * @returns {Boolean} 1163 */ 1164function isStringEmpty(str) { 1165 'use strict'; 1166if ((str !== undefined) && (str !== null)) { 1167 return (str.length === 0); 1168} 1169else { return 0; 1170}} 1171 1172/** 1173 * Creates a JSON message for a particular skillset value. 1174 * @param value for the skillset 1175 * @returns msg as a JSON string 1176 */ 1177function createJson(value) { 1178 'use strict'; 1179 var msg = { 1180 'skillset' : value 1181 }; 1182 return JSON.stringify(msg); 1183} 1184 1185/** 1186 * Receives a JSON object from the server which contains the queue status. 1187 * @param data from the server 1188 * @param button or elemen that enables the chat window 1189 */ 1190function receiveQueueStatus(data, button) { 1191 'use strict'; 1192 var msg = JSON.parse(data), metrics = msg.body.metrics; 1193 var queueAvailable = false; 1194 // If the customer is asking for any skillset, check that at least one queue 1195 // is available. Otherwise, check if the specific queue is available 1196 if (isStringEmpty(pageSkillset)) { 1197 queueAvailable = checkAllSkillsets(metrics); 1198 } else { 1199 queueAvailable = checkSkillset(metrics); 1200 } 1201 1202 toggleChatButton(queueAvailable, button, metrics); 1203} 1204 1205/** 1206 * Checks that a specific queue is available. 1207 * @param queueMetrics 1208 * @returns {Boolean} 1209 */ 1210function checkSkillset(queueMetrics) { 1211 'use strict'; 1212 logToConsole('Asked specifically for ' + pageSkillset); 1213 queueMetrics = removeFullQueues(queueMetrics); 1214 1215 if (queueMetrics.length > 0) { 1216 setSkillsets(queueMetrics); 1217 return true; 1218 } 1219 return false; 1220 1221} 1222 1223/** 1224 * Checks that at least one queue is available. 1225 * @param queueMetrics 1226 * @returns {Boolean} 1227 */ 1228function checkAllSkillsets(queueMetrics) { 1229 'use strict'; 1230 queueMetrics = removeFullQueues(queueMetrics); 1231 if (queueMetrics.length > 0) { 1232 setSkillsets(queueMetrics); 1233 return true; 1234 } 1235 return false; 1236} 1237 1238/** 1239 * Filters out full queues 1240 * @param metrics 1241 * @returns the filtered and sorted queue 1242 */ 1243function removeFullQueues(metrics) { 1244 'use strict'; 1245 1246 return metrics 1247 .filter(function(queue) { 1248 return ((queue.customersInQueue <= (queue.availableAgentsInQueue * customerSoakValue) && queue.customersInQueue <= queue.maxConcurrentChats) && queue.availableAgentsInQueue > 0); 1249 }); 1250} 1251 1252/** 1253 * Toggles the chat button if there is a queue available. 1254 * @param isQueueAvailable 1255 */ 1256function toggleChatButton(isQueueAvailable, chatButton, metrics) { 1257 'use strict'; 1258 if (isQueueAvailable === true) { 1259 jQuery('.js-chat').addClass('is-active'); 1260 chatButton.removeAttribute('hidden'); 1261 chatButton.style.visibility = 'visible'; 1262 var chatNotAvailable = getEl('chatNA'); 1263 chatNotAvailable.setAttribute('hidden','hidden'); 1264 chatNotAvailable.style.visibility = 'hidden'; 1265 1266 } 1267} 1268 1269/** 1270 * Toggles the chat button depending on the availability of a particular 1271 * skillset 1272 * @param skillset 1273 */ 1274function startQueueCheck() { 1275 'use strict'; 1276 1277 // if XMLHttpRequest and WebSocket are supported by the browser, 1278 // perform the queue check. Otherwise, don't bother. 1279 if (XMLHttpRequest && WebSocket) { 1280 var chatButton = getEl('chatPanel'), server = new XMLHttpRequest({ 1281 mozSystem : true, 1282 timeout : queueTimeout 1283 }); 1284 var chatNotAvailable = getEl('chatNA'); 1285 pageSkillset = getEl('pageSkillSet').value; 1286 chatButton.setAttribute('hidden', 'hidden'); 1287 chatButton.style.visibility = 'hidden'; 1288 chatNotAvailable.removeAttribute('hidden'); 1289 chatNotAvailable.style.visibility = 'visible'; 1290 1291 // Creates an event listener for the request using the 1292 // requestQueueStatus function 1293 server.onreadystatechange = function() { 1294 if (server.readyState === 4 && server.status === 200) { 1295 receiveQueueStatus(server.response, chatButton); 1296 } 1297 }; 1298 1299 server.ontimeout = function() { 1300 logToConsole('Could not connect to the backend'); 1301 }; 1302 1303 // by default, XMLHttpRequests are asynchronous, and therefore require 1304 // an event listener 1305 server.open('POST', restUrl); 1306 server.setRequestHeader('Content-Type', 'application/json'); 1307 server.send(createJson(pageSkillset)); 1308 } 1309} 1310 1311/** 1312 * Opens the webChatLogon page in a separate window. 1313 */ 1314function createLogonWindow() { 1315 'use strict'; 1316 var link = 'webChatLogon.html'; 1317 window.open(link, 'Web Chat Logon', 'width=400,height=500,resizable=yes'); 1318} 1319 1320/** 1321 * webChatLogon.js 1322 * 1323 * Copyright 2015 Avaya Inc. All Rights Reserved. 1324 *
1325 * Usage of this source is bound to the terms described in 1326 * licences/License.txt 1327 * 1328 * Avaya - Confidential & Proprietary. Use pursuant to your signed agreement or 1329 * Avaya Policy 1330 */ 1331 1332/* 1333 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 1334 * 1335 * This section defines the functionality for the logon window 1336 * 1337 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 1338 */ 1339 1340/** 1341 * Parses an email to check that it is valid. Only checks if there is an '@' and 1342 * a dot after the '@', but that will cover the vast majority of all email 1343 * addresses 1344 * @param emailToParse 1345 * @returns {Boolean} 1346 */ 1347function parseEmail(emailToParse) { 1348 'use strict'; 1349 // this regex allows any alphanumeric character 1350 var re = /[\w\.]+@\w+\.\w+/; 1351 return re.test(emailToParse); 1352} 1353 1354/** 1355 * Logs a user with specific details into the chat. 1356 * @param name 1357 * @param phone 1358 * @param email 1359 * @param skillset 1360 */ 1361function logon(name, skillset) { 1362 'use strict'; 1363 // if the email address is empty or otherwise invalid, alert the user. 1364 // if they accept this, then continue 1365 var confirm = true; 1366 //var isValid = parseEmail(email); 1367 //if (isValid === false) { 1368 // confirm = window 1369 // .confirm('You may use the chat without a valid email, but you will not be able to receive transcripts of the conversation'); 1370 //} 1371 1372 if (confirm === true) { 1373 1374 // if the customer's email was invalid, and they went ahead, 1375 // reset it 1376 //if (isValid === false) { 1377 // logToConsole('Email was invalid, resetting'); 1378 // email = ''; 1379 // setSessionStorage('email', email); 1380 //} 1381 1382 if (usePopupForChat) { 1383 window.open('WebChat.html', '_self'); 1384 } else { 1385 switchFormToChat(); 1386 openChat(); 1387 } 1388 } 1389} 1390 1391/** 1392 * Switches the form to logon mode 1393 */ 1394function switchFormToLogon() { 1395 'use strict'; 1396 getEl('chatForm').style.display = 'block'; 1397 getEl('chatInterface').style.display = 'none'; 1398 getEl('chatPanel').style.height = '183px'; 1399} 1400 1401/** 1402 * Switches the form to chat mode 1403 */ 1404function switchFormToChat() { 1405 'use strict'; 1406 getEl('chatForm').style.display = 'none'; 1407 getEl('chatInterface').style.display = 'block'; 1408 getEl('chatPanel').style.height = '363px'; 1409} 1410 1411/** 1412 * Set the skillsets according to the queue information. 1413 */ 1414function setSkillsets(queues) { 1415 'use strict'; 1416 var skillsetSelect = getEl('skillset-chat'); 1417 1418 // add the country codes to the phone callback 1419 //var chatCountry = getEl('country-chat'); 1420 //addCountryCodes(chatCountry); 1421 1422 // if a queue has space, show it as being available 1423 for (var l = 0; l < queues.length; l++) { 1424 var theQueue = queues[l]; 1425 logToConsole('Adding ' + theQueue.queueName + ' to the logon page'); 1426 var newOption = document.createElement('option'); 1427 newOption.value = theQueue.queueName; 1428 newOption.text = theQueue.queueName; 1429 skillsetSelect.appendChild(newOption); 1430 } 1431} 1432 1433/** 1434 * Gathers the users details and adds them to sessionStorage. 1435 */ 1436function gatherDetails() { 1437 'use strict'; 1438 var l_user = getEl('user-chat').value, l_skillset = getEl('skillset-chat').value; 1439 1440 // store the phone details in a JSON object for convenience 1441 //var l_phone = { 1442 // 'country' : getEl('country-chat').value, 1443 // 'area' : getEl('area-chat').value, 1444 // 'phone' : getEl('phone-chat').value 1445 //}; 1446 1447 setSessionStorage('user', l_user); 1448 //setSessionStorage('phone', JSON.stringify(l_phone)); 1449 //setSessionStorage('country', l_country); 1450 //setSessionStorage('email', l_email); 1451 setSessionStorage('skillset', l_skillset); 1452 logon(l_user, l_skillset); 1453} 1454/** 1455 * callback.js 1456 * 1457 * Copyright 2015 Avaya Inc. All Rights Reserved. 1458 * 1459 * Usage of this source is bound to the terms described in 1460 * licences/License.txt 1461 * 1462 * Avaya - Confidential & Proprietary. Use pursuant to your signed agreement or 1463 * Avaya Policy 1464 */ 1465 1466/* 1467 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 1468 * This section handles callback requests and does not rely on WebSockets. 1469 * Instead, it sends a JSON object to the backend, which handles the grunt work. 1470 * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 1471 */ 1472 1473var callbackDay; 1474var callbackMonth; 1475var callbackHour; 1476var callbackMinute; 1477 1478var callbackButton; 1479var callbackTime; 1480var callbackDate; 1481 1482var callbackEmail; 1483var callbackPhone; 1484var callbackCountry; 1485var callbackArea; 1486var callbackName; 1487var callbackSkillset; 1488 1489var countryCode = ''; 1490var areaCode = ''; 1491var email = ''; 1492var custName = ''; 1493var phone; 1494var skillset; 1495 1496var theDate; 1497 1498// store the user details in a JSON object for convenience 1499var user = {}; 1500 1501/** 1502 * Hides the callback date form. 1503 */ 1504function hideCallbackDateForm() { 1505 'use strict'; 1506 1507 // call jQuery and tell it to slide the div in if it's open. Replace with 1508 // own code if not using jQuery 1509 jQuery(function($) { 1510 var formHandle = $('.callbackHandle'); 1511 if ($('.dateInputClass').hasClass('open')) { 1512 formHandle.click(); 1513 } 1514 }); 1515} 1516 1517/** 1518 * Add the international dialling codes to the specified dropdown menu. 1519 */ 1520function addCountryCodes(menu) { 1521 'use strict'; 1522 logToConsole('Adding country codes'); 1523 var countries = codes.countries; 1524 for ( var entry in countries) { 1525 if (countries.hasOwnProperty(entry)) { 1526 var country = countries[entry]; 1527 menu.appendChild(addSelectOption(country.name + ' (' + country.code + ')', country.code)); 1528 } 1529 } 1530} 1531 1532/** 1533 * Turns on the form to get the user date 1534 */ 1535/** 1536function showCallbackDateForm() { 1537 'use strict'; 1538 1539 callbackButton = getEl('button-callback'); 1540 callbackTime = getEl('time-callback'); 1541 callbackDate = getEl('date-callback'); 1542 1543 callbackEmail = getEl('email-callback'); 1544 callbackPhone = getEl('phone-callback'); 1545 callbackArea = getEl('area-callback'); 1546 callbackName = getEl('user-callback'); 1547 callbackCountry = getEl('country-callback'); 1548 callbackSkillset = getEl('skillset-callback'); 1549 1550 // add the country codes 1551 //addCountryCodes(callbackCountry); 1552 1553 // get the skillsets for display purposes 1554 getSkillsets(); 1555 1556 callbackButton.onclick = function() {
1557 setCallbackDate(); 1558 }; 1559 1560 for (var i = 6; i <= 20; i++) { 1561 callbackTime.appendChild(addSelectOption(i + ':00 ', i + ':00 ')); 1562 callbackTime.appendChild(addSelectOption(i + ':15 ', i + ':15 ')); 1563 callbackTime.appendChild(addSelectOption(i + ':30 ', i + ':30 ')); 1564 callbackTime.appendChild(addSelectOption(i + ':45 ', i + ':45 ')); 1565 } 1566 1567} 1568*/ 1569/** 1570 * Creates and returns a select option with the specified value 1571 * @param value 1572 * @returns {___anonymous1888_1899} 1573 */ 1574function addSelectOption(textContent, value) { 1575 'use strict'; 1576 var selectOption = document.createElement('option'); 1577 selectOption.textContent = textContent; 1578 selectOption.value = value; 1579 return selectOption; 1580} 1581 1582/** 1583 * Allows the user to set the time that suits them for an agent to call them 1584 * back. 1585 */ 1586function setCallbackDate() { 1587 'use strict'; 1588 1589 phone = callbackPhone.value; 1590 custName = callbackName.value; 1591 email = callbackEmail.value; 1592 skillset = callbackSkillset.value; 1593 countryCode = callbackCountry.value; 1594 areaCode = callbackArea.value; 1595 var currentTime = new Date(); 1596 1597 user = { 1598 'method' : 'requestCallback', 1599 'name' : custName, 1600 'phone' : phone, 1601 'email' : email, 1602 'countryCode' : countryCode, 1603 'areaCode' : areaCode, 1604 'skillset' : skillset 1605 }; 1606 1607 // the below is dependent on jQuery 1608 theDate = $('#date-callback').datepicker('getDate'); 1609 var theTime = callbackTime.value.split(':'); 1610 theDate.setHours(theTime[0], theTime[1]); 1611 1612 // create a new UTC date to maintain consistency with the PHP 1613 // version, then reset the customer date to use that 1614 var utcDate = Date.UTC(theDate.getFullYear(), theDate.getMonth(), theDate.getDate(), theTime[0], theTime[1]); 1615 logToConsole('theDate is ' + theDate.toString() + '. utcDate is ' + new Date(utcDate).toString()); 1616 theDate = new Date(utcDate); 1617 1618 if (verifyPhone(phone) && verifyDate(currentTime, theDate) && !isCountryCodeEmpty(countryCode)) { 1619 1620 // calculate the offset for the callback date relative to UTC, 1621 // using the system clock for the customer's browser 1622 var rawDate = theDate.getTime(); 1623 var offset = theDate.getTimezoneOffset(); 1624 var newDate = rawDate + (offset * 60000); 1625 user.date = (newDate); 1626 sendJsonRequest(user); 1627 } else { 1628 alert('Please enter a valid date and phone number'); 1629 } 1630} 1631 1632/** 1633 * Verifies that a country code has been selected. 1634 * @param code 1635 */ 1636function isCountryCodeEmpty(code) { 1637 'use strict'; 1638 return isStringEmpty(code); 1639} 1640 1641/** 1642 * Verifies a phone number. This one makes sure there are no spaces or letters 1643 * @param number 1644 * @returns 1645 */ 1646function verifyPhone(number) { 1647 'use strict'; 1648 1649 // this should allow calls from any country in the format 00xxx yyy zzzzzzz 1650 var re = /^[\+\d][\s\d]+$/; 1651 return re.test(number); 1652} 1653 1654/** 1655 * Checks that a user hasn't entered a date earlier than their current time. 1656 * @param currentDate 1657 * @param date 1658 * @returns {Boolean} 1659 */ 1660function verifyDate(currentDate, date) { 1661 'use strict'; 1662 1663 logToConsole('Customer requested time of ' + date.getTime()); 1664 logToConsole('Customer\'s current time is ' + currentDate.getTime()); 1665 return (date.getTime() > currentDate.getTime()); 1666} 1667 1668/** 1669 * Send a JSON request to the server. 1670 * @param user 1671 */ 1672function sendJsonRequest(user) { 1673 'use strict'; 1674 1675 var request = new XMLHttpRequest(); 1676 request.open('POST', callbackUrl); 1677 request.timeout = callbackTimeout; 1678 1679 request.onreadystatechange = function() { 1680 if (request.readyState === 4 && request.status === 200) { 1681 var text = JSON.parse(request.responseText); 1682 alert(text.status); 1683 } 1684 }; 1685 1686 request.setRequestHeader('Content-Type', 'application/json'); 1687 request.send(JSON.stringify(user)); 1688} 1689 1690/** 1691 * Get all the outbound skillsets. 1692 */ 1693function getSkillsets() { 1694 'use strict'; 1695 1696 if (window.XMLHttpRequest) { 1697 var request = new XMLHttpRequest(); 1698 request.open('POST', callbackUrl); 1699 request.timeout = callbackTimeout; 1700 1701 request.onreadystatechange = function() { 1702 if (request.readyState === 4 && request.status === 200) { 1703 var data = JSON.parse(request.response); 1704 processSkillsetsResponse(data); 1705 } 1706 }; 1707 1708 // send a basic JSON object 1709 var json = { 1710 'method' : 'getSkillsets' 1711 }; 1712 1713 request.setRequestHeader('Content-Type', 'application/json'); 1714 request.send(JSON.stringify(json)); 1715 } 1716 1717} 1718 1719/** 1720 * Refactors the skillset response processing. 1721 * @param request 1722 */ 1723function processSkillsetsResponse(requestData) { 1724 'use strict'; 1725 1726 // loop through the keys of the JSON object and pass the value 1727 // into a select. 1728 for ( var skillset in requestData) { 1729 if (requestData.hasOwnProperty(skillset)) { 1730 var name = requestData[skillset]; 1731 callbackSkillset.appendChild(addSelectOption(name, name)); 1732 } 1733 1734 } 1735}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.