├── public ├── images │ ├── favicon1.ico │ ├── livechart.PNG │ ├── maincharts.PNG │ ├── livechart_filtered.PNG │ └── recent_notifications.PNG ├── stylesheets │ └── main.css ├── mainChart.html ├── indexPage.html └── javascripts │ ├── mainChart.js │ ├── index.js │ ├── chart.js │ └── lib │ ├── popper.min.js │ ├── bootstrap.min.js │ └── jquery-3.3.1.min.js ├── web.config ├── package.json ├── LICENSE ├── .gitignore ├── config └── config.js ├── README.md └── server.js /public/images/favicon1.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AuthorizeNet/webhooks-sample-app/HEAD/public/images/favicon1.ico -------------------------------------------------------------------------------- /public/images/livechart.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AuthorizeNet/webhooks-sample-app/HEAD/public/images/livechart.PNG -------------------------------------------------------------------------------- /public/images/maincharts.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AuthorizeNet/webhooks-sample-app/HEAD/public/images/maincharts.PNG -------------------------------------------------------------------------------- /public/images/livechart_filtered.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AuthorizeNet/webhooks-sample-app/HEAD/public/images/livechart_filtered.PNG -------------------------------------------------------------------------------- /public/images/recent_notifications.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AuthorizeNet/webhooks-sample-app/HEAD/public/images/recent_notifications.PNG -------------------------------------------------------------------------------- /public/stylesheets/main.css: -------------------------------------------------------------------------------- 1 | html { 2 | overflow-y: scroll; 3 | } 4 | 5 | .event-headings { 6 | width: 100%; 7 | margin: 0 auto; 8 | text-align: left; 9 | } 10 | 11 | .log-header { 12 | margin: 0 auto; 13 | text-align: center; 14 | } 15 | 16 | .display-none { 17 | display: none; 18 | } 19 | 20 | .dashboard-chart-row { 21 | width: 90%; 22 | margin: 0 auto; 23 | text-align: center; 24 | } -------------------------------------------------------------------------------- /public/mainChart.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |
7 | 8 |
9 |
10 | 11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 | 19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webhooks-sample-app", 3 | "version": "1.0.0", 4 | "description": "Webhook Dashboard to monitor real time events and recent business happenings", 5 | "main": "server.js", 6 | "dependencies": { 7 | "body-parser": "^1.18.3", 8 | "cookie-parser": "^1.4.3", 9 | "csurf": "^1.9.0", 10 | "ejs": "^2.6.1", 11 | "express": "^4.16.3", 12 | "helmet-csp": "^2.7.1", 13 | "http": "0.0.0", 14 | "lokijs": "^1.5.5", 15 | "request": "^2.87.0", 16 | "socket.io": "^2.1.1" 17 | }, 18 | "devDependencies": { 19 | "nodemon": "^1.17.5", 20 | "jslint": "^0.12.0" 21 | }, 22 | "scripts": { 23 | "test": "echo \"Error: no test specified\" && exit 1", 24 | "start": "node server" 25 | }, 26 | "keywords": [ 27 | "authorize.net", 28 | "webhooks" 29 | ], 30 | "author": "", 31 | "license": "ISC", 32 | "engines": { 33 | "node": "^8.11.2" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "https://github.com/AuthorizeNet/webhooks-sample-app.git" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Authorize.Net 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # database file 64 | *.db 65 | -------------------------------------------------------------------------------- /config/config.js: -------------------------------------------------------------------------------- 1 | const env = 'dev'; // 'dev' or 'test' 2 | 3 | const dev = { 4 | apiEndpoint: 'https://apitest.authorize.net/rest/v1', 5 | apiLoginId: process.env.apiLogin || 'your api login id', 6 | transactionKey: process.env.transactionKey || 'your transaction key', 7 | app: { 8 | port: parseInt(process.env.PORT) || 9000, 9 | host: process.env.APP_DB_HOST || '0.0.0.0' 10 | }, 11 | db: { 12 | name: process.env.DEV_DB_NAME || 'notification.db', 13 | size: 1000 14 | }, 15 | graph: { 16 | // Number of Days to show in payment, refund, fraud and customer charts 17 | noOfDays: 7, 18 | maxNotificationCount: 100, 19 | // In seconds - interval between each time in X axis. 20 | intervalTimeSeconds: 300, 21 | graphTimeScale: 12 // Number of time intervals to show in graph 22 | } 23 | }; 24 | 25 | const test = { 26 | apiEndpoint: 'https://apitest.authorize.net/rest/v1', 27 | apiLoginId: process.env.apiLogin || 'your api login id', 28 | transactionKey: process.env.transactionKey || 'your transaction key', 29 | app: { 30 | port: parseInt(process.env.PORT) || 9000, 31 | host: process.env.APP_DB_HOST || '0.0.0.0' 32 | }, 33 | db: { 34 | name: process.env.DEV_DB_NAME || 'testnotification.db', 35 | size: 1000 36 | }, 37 | graph: { 38 | // Number of Days to show in payment, refund, fraud and customer charts 39 | noOfDays: 7, 40 | maxNotificationCount: 100, 41 | // In seconds - interval between each time in X axis. 42 | intervalTimeSeconds: 300, 43 | graphTimeScale: 12 // Number of time intervals to show in graph 44 | } 45 | }; 46 | 47 | const config = { 48 | dev, 49 | test 50 | }; 51 | 52 | module.exports = config[env]; -------------------------------------------------------------------------------- /public/indexPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

Webhook Manager

19 |
20 |
21 | 22 |
23 |
24 | 25 |
26 |
27 | 28 |
29 | 30 | 31 | 32 |
33 | 34 |
35 |
36 | 37 |
38 |
39 |
40 | 41 |
42 |
43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 |
TimestampEvent TypePayload
52 |
53 | 54 |
55 | 64 |
65 |
66 | 67 |
68 | 69 |
70 |
71 |
72 |
73 | 74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Webhooks Sample App 2 | 3 | Webhook sample application to demonstrate the usage of Webhooks to monitor real time events from Authorize.Net. 4 | 5 | * To learn more about Webhooks creation and configuration, authentication, API endpoints, event types and payload, visit [Webhooks](https://developer.authorize.net/api/reference/features/webhooks.html) 6 | 7 | * To get started with Authorize.Net, set up [Sandbox account](https://developer.authorize.net/hello_world/) and play. 8 | 9 | ## Screenshots 10 | 11 | #### Live Event Tracker 12 | 13 | Tracks live event occurrence and can be filtered based on the event types. 14 | 15 | ![Live Event Tracker image](/public/images/livechart_filtered.PNG) 16 | 17 | #### Recent Notifications 18 | 19 | Displays recent notifications. Can be filtered by event type using the drop down. 20 | 21 | ![Recent Notifications image](/public/images/recent_notifications.PNG) 22 | 23 | #### Dashboard Charts 24 | 25 | Payment, Refund, Customer and Fraud charts are displayed for past 7 days by default. 26 | 27 | ![Dashboard charts image](/public/images/maincharts.PNG) 28 | 29 | ## Register and configure Webhooks 30 | 31 | ### Prerequites 32 | * Authorize.NET production or sandbox credentials (api login id and transaction key) 33 | 34 | ### Steps to register a Webhook endpoint in Sandbox account 35 | * Login to [Sandbox](https://sandbox.authorize.net/) using sandbox account credentials. 36 | 37 | * Select the ACCOUNTS tab and click "Webhooks" link under Business Settings. 38 | 39 | * In Webhooks page, select "Add Endpoint" button. 40 | 41 | * Suppose this app is hosted as "https://my-webhooks-app.com" then enter "https://my-webhooks-app.com/notifications" in the Endpoint URL field. 42 | 43 | * Select the events for which you need to get notified in the app and click save button. Now the webhook is successfully configured to receive notifications. 44 | 45 | 46 | ## Installation 47 | 48 | Ensure that you have Node.js installed. node v8.11.2 and npm v5.6.0 are used for development. Please install the above or latest versions to use the app. 49 | 50 | The official Node.js website has installation instructions for Node.js: https://nodejs.org. 51 | 52 | Clone the Repository. Go to the project Directory and follow the below steps 53 | 54 | To install necessary packages, run following command in the terminal. 55 | 56 | ``` 57 | npm install 58 | ``` 59 | ## Usage 60 | 61 | In your terminal, run the following command: 62 | 63 | ``` 64 | npm start 65 | ``` 66 | 67 | The server is started at a port number (displayed in console) Eg. 9000. 68 | If you see no error message, navigate to `http://localhost:9000` in your browser. 69 | 70 | ## Config file 71 | 72 | config.js file in /config folder contains the following information: 73 | 74 | 1. API end point and credentials (required to populate the event types dropdown menu). In config/config.js file, 75 | 76 | ``` 77 | apiEndpoint: 'https://apitest.authorize.net/rest/v1', 78 | apiLoginId: process.env.apiLogin || 'enter your api login id here', 79 | transactionKey: process.env.transactionKey || 'enter your transaction key here', 80 | ``` 81 | 82 | 2. Server port number and hostname. 83 | 84 | ``` 85 | port: parseInt(process.env.PORT) || 9000, 86 | host: process.env.APP_DB_HOST || '0.0.0.0' 87 | ``` 88 | 89 | 3. Database name and size. 90 | 91 | ``` 92 | name: process.env.DEV_DB_NAME || './db/notification.db', 93 | size: 1000 94 | ``` 95 | 96 | 4. Graph Parameters: 97 | 98 | a) noOfDays: Number of days to plot in the graph for Payment, Refund, Customer and Fraud charts. 99 | Example: If noOfDays = 7:- Above 4 charts are shown for last 7 days. 100 | 101 | b) maxNotificationCount: Maximum number of recent notifications to display. 102 | 103 | c) intervalTimeSeconds: Interval time in seconds between each points in X axis for live event chart. 104 | 105 | d) graphTimeScale: Number of intervals to plot in live event chart. 106 | 107 | Example: If intervalTimeSeconds = 300 and graphTimeScale = 12:- live event chart is displayed for each 5 minutes for last one hour. 108 | 109 | ## Database File 110 | 111 | By default a database file to store recent notifications is created at /db/notification.db. Initially the live event chart is not displayed, notification log in UI does not contain any recent notifications and also empty charts are displayed in dashboard tab of the application since the server is just started and database is empty. When the server begins to receive notifications, charts and notification logs are updated. 112 | 113 | ## UI 114 | 115 | 1. Two tabs namely "Live Event Monitoring" and "Dashboard" are present. 116 | 2. First tab shows the Live Event chart at the top and live notification monitor at the bottom. A dropdown is present to filter the notifications by event type. 117 | 3. Second tab contains charts of Payment amount, Refund amount, number of Customers created and number of fraud transactions held in last few days. 118 | 119 | 120 | -------------------------------------------------------------------------------- /public/javascripts/mainChart.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var chartColorIndex = 0, chartConfig; 3 | 4 | plotAllGraphs(); 5 | 6 | /** 7 | * Calls the plotGraph() function with different chart types and DOM id for chart's location 8 | */ 9 | function plotAllGraphs() { 10 | // console.log("in ploatallgraphs func - mainchart.js"); 11 | plotGraph("Payment", "chart1"); 12 | plotGraph("Refund", "chart2"); 13 | plotGraph("Customer", "chart3"); 14 | plotGraph("Fraud", "chart4"); 15 | } 16 | 17 | /** 18 | * Make "/charts" call and get graph data 19 | * @param {string} eventCategory 20 | * @param {string} chartId 21 | */ 22 | function plotGraph(eventCategory, chartId) { 23 | $.getJSON('/charts', { name: eventCategory }, function (resultsMap) { 24 | var results = resultsMap.data; 25 | if(Object.keys(results)) { 26 | chartConfig = { 27 | type: 'line', 28 | data: { 29 | // Extract the labels from results object 30 | labels: (function() { 31 | return Object.keys(results); 32 | }()), 33 | 34 | // Extract the values to plot from results object 35 | datasets: (function() { 36 | var datasetList = [], eventDataMap = {}, i, colorName, newColor; 37 | var nameList = Object.keys(results[Object.keys(results)[0]]); 38 | 39 | nameList.forEach((name) => { 40 | eventDataMap[name] = []; 41 | }); 42 | Object.keys(results).forEach((graphDate) => { 43 | Object.keys(results[graphDate]).forEach((set) => { 44 | eventDataMap[set].push(results[graphDate][set]); 45 | }); 46 | }); 47 | 48 | for(i = 0; i < nameList.length; i += 1) { 49 | colorName = colorNames[chartColorIndex % colorNames.length]; 50 | newColor = chartColors[colorName]; 51 | ++chartColorIndex; 52 | 53 | datasetList.push({ 54 | label: nameList[i], 55 | data: eventDataMap[nameList[i]], 56 | borderColor: newColor, 57 | backgroundColor: newColor, 58 | }); 59 | } 60 | return datasetList; 61 | } ()) 62 | }, 63 | options: { 64 | tooltips: { 65 | callbacks: { 66 | // Change the tooltip display format 67 | label: function(tooltipItem, data) { 68 | var label = (data.datasets[tooltipItem.datasetIndex].label).split("=")[0] || ''; 69 | var labelValue = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] || 0; 70 | return (resultsMap.yaxis === "Amount")? ( label + ": $ " + labelValue): ( label + ": " + labelValue); 71 | } 72 | } 73 | }, 74 | title: { 75 | display: true, 76 | text: eventCategory, 77 | fontSize: 20 78 | }, 79 | legend:{ 80 | position: "top", 81 | }, 82 | scales: { 83 | xAxes: [{ 84 | scaleLabel: { 85 | display: true, 86 | labelString: 'Date', 87 | fontStyle: "bold", 88 | fontSize: 14 89 | } 90 | }], 91 | yAxes : [{ 92 | scaleLabel: { 93 | display: true, 94 | labelString: resultsMap.yaxis, 95 | fontStyle: "bold", 96 | fontSize: 14 97 | }, 98 | ticks : { 99 | // Graph's Y-axis must start with zero 100 | min : 0, 101 | // Converts the Y-axis scale values to int if they are float. 102 | userCallback: function(label) { 103 | if (Math.floor(label) === label) { 104 | return label; 105 | } 106 | } 107 | } 108 | }] 109 | }, 110 | animation: { 111 | duration: 0, 112 | }, 113 | hover: { 114 | animationDuration: 0, 115 | }, 116 | responsiveAnimationDuration: 0 117 | }, 118 | plugins: [ 119 | { // Change the way labels are displayed 120 | beforeInit: function(chartConfig) { 121 | chartConfig.data.datasets.forEach((dataset) => { 122 | var eventTotal = dataset.data.reduce((a,b) => a + b, 0); 123 | dataset.label = (resultsMap.yaxis === "Amount")? (dataset.label + " = $ " + eventTotal): 124 | (dataset.label + " = " + eventTotal); 125 | }); 126 | }, 127 | } 128 | ] 129 | }; 130 | // Draw the chart with above data 131 | new Chart(document.getElementById(chartId).getContext("2d"), chartConfig); 132 | } 133 | }); 134 | } 135 | -------------------------------------------------------------------------------- /public/javascripts/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // Initialize socket variable 3 | var socket = io(), maxNotificationCount; 4 | 5 | /** 6 | * Triggered when an item is dropdown is selected. 7 | * Makes current panel content empty 8 | * Socket listens to capture the initial chart parameter 9 | * Calls filterNotificationLogByEventType() to filter the logs 10 | */ 11 | $(document).on('click', '.dropdown-menu li a', function() { 12 | $(this).parents(".dropdown").find('.btn').html($(this).text() + ' '); 13 | $(this).parents(".dropdown").find('.btn').val($(this).data('value')); 14 | $("#panelCurrentEvent").html(""); 15 | 16 | // Socket listens during initialization and gets the chart values from server 17 | if(!maxNotificationCount) { 18 | socket.on("init", (body) => { 19 | // console.log("in socket - index.js to get chart values ", body.noOfDaysGraph); 20 | maxNotificationCount = body.noOfDaysGraph; 21 | filterNotificationLogByEventType($(this).data('value')); 22 | }); 23 | } 24 | else { 25 | filterNotificationLogByEventType($(this).data('value')); 26 | } 27 | 28 | }); 29 | 30 | /** 31 | * Makes API call and gets the notification log based on filtered eventType 32 | * @param {string} eventType 33 | */ 34 | function filterNotificationLogByEventType(eventType) { 35 | // console.log("eventtype selected in filterNotificationLogByEventType is ", eventType); 36 | $.getJSON("/notifications", { limit: maxNotificationCount, name: eventType }, function (notifications) { 37 | var notificationsLength = notifications.length; 38 | if(notificationsLength) { 39 | $("#logCount").html(`Displaying recent ${notificationsLength} notifications`); 40 | } 41 | else { 42 | $("#logCount").html(`No recent notifications`); 43 | } 44 | // Create a notification log in UI for each notification 45 | notifications.forEach((notification) => 46 | displayEventMessage(notification)); 47 | }); 48 | } 49 | 50 | /** 51 | * Display either Event monitoring or dashboard of graphs 52 | * when the respective button is clicked 53 | */ 54 | $(() => { 55 | // Set the CSRF headers 56 | $.ajaxSetup({ 57 | headers: { 'x-csrf-token': $('input[name="_csrf"]').val() }, 58 | data: { csrf: $('input[name="_csrf"]').val() } 59 | }); 60 | 61 | // Get All eventTypes and construct the notification filtering dropdown menu 62 | $.ajax({ 63 | method: "GET", 64 | url:"/eventtypes", 65 | dataType: "json", 66 | }).done(function(data) { 67 | 68 | // If error occurred in getting event types, error message is sent from server 69 | if(data.hasOwnProperty("message")) { 70 | $(".error-message").append(`

${data.message}

\n`); 71 | } 72 | // console.log("adding dropdown items"); 73 | else { 74 | data.forEach((eventName) => { 75 | var div_data = `
  • ${eventName.name}
  • `; 76 | $(div_data).appendTo('.dropdown-menu'); 77 | }); 78 | } 79 | 80 | }).fail(function(err) { 81 | console.log("Error occured in ajax \"/events\" call ", err); 82 | }); 83 | 84 | // Load the mainChart.html file that contains placeholder 85 | // payment, refund, customer and fraud charts 86 | $.ajax({ 87 | url: '../mainChart.html', 88 | dataType: 'html', 89 | timeout: 5000, // 5 seconds 90 | success: function(html) { 91 | $("#includedContent").html(html); 92 | } 93 | }); 94 | 95 | // Include the mainChart.js file that has function for dashboard charts 96 | $.getScript("./javascripts/mainChart.js"); 97 | 98 | // By default enable the Event Monitoring tab 99 | $("#currentEvents").css("background-color", "palevioletred"); 100 | 101 | $("#chart").click(() => { 102 | // Change Dashboard tab's background color 103 | $("#chart").css("background-color", "palevioletred"); 104 | // Reset Event Monitoring tab's background color 105 | $("#currentEvents").css("background-color", ""); 106 | // Hide the Event Monitoring tab's content 107 | $(".event-monitoring").css("display", "none"); 108 | // Display the Dashboard tab's content 109 | $("#includedContent").css("display", "block"); 110 | }); 111 | 112 | $("#currentEvents").click(() => { 113 | // Change Event Monitoring tab's background color 114 | $("#currentEvents").css("background-color", "palevioletred"); 115 | // Reset Dashboard tab's background color 116 | $("#chart").css("background-color", ""); 117 | // Hide the Dashboard tab's content 118 | $("#includedContent").css("display", "none"); 119 | // Display the Event Monitoring tab's content 120 | $(".event-monitoring").css("display", "block"); 121 | }); 122 | // // console.log("clicking all items by defaut"); 123 | $('.dropdown li a:first-child').click(); 124 | 125 | }); 126 | 127 | // Socket listens for new event that is emitted in the server side. 128 | // Calls onNewEvent method when a new event occurs and updates the graph 129 | socket.on("newNotification", (body) => { 130 | console.log("in socket - index.js"); 131 | onNewEvent(body); 132 | plotAllGraphs(); 133 | }); 134 | 135 | /** 136 | * Formats the event date into only date time format 137 | * Converting "2018-07-21T20:52:59.2144524Z" to "2018-07-21 20:52:59" 138 | * @param {string} eDate 139 | */ 140 | function formatEventDate(eDate) { 141 | var eventDate = eDate.split("T"); 142 | eventDate[1] = eventDate[1].split(".")[0]; 143 | eventDate = eventDate.join(" "); 144 | return eventDate; 145 | } 146 | 147 | /** 148 | * Called when new notification is received. Calls methods to display the event 149 | * log message and update event frequency in event log graph 150 | * @param {*} body 151 | */ 152 | function onNewEvent(body) { 153 | console.log("in onNewEvent func"); 154 | // var eventName = extracteventNameFromEventType(body.eventDetails.eventType); 155 | displayEventMessage(body.eventDetails); 156 | findEventFrequencyInGraphInterval(body.eventDetails.eventType); 157 | } 158 | 159 | /** 160 | * Creates a new event message with event timestamp, event type and payload. 161 | * Appends this message to an UI element adds animation to it 162 | * @param {*} eventDetails 163 | */ 164 | function displayEventMessage(eventDetails) { 165 | // console.log("in displayEventMessage func"); 166 | var eventDate = formatEventDate(eventDetails.eventDate); 167 | var mainPanel = document.getElementById("panelCurrentEvent"); 168 | 169 | // Checks the limit to display number of recent events 170 | if(mainPanel.childElementCount >= maxNotificationCount) { 171 | mainPanel.removeChild(mainPanel.lastChild); 172 | } 173 | var newPanel = document.createElement("div"); 174 | newPanel.classList.add("panel", "panel-success"); 175 | 176 | var formatedPayload = JSON.stringify(eventDetails, null,"\t") 177 | .slice(1,-1); 178 | 179 | newPanel.innerHTML = 180 | `
    181 |
    ${eventDate}
    182 |
    ${eventDetails.eventType}
    183 |
    184 |
    ${formatedPayload}
    185 |
    186 |
    `; 187 | 188 | // For animation 189 | newPanel.animate([ 190 | // keyframes 191 | { transform: "translateX(300px)" }, 192 | { transform: "translateY(0px)" } 193 | ], { 194 | // timing options 195 | duration: 500, 196 | }); 197 | // Insert message as the first child 198 | mainPanel.insertBefore(newPanel, mainPanel.childNodes[0]); 199 | } 200 | -------------------------------------------------------------------------------- /public/javascripts/chart.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var chartColors = { 3 | Yellow: 'rgb(255, 225, 25)', 4 | Blue: 'rgb(0, 130, 200)', 5 | Orange: 'rgb(245, 130, 48)', 6 | Purple: 'rgb(145, 30, 180)', 7 | Cyan: 'rgb(70, 240, 240)', 8 | Magenta: 'rgb(240, 50, 230)', 9 | Lime: 'rgb(210, 245, 60)', 10 | Pink: 'rgb(250, 190, 190)', 11 | Teal: 'rgb(0, 128, 128)', 12 | Lavender: 'rgb(230, 190, 255)', 13 | Red: 'rgb(230, 25, 75)', 14 | Brown: 'rgb(170, 110, 40)', 15 | Beige: 'rgb(255, 250, 200)', 16 | Maroon: 'rgb(128, 0, 0)', 17 | Mint: 'rgb(170, 255, 195)', 18 | Olive: 'rgb(128, 128, 0)', 19 | Coral: 'rgb(255, 215, 180)', 20 | Navy: 'rgb(0, 0, 128)', 21 | Green: 'rgb(60, 180, 75)', 22 | Grey: 'rgb(128, 128, 128)', 23 | Black: 'rgb(0, 0, 0)', 24 | }; 25 | 26 | var config, 27 | initialChartData, 28 | eventFrequencyInGraphInterval = {}, 29 | myLine, 30 | colorNames = Object.keys(chartColors); 31 | 32 | /** 33 | * Initialize chart data and draws chart if the chart data is populated 34 | * @param {*} chartResults 35 | */ 36 | function initialChartCallback(chartResults) { 37 | initialChartData = chartResults; 38 | 39 | if(Object.keys(initialChartData.eventFrequencyAtEachTimeMap).length!= 0) { 40 | drawChart(initialChartData); 41 | } 42 | } 43 | initializeChart(); 44 | 45 | /** 46 | * Gets chart values from "/charts" endpoint. 47 | * calls initialChartCallback() with chart data 48 | */ 49 | function initializeChart() { 50 | // For live event chart. Query parameter name is passed with value "all" 51 | $.getJSON('/charts', { name: "all", csrf: $('input[name="_csrf"]').val() }, (results) => { initialChartCallback(results); }); 52 | } 53 | 54 | /** 55 | * Initializes chart configurations. Plots chart and calls function to 56 | * update chart regularly 57 | * @param {*} results 58 | */ 59 | function drawChart(results) { 60 | try{ 61 | config = { 62 | type: "line", 63 | data: { 64 | // Extract the labels from results object 65 | labels: (function() { 66 | var timeLabelList = []; 67 | for(var i=0; i { 145 | var eventCount = dataset.data.reduce((a,b) => a + b, 0); 146 | dataset.label = dataset.label + " ("+ eventCount + ")"; 147 | // Hide the event if its count is zero in this timeframe 148 | if(!eventCount) { 149 | dataset.hidden = true; 150 | } 151 | }); 152 | } 153 | }, 154 | { 155 | // After Updating chart, remove the count from event label 156 | afterUpdate: function(config) { 157 | config.data.datasets.forEach((dataset) => { 158 | dataset.label = dataset.label.split(" ")[0]; 159 | if(dataset.hidden) { 160 | dataset.hidden = false; 161 | } 162 | }); 163 | } 164 | } 165 | ] 166 | }; 167 | }catch(err) { 168 | console.error("Error in chart.js ", err); 169 | } 170 | 171 | // Plot the chart 172 | $(document).ready(function() { 173 | var ctx = document.getElementById("chartBox").getContext("2d"); 174 | myLine = new Chart(ctx, config); 175 | }); 176 | 177 | // Execute the setInterval's callback function repeatedly 178 | var stopGraphUpdate = setInterval(function () { 179 | updateLiveEventGraph(results.intervalTimeSeconds, eventFrequencyInGraphInterval); 180 | eventFrequencyInGraphInterval = {}; 181 | }, results.intervalTimeSeconds * 1000); 182 | } 183 | 184 | /** 185 | * During each call, updates the X-axis, adds new event if occurred, 186 | * updates existing event whether occurred in this time interval or not 187 | * @param {number} intervalTimeSeconds 188 | * @param {*} eventFrequencyInGraphInterval 189 | */ 190 | function updateLiveEventGraph(intervalTimeSeconds, eventFrequencyInGraphInterval) { 191 | if(config.data.datasets) { 192 | updateXAxis(intervalTimeSeconds); 193 | } 194 | 195 | config.data.datasets.forEach((dataset)=> { 196 | var updatedFrequencyFlag = false; 197 | Object.keys(eventFrequencyInGraphInterval).forEach(function(newEvent) { 198 | 199 | // If already present event has occurred in this timeframe, 200 | // update with new count 201 | if(newEvent === dataset.label) { 202 | // console.log("If already present event has occurred in this timeframe, update with new count") 203 | addData(dataset.label, eventFrequencyInGraphInterval[newEvent]); 204 | updatedFrequencyFlag = true; 205 | } 206 | }); 207 | 208 | // If already present event does not occur in the time frame, add "0" as count 209 | if(!updatedFrequencyFlag) { 210 | // console.log("If already present event does not occur in the time frame, add 0 as count") 211 | addData(dataset.label, 0); 212 | } 213 | }); 214 | 215 | Object.keys(eventFrequencyInGraphInterval).forEach(function(newEvent) { 216 | var updatedFrequency1Flag = false; 217 | config.data.datasets.forEach(function(dataset) { 218 | if(newEvent === dataset.label) { 219 | updatedFrequency1Flag = true; 220 | } 221 | }); 222 | 223 | // If new event has occurred, add a dataset for it and update its latest count 224 | if(!updatedFrequency1Flag) { 225 | // console.log("If new event has occurred, add a dataset for it and update its latest count") 226 | addDataset(newEvent); 227 | addData(newEvent, eventFrequencyInGraphInterval[newEvent]); 228 | } 229 | }); 230 | // Update the graph after making necessary changes 231 | myLine.update(); 232 | } 233 | 234 | /** 235 | * Add a dataset for newly occurred event 236 | * @param {*} newEvent 237 | */ 238 | function addDataset(newEvent) { 239 | // console.log("new series adding for event ", newEvent); 240 | var colorName = colorNames[config.data.datasets.length % colorNames.length]; 241 | var newColor = chartColors[colorName]; 242 | var newDataset = { 243 | label: newEvent, 244 | data: [], 245 | borderColor: newColor, 246 | backgroundColor: newColor, 247 | }; 248 | 249 | // Make count as zero for each value in X axis 250 | for (var index = 0; index < config.data.labels.length; ++index) 251 | newDataset.data.push(0); 252 | 253 | config.data.datasets.push(newDataset); 254 | } 255 | 256 | /** 257 | * Add a data point for already existing event or new event 258 | * @param {string} datasetName 259 | * @param {number} value 260 | */ 261 | function addData(datasetName, value) { 262 | if (config.data.datasets.length > 0 && datasetName!= undefined) { 263 | config.data.datasets.forEach(function(dataset, index, chartSeriesList) { 264 | if(dataset.label === datasetName) { 265 | chartSeriesList[index].data.push(value); 266 | chartSeriesList[index].data.shift(); 267 | } 268 | }); 269 | } 270 | } 271 | 272 | /** 273 | * Updates the X axis by adding new time and removing the earliest time 274 | * @param {number} intervalTimeSeconds 275 | */ 276 | function updateXAxis(intervalTimeSeconds) { 277 | var latestTime = config.data.labels[config.data.labels.length-1]; 278 | var nextTime = new Date(new Date(latestTime).getTime() + (1000 * intervalTimeSeconds)).toISOString(); 279 | config.data.labels.push(nextTime); 280 | config.data.labels.shift(); 281 | } 282 | 283 | /** 284 | * Maintains the event occurrence count for each interval 285 | * @param {string} eventType 286 | */ 287 | function findEventFrequencyInGraphInterval(eventType) { 288 | if(Object.keys(initialChartData.eventFrequencyAtEachTimeMap).length == 0) { 289 | initializeChart(); 290 | } 291 | else { 292 | if(eventType in eventFrequencyInGraphInterval) 293 | eventFrequencyInGraphInterval[eventType] += 1; 294 | else 295 | eventFrequencyInGraphInterval[eventType] = 1; 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /public/javascripts/lib/popper.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) Federico Zivolo 2018 3 | Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). 4 | */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=getComputedStyle(e,null);return t?o[t]:o}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=J(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!q(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,y=t(e.instance.popper),w=parseFloat(y['margin'+f],10),E=parseFloat(y['border'+f+'Width'],10),v=b-e.offsets.popper[m]-w-E;return v=$(J(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,Q(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case he.FLIP:p=[n,i];break;case he.CLOCKWISE:p=z(n);break;case he.COUNTERCLOCKWISE:p=z(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,y=-1!==['top','bottom'].indexOf(n),w=!!t.flipVariations&&(y&&'start'===r&&h||y&&'end'===r&&c||!y&&'start'===r&&g||!y&&'end'===r&&u);(m||b||w)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),w&&(r=G(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport'},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!q(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.right 0) { 158 | var someDate = new Date(new Date().setDate(new Date().getDate()- x + 1)).toISOString().slice(0,10).split('-'); 159 | tempDate = someDate[1] +'-'+ someDate[2] +'-'+ someDate[0]; 160 | recentDateMap[tempDate] = {}; 161 | --x; 162 | } 163 | return recentDateMap; 164 | } 165 | 166 | /** 167 | * Filter based on event list from DB 168 | * @param {*} notifications 169 | * @param {Array} eventFilter 170 | */ 171 | async function getMatchingEventsFromDB(notifications, eventFilter, offsetValue, limitValue) { 172 | 173 | if(offsetValue === -1 && limitValue === -1) { 174 | return notifications.chain().find({eventType: { $in: eventFilter } }).data({removeMeta: true}); 175 | } 176 | else { 177 | if(eventFilter.length!= 0) { 178 | return notifications.chain().find({eventType: { $in: eventFilter } }).offset(offsetValue).limit(limitValue).data({removeMeta: true}); 179 | } 180 | 181 | else { 182 | return notifications.chain().offset(offsetValue).limit(limitValue).data({removeMeta: true}); 183 | } 184 | } 185 | } 186 | 187 | /** 188 | * Calculates the date range and values to plot in chart based on input parameters 189 | * @param {Array} eventFilter 190 | * @param {Array} eventKeyList 191 | * @param {string} calculateParameter 192 | */ 193 | async function getGraphData(eventFilter, eventKeyList, calculateParameter) { 194 | var recentDateMap = calculateLastXDays(noOfDaysGraph); 195 | 196 | var notifications = await getCollectionFunction("notifications"); 197 | 198 | var recentDocs = await getMatchingEventsFromDB(notifications, eventFilter, -1, -1); 199 | 200 | // If recentDocs is null, the charts are displayed with all values as zero 201 | // for each date in recentDateMap 202 | if(!(eventKeyList === undefined || eventKeyList.length === 0)) { 203 | eventKeyList.forEach((eventKey) => { 204 | Object.keys(recentDateMap).forEach((eachDate) => { 205 | recentDateMap[eachDate][eventKey] = 0; 206 | }); 207 | }); 208 | } 209 | 210 | recentDocs.forEach( (element) => { 211 | var tempDate = element.eventDate.slice(0, 10).split('-'); 212 | var elementDate = tempDate[1] +'-'+ tempDate[2] +'-'+ tempDate[0]; 213 | 214 | if(elementDate in recentDateMap) { 215 | Object.keys(recentDateMap[elementDate]).forEach((eventLegend) => { 216 | if(eventKeyList.includes(eventLegend)) { 217 | if(calculateParameter === "Amount") { 218 | recentDateMap[elementDate][eventLegend] += parseInt(element.payload.authAmount); 219 | } 220 | else if(calculateParameter === "Count") { 221 | recentDateMap[elementDate][eventLegend] += 1; 222 | } 223 | } 224 | else { 225 | recentDateMap[elementDate][eventLegend] = 0; 226 | } 227 | }); 228 | } 229 | 230 | }); 231 | var chartMap = { 232 | "data": recentDateMap, 233 | "yaxis": calculateParameter 234 | }; 235 | return chartMap; 236 | } 237 | 238 | /** 239 | * Define what to display in the chart and call getGraphData() with it 240 | * @param {string} eventCategory 241 | */ 242 | async function setGraphCriteria(eventCategory) { 243 | var eventFilter, eventKeyList, calculateParameter; 244 | switch(eventCategory) { 245 | case "Payment": 246 | eventFilter = [ 247 | "net.authorize.payment.authcapture.created", 248 | "net.authorize.payment.priorAuthCapture.created", 249 | "net.authorize.payment.capture.created" 250 | ]; 251 | // eventKeyList = ["Total Amount", "second dataset"]; 252 | eventKeyList = ["Total Payment Amount"]; 253 | calculateParameter = "Amount"; 254 | break; 255 | 256 | case "Refund": 257 | eventFilter = [ 258 | "net.authorize.payment.refund.created", 259 | "net.authorize.payment.void.created" 260 | ]; 261 | eventKeyList = ["Total Refund Amount"]; 262 | calculateParameter = "Amount"; 263 | break; 264 | 265 | case "Customer": 266 | eventFilter = [ 267 | "net.authorize.customer.created" 268 | ]; 269 | eventKeyList = ["# of Customer Profile created"]; 270 | calculateParameter = "Count"; 271 | break; 272 | 273 | case "Fraud": 274 | eventFilter = [ 275 | "net.authorize.payment.fraud.held" 276 | ]; 277 | eventKeyList = ["# of Fraud transactions held"]; 278 | calculateParameter = "Count"; 279 | break; 280 | } 281 | 282 | var recentDateMap = await getGraphData(eventFilter, eventKeyList, calculateParameter); 283 | return recentDateMap; 284 | } 285 | 286 | /** 287 | * Returns the oldest notification present 288 | * @param {*} notifications 289 | */ 290 | async function getOldestNotification(notifications) { 291 | return notifications.chain().limit(1).data(); 292 | } 293 | 294 | /** 295 | * Removes the oldest notification from available set of notifications 296 | * @param {*} notifications 297 | * @param {*} oldestNotification 298 | */ 299 | async function removeOldestNotification(notifications, oldestNotification) { 300 | notifications.remove(oldestNotification); 301 | return notifications; 302 | } 303 | 304 | /** 305 | * Retrieves and Deletes the oldest notification present in the set of notifications 306 | * @param {*} notifications 307 | */ 308 | async function handleOldestNotification(notifications) { 309 | if (notifications.count() >= dbSize) { 310 | var oldestNotification = await getOldestNotification(notifications); 311 | notifications = await removeOldestNotification(notifications,oldestNotification); 312 | return notifications; 313 | } 314 | else { 315 | return notifications; 316 | } 317 | } 318 | 319 | /** 320 | * Initializes current set of notifications and removes the oldest one from it 321 | */ 322 | async function updateNotifications() { 323 | var notifications = await getCollectionFunction("notifications"); 324 | 325 | notifications = await handleOldestNotification(notifications); 326 | 327 | return notifications; 328 | } 329 | 330 | /** 331 | * Insert incoming new notification into available notifications set and returns the new set 332 | * @param {*} notifications 333 | * @param {*} newNotification 334 | */ 335 | async function insertNewNotification(notifications, newNotification) { 336 | notifications.insert(newNotification); 337 | return notifications; 338 | } 339 | 340 | /** 341 | * Finds the time values to be plotted in live event chart and returns as an Array 342 | * @param {Date} startTime 343 | */ 344 | function calculateEventGraphInterval(startTime) { 345 | // console.log("start time: ", startTime); 346 | var newTime = new Date(startTime.getTime() + (1000 * config.graph.intervalTimeSeconds)); 347 | var graphTimeList = [], currentTime = new Date(), tempDate; 348 | // console.log("currentTime", currentTime); 349 | tempDate = startTime.toISOString().slice(0, 24); 350 | graphTimeList.push(startTime); 351 | 352 | while(newTime <= currentTime) { 353 | tempDate = newTime.toISOString().slice(0, 24); 354 | graphTimeList.push(newTime); 355 | newTime = new Date(newTime.getTime() + (1000 * config.graph.intervalTimeSeconds)); 356 | } 357 | return graphTimeList; 358 | } 359 | 360 | /** 361 | * Gets the collection from database 362 | * @param {string} collectionName 363 | */ 364 | async function getCollectionFunction(collectionName) { 365 | return db.getCollection(collectionName); 366 | } 367 | 368 | /** 369 | * Returns the mapping of eventType and it count of occurrence 370 | * @param {Array} graphTimeList 371 | * @param {*} graphEvents 372 | */ 373 | function findEventsFrequencyInGraphInterval(graphTimeList, graphEvents) { 374 | var eventFrequencyAtEachTimeMap = {}; // {"event1": [4,0,3,1,0,6,2,3,0,0], "event2": [0,0,5,3,2,0,5,7,8,9]} 375 | if(graphEvents) { 376 | graphEvents.forEach(function(event) { 377 | var timeDiff = Math.abs(new Date(event.eventDate).getTime() - new Date(graphTimeList[0]).getTime()); 378 | var index = Math.ceil(timeDiff/ (config.graph.intervalTimeSeconds * 1000)); 379 | 380 | if (eventFrequencyAtEachTimeMap[event.eventType] === undefined || eventFrequencyAtEachTimeMap[event.eventType].length == 0) { 381 | eventFrequencyAtEachTimeMap[event.eventType] = new Array(config.graph.graphTimeScale).fill(0); 382 | eventFrequencyAtEachTimeMap[event.eventType][index-1] = 1; 383 | } 384 | else { 385 | eventFrequencyAtEachTimeMap[event.eventType][index-1] += 1; 386 | } 387 | }); 388 | } 389 | else { 390 | console.log("no events occured in requested interval"); 391 | } 392 | return eventFrequencyAtEachTimeMap; 393 | } 394 | 395 | /** 396 | * Returns the notifications occurred after the graph start time 397 | * @param {*} notifications 398 | * @param {Date} graphStartTime 399 | */ 400 | async function filterToGetGraphEvents(notifications, graphStartTime) { 401 | return notifications.chain().where(function(obj) { 402 | return graphStartTime < new Date(obj.eventDate)}).data({removeMeta: true}); 403 | } 404 | 405 | /** 406 | * Returns the data required for live event chart 407 | */ 408 | async function getAllEventsChart() { 409 | var currentTime = new Date(); 410 | var calculateFromTime = new Date(new Date().setTime(currentTime.getTime()- (1000 * config.graph.intervalTimeSeconds * config.graph.graphTimeScale))); 411 | var graphStartTime = new Date(calculateFromTime.getTime() + (1000 * config.graph.intervalTimeSeconds)); 412 | 413 | var graphTimeList = calculateEventGraphInterval(calculateFromTime); 414 | try { 415 | var notifications = await getCollectionFunction("notifications"); 416 | 417 | var graphEvents = await filterToGetGraphEvents(notifications, graphStartTime); 418 | 419 | var eventFrequencyAtEachTimeMap = findEventsFrequencyInGraphInterval(graphTimeList, graphEvents); 420 | 421 | return { 422 | eventFrequencyAtEachTimeMap: eventFrequencyAtEachTimeMap, 423 | graphStartTime: graphTimeList[1], 424 | intervalTimeSeconds: config.graph.intervalTimeSeconds 425 | }; 426 | }catch(err) { 427 | console.error("Error happened in getting graphEvents ", err); 428 | return {}; 429 | } 430 | } 431 | 432 | /** 433 | * Returns the count of collection 434 | * @param {string} collectionName 435 | */ 436 | async function findCollectionsCount(collectionName) { 437 | return collectionName.count() 438 | } 439 | 440 | /** 441 | * Returns recent notifications by filtering from available set 442 | * @param {*} notifications 443 | * @param {*} limit 444 | */ 445 | async function filterToGetRecentNotifications(notifications, limit, eventType) { 446 | 447 | var collectionCount = await findCollectionsCount(notifications); 448 | 449 | var offset = 0, eventFilter = [], limitValue = limit; 450 | if(eventType === "all") { 451 | if(collectionCount > limitValue) { 452 | offset = collectionCount - limitValue; 453 | } 454 | else { 455 | limitValue = collectionCount; 456 | } 457 | } 458 | 459 | else { 460 | limitValue = collectionCount; 461 | eventFilter = [eventType]; 462 | } 463 | 464 | var recentNotifications = await getMatchingEventsFromDB(notifications, eventFilter, offset, limitValue); 465 | 466 | if(eventType!= "all") { 467 | if(recentNotifications.length > limit) { 468 | recentNotifications.splice(0, recentNotifications.length - limit); 469 | } 470 | } 471 | 472 | return recentNotifications; 473 | } 474 | 475 | /** 476 | * Calls filterToGetRecentNotifications() to get the required recent notifications and returns it 477 | * @param {number} limit 478 | */ 479 | async function getRecentNotifications(limit, eventType) { 480 | try { 481 | var notifications = await getCollectionFunction("notifications"); 482 | 483 | var recentNotifications = await filterToGetRecentNotifications(notifications, limit, eventType); 484 | 485 | return recentNotifications; 486 | 487 | }catch(err) { 488 | console.error("Error happened in getting recent notifications ", err); 489 | return []; 490 | } 491 | } 492 | 493 | // Endpoint to return recent requested number of notifications 494 | app.get("/notifications", csrfProtection, async function(req, res) { 495 | var recentNotifications = await getRecentNotifications(req.query.limit, req.query.name); 496 | returnApiResponse(res, recentNotifications); 497 | }); 498 | 499 | /** 500 | * Sends the API response. 501 | * @param {*} res - Response Object. 502 | * @param {*} returnJsonValue Value to be returned from the API 503 | */ 504 | function returnApiResponse(res, returnJsonValue) { 505 | res.format({ 506 | json: function () { 507 | res.send(returnJsonValue); 508 | }, 509 | html: function () { 510 | res.json({ 511 | error: "Page not found !!" 512 | }); 513 | }, 514 | }); 515 | } 516 | 517 | // Endpoint to get all the charts data 518 | app.get("/charts", csrfProtection, async function (req, res) { 519 | var returnValue; 520 | try{ 521 | if(req.query.name === "all"){ 522 | returnValue = await getAllEventsChart(); 523 | } 524 | else 525 | returnValue = await setGraphCriteria(req.query.name); 526 | 527 | }catch(err) { 528 | console.log("Error in GET /charts ", err) 529 | } 530 | returnApiResponse(res, returnValue); 531 | }); 532 | 533 | /** 534 | * Makes API call and gets the available eventtypes 535 | * @param {Function} callback(eventTypes): called after the getting eventTypes 536 | */ 537 | function getEvents(callback) { 538 | request(eventRequestData, function (error, response, body) { 539 | if(error) { 540 | console.error("Error in request getevents ", error); 541 | var errorMessage = {}; 542 | errorMessage.message = "Error in getting available EventTypes from ANET. Please try again"; 543 | callback(errorMessage) 544 | } 545 | else { 546 | callback(JSON.parse(body)) 547 | } 548 | }); 549 | } 550 | 551 | 552 | // Handles "/eventtypes" GET endpoint. Calls getEvents() to get available eventTypes. 553 | // Sends the response with error message or eventTypes 554 | app.get('/eventtypes', csrfProtection, function (req, res) { 555 | getEvents(function(allEventTypes) { 556 | res.send(allEventTypes); 557 | }); 558 | }); 559 | 560 | // error handler 561 | app.use(function (err, req, res, next) { 562 | if (err.code !== 'EBADCSRFTOKEN') return next(err) 563 | 564 | // handle CSRF token errors here 565 | res.status(403); 566 | res.send("CSRF token validation failed. Suspicious request!!"); 567 | }) 568 | 569 | // Handles invalid URL 570 | app.use((req, res) => { 571 | res.status(404).send("

    Page Not Found!

    "); 572 | }); 573 | 574 | // App listens on the configured port in config.js file 575 | server.listen(config.app.port, function () { 576 | console.log("Application listening on port ", server.address().port); 577 | }); 578 | -------------------------------------------------------------------------------- /public/javascripts/lib/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v4.1.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e(t.bootstrap={},t.jQuery,t.Popper)}(this,function(t,e,c){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)P(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
    ',trigger:"hover focus",title:"",delay:0,html:!(_e={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(de={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},pe="out",ve={HIDE:"hide"+he,HIDDEN:"hidden"+he,SHOW:(me="show")+he,SHOWN:"shown"+he,INSERTED:"inserted"+he,CLICK:"click"+he,FOCUSIN:"focusin"+he,FOCUSOUT:"focusout"+he,MOUSEENTER:"mouseenter"+he,MOUSELEAVE:"mouseleave"+he},Ee="fade",ye="show",Te=".tooltip-inner",Ce=".arrow",Ie="hover",Ae="focus",De="click",be="manual",Se=function(){function i(t,e){if("undefined"==typeof c)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=oe(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),oe(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(oe(this.getTipElement()).hasClass(ye))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),oe.removeData(this.element,this.constructor.DATA_KEY),oe(this.element).off(this.constructor.EVENT_KEY),oe(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&oe(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===oe(this.element).css("display"))throw new Error("Please use show on visible elements");var t=oe.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){oe(this.element).trigger(t);var n=oe.contains(this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=Cn.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&oe(i).addClass(Ee);var s="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,o=this._getAttachment(s);this.addAttachmentClass(o);var a=!1===this.config.container?document.body:oe(this.config.container);oe(i).data(this.constructor.DATA_KEY,this),oe.contains(this.element.ownerDocument.documentElement,this.tip)||oe(i).appendTo(a),oe(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new c(this.element,i,{placement:o,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:Ce},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){e._handlePopperPlacementChange(t)}}),oe(i).addClass(ye),"ontouchstart"in document.documentElement&&oe(document.body).children().on("mouseover",null,oe.noop);var l=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,oe(e.element).trigger(e.constructor.Event.SHOWN),t===pe&&e._leave(null,e)};if(oe(this.tip).hasClass(Ee)){var h=Cn.getTransitionDurationFromElement(this.tip);oe(this.tip).one(Cn.TRANSITION_END,l).emulateTransitionEnd(h)}else l()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=oe.Event(this.constructor.Event.HIDE),r=function(){e._hoverState!==me&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),oe(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(oe(this.element).trigger(i),!i.isDefaultPrevented()){if(oe(n).removeClass(ye),"ontouchstart"in document.documentElement&&oe(document.body).children().off("mouseover",null,oe.noop),this._activeTrigger[De]=!1,this._activeTrigger[Ae]=!1,this._activeTrigger[Ie]=!1,oe(this.tip).hasClass(Ee)){var s=Cn.getTransitionDurationFromElement(n);oe(n).one(Cn.TRANSITION_END,r).emulateTransitionEnd(s)}else r();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){oe(this.getTipElement()).addClass(ue+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||oe(this.config.template)[0],this.tip},t.setContent=function(){var t=oe(this.getTipElement());this.setElementContent(t.find(Te),this.getTitle()),t.removeClass(Ee+" "+ye)},t.setElementContent=function(t,e){var n=this.config.html;"object"==typeof e&&(e.nodeType||e.jquery)?n?oe(e).parent().is(t)||t.empty().append(e):t.text(oe(e).text()):t[n?"html":"text"](e)},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getAttachment=function(t){return _e[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)oe(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==be){var e=t===Ie?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Ie?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;oe(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}oe(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=h({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||oe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),oe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Ae:Ie]=!0),oe(e.getTipElement()).hasClass(ye)||e._hoverState===me?e._hoverState=me:(clearTimeout(e._timeout),e._hoverState=me,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===me&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||oe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),oe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Ae:Ie]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=pe,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===pe&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){return"number"==typeof(t=h({},this.constructor.Default,oe(this.element).data(),"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),Cn.typeCheckConfig(ae,t,this.constructor.DefaultType),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=oe(this.getTipElement()),e=t.attr("class").match(fe);null!==e&&0

    '}),He=h({},Nn.DefaultType,{content:"(string|element|function)"}),We="fade",xe=".popover-header",Ue=".popover-body",Ke={HIDE:"hide"+ke,HIDDEN:"hidden"+ke,SHOW:(Me="show")+ke,SHOWN:"shown"+ke,INSERTED:"inserted"+ke,CLICK:"click"+ke,FOCUSIN:"focusin"+ke,FOCUSOUT:"focusout"+ke,MOUSEENTER:"mouseenter"+ke,MOUSELEAVE:"mouseleave"+ke},Fe=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){we(this.getTipElement()).addClass(Le+"-"+t)},r.getTipElement=function(){return this.tip=this.tip||we(this.config.template)[0],this.tip},r.setContent=function(){var t=we(this.getTipElement());this.setElementContent(t.find(xe),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(Ue),e),t.removeClass(We+" "+Me)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=we(this.getTipElement()),e=t.attr("class").match(je);null!==e&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||t=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w("